/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ /*! \class SoTextureCoordinateBundle include/Inventor/bundles/SoTextureCoordinateBundle.h \brief The SoTextureCoordinateBundle class simplifies texture coordinate handling. \ingroup bundles It is unlikely that application programmers should need to know how to use this class, as it is mostly intended for internal use. */ // FIXME: document class better. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #include #include #define FLAG_FUNCTION 0x01 #define FLAG_NEEDCOORDS 0x02 #define FLAG_DEFAULT 0x04 #define FLAG_DIDPUSH 0x08 #define FLAG_3DTEXTURES 0x10 #define FLAG_DIDINITDEFAULT 0x20 #define FLAG_NEEDINDICES 0x40 /*! Constructor with \a action being the action applied to the node. The \a forRendering parameter must be \e TRUE if the bundle is to be used for sending texture coordinates to GL during rendering. The \a setUpDefault must be \e TRUE if default texture coordinates should be generated. */ SoTextureCoordinateBundle:: SoTextureCoordinateBundle(SoAction * const action, const SbBool forRendering, const SbBool setUpDefault) : SoBundle(action) { this->flags = 0; // // return immediately if there is no texture // SbBool needinit = SoTextureEnabledElement::get(this->state) || SoTexture3EnabledElement::get(this->state); SbBool glrender = forRendering || action->isOfType(SoGLRenderAction::getClassTypeId()); const SbBool * multienabled = NULL; int multimax = 0; if (glrender) { multienabled = SoMultiTextureEnabledElement::getEnabledUnits(this->state, multimax); } SbBool bumpenabled = glrender && (SoBumpMapElement::get(this->state) != NULL); if (!needinit && !multienabled && !bumpenabled) return; // It is safe to assume that shapenode is of type SoShape, so we // cast to SoShape before doing any operations on the node. this->shapenode = (SoShape *)action->getCurPathTail(); this->coordElt = SoTextureCoordinateElement::getInstance(this->state); switch (this->coordElt->getType()) { case SoTextureCoordinateElement::DEFAULT: this->initDefault(action, forRendering); break; case SoTextureCoordinateElement::EXPLICIT: if (this->coordElt->getNum() > 0) { this->flags |= FLAG_NEEDCOORDS; } else { this->initDefault(action, forRendering); } break; case SoTextureCoordinateElement::FUNCTION: this->flags |= FLAG_FUNCTION; this->flags |= FLAG_NEEDCOORDS; // not automatically generated break; case SoTextureCoordinateElement::TEXGEN: // texcoord won't be needed. This will only happen during SoGLRenderAction, // when GL generates texture coorinates. Therefore, we will not set // the FLAG_NEEDCOORDS here. this->flags |= FLAG_FUNCTION; if (!forRendering) { this->flags |= FLAG_NEEDCOORDS; } break; default: assert(0 && "unknown CoordType"); break; } this->glElt = NULL; if (glrender) { SbBool needindices = !this->isFunction(); if (multienabled) { const SoMultiTextureCoordinateElement * melem = SoMultiTextureCoordinateElement::getInstance(state); for (int i = 1; i <= multimax; i++) { if (multienabled[i]) { if ((melem->getType(i) == SoTextureCoordinateElement::DEFAULT) || ((melem->getType(state, i) == SoTextureCoordinateElement::EXPLICIT) && (melem->getNum(i) == 0))) { this->initDefaultMulti(action, i); } else if (!needindices && (melem->getType(state, i) == SoTextureCoordinateElement::EXPLICIT)) { needindices = TRUE; } } } } if (!needindices && this->isFunction()) { // check if bump mapping needs texture coordinate indices if (bumpenabled && (SoBumpMapCoordinateElement::getInstance(state)->getNum())) { needindices = TRUE; } } if (needindices && this->isFunction()) this->flags |= FLAG_NEEDINDICES; this->glElt = (SoGLTextureCoordinateElement*) this->coordElt; this->glElt->initMulti(action->getState()); } if ((this->flags & FLAG_DEFAULT) && !setUpDefault) { // FIXME: I couldn't be bothered to support this yet. It is for picking // optimization only, I think. pederb, 20000218 } } /*! Destructor. */ SoTextureCoordinateBundle::~SoTextureCoordinateBundle() { if (this->flags & FLAG_DIDPUSH) this->state->pop(); } /*! Returns \e TRUE if texture coordinates is needed during rendering. */ SbBool SoTextureCoordinateBundle::needCoordinates() const { return (this->flags & FLAG_NEEDCOORDS) != 0; } /*! Returns \e TRUE if a texture coordinate function should be used. */ SbBool SoTextureCoordinateBundle::isFunction() const { return (this->flags & FLAG_FUNCTION) != 0; } /*! Returns \e TRUE if isFunction() is \e TRUE, but the texture coordinate indices are needed either by bump mapping or by one of the other texture units. \since Coin 2.2 */ SbBool SoTextureCoordinateBundle::needIndices(void) const { return (this->flags & FLAG_NEEDINDICES) != 0; } /*! Returns the texture coordinates based on \a point and \a normal. Should only be used if SoTextureCoordinateBundle::isFunction() is \a TRUE. */ const SbVec4f & SoTextureCoordinateBundle::get(const SbVec3f &point, const SbVec3f &normal) { assert(this->coordElt != NULL && (this->flags & FLAG_FUNCTION)); if (this->flags & FLAG_DEFAULT) { SbVec3f pt; if (this->flags & FLAG_3DTEXTURES) { pt = point - this->defaultorigo; this->dummyInstance[2] = pt[2]/this->defaultsize[2]; } else { pt.setValue(point[this->defaultdim0]-this->defaultorigo[0], point[this->defaultdim1]-this->defaultorigo[1], 0.0f); } this->dummyInstance[0] = pt[0]/this->defaultsize[0]; this->dummyInstance[1] = pt[1]/this->defaultsize[1]; return this->dummyInstance; } else { return coordElt->get(point, normal); } } /*! Returns the texture coordinates at index \a index. Should only be used if SoTextureCoordinateBundle::isFunction() is \a FALSE. */ const SbVec4f & SoTextureCoordinateBundle::get(const int index) { assert(coordElt && !(this->flags & FLAG_FUNCTION)); return coordElt->get4(index); } /*! \fn void SoTextureCoordinateBundle::send(const int index) const Send texture coordinates to GL. Should only be used if SoTextureCoordinateBundle::isFunction() is \a FALSE. */ /*! \fn void SoTextureCoordinateBundle::send(const int index, const SbVec3f &point, const SbVec3f &normal) const Convenience function that will make it transparent to the rendering code if ordinary texture coordinates or function texture coordinates are used. */ // // initialize default texture coordinates for unit 0 // void SoTextureCoordinateBundle::initDefault(SoAction * const action, const SbBool forRendering) { this->flags |= FLAG_NEEDCOORDS; this->flags |= FLAG_DEFAULT; this->flags |= FLAG_FUNCTION; if (!(this->flags & FLAG_DIDPUSH)) { this->state->push(); this->flags |= FLAG_DIDPUSH; } // have coordelt generate the default texture coordinates using a // callback to this instance. SoTextureCoordinateElement::setFunction(this->state, this->shapenode, SoTextureCoordinateBundle::defaultCB, this); this->coordElt = SoTextureCoordinateElement::getInstance(this->state); if (!(this->flags & FLAG_DIDINITDEFAULT)) { this->initDefaultCallback(action); } } // // initialize default texture coordinates for unit > 0 // void SoTextureCoordinateBundle::initDefaultMulti(SoAction * action, const int unit) { this->flags |= FLAG_NEEDCOORDS; if (!(this->flags & FLAG_DIDPUSH)) { this->state->push(); this->flags |= FLAG_DIDPUSH; } SoMultiTextureCoordinateElement::setFunction(this->state, this->shapenode, unit, SoTextureCoordinateBundle::defaultCBMulti, this); if (!(this->flags & FLAG_DIDINITDEFAULT)) { this->initDefaultCallback(action); } } // // callback for default texture coordinates (for texture unit 0) // const SbVec4f & SoTextureCoordinateBundle::defaultCB(void * userdata, const SbVec3f & point, const SbVec3f & normal) { return ((SoTextureCoordinateBundle*)userdata)->get(point, normal); } // // callback for default texture coordinates (for texture units > 0) // const SbVec4f & SoTextureCoordinateBundle:: defaultCBMulti(void * userdata, const SbVec3f & point, const SbVec3f & normal) { SoTextureCoordinateBundle * thisp = (SoTextureCoordinateBundle*) userdata; SbVec3f pt; if (thisp->flags & FLAG_3DTEXTURES) { pt = point - thisp->defaultorigo; thisp->dummyInstance[2] = pt[2]/thisp->defaultsize[2]; } else { pt.setValue(point[thisp->defaultdim0]-thisp->defaultorigo[0], point[thisp->defaultdim1]-thisp->defaultorigo[1], 0.0f); } thisp->dummyInstance[0] = pt[0]/thisp->defaultsize[0]; thisp->dummyInstance[1] = pt[1]/thisp->defaultsize[1]; return thisp->dummyInstance; } // // Set up stuff needed for default texture coordinate mapping callback // void SoTextureCoordinateBundle::initDefaultCallback(SoAction * action) { this->flags |= FLAG_DIDINITDEFAULT; // // calculate needed stuff for default mapping // SbBox3f box; SbVec3f center; // this could be very slow, but if you're looking for speed, default // texture coordinate mapping shouldn't be used. We might optimize this // by using a SoTextureCoordinateCache soon though. pederb, 20000218 SoShape * shape = (SoShape *)this->shapenode; const SoBoundingBoxCache * bboxcache = shape->getBoundingBoxCache(); if (bboxcache && bboxcache->isValid(action->getState())) { box = bboxcache->getProjectedBox(); if (bboxcache->isCenterSet()) center = bboxcache->getCenter(); else center = box.getCenter(); } else { shape->computeBBox(action, box, center); } // just use som default values if the shape bbox is empty SbVec3f size(1.0f, 1.0f, 1.0f); SbVec3f origo(0.f, 0.0f, 0.0f); if (!box.isEmpty()) { box.getSize(size[0], size[1], size[2]); origo = box.getMin(); } // Map S,T,R to X,Y,Z for 3D texturing if (SoTexture3EnabledElement::get(this->state)) { this->flags |= FLAG_3DTEXTURES; this->defaultdim0 = 0; this->defaultdim1 = 1; this->defaultorigo[2] = origo[2]; this->defaultsize[2] = size[2]; } else { // 2D textures this->defaultsize[2] = 1.0f; this->flags &= ~FLAG_3DTEXTURES; // find the two biggest dimensions int smallest = 0; float smallval = size[0]; if (size[1] < smallval) { smallest = 1; smallval = size[1]; } if (size[2] < smallval) { smallest = 2; } this->defaultdim0 = (smallest + 1) % 3; this->defaultdim1 = (smallest + 2) % 3; if (size[this->defaultdim0] == size[this->defaultdim1]) { // FIXME: this is probably an OIV bug. The OIV man pages are not // clear on this point (surprise), but the VRML specification states // that if the two dimensions are equal, the ordering X>Y>Z should // be used. #if 0 // the correct way to do it if (this->defaultdim0 > this->defaultdim1) { SbSwap(this->defaultdim0, this->defaultdim1); } #else // the OIV way to do it. if (this->defaultdim0 < this->defaultdim1) { SbSwap(this->defaultdim0, this->defaultdim1); } #endif // OIV compatibility fix } else if (size[this->defaultdim0] < size[this->defaultdim1]) { SbSwap(this->defaultdim0, this->defaultdim1); } } this->defaultorigo[0] = origo[this->defaultdim0]; this->defaultorigo[1] = origo[this->defaultdim1]; this->defaultsize[0] = size[this->defaultdim0]; this->defaultsize[1] = size[this->defaultdim1]; // if bbox is empty in one dimension we just want to set it to // 0.0. The size should be set to 1.0 to avoid division by zero. for (int i = 0; i < 3; i++) { if (this->defaultsize[i] <= 0.0f) { this->defaultsize[i] = 1.0f; } } this->dummyInstance[2] = 0.0f; this->dummyInstance[3] = 1.0f; assert(this->defaultsize[0] > 0.0f); assert(this->defaultsize[1] > 0.0f); }