/**************************************************************************\ * * 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 SoVertexProperty SoVertexProperty.h Inventor/nodes/SoVertexProperty.h \brief The SoVertexProperty class collects the functionality of various appearance nodes. \ingroup nodes Instead of reading data from the current state stack of the scenegraph traversal, nodes inheriting SoVertexShape can be set up with an SoVertexProperty node in the SoVertexShape::vertexProperty field. Coordinates, normals, texture coordinates and material / color information will then be fetched from the vertexshape's SoVertexProperty node instead of from the state stack. The SoVertexProperty node provides fields for duplicating the functionality of all these other Inventor node types: SoCoordinate3, SoTextureCoordinate2, SoTextureCoordinate3, SoNormal, SoPackedColor, SoMaterialBinding and SoNormalBinding. The SoVertexProperty node was introduced fairly late in the design of the Inventor API by SGI. The idea behind it was to provide a means to specify the necessary data for vertexshape-derived nodes which would be more efficient to work with than fetching the data from the traversal state stack. In practice, the effect is not at all very noticable. Since the use of SoVertexProperty nodes in the SoVertexShape::vertexProperty field somewhat breaks with the basic design of the Open Inventor API (the SoVertexProperty data is not pushed to the traversal state stack), you might be better off design-wise by using the usual mechanisms, ie by setting up the individual nodes SoVertexProperty "collects". One of the drawbacks will for instance be that it's not possible to share \e parts of the SoVertexProperty node between several shapes, which is something that can easily be done when setting up individual state-changing nodes in the scenegraph. FILE FORMAT/DEFAULTS: \code VertexProperty { vertex [ ] normal [ ] texCoord [ ] orderedRGBA [ ] texCoord3 [ ] normalBinding PER_VERTEX_INDEXED materialBinding OVERALL } \endcode \since Inventor 2.1 \sa SoVertexShape \sa SoCoordinate3, SoTextureCoordinate2, SoTextureCoordinate3, SoNormal \sa SoPackedColor \sa SoMaterialBinding, SoNormalBinding */ // FIXME: should have a usage-example in the class-doc. 20020109 mortene. // FIXME: this class is really not optimally supported in Coin. Each // vertex shape node should support this node with internal handling // to harvest the expected efficiency gains. Right now we just push // its values on the stack. ????-??-?? pederb. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../misc/SoVBO.h" #include "../misc/PointerMap.h" /*! \enum SoVertexProperty::Binding The binding types available for our SoVertexProperty::normalBinding and SoVertexProperty::materialBinding fields. For a detailed explanation of each of the enumeration value binding types, see the documentation of the SoMaterialBinding node. */ /*! \var SoMFVec3f SoVertexProperty::vertex This field sets up vertex coordinates in the same manner as SoCoordinate3::point. By default the field contains no coordinates. \sa SoCoordinate3 */ /*! \var SoMFVec2f SoVertexProperty::texCoord Same functionality as SoTextureCoordinate2::point. By default the field contains no coordinates. \sa SoTextureCoordinate2 */ // FIXME: this field was added between TGS Inventor 2.5 and 2.6, and // between Coin 1.0 and Coin 2.0. This means it must get special // handling when exporting .iv-files, with regard to what header we // can put on the output. See also item #003 in the Coin/docs/todo.txt // file. 20030519 mortene. /*! \var SoMFVec3f SoVertexProperty::texCoord3 Same functionality as SoTextureCoordinate3::point. By default the field contains no coordinates. \sa SoTextureCoordinate3 \since Coin 2.0 \since TGS Inventor 2.6 */ /*! \var SoMFVec3f SoVertexProperty::normal This field defines a set of normal vectors in the same manner as SoNormal::vector. By default the field contains no vectors. \sa SoNormal */ /*! \var SoSFEnum SoVertexProperty::normalBinding Defines how to bind the normals specified in the SoVertexProperty::normal set to the parts of the "owner" shape. Must be one of the values in the SoVertexProperty::Binding enum. Default value of the field is SoVertexProperty::PER_VERTEX_INDEXED. \sa SoNormalBinding */ /*! \var SoMFUInt32 SoVertexProperty::orderedRGBA A set of "packed" 32-bit diffusecolor plus transparency values. Works in the same manner as the SoPackedColor::orderedRGBA field. By default the field has no data. \sa SoPackedColor */ /*! \var SoSFEnum SoVertexProperty::materialBinding Defines how to bind the colorvalues specified in the SoVertexProperty::orderedRGBA set to the parts of the "owner" shape. Must be one of the values in the SoVertexProperty::Binding enum. Default value of the field is SoVertexProperty::OVERALL. \sa SoMaterialBinding */ class SoVertexPropertyP { public: SoVertexPropertyP(void) : vertexvbo(NULL), normalvbo(NULL), texcoordvbo(NULL), colorvbo(NULL) { this->checktransparent = FALSE; this->transparent = FALSE; } ~SoVertexPropertyP() { delete this->vertexvbo; delete this->normalvbo; delete this->texcoordvbo; delete this->colorvbo; } SoVertexProperty * master; SbBool transparent; SbBool checktransparent; SoVBO * vertexvbo; SoVBO * normalvbo; SoVBO * texcoordvbo; SoVBO * colorvbo; }; #define PRIVATE(obj) ((SoVertexPropertyP*) PointerMap::get(obj)) SO_NODE_SOURCE(SoVertexProperty); /*! Constructor. */ SoVertexProperty::SoVertexProperty(void) { PointerMap::add(this, new SoVertexPropertyP); SO_NODE_INTERNAL_CONSTRUCTOR(SoVertexProperty); SO_NODE_ADD_FIELD(vertex, (0)); SO_NODE_ADD_FIELD(normal, (0)); SO_NODE_ADD_FIELD(texCoord, (0)); SO_NODE_ADD_FIELD(orderedRGBA, (0)); // FIXME: this field was added in TGS Inventor 2.6 and Coin // 2.0. This should have repercussions for file format // compatibility. 20030227 mortene. SO_NODE_ADD_FIELD(texCoord3, (0)); // Make multivalue fields empty. this->vertex.setNum(0); this->texCoord.setNum(0); this->texCoord3.setNum(0); this->normal.setNum(0); this->orderedRGBA.setNum(0); // So they are not written in their default state on SoWriteAction // traversal. this->vertex.setDefault(TRUE); this->texCoord.setDefault(TRUE); this->texCoord3.setDefault(TRUE); this->normal.setDefault(TRUE); this->orderedRGBA.setDefault(TRUE); SO_NODE_ADD_FIELD(normalBinding, (SoVertexProperty::PER_VERTEX_INDEXED)); SO_NODE_ADD_FIELD(materialBinding, (SoVertexProperty::OVERALL)); SO_NODE_DEFINE_ENUM_VALUE(Binding, OVERALL); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_PART); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_PART_INDEXED); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_FACE); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_FACE_INDEXED); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_VERTEX); SO_NODE_DEFINE_ENUM_VALUE(Binding, PER_VERTEX_INDEXED); SO_NODE_SET_SF_ENUM_TYPE(normalBinding, Binding); SO_NODE_SET_SF_ENUM_TYPE(materialBinding, Binding); } /*! Destructor. */ SoVertexProperty::~SoVertexProperty() { SoVertexPropertyP * pimpl = PRIVATE(this); PointerMap::remove(this); delete pimpl; } // Documented in superclass. void SoVertexProperty::initClass(void) { SO_NODE_INTERNAL_INIT_CLASS(SoVertexProperty, SO_FROM_INVENTOR_2_1); SO_ENABLE(SoGetBoundingBoxAction, SoCoordinateElement); SO_ENABLE(SoGLRenderAction, SoGLCoordinateElement); SO_ENABLE(SoGLRenderAction, SoMaterialBindingElement); SO_ENABLE(SoGLRenderAction, SoNormalBindingElement); SO_ENABLE(SoGLRenderAction, SoGLNormalElement); SO_ENABLE(SoGLRenderAction, SoGLTextureCoordinateElement); SO_ENABLE(SoPickAction, SoCoordinateElement); SO_ENABLE(SoPickAction, SoMaterialBindingElement); SO_ENABLE(SoPickAction, SoNormalBindingElement); SO_ENABLE(SoPickAction, SoNormalElement); SO_ENABLE(SoPickAction, SoTextureCoordinateElement); SO_ENABLE(SoCallbackAction, SoCoordinateElement); SO_ENABLE(SoCallbackAction, SoMaterialBindingElement); SO_ENABLE(SoCallbackAction, SoNormalBindingElement); SO_ENABLE(SoCallbackAction, SoNormalElement); SO_ENABLE(SoCallbackAction, SoTextureCoordinateElement); SO_ENABLE(SoGetPrimitiveCountAction, SoCoordinateElement); SO_ENABLE(SoGetPrimitiveCountAction, SoMaterialBindingElement); SO_ENABLE(SoGetPrimitiveCountAction, SoNormalBindingElement); SO_ENABLE(SoGetPrimitiveCountAction, SoNormalElement); SO_ENABLE(SoGetPrimitiveCountAction, SoTextureCoordinateElement); } // Documented in superclass. void SoVertexProperty::getBoundingBox(SoGetBoundingBoxAction * action) { if (vertex.getNum() > 0) { SoCoordinateElement::set3(action->getState(), this, vertex.getNum(), vertex.getValues(0)); } } // Documented in superclass. void SoVertexProperty::GLRender(SoGLRenderAction * action) { SoVertexProperty::doAction(action); } // Documented in superclass. void SoVertexProperty::doAction(SoAction *action) { SoVertexPropertyP * pimpl = PRIVATE(this); SoState * state = action->getState(); uint32_t overrideflags = SoOverrideElement::getFlags(state); #define TEST_OVERRIDE(bit) ((SoOverrideElement::bit & overrideflags) != 0) SbBool glrender = action->isOfType(SoGLRenderAction::getClassTypeId()); if (pimpl->checktransparent) { pimpl->checktransparent = FALSE; pimpl->transparent = FALSE; int n = this->orderedRGBA.getNum(); for (int i = 0; i < n; i++) { if ((this->orderedRGBA[i] & 0xff) != 0xff) { pimpl->transparent = TRUE; break; } } } if (glrender) SoBase::staticDataLock(); int num = this->vertex.getNum(); const int numvertex = num; #ifdef COIN_NEXT_MINOR const SbBool shouldcreatevbo = glrender ? SoGLVBOElement::shouldCreateVBO(state, numvertex) : FALSE; #else // COIN_NEXT_MINOR const SbBool shouldcreatevbo = FALSE; #endif // ! COIN_NEXT_MINOR if (num > 0) { SoCoordinateElement::set3(state, this, num, this->vertex.getValues(0)); SbBool setvbo = FALSE; #ifdef COIN_NEXT_MINOR if (glrender) { if (shouldcreatevbo) { SbBool dirty = FALSE; setvbo = TRUE; if (pimpl->vertexvbo == NULL) { pimpl->vertexvbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW); dirty = TRUE; } else if (pimpl->vertexvbo->getBufferDataId() != this->getNodeId()) { dirty = TRUE; } if (dirty) { pimpl->vertexvbo->setBufferData(this->vertex.getValues(0), num*sizeof(SbVec3f), this->getNodeId()); } } else if (pimpl->vertexvbo && pimpl->vertexvbo->getBufferDataId()) { // clear buffers to deallocate VBO memory pimpl->vertexvbo->setBufferData(NULL, 0, 0); } if (setvbo) { SoGLVBOElement::setVertexVBO(state, pimpl->vertexvbo); } } #endif // COIN_NEXT_MINOR } num = this->texCoord3.getNum(); if (num > 0) { if (glrender) { if (SoGLTexture3EnabledElement::get(state)) { // it's important to call this _before_ setting the coordinates // on the state. SoGLTextureCoordinateElement::setTexGen(state, this, NULL); } } SoTextureCoordinateElement::set3(state, this, num, this->texCoord3.getValues(0)); #ifdef COIN_NEXT_MINOR if (glrender) { SbBool setvbo = FALSE; if ((num == numvertex) && shouldcreatevbo) { SbBool dirty = FALSE; setvbo = TRUE; if (pimpl->texcoordvbo == NULL) { pimpl->texcoordvbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW); dirty = TRUE; } else if (pimpl->texcoordvbo->getBufferDataId() != this->getNodeId()) { dirty = TRUE; } if (dirty) { pimpl->texcoordvbo->setBufferData(this->texCoord3.getValues(0), num*sizeof(SbVec3f), this->getNodeId()); } } else if (pimpl->texcoordvbo && pimpl->texcoordvbo->getBufferDataId()) { // clear buffers to deallocate VBO memory pimpl->texcoordvbo->setBufferData(NULL, 0, 0); } if (setvbo) { SoGLVBOElement::setTexCoordVBO(state, 0, pimpl->texcoordvbo); } } #endif // COIN_NEXT_MINOR } else { num = this->texCoord.getNum(); if (num > 0) { if (glrender) { // it's important to call this _before_ setting the coordinates // on the state. SoGLTextureCoordinateElement::setTexGen(state, this, NULL); } SoTextureCoordinateElement::set2(state, this, num, this->texCoord.getValues(0)); #ifdef COIN_NEXT_MINOR if (glrender) { SbBool setvbo = FALSE; if ((num == numvertex) && shouldcreatevbo) { SbBool dirty = FALSE; setvbo = TRUE; if (pimpl->texcoordvbo == NULL) { pimpl->texcoordvbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW); dirty = TRUE; } else if (pimpl->texcoordvbo->getBufferDataId() != this->getNodeId()) { dirty = TRUE; } if (dirty) { pimpl->texcoordvbo->setBufferData(this->texCoord.getValues(0), num*sizeof(SbVec2f), this->getNodeId()); } } else if (pimpl->texcoordvbo && pimpl->texcoordvbo->getBufferDataId()) { // clear buffers to deallocate VBO memory pimpl->texcoordvbo->setBufferData(NULL, 0, 0); } if (setvbo) { SoGLVBOElement::setTexCoordVBO(state, 0, pimpl->texcoordvbo); } } #endif // COIN_NEXT_MINOR } } num = this->normal.getNum(); if (num > 0 && !TEST_OVERRIDE(NORMAL_VECTOR)) { SoNormalElement::set(state, this, num, this->normal.getValues(0)); if (this->isOverride()) { SoOverrideElement::setNormalVectorOverride(state, this, TRUE); } SbBool setvbo = FALSE; #ifdef COIN_NEXT_MINOR if (glrender) { if ((num == numvertex) && shouldcreatevbo) { SbBool dirty = FALSE; setvbo = TRUE; if (pimpl->normalvbo == NULL) { pimpl->normalvbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW); dirty = TRUE; } else if (pimpl->normalvbo->getBufferDataId() != this->getNodeId()) { dirty = TRUE; } if (dirty) { pimpl->normalvbo->setBufferData(this->normal.getValues(0), num*sizeof(SbVec3f), this->getNodeId()); } } else if (pimpl->normalvbo && pimpl->normalvbo->getBufferDataId()) { // clear buffers to deallocate VBO memory pimpl->normalvbo->setBufferData(NULL, 0, 0); } if (setvbo) { SoGLVBOElement::setNormalVBO(state, pimpl->normalvbo); } } #endif // COIN_NEXT_MINOR } if (this->normal.getNum() > 0 && !TEST_OVERRIDE(NORMAL_BINDING)) { SoNormalBindingElement::set(state, this, (SoNormalBindingElement::Binding) this->normalBinding.getValue()); if (this->isOverride()) { SoOverrideElement::setNormalBindingOverride(state, this, TRUE); } } num = this->orderedRGBA.getNum(); if (num > 0 && !TEST_OVERRIDE(DIFFUSE_COLOR)) { SoLazyElement::setPacked(state, this, num, this->orderedRGBA.getValues(0), pimpl->transparent); if (this->isOverride()) { SoOverrideElement::setDiffuseColorOverride(state, this, TRUE); } #ifdef COIN_NEXT_MINOR if (glrender) { SbBool setvbo = FALSE; if ((num == numvertex) && shouldcreatevbo) { SbBool dirty = FALSE; setvbo = TRUE; if (pimpl->colorvbo == NULL) { pimpl->colorvbo = new SoVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW); dirty = TRUE; } else if (pimpl->colorvbo->getBufferDataId() != this->getNodeId()) { dirty = TRUE; } if (dirty) { if (coin_host_get_endianness() == COIN_HOST_IS_BIGENDIAN) { pimpl->colorvbo->setBufferData(this->orderedRGBA.getValues(0), num*sizeof(uint32_t), this->getNodeId()); } else { const uint32_t * src = this->orderedRGBA.getValues(0); uint32_t * dst = (uint32_t*) pimpl->colorvbo->allocBufferData(num*sizeof(uint32_t), this->getNodeId()); for (int i = 0; i < num; i++) { uint32_t tmp = src[i]; dst[i] = (tmp << 24) | ((tmp & 0xff00) << 8) | ((tmp & 0xff0000) >> 8) | (tmp >> 24); } } } } else if (pimpl->colorvbo) { pimpl->colorvbo->setBufferData(NULL, 0, 0); } if (setvbo) { SoGLVBOElement::setColorVBO(state, pimpl->colorvbo); } } #endif // COIN_NEXT_MINOR } if (glrender) SoBase::staticDataUnlock(); if (this->orderedRGBA.getNum() && !TEST_OVERRIDE(MATERIAL_BINDING)) { SoMaterialBindingElement::set(state, this, (SoMaterialBindingElement::Binding) this->materialBinding.getValue()); if (this->isOverride()) { SoOverrideElement::setMaterialBindingOverride(state, this, TRUE); } } #undef TEST_OVERRIDE } // Documented in superclass. void SoVertexProperty::callback(SoCallbackAction *action) { SoVertexProperty::doAction((SoAction*)action); } // Documented in superclass. void SoVertexProperty::pick(SoPickAction *action) { SoVertexProperty::doAction((SoAction*)action); } // Documented in superclass. void SoVertexProperty::getPrimitiveCount(SoGetPrimitiveCountAction *action) { SoVertexProperty::doAction((SoAction*)action); } // Documented in superclass. Overridden to check for transparency when // orderedRGBA changes. void SoVertexProperty::notify(SoNotList *list) { SoField *f = list->getLastField(); if (f == &this->orderedRGBA) { PRIVATE(this)->checktransparent = TRUE; } inherited::notify(list); } #undef PRIVATE