/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include void purgeOldStuff(SoSeparator *); void convertAllVertexShapes(SoSeparator *); void untangleInstances(SoSeparator *); SoCallbackAction::SoCallbackActionCB convertVertexShape; void splitGraph( int refpath, int copypath, SoPathList pathList); void removeInstances(SoSeparator *root, SoPathList& vertexPathList); SoPathList instancedPathList; SbBool doCopy, saveNormals; //Counters for work done: int numDeleted, numVPInserted, numCopied; static void print_usage(const char *progname) { (void)fprintf(stderr, "Usage: %s [-bch] [-o filename] file ...\n", progname); (void)fprintf(stderr, "-b : Output binary format (default ASCII)\n"); (void)fprintf(stderr, "-o : Write output to [filename]\n"); (void)fprintf(stderr, "-n : Keep unused normal vectors\n"); (void)fprintf(stderr, "-c : Make copies of instanced nodes\n"); (void)fprintf(stderr, "-h : This message (help)\n"); (void)fprintf(stderr, "If given a filename of '-' or if not given\n"); (void)fprintf(stderr, "any filenames, standard input will be read\n"); (void)fprintf(stderr, "converts vertex shapes in input to use\n"); (void)fprintf(stderr, "Vertex Property nodes, unless they are instanced\n"); (void)fprintf(stderr, "If lighting is off, does not use normals \n"); (void)fprintf(stderr, "unless option -n is specified.\n"); (void)fprintf(stderr, "Requires Inventor 2.1\n"); exit(99); } static int parse_args(int argc, char **argv, char **outfilename) { int err = 0; // Flag: error in options? int c; int result=0; // 0 = ASCII, 1 = BINARY doCopy = saveNormals = FALSE; while ((c = getopt(argc, argv, "bctno:h")) != -1) { switch(c) { case 'b': result = 1; break; case 'c': doCopy = TRUE; break; case 'n': saveNormals = TRUE; break; case 'o': *outfilename = optarg; break; case 'h': // Help default: err = 1; break; } } if (err) { print_usage(argv[0]); } return result; } int main(int argc, char **argv) { numDeleted = 0; numVPInserted = 0; numCopied = 0; SoInteraction::init(); // Parse arguments char *outputfile = NULL; int binary = 0; binary = parse_args(argc, argv, &outputfile); // read stuff: SoInput in; SoSeparator *root; if (optind == argc) { ++argc; // Act like one argument "-" was given argv[optind] = "-"; } for (; optind < argc; optind++) { char *filename = argv[optind]; if (strcmp(filename, "-") == 0) { if (isatty(fileno(stdin))) { fprintf(stderr, "Trying to read from standard input, "); fprintf(stderr, "but standard input is a tty!\n"); print_usage(argv[0]); } in.setFilePointer(stdin); } else if (in.openFile(filename) == FALSE) { fprintf(stderr, "Could not open file %s\n", filename); } } root = SoDB::readAll(&in); root->ref(); //Now convert the VertexShape nodes: convertAllVertexShapes(root); //Delete the unnecessary nodes from the graph: purgeOldStuff(root); // write stuff SoOutput out; out.setBinary(binary); if (outputfile == NULL) { out.setFilePointer(stdout); } else { if (out.openFile(outputfile) == FALSE) { fprintf(stderr, "Couldn't open %s for writing\n", outputfile); print_usage(argv[0]); } } SoWriteAction writer(&out); writer.apply((SoNode*)root); if (outputfile != NULL) out.closeFile(); fprintf(stderr, "Number of Vertex Property nodes inserted: %d\n", numVPInserted); if (numCopied)fprintf(stderr, "Number of instanced nodes copied: %d\n", numCopied); fprintf(stderr, "Number of property nodes deleted: %d\n", numDeleted); return 0; } /////////////////////////////////////////////////////////////////////////// // // Description: // // This callback function converts VertexShape nodes to use the vertexProperty // field. // //////////////////////////////////////////////////////////////////////////// SoCallbackAction::Response convertVertexShape(void *, SoCallbackAction *action, const SoNode *node) { SoVertexShape* vertShape = (SoVertexShape *)node; if (vertShape->vertexProperty.getValue() != NULL) return SoCallbackAction::ABORT; SoState* state = action->getState(); SoVertexProperty *vp = new SoVertexProperty; vertShape->vertexProperty.setValue(vp); numVPInserted++; int startIndex = 0; // other indices start at zero unless binding is PER_VERTEX: int startMtlIndex = 0; int startNrmIndex = 0; int startTxtIndex = 0; if (vertShape->isOfType(SoNonIndexedShape::getClassTypeId())){ startIndex = ((SoNonIndexedShape*)vertShape)->startIndex.getValue(); ((SoNonIndexedShape*)vertShape)->startIndex.setValue(0); } //Put the current coordinates into the vertex property node, if 3D const SoCoordinateElement* ce = SoCoordinateElement::getInstance(state); if (ce->is3D() ){ vp->vertex.setValues(0, ce->getNum() - startIndex, &ce->get3(startIndex)); } //If it is a triangle strip set, line set, or point set, or face set, //and if numVertices == -1, we need to count the vertices. if (vertShape->isOfType(SoTriangleStripSet::getClassTypeId())){ SoTriangleStripSet* tss = (SoTriangleStripSet*)vertShape; int last = tss->numVertices.getNum()-1; if (tss->numVertices[last] == -1){ // Count the vertices that are specified in numVertices field int totvert=0; for (int i=0; i< last ; i++){ totvert+= tss->numVertices[i]; } int32_t totleft = ce->getNum() - startIndex - totvert; tss->numVertices.set1Value(last, totleft); } } if (vertShape->isOfType(SoFaceSet::getClassTypeId())){ SoFaceSet* fs = (SoFaceSet*)vertShape; int last = fs->numVertices.getNum()-1; if (fs->numVertices[last] == -1){ // Count the vertices that are specified in numVertices field int totvert=0; for (int i=0; i< last ; i++){ totvert+= fs->numVertices[i]; } int32_t totleft = ce->getNum() - startIndex - totvert; fs->numVertices.set1Value(last, totleft); } } if (vertShape->isOfType(SoLineSet::getClassTypeId())){ SoLineSet* ls = (SoLineSet*)vertShape; int last = ls->numVertices.getNum()-1; if (ls->numVertices[last] == -1){ // Count the vertices that are specified in numVertices field int totvert=0; for (int i=0; i< last ; i++){ totvert+= ls->numVertices[i]; } int32_t totleft = ce->getNum() - startIndex - totvert; ls->numVertices.set1Value(last, totleft); } } if (vertShape->isOfType(SoPointSet::getClassTypeId())){ SoPointSet* ps = (SoPointSet*)vertShape; if (ps->numPoints.getValue() == -1){ int32_t totleft = ce->getNum() - startIndex; ps->numPoints.setValue(totleft); } } //If there are multiple diffuse colors in the state, or the //materialBinding is not OVERALL, put them in //the vertex property node. SoLazyElement* le = SoLazyElement::getInstance(state); int32_t mb = (int32_t) SoMaterialBindingElement::get(state); if (mb == SoMaterialBindingElement::PER_VERTEX){ startMtlIndex = startIndex; } if (le->getNumDiffuse() > 1 || (mb != SoMaterialBindingElement::OVERALL)){ vp->materialBinding.setValue(mb); if (le->isPacked()){ vp->orderedRGBA.setValues(0, le->getNumDiffuse() - startMtlIndex, SoLazyElement::getPackedColors(state) + startMtlIndex ); } else { for (int i= startMtlIndex ; i< le->getNumDiffuse(); i++){ float transp; if (igetNumTransparencies()) transp = SoLazyElement::getTransparency(state, i); else transp = 0.0; vp->orderedRGBA.set1Value(i-startMtlIndex, (SoLazyElement::getDiffuse(state, i)).getPackedValue(transp)); } } } //If lighting is on, and there are normals in the state, // or if it is specified to keep normals, put // the normals and normal binding into the //vertex property node. if (saveNormals || SoLazyElement::getLightModel(state) == SoLazyElement::PHONG){ const SoNormalElement* ne = SoNormalElement::getInstance(state); SoNormalBinding::Binding nb = (SoNormalBinding::Binding)SoNormalBindingElement::get(state); if (ne != NULL && ne->getNum()>0){ vp->normalBinding.setValue(nb); if (nb == SoNormalBinding::PER_VERTEX){ startNrmIndex = startIndex; } vp->normal.setValues(0, (ne->getNum())-startNrmIndex, &ne->get(startNrmIndex)); } } // If there are texture coordinates in the state, //put them into the vertex property node. SoTextureCoordinateBinding::Binding tcb = (SoTextureCoordinateBinding::Binding) SoTextureCoordinateBindingElement::get(state); if (tcb == SoTextureCoordinateBinding::PER_VERTEX) startTxtIndex = startIndex; const SoTextureCoordinateElement* tce = SoTextureCoordinateElement::getInstance(state); if (tce->getType() == SoTextureCoordinateElement::EXPLICIT && (tce->getNum() > 0 )){ vp->texCoord.setValues(0, tce->getNum() - startTxtIndex, &tce->get2(startTxtIndex)); } return SoCallbackAction::ABORT; } ///////////////////////////////////////////////////////////////////////////// // Description: // This function removes nodes and diffuse colors from the scene graph if // they are not necessary after the conversion to using vertexProperty nodes. // // // ////////////////////////////////////////////////////////////////////////////// void purgeOldStuff(SoSeparator* root) { SoPathList nonVertexPathList; //Make a pathlist of all paths to non-vertex shapes: // cube,sphere,cone,cylinder,nurb,text2,text3, unknown shape // we will need to be sure to keep the property nodes these shapes // depend on. SoSearchAction searchAction; searchAction.setInterest(SoSearchAction::ALL); searchAction.setSearchingAll(TRUE); searchAction.setType(SoShape::getClassTypeId()); searchAction.apply(root); nonVertexPathList = searchAction.getPaths(); // delete the vertex-based shapes from this list so we will be left with: // cube,sphere,cone,cylinder,nurb,text2,text3. searchAction.setType(SoVertexShape::getClassTypeId()); searchAction.apply(root); for (int i=searchAction.getPaths().getLength()-1; i>= 0; i--) { int index = nonVertexPathList.findPath(*(searchAction.getPaths()[i])); if (index >= 0){ nonVertexPathList.remove(index); } else fprintf(stderr, "Error, shape not found %d\n", index); } //If we avoided converting instanced vertex-shapes, add this list to //the current pathlist. That way if these depend on property nodes, //we won't be deleting those property nodes: for (i = 0; i=0; i--){ coordPathList.append(searchAction.getPaths()[i]); } //SoNormalBinding: searchAction.setType(SoNormalBinding::getClassTypeId()); searchAction.apply(root); SoPathList normalPathList = searchAction.getPaths(); //SoNormal: searchAction.setType(SoNormal::getClassTypeId()); searchAction.apply(root); for (i = searchAction.getPaths().getLength()-1; i>=0; i--){ normalPathList.append(searchAction.getPaths()[i]); } //SoVertexProperty: searchAction.setType(SoVertexProperty::getClassTypeId()); searchAction.apply(root); SoPathList VPPathList = searchAction.getPaths(); //Make pathlists of material nodes-- //paths to SoMaterial, SoPackedColor, BaseColor, SoMaterialBinding. //Multiple colors can be deleted if nonvertex shapes don't depend on them //but single colors and overall binding must remain. searchAction.setType(SoMaterial::getClassTypeId()); searchAction.apply(root); SoPathList materialPathList = searchAction.getPaths(); searchAction.setType(SoMaterialBinding::getClassTypeId()); searchAction.apply(root); SoPathList mBindPathList = searchAction.getPaths(); searchAction.setType(SoPackedColor::getClassTypeId()); searchAction.apply(root); SoPathList packedPathList = searchAction.getPaths(); searchAction.setType(SoBaseColor::getClassTypeId()); searchAction.apply(root); SoPathList baseColorPathList = searchAction.getPaths(); //create a search action for getting last node in path: SoSearchAction searchLastNode; searchLastNode.setInterest(SoSearchAction::LAST); searchLastNode.setSearchingAll(TRUE); SoPath* foundPath; // now we go through all the nonVertexPathList, finding the last // property node that affects the nonVertex shape. These we will have // to keep. //for each path in nonVertexPathList: for (i = nonVertexPathList.getLength()-1; i>=0; i--){ SoPath *path = nonVertexPathList[i]; //find last SoMaterial influencing path searchLastNode.setType(SoMaterial::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the material path list: int index = materialPathList.findPath(*foundPath); if (index >= 0) materialPathList.remove(index); // if we didn't find it, it must have been previously deleted. } //find last SoMaterialBinding influencing path searchLastNode.setType(SoMaterialBinding::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the material binding path list: int index = mBindPathList.findPath(*foundPath); if (index >= 0) mBindPathList.remove(index); } //find last SoPackedColor influencing path searchLastNode.setType(SoPackedColor::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the packed color path list: int index = packedPathList.findPath(*foundPath); if (index >= 0) packedPathList.remove(index); } //find last SoBaseColor influencing path searchLastNode.setType(SoBaseColor::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the basecolor path list: int index = baseColorPathList.findPath(*foundPath); if (index >= 0) baseColorPathList.remove(index); } //find last SoVertexProperty influencing path searchLastNode.setType(SoVertexProperty::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if(foundPath) { //delete this from the vertexProperty pathlist: int index = VPPathList.findPath(*foundPath); if (index >= 0) VPPathList.remove(index); } //If the path is to SoSphere,SoCube,SoCylinder,SoText3,SoText2, //we don't need to look further: SoNode *node = path->getTail(); if (node->isOfType(SoSphere::getClassTypeId()) || node->isOfType(SoCube::getClassTypeId()) || node->isOfType(SoCylinder::getClassTypeId()) || node->isOfType(SoText3::getClassTypeId()) || node->isOfType(SoText2::getClassTypeId())) continue; //If this is a NURBS or unknown shape, it may use coordinate nodes: //find last SoCoordinate3 influencing path searchLastNode.setType(SoCoordinate3::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the deletion path list: int index = coordPathList.findPath(*foundPath); if (index >= 0) coordPathList.remove(index); } //find last SoTextureCoordinate2 influencing path searchLastNode.setType(SoTextureCoordinate2::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the deletion path list: int index = coordPathList.findPath(*foundPath); if (index >= 0) coordPathList.remove(index); } //If the path is to NURBS, //we don't need to look further: if (node->isOfType(SoNurbsCurve::getClassTypeId()) || node->isOfType(SoNurbsSurface::getClassTypeId()) || node->isOfType(SoIndexedNurbsCurve::getClassTypeId()) || node->isOfType(SoIndexedNurbsSurface::getClassTypeId())) continue; //If this is unknown shape, it may use normals or normal binding: //find last SoNormal influencing path searchLastNode.setType(SoNormal::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the normal path list: int index = normalPathList.findPath(*foundPath); if (index >= 0) normalPathList.remove(index); } //find last SoNormalBinding influencing path searchLastNode.setType(SoNormalBinding::getClassTypeId()); searchLastNode.apply(path); foundPath = searchLastNode.getPath(); if (foundPath){ //delete this from the normal path list: int index = normalPathList.findPath(*foundPath); if (index >= 0) normalPathList.remove(index); } } //Now use these path lists to delete nodes from the graph //Or, with material nodes, to remove stuff from the graph that //vertex shapes won't need. // First, remove normals and normal binding: for (i = normalPathList.getLength()-1; i>= 0; i--){ SoNode* node = normalPathList[i]->getTail(); SoGroup* parent = (SoGroup*)(normalPathList[i]->getNodeFromTail(1)); //Make sure parent exists (may have been dereferenced) if (parent == NULL) continue; //Find the node (it may not still be there, due to instancing) int index = parent->findChild(node); if (index >=0 ){ parent->removeChild(index); numDeleted++; } } // then, remove coordinates, texture coords for (i = coordPathList.getLength()-1; i>= 0; i--){ SoNode* node = coordPathList[i]->getTail(); SoGroup* parent = (SoGroup*)(coordPathList[i]->getNodeFromTail(1)); if (parent == NULL) continue; int index = parent->findChild(node); if (index >= 0){ parent->removeChild(index); numDeleted++; } } // material binding can be removed if it is not OVERALL. // packed and base_color nodes can be removed if they have multiple colors. // diffuse color fields can be emptied if they have >1 color. for (i = packedPathList.getLength()-1; i>= 0; i--){ SoPackedColor* node = (SoPackedColor*)(packedPathList[i]->getTail()); if ( node->orderedRGBA.getNum() > 1 ){ SoGroup* parent = (SoGroup*)(packedPathList[i]->getNodeFromTail(1)); if (parent == NULL) continue; int index = parent->findChild(node); if (index >= 0){ parent->removeChild(index); numDeleted++; } } } for (i = baseColorPathList.getLength()-1; i>= 0; i--){ SoBaseColor* node = (SoBaseColor*)(baseColorPathList[i]->getTail()); if ( node->rgb.getNum() > 1 ){ SoGroup* parent = (SoGroup*)(baseColorPathList[i]->getNodeFromTail(1)); if (parent == NULL) continue; int index = parent->findChild(node); if (index >= 0) { parent->removeChild(index); numDeleted++; } } } for (i = mBindPathList.getLength()-1; i>= 0; i--){ SoMaterialBinding* node = (SoMaterialBinding*)(mBindPathList[i]->getTail()); if ( node->value.getValue() != SoMaterialBinding::OVERALL){ SoGroup* parent = (SoGroup*)(mBindPathList[i]->getNodeFromTail(1)); if (parent == NULL) continue; int index = parent->findChild(node); if (index >= 0){ parent->removeChild(index); numDeleted++; } } } // Delete excess (>1) diffuse colors from material nodes: for (i = materialPathList.getLength()-1; i>= 0; i--){ SoMaterial* node = (SoMaterial*)(materialPathList[i]->getTail()); if (node && node->diffuseColor.getNum() > 1){ node->diffuseColor.deleteValues(0, -1); } } //delete all stuff (except possibly exactly 1 diffuse color) from //vertexProperty nodes in scene graph that are still in VPPathList. for (i = VPPathList.getLength()-1; i>= 0; i--){ SoVertexProperty* node = (SoVertexProperty*)(VPPathList[i]->getTail()); //If there is exactly 1 diffuse color, and the material binding //is OVERALL, we need to keep it. Everything else can go. if( node && (node->orderedRGBA.getNum()== 1) && (node->materialBinding.getValue() == SoVertexProperty::OVERALL)){ node->normal.deleteValues(0, -1); node->texCoord.deleteValues(0, -1); node->vertex.deleteValues(0, -1); } else { SoGroup* parent = (SoGroup*)(VPPathList[i]->getNodeFromTail(1)); if (parent == NULL) continue; int index = parent->findChild(node); if (index >= 0){ parent->removeChild(index); numDeleted++; } } } } ////////////////////////////////////////////////////////////////////////////// // // Description: find all vertex shapes in graph, convert them to // use the vertexProperty node. If multiply instanced, make copies of // subsequent instances, or ignore instanced things. // ////////////////////////////////////////////////////////////////////////////// void convertAllVertexShapes(SoSeparator *root) { //Construct a pathlist of all paths to vertex shapes SoPathList vertexPathList; SoSearchAction searchAction; searchAction.setInterest(SoSearchAction::ALL); searchAction.setSearchingAll(TRUE); searchAction.setType(SoVertexShape::getClassTypeId()); //Go through each path in pathlist, replacing instances with copies, //but only do this if doCopy was specified: if (doCopy) untangleInstances(root); //Now build the pathlist using the new scene graph: searchAction.apply(root); vertexPathList = searchAction.getPaths(); //If doCopy was not specified, we have to remove all paths to // instanced nodes in VertexPathList, and put them into instancedPathList. if (!doCopy) { instancedPathList.truncate(0); removeInstances(root, vertexPathList); } //Now go through and convert the tail nodes of the paths: SoCallbackAction cbAction; cbAction.addPreTailCallback(convertVertexShape, NULL); int* indexArray = NULL; for (int i = 0; i< vertexPathList.getLength(); i++){ //If there are SoSwitch nodes in the path, and they don't //follow the path, temporarily modify them before doing the //conversion: int pathlen = vertexPathList[i]->getLength(); //Look for switches in the path: for (int j= 0; j< pathlen-1; j++){ if ((vertexPathList[i]->getNode(j))-> isOfType(SoSwitch::getClassTypeId())){ SoSwitch* sw = (SoSwitch*)vertexPathList[i]-> getNode(j); // identify if the switch doesn't follow the path: if ((sw->whichChild.getValue() != -3 )&& (sw->whichChild.getValue() != vertexPathList[i]->getIndex(j+1))){ if (indexArray == NULL) { indexArray = new int[pathlen]; for (int k=0;kwhichChild.getValue(); sw->whichChild.setValue(vertexPathList[i]-> getIndex(j+1)); } } } //Traverse the path, convert the tail node cbAction.apply(vertexPathList[i]); // if switches were changed, reset to old values: if (indexArray != NULL){ for (int j= 0; j getNode(j); swtch->whichChild.setValue(indexArray[j]); } } delete [] indexArray; indexArray = NULL; } } } //////////////////////////////////////////////////////////////////////////// // // Description: Search a graph for instanced vertex shapes. // Whenever one is found split the graph by making copies of all // instanced nodes in the paths above the two nodes. // Repeatedly do this until all instances have been removed. // /////////////////////////////////////////////////////////////////////////// void untangleInstances(SoSeparator *root) { //Construct a pathlist of all paths to vertex shapes SoPathList vertexPathList; SoSearchAction searchAction; searchAction.setInterest(SoSearchAction::ALL); searchAction.setSearchingAll(TRUE); searchAction.setType(SoVertexShape::getClassTypeId()); //Create a dictionary to look for instanced nodes SbDict* nodeDict = new SbDict; //Repeatedly apply this search until graph is untangled. //The search action has to be repeated each time a node is de-instanced, //because the original search action is not valid after the splitting. while(1){ SbBool done = TRUE; searchAction.apply(root); vertexPathList = searchAction.getPaths(); //Clear the dictionary nodeDict->clear(); //Go through each path in pathlist, looking for instancing for (int i = 0; i< vertexPathList.getLength(); i++){ SoVertexShape* node = (SoVertexShape*)(vertexPathList[i]->getTail()); // see if it's been previously instanced: uint32_t key = (uint32_t)(node->getNodeId()); void *value; if (!nodeDict->find(key, value)){ nodeDict->enter(key, (void*)i); } else{ // an instance was found, split the graph there: int matchvalue = (long)value; splitGraph(matchvalue, i, vertexPathList); done = FALSE; break; } } if (done) return; } } //////////////////////////////////////////////////////////////////////////// // // Description: Search a graph for instanced vertex shapes. // Whenever one is found, put its path into instancedPathList. // Then go through inputPathList, and remove all of these instanced // paths from it. // /////////////////////////////////////////////////////////////////////////// void removeInstances(SoSeparator *root, SoPathList& inputPathList) { //Construct a pathlist of all paths to vertex shapes SoPathList vertexPathList; SoSearchAction searchAction; searchAction.setInterest(SoSearchAction::ALL); searchAction.setSearchingAll(TRUE); searchAction.setType(SoVertexShape::getClassTypeId()); //Create a dictionary to look for instanced nodes, //And a dictionary to identify nodes that have been put into instanced list: SbDict* nodeDict = new SbDict; SbDict* instDict = new SbDict; searchAction.apply(root); vertexPathList = searchAction.getPaths(); //Clear the dictionaries nodeDict->clear(); instDict->clear(); //Go through each path in pathlist, looking for instanced nodes for (int i = 0; i< vertexPathList.getLength(); i++){ SoVertexShape* node = (SoVertexShape*)(vertexPathList[i]->getTail()); // see if it's been previously instanced: uint32_t key = (uint32_t)(node->getNodeId()); void *value; if (!nodeDict->find(key, value)){ nodeDict->enter(key, (void*)i); } else{ // an instance was found // we always put the new one "i" in instanced list. // we conditionally put the matching one in if it's not // already there: int matchvalue = (long)value; if (!instDict->find(key, value)){ instDict->enter(key, (void*)i); instancedPathList.append(vertexPathList[matchvalue]); } instancedPathList.append(vertexPathList[i]); } } //Now remove every one of these paths from the inputPathList for (i=0; igetLength(); int copylen = pathList[copypath]->getLength(); // find the furthest position (up from the end) where // the two paths are the same. We will have to copy that node. int j; for (j= 0; (jgetNodeFromTail(j) != pathList[copypath]->getNodeFromTail(j)) break; if ((pathList[refpath]->getIndexFromTail(j)) != pathList[copypath]->getIndexFromTail(j)) { j++; break;} } if (j >= copylen) fprintf(stderr, "Error, paths are identical\n"); if (j == 0) fprintf(stderr, "Error, instanced node not found\n"); int firstmatch = j-1; //Make copies at the node where they meet: SoGroup* parent = (SoGroup*)(pathList[copypath]->getNodeFromTail(firstmatch+1)); if (parent == NULL) { fprintf(stderr, "Error, no parent for instances\n"); } SoNode* node = (SoNode*)(pathList[copypath]->getNodeFromTail(firstmatch)); SoNode* newNode = node->copy(TRUE); //Replace node with newNode in the scene graph: int index = pathList[copypath]->getIndexFromTail(firstmatch); parent->replaceChild(index, newNode); numCopied++; return; }