/**************************************************************************\
*
* 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