/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2005 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 for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * . * \**************************************************************************/ /*! \page vbo_rendering Vertex array and VBO rendering in Coin Coin 2.5 added improved support for OpenGL vertex array and VBO rendering. This might lead to major rendering performance improvements compared to the old rendering code. The new rendering code has been added for the SoIndexedFaceSet, SoVRMLIndexedFaceSet, SoIndexedLineSet, SoVRMLIndexedLineSet, SoPointSet and SoVRMLPointSet nodes. To take advantage of the improved performance vertex array and VBO rendering yields, you'll need to organize your vertex data in a way that makes it possible to render it with OpenGL vertex arrays. OpenGL vertex array rendering does not support multiple index arrays, so all your vertex data (coordinates, normals, colors and texture coordinates) must use the same index array; or use OVERALL binding. For the indexed nodes, this means that PER_VERTEX_INDEXED and OVERALL are the only supported bindings for materials, normals and texture coordinates. When PER_VERTEX_INDEXED binding is used, the corresponding index field should by empty. This will signal the shape to use the coordIndex field for indices. Below is an example scene graph that will be rendered using vertex arrays: \verbatim NormalBinding { value PER_VERTEX_INDEXED } Coordinate3 { point [ 0 0 0, # 0 1 0 0, # 1 2 0 0, # 2 0 1 0, # 3 1 1 0, # 4 2 1 0, # 5 0 2 0, # 6 1 2 0, # 7 2 2 0, # 8 2 0 0, # 9 2 0 -1, # 10 2 1 0, # 11 2 1 -1, # 12 2 2 0, # 13 2 2 -1 # 14 ] } Normal { vector [ 0 0 1, # 0 0 0 1, # 1 0 0 1, # 2 0 0 1, # 3 0 0 1, # 4 0 0 1, # 5 0 0 1, # 6 0 0 1, # 7 0 0 1, # 8 1 0 0, # 9 1 0 0, # 10 1 0 0, # 11 1 0 0, # 12 1 0 0, # 13 1 0 0 # 14 ] } IndexedFaceSet { coordIndex [ 0, 1, 4, 3, -1, 1, 2, 5, 4, -1, 3, 4, 7, 6, -1, 4, 5, 8, 7, -1, 9, 10, 12, 11, -1, 11, 12, 14, 13, -1 ] normalIndex [ ] # = use coordIndex } \endverbatim Please note that since only one index array can be used, it might be necessary to supply duplicate normals and coordinates to meet this requirement. Also, if normals are needed, you have to supply them. A shape with autogenerated normals can't be rendered using vertex arrays (since a single coordinate might get multiple normals). The PointSet nodes can always be rendered using vertex arrays since these nodes haven't got index arrays, and the only bindings supported are PER_VERTEX and OVERALL. If it's inconvenient to create vertex array ready scene graphs directly from your application, it's also possible to use SoReorganizeAction to reorganize the geometry before rendering. */ /*! \class SoReorganizeAction Inventor/include/SoReorganizeAction.h \brief The SoReorganizeAction class reorganizes your scene graph to optimize traversal/rendering. Note. This is work-in-progress. pederb, 2005-04-05. The code below is an example of a program that applies an SoReorganizeAction on a scene graph, converting all shapes into shapes that can be rendered using vertex array or VBO rendering. \verbatim #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void strip_node(SoType type, SoNode * root) { SoSearchAction sa; sa.setType(type); sa.setSearchingAll(TRUE); sa.setInterest(SoSearchAction::ALL); sa.apply(root); SoPathList & pl = sa.getPaths(); for (int i = 0; i < pl.getLength(); i++) { SoFullPath * p = (SoFullPath*) pl[i]; if (p->getTail()->isOfType(type)) { SoGroup * g = (SoGroup*) p->getNodeFromTail(1); g->removeChild(p->getIndexFromTail(0)); } } sa.reset(); } int main(int argc, char ** argv ) { if (argc < 3) { fprintf(stderr,"Usage: reorganize [nostrip]\n"); return -1; } SbBool strip = TRUE; if (argc > 3) { if (strcmp(argv[3], "nostrip") == 0) strip = FALSE; else { fprintf(stderr,"Usage: reorganize [nostrip]\n"); return -1; } } SoDB::init(); SoInteraction::init(); SoInput input; SbBool ok = input.openFile(argv[1]); if (!ok) { fprintf(stderr,"Unable to open file.\n"); return -1; } SoSeparator * root = SoDB::readAll(&input); SbBool vrml1 = input.isFileVRML1(); SbBool vrml2 = input.isFileVRML2(); if (vrml2) { fprintf(stderr,"VRML2 not supported yet\n"); return -1; } if (!root) { fprintf(stderr,"Unable to read file.\n"); return -1; } root->ref(); fprintf(stderr,"Applying SoReorganizeAction..."); SoReorganizeAction reorg; reorg.apply(root); fprintf(stderr,"done\n"); SoOutput out; if (out.openFile(argv[2])) { if (strip) { // strip coord3, texcoord and normal nodes fprintf(stderr,"stripping old nodes from scene graph\n"); strip_node(SoCoordinate3::getClassTypeId(), root); strip_node(SoCoordinate4::getClassTypeId(), root); strip_node(SoNormal::getClassTypeId(), root); strip_node(SoTextureCoordinate2::getClassTypeId(), root); } fprintf(stderr,"writing target\n"); SoWriteAction wa(&out); wa.apply(root); } root->unref(); return 0; } // main() \endvarbatim \since Coin 2.5 */ #include #include #ifdef COIN_NEXT_MINOR #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // COIN_STUB() class SoReorganizeActionP { public: SoReorganizeActionP(SoReorganizeAction * masterin) : master(masterin), gennormals(TRUE), gentexcoords(TRUE), gentristrips(FALSE), genvp(FALSE), matchidx(TRUE), cbaction(SbViewportRegion(640, 480)), pvcache(NULL) { cbaction.addTriangleCallback(SoVertexShape::getClassTypeId(), triangle_cb, this); cbaction.addLineSegmentCallback(SoVertexShape::getClassTypeId(), line_segment_cb, this); cbaction.addTriangleCallback(SoVRMLIndexedFaceSet::getClassTypeId(), triangle_cb, this); cbaction.addLineSegmentCallback(SoVRMLIndexedLineSet::getClassTypeId(), line_segment_cb, this); cbaction.addPreCallback(SoVertexShape::getClassTypeId(), pre_shape_cb, this); cbaction.addPostCallback(SoVertexShape::getClassTypeId(), post_shape_cb, this); cbaction.addPreCallback(SoVRMLIndexedFaceSet::getClassTypeId(), pre_shape_cb, this); cbaction.addPostCallback(SoVRMLIndexedFaceSet::getClassTypeId(), post_shape_cb, this); cbaction.addPreCallback(SoVRMLIndexedLineSet::getClassTypeId(), pre_shape_cb, this); cbaction.addPostCallback(SoVRMLIndexedLineSet::getClassTypeId(), post_shape_cb, this); } SoReorganizeAction * master; SbBool gennormals; SbBool gentexcoords; SbBool gentristrips; SbBool genvp; SbBool matchidx; SbList needtexcoords; int lastneeded; int numtriangles; int numlines; int numpoints; SbBool isvrml; SbBool didinit; SbBool hastexture; SbColor4f diffusecolor; SbBool lighting; SbBool normalsonstate; SoCallbackAction cbaction; SoSearchAction sa; SoPrimitiveVertexCache * pvcache; static SoCallbackAction::Response pre_shape_cb(void * userdata, SoCallbackAction * action, const SoNode * node); static SoCallbackAction::Response post_shape_cb(void * userdata, SoCallbackAction * action, const SoNode * node); static void triangle_cb(void * userdata, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, const SoPrimitiveVertex * v3); static void line_segment_cb(void * userdata, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2); SbBool initShape(SoCallbackAction * action); void replaceNode(SoFullPath * path); void replaceIfs(SoFullPath * path); void replaceVrmlIfs(SoFullPath * path); void replaceIls(SoFullPath * path); void replaceVrmlIls(SoFullPath * path); SoVertexProperty * createVertexProperty(const SbBool forlines); }; #define PRIVATE(obj) obj->pimpl SO_ACTION_SOURCE(SoReorganizeAction); // Override from parent class. void SoReorganizeAction::initClass(void) { SO_ACTION_INTERNAL_INIT_CLASS(SoReorganizeAction, SoSimplifyAction); } /*! A constructor. */ SoReorganizeAction::SoReorganizeAction(SoSimplifier * simplifier) { PRIVATE(this) = new SoReorganizeActionP(this); SO_ACTION_CONSTRUCTOR(SoReorganizeAction); } /*! The destructor. */ SoReorganizeAction::~SoReorganizeAction(void) { delete PRIVATE(this); } SoSeparator * SoReorganizeAction::getSimplifiedSceneGraph(void) const { return NULL; } void SoReorganizeAction::generateNormals(SbBool onoff) { PRIVATE(this)->gennormals = onoff; } SbBool SoReorganizeAction::areNormalGenerated(void) const { return PRIVATE(this)->gennormals; } void SoReorganizeAction::generateTriangleStrips(SbBool onoff) { PRIVATE(this)->gentristrips = onoff; } SbBool SoReorganizeAction::areTriangleStripGenerated(void) const { return PRIVATE(this)->gentristrips; } void SoReorganizeAction::generateTexCoords(SbBool onoff) { PRIVATE(this)->gentexcoords = onoff; } SbBool SoReorganizeAction::areTexCoordsGenerated(void) const { return PRIVATE(this)->gentexcoords; } void SoReorganizeAction::generateVPNodes(SbBool onoff) { PRIVATE(this)->genvp = onoff; } SbBool SoReorganizeAction::areVPNodesGenerated(void) { return PRIVATE(this)->genvp; } void SoReorganizeAction::matchIndexArrays(SbBool onoff) { PRIVATE(this)->matchidx = onoff; } SbBool SoReorganizeAction::areIndexArraysMatched(void) const { return PRIVATE(this)->matchidx; } SoSimplifier * SoReorganizeAction::getSimplifier(void) const { return NULL; } void SoReorganizeAction::apply(SoNode * root) { int i; PRIVATE(this)->sa.setType(SoVertexShape::getClassTypeId()); PRIVATE(this)->sa.setSearchingAll(TRUE); PRIVATE(this)->sa.setInterest(SoSearchAction::ALL); PRIVATE(this)->sa.apply(root); SoPathList & pl = PRIVATE(this)->sa.getPaths(); for (i = 0; i < pl.getLength(); i++) { this->apply(pl[i]); } PRIVATE(this)->sa.reset(); PRIVATE(this)->sa.setType(SoVRMLIndexedFaceSet::getClassTypeId()); PRIVATE(this)->sa.setSearchingAll(TRUE); PRIVATE(this)->sa.setInterest(SoSearchAction::ALL); PRIVATE(this)->sa.apply(root); SoPathList & pl2 = PRIVATE(this)->sa.getPaths(); for (i = 0; i < pl2.getLength(); i++) { this->apply(pl2[i]); } PRIVATE(this)->sa.reset(); PRIVATE(this)->sa.setType(SoVRMLIndexedLineSet::getClassTypeId()); PRIVATE(this)->sa.setSearchingAll(TRUE); PRIVATE(this)->sa.setInterest(SoSearchAction::ALL); PRIVATE(this)->sa.apply(root); SoPathList & pl3 = PRIVATE(this)->sa.getPaths(); for (i = 0; i < pl3.getLength(); i++) { this->apply(pl3[i]); } PRIVATE(this)->sa.reset(); } void SoReorganizeAction::apply(SoPath * path) { PRIVATE(this)->cbaction.apply(path); PRIVATE(this)->replaceNode((SoFullPath*) path); } void SoReorganizeAction::apply(const SoPathList & pathlist, SbBool obeysrules) { for (int i = 0; i < pathlist.getLength(); i++) { this->apply(pathlist[i]); } } void SoReorganizeAction::startReport(const char * msg) { COIN_STUB(); } void SoReorganizeAction::finishReport(void) { COIN_STUB(); } // Documented in superclass. void SoReorganizeAction::beginTraversal(SoNode * /* node */) { assert(0 && "should never get here"); } SoCallbackAction::Response SoReorganizeActionP::pre_shape_cb(void * userdata, SoCallbackAction * action, const SoNode * node) { SoReorganizeActionP * thisp = (SoReorganizeActionP*) userdata; thisp->didinit = FALSE; thisp->isvrml = node->isOfType(SoVRMLGeometry::getClassTypeId()); thisp->numtriangles = 0; thisp->numpoints = 0; thisp->numlines = 0; return SoCallbackAction::CONTINUE; } SoCallbackAction::Response SoReorganizeActionP::post_shape_cb(void * userdata, SoCallbackAction * action, const SoNode * node) { SoReorganizeActionP * thisp = (SoReorganizeActionP*) userdata; #if 0 // debug fprintf(stderr,"shape: %s, numtri: %d, pvcache: %p\n", node->getTypeId().getName().getString(), thisp->numtriangles, thisp->pvcache); #endif // debug return SoCallbackAction::CONTINUE; } void SoReorganizeActionP::triangle_cb(void * userdata, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2, const SoPrimitiveVertex * v3) { SoReorganizeActionP * thisp = (SoReorganizeActionP*) userdata; if (!thisp->didinit) { if (thisp->initShape(action)) { assert(thisp->pvcache == NULL); thisp->pvcache = new SoPrimitiveVertexCache(action->getState()); thisp->pvcache->ref(); } } thisp->numtriangles++; if (thisp->pvcache) { thisp->pvcache->addTriangle(v1, v2, v3); } } void SoReorganizeActionP::line_segment_cb(void * userdata, SoCallbackAction * action, const SoPrimitiveVertex * v1, const SoPrimitiveVertex * v2) { SoReorganizeActionP * thisp = (SoReorganizeActionP*) userdata; if (!thisp->didinit) { if (thisp->initShape(action)) { assert(thisp->pvcache == NULL); thisp->pvcache = new SoPrimitiveVertexCache(action->getState()); thisp->pvcache->ref(); } } thisp->numlines++; if (thisp->pvcache) { thisp->pvcache->addLine(v1, v2); } } SbBool SoReorganizeActionP::initShape(SoCallbackAction * action) { this->didinit = TRUE; SoState * state = action->getState(); SbBool canrenderasvertexarray = TRUE; unsigned int shapeflags = SoShapeStyleElement::get(state)->getFlags(); this->lighting = SoLightModelElement::get(state) != SoLightModelElement::BASE_COLOR; this->normalsonstate = SoNormalElement::getInstance(state)->getNum() > 0; SbBool texture0enabled = SoTextureEnabledElement::get(state) != FALSE; this->hastexture = texture0enabled; int lastenabled; const SbBool * enabledunits = SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled); this->needtexcoords.truncate(0); this->needtexcoords.append(FALSE); if (shapeflags & (SoShapeStyleElement::BUMPMAP| SoShapeStyleElement::BBOXCMPLX| SoShapeStyleElement::INVISIBLE| SoShapeStyleElement::BIGIMAGE)) { canrenderasvertexarray = FALSE; } if (canrenderasvertexarray && texture0enabled) { const SoTextureCoordinateElement * celem = (const SoTextureCoordinateElement *) SoTextureCoordinateElement::getInstance(state); switch (celem->getType()) { case SoTextureCoordinateElement::DEFAULT: case SoTextureCoordinateElement::EXPLICIT: this->needtexcoords[0] = TRUE; break; case SoTextureCoordinateElement::TEXGEN: // don't need texcoords for unit0 break; case SoTextureCoordinateElement::FUNCTION: this->needtexcoords[0] = TRUE; default: canrenderasvertexarray = FALSE; break; } } if (canrenderasvertexarray && enabledunits) { const SoMultiTextureCoordinateElement * melem = SoMultiTextureCoordinateElement::getInstance(state); for (int i = 1; i <= lastenabled; i++) { this->needtexcoords.append(FALSE); if (enabledunits[i]) { // FIXME: multitexturing is not supported yet, since it's not // supported by SoVertexProperty. We might fix this later by // inserting new nodes though. pederb, 2005-04-28 canrenderasvertexarray = FALSE; this->hastexture = TRUE; switch (melem->getType(i)) { case SoTextureCoordinateElement::DEFAULT: case SoTextureCoordinateElement::EXPLICIT: this->needtexcoords[i] = TRUE; break; case SoTextureCoordinateElement::TEXGEN: // don't need texcoords for unit i break; case SoTextureCoordinateElement::FUNCTION: this->needtexcoords[i] = TRUE; break; default: canrenderasvertexarray = FALSE; break; } } } } if (canrenderasvertexarray) { SbColor diffuse = SoLazyElement::getDiffuse(state, 0); float transp = SoLazyElement::getTransparency(state, 0); this->diffusecolor = SbColor4f(diffuse, 1.0f - transp); } return canrenderasvertexarray; } void SoReorganizeActionP::replaceNode(SoFullPath * path) { if (this->pvcache == NULL) return; this->pvcache->fit(); // needed to do optimize-sort of data if (this->pvcache->getNumIndices()) { if (this->isvrml) { this->replaceVrmlIfs(path); } else { this->replaceIfs(path); } } else if (this->pvcache->getNumLineIndices()) { if (this->isvrml) { this->replaceVrmlIls(path); } else { this->replaceIls(path); } } this->pvcache->unref(); this->pvcache = NULL; } SoVertexProperty * SoReorganizeActionP::createVertexProperty(const SbBool forlines) { SoVertexProperty * vp = new SoVertexProperty; vp->ref(); SoVertexProperty::Binding nbind = SoVertexProperty::PER_VERTEX_INDEXED; if (!this->lighting || (forlines && !this->normalsonstate)) { nbind = SoVertexProperty::OVERALL; } vp->normalBinding = nbind; int numv = this->pvcache->getNumVertices(); if (this->hastexture) { vp->texCoord.setNum(numv); SbVec2f * dst = vp->texCoord.startEditing(); const SbVec4f * src = this->pvcache->getTexCoordArray(); for (int i = 0; i < numv; i++) { SbVec4f tmp = src[i]; if (tmp[3] != 0.0f) { tmp[0] /= tmp[3]; tmp[1] /= tmp[3]; } dst[i][0] = tmp[0]; dst[i][1] = tmp[1]; } vp->texCoord.finishEditing(); } vp->vertex.setValues(0, numv, this->pvcache->getVertexArray()); if (nbind == SoVertexProperty::PER_VERTEX_INDEXED) { vp->normal.setValues(0, numv, this->pvcache->getNormalArray()); } vp->materialBinding = SoVertexProperty::OVERALL; vp->orderedRGBA = this->diffusecolor.getPackedValue(); if (this->pvcache->colorPerVertex()) { vp->materialBinding = SoVertexProperty::PER_VERTEX_INDEXED; uint8_t * src = (uint8_t*) this->pvcache->getColorArray(); vp->orderedRGBA.setNum(numv); uint32_t * dst = vp->orderedRGBA.startEditing(); for (int i = 0; i < numv; i++) { dst[i] = (src[0]<<24)|(src[1]<<16)|(src[2]<<8)|src[3]; src += 4; } vp->orderedRGBA.finishEditing(); } vp->unrefNoDelete(); return vp; } void SoReorganizeActionP::replaceIfs(SoFullPath * path) { SoNode * parent = path->getNodeFromTail(1); if (!parent->isOfType(SoGroup::getClassTypeId())) { return; } SoVertexProperty * vp = this->createVertexProperty(FALSE); SoIndexedFaceSet * ifs = new SoIndexedFaceSet; ifs->ref(); ifs->vertexProperty = vp; ifs->normalIndex.setNum(0); ifs->materialIndex.setNum(0); ifs->textureCoordIndex.setNum(0); int numtri = this->pvcache->getNumIndices() / 3; const GLint * indices = this->pvcache->getIndices(); ifs->coordIndex.setNum(numtri * 4); int32_t * ptr = ifs->coordIndex.startEditing(); for (int i = 0; i < numtri; i++) { *ptr++ = (int32_t) indices[i*3]; *ptr++ = (int32_t) indices[i*3+1]; *ptr++ = (int32_t) indices[i*3+2]; *ptr++ = -1; } ifs->coordIndex.finishEditing(); int idx = path->getIndexFromTail(0); path->pop(); SoGroup * g = (SoGroup*)parent; g->replaceChild(idx, ifs); path->push(idx); ifs->unrefNoDelete(); } void SoReorganizeActionP::replaceVrmlIfs(SoFullPath * path) { SoNode * parent = path->getNodeFromTail(1); if (!parent->isOfType(SoGroup::getClassTypeId()) && !parent->isOfType(SoVRMLShape::getClassTypeId())) { return; } SoVRMLIndexedFaceSet * oldifs = (SoVRMLIndexedFaceSet*) path->getTail(); assert(oldifs->isOfType(SoVRMLIndexedFaceSet::getClassTypeId())); SoVRMLIndexedFaceSet * ifs = new SoVRMLIndexedFaceSet; ifs->ref(); ifs->normalPerVertex = this->lighting; ifs->colorPerVertex = this->pvcache->colorPerVertex(); ifs->ccw = oldifs->ccw; ifs->solid = oldifs->solid; ifs->creaseAngle = oldifs->creaseAngle; int numv = this->pvcache->getNumVertices(); if (this->hastexture) { SoVRMLTextureCoordinate * tc = new SoVRMLTextureCoordinate; tc->point.setNum(numv); SbVec2f * dst = tc->point.startEditing(); const SbVec4f * src = this->pvcache->getTexCoordArray(); for (int i = 0; i < numv; i++) { SbVec4f tmp = src[i]; if (tmp[3] != 0.0f) { tmp[0] /= tmp[3]; tmp[1] /= tmp[3]; } dst[i][0] = tmp[0]; dst[i][1] = tmp[1]; } tc->point.finishEditing(); ifs->texCoord = tc; } SoVRMLCoordinate * c = new SoVRMLCoordinate; c->point.setValues(0, numv, this->pvcache->getVertexArray()); ifs->coord = c; if (this->lighting) { SoVRMLNormal * norm = new SoVRMLNormal; norm->vector.setValues(0, numv, this->pvcache->getNormalArray()); ifs->normal = norm; } if (this->pvcache->colorPerVertex()) { SoVRMLColor * col = new SoVRMLColor; col->color.setNum(numv); uint8_t * src = (uint8_t*) this->pvcache->getColorArray(); SbColor * dst = col->color.startEditing(); for (int i = 0; i < numv; i++) { dst[i] = SbColor(src[0]/255.0f, src[1]/255.0f, src[2]/255.0f); src += 4; } col->color.finishEditing(); ifs->color = col; } ifs->normalIndex.setNum(0); ifs->colorIndex.setNum(0); ifs->texCoordIndex.setNum(0); int numtri = this->pvcache->getNumIndices() / 3; const GLint * indices = this->pvcache->getIndices(); ifs->coordIndex.setNum(numtri * 4); int32_t * ptr = ifs->coordIndex.startEditing(); for (int i = 0; i < numtri; i++) { *ptr++ = (int32_t) indices[i*3]; *ptr++ = (int32_t) indices[i*3+1]; *ptr++ = (int32_t) indices[i*3+2]; *ptr++ = -1; } ifs->coordIndex.finishEditing(); int idx = path->getIndexFromTail(0); path->pop(); if (parent->isOfType(SoGroup::getClassTypeId())) { SoGroup * g = (SoGroup*)parent; g->replaceChild(idx, ifs); } else { SoVRMLShape * shape = (SoVRMLShape*) parent; shape->geometry = ifs; } path->push(idx); ifs->unrefNoDelete(); } void SoReorganizeActionP::replaceIls(SoFullPath * path) { SoNode * parent = path->getNodeFromTail(1); if (!parent->isOfType(SoGroup::getClassTypeId())) { return; } SoVertexProperty * vp = this->createVertexProperty(TRUE); SoIndexedLineSet * ils = new SoIndexedLineSet; ils->ref(); ils->vertexProperty = vp; ils->normalIndex.setNum(0); ils->materialIndex.setNum(0); ils->textureCoordIndex.setNum(0); int numlines = this->pvcache->getNumLineIndices() / 2; const GLint * indices = this->pvcache->getLineIndices(); ils->coordIndex.setNum(numlines * 3); int32_t * ptr = ils->coordIndex.startEditing(); for (int i = 0; i < numlines; i++) { *ptr++ = (int32_t) indices[i*2]; *ptr++ = (int32_t) indices[i*2+1]; *ptr++ = -1; } ils->coordIndex.finishEditing(); int idx = path->getIndexFromTail(0); path->pop(); SoGroup * g = (SoGroup*)parent; g->replaceChild(idx, ils); path->push(idx); ils->unrefNoDelete(); } void SoReorganizeActionP::replaceVrmlIls(SoFullPath * path) { SoNode * parent = path->getNodeFromTail(1); if (!parent->isOfType(SoGroup::getClassTypeId()) && !parent->isOfType(SoVRMLShape::getClassTypeId())) { return; } SoVRMLIndexedLineSet * ils = new SoVRMLIndexedLineSet; ils->ref(); int numv = this->pvcache->getNumVertices(); int numlines = this->pvcache->getNumLineIndices() / 2; const GLint * indices = this->pvcache->getLineIndices(); ils->coordIndex.setNum(numlines * 3); int32_t * ptr = ils->coordIndex.startEditing(); for (int i = 0; i < numlines; i++) { *ptr++ = (int32_t) indices[i*2]; *ptr++ = (int32_t) indices[i*2+1]; *ptr++ = -1; } ils->coordIndex.finishEditing(); SoVRMLCoordinate * c = new SoVRMLCoordinate; c->point.setValues(0, numv, this->pvcache->getVertexArray()); ils->coord = c; if (this->pvcache->colorPerVertex()) { ils->colorPerVertex = TRUE; SoVRMLColor * col = new SoVRMLColor; col->color.setNum(numv); uint8_t * src = (uint8_t*) this->pvcache->getColorArray(); SbColor * dst = col->color.startEditing(); for (int i = 0; i < numv; i++) { dst[i] = SbColor(src[0]/255.0f, src[1]/255.0f, src[2]/255.0f); src += 4; } col->color.finishEditing(); ils->color = col; } ils->colorIndex.setNum(0); int idx = path->getIndexFromTail(0); path->pop(); if (parent->isOfType(SoGroup::getClassTypeId())) { SoGroup * g = (SoGroup*)parent; g->replaceChild(idx, ils); } else { SoVRMLShape * shape = (SoVRMLShape*) parent; shape->geometry = ils; } path->push(idx); ils->unrefNoDelete(); } #endif // COIN_NEXT_MINOR