X3D Specification:
www.web3d.org/x3d/specifications/ISO-IEC-19775-X3DAbstractSpecification/
VRML97 Specification:
www.web3d.org/x3d/specifications/vrml/ISO-IEC-14772-VRML97/
Open Inventor on SGI machines: www.sgi.com
Open Inventor everywhere else: www.mc.com/tgs
At the time this was written, there were some limitations in the Open Inventor VRML nodes. Please check the documentation (README, RELNOTES, Help File, man pages, etc.) for the Open Inventor version you are currently using. The following information is based on the Open Inventor 6.0 release by FEI. The information is organized by VRML node name in alphabetical order. Remember that the Open Inventor documentation is organized by classname and the classname is formed by prefixing “ SoVRML” to the VRML node name.
Also remember that all nodes and their fields are read and created in the scene graph, so the information is available if applications want or need to implement some of the features that are not available yet. We are continuing to work on the missing features and our goal is to have a complete implementation that would allow an application to create a conforming viewer.
Appearance | The fillProperties field is not implemented. |
AudioClip | Available on Microsoft Windows only. pauseTime and resumeTime fields not implemented. |
Background | Not implemented by viewers, but application can implement. |
Collision | Functions correctly as a grouping node, but collideTime eventOut is not implemented. Note that Open Inventor supports collision detection, but it is the application’s responsibility to use it or not. The collide field (VRML 2.0) and the enabled field (X3D) are not implemented. |
ColorRGBA | Not implemented. |
CylinderSensor | Not implemented. |
FillProperties | Not implemented. |
Fog | Not implemented. |
FontStyle | The leftToRight and topToBottom fields are not implemented. Only horizontal justification is implemented. |
ImageTexture | Open Inventor will only try to open the first URL specified (app can override). |
IndexedTriangleFanSet | Not implemented. |
IndexedTriangleSet | Not implemented. |
IndexedTriangleStripSet | Not implemented. |
Inline | Open Inventor will only try to open the first URL specified (app can override). The load field is not implemented. |
KeySensor | Not implemented. |
LineSet | Not implemented. |
LoadSensor | Not implemented. |
MovieTexture | Negative speed values are not supported. The pauseTime and resumeTime fields are not implemented. |
MultiTexture | Not implemented. |
MultiTextureCoordinate | Not implemented. |
MultiTextureTransform | Not implemented. |
NavigationInfo | Partially implemented. The speed, visibilityLimit, and transitionType fields are not implemented. |
PlaneSensor | Not implemented. |
PointLight | The radius field is not implemented (acceptable per VRML97 spec, section 7.3.3). |
Script | Not implemented. |
Sound | Available on Microsoft Windows only. |
SphereSensor | Not implemented. |
SpotLight | The radius field is not implemented (acceptable per VRML97 spec, section 7.3.3). |
StaticGroup | Not implemented. |
StringSensor | Not implemented. |
Text | The maxExtent and solid fields are not implemented. |
TextureBackground | Not implemented. |
TextureCoordinateGenerator | Not implemented. |
TextureTransform | Transformations are applied in the (more intuitive) pre-ISO order. |
TimeSensor | The pauseTime and resumeTime fields are not implemented. |
TouchSensor | The isOver event is only generated while one of the mouse buttons is pressed. The xxx_changed events are not generated unless isActive is TRUE, in other words, only while a mouse button is pressed. |
TriangleFanSet | Not implemented. |
TriangleSet | Not implemented. |
TriangleStripSet | Not implemented. |
Viewpoint | The “binding” behavior is not implemented. The centerOfRotation field is not implemented. |
VisibilitySensor | Not implemented. |
Example 22.9. How to attach a Color Editor
SoPath *selectionPath SoXtColorEditor *colorEditor SoNode *pTailNode = selectionPath->getTail(); if (pTailNode->isOfType(SoVRMLShape::getClassTypeId())) { SoVRMLShape *pShapeNode = (SoVRMLShape*)pTailNode; // Note: We're only interested in editing the diffuseColor and // that simplifies the problem somewhat -- // // 1) If geometry contains a color field and that field contains // a Color node (ie. is not NULL), edit the first value in the // Color node's (SoMFColor) color field. DONE. // // 2) Else, if Appearance node is NULL, create a Appearance node // (created node will have a NULL Material node of course.) // // 3) If Material node is NULL, create a Material node, set the // diffuseColor field to <0 0 0>, set emissiveColor field to // <1 1 1>, then edit the (SoSFColor) emissiveColor field. // // Note: If either Appearance or Material node is NULL then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // 4) Else Appearance and Material nodes exist, so edit the // Material node's (SoSFColor) diffuseColor field. // // First check geometry. // Get ptr to geometry node, then see if it's one of the ones // that has a color field (IndexedFaceSet, IndexedLineSet, // PointSet, ElevationGrid) Unfortunately we have to check each // one separately because currently there is no common ancestor // class that contains the color field. SoNode *pGeoNode = pShapeNode->geometry.getValue(); if (pGeoNode != NULL) { SoSFNode *pClrNodeField = NULL; if (pGeoNode->isOfType(SoVRMLVertexShape::getClassTypeId())) pClrNodeField = &(((SoVRMLVertexShape*)pGeoNode)->color); else if (pGeoNode->isOfType(SoVRMLVertexPoint::getClassTypeId())) pClrNodeField = &(((SoVRMLVertexPoint*)pGeoNode)->color); else if (pGeoNode->isOfType(SoVRMLVertexLine::getClassTypeId())) pClrNodeField = &(((SoVRMLVertexLine*)pGeoNode)->color); else if (pGeoNode->isOfType(SoVRMLGridShape::getClassTypeId())) pClrNodeField = &(((SoVRMLGridShape*)pGeoNode)->color); // If the geometry has a color node if (pClrNodeField != NULL) { // and it contains a Color node, attach to color field SoVRMLColor *pClrNode = (SoVRMLColor*)pClrNodeField->getValue(); if (pClrNode != NULL) { colorEditor->attach(&(pClrNode->color), 0, pClrNode); colorEditor->show(); return; } } } // Now check Appearance/Material SoVRMLAppearance *pAppNode = (SoVRMLAppearance*)pShapeNode->appearance.getValue(); SoVRMLMaterial *pMatNode = NULL; SoSFColor *pClrField = NULL; // If no Appearance node, create one // (Created node's material field will contain NULL of course.) if (pAppNode == NULL) { pAppNode = new SoVRMLAppearance; pShapeNode->appearance.setValue(pAppNode); } // Similarly, if no Material node, create one so we can edit it. // Set diffuse and emissive colors to simulate unlit appearance, // (see note above) then attach to emissiveColor field. You // might want to attach to the diffuseColor field anyway... pMatNode = (SoVRMLMaterial*)pAppNode->material.getValue(); if (pMatNode == NULL) { pMatNode = new SoVRMLMaterial; pAppNode->material.setValue(pMatNode); pMatNode->diffuseColor.setValue(0.,0.,0.); pMatNode->emissiveColor.setValue(1.,1.,1.); pClrField = &(pMatNode->emissiveColor); } // Else both Appearance and Material nodes *do* exist, // so attach to diffuse color field. else { pClrField = &(pMatNode->diffuseColor); } // Do the actual attach and display the color editor colorEditor->attach(pClrField, pMatNode); colorEditor->show();
SoPath selectionPath SoWinColorEditor colorEditor = new SoWinColorEditor(); SoNode tailNode = selectionPath.GetTail(); if (tailNode is SoVRMLShape) { SoVRMLShape shapeNode = (SoVRMLShape)tailNode; // Note: We're only interested in editing the diffuseColor and // that simplifies the problem somewhat -- // // 1) If geometry contains a color field and that field contains // a Color node (ie. is not null), edit the first value in the // Color node's (SoMFColor) color field. DONE. // // 2) Else, if Appearance node is null, create a Appearance node // (created node will have a null Material node of course.) // // 3) If Material node is null, create a Material node, set the // diffuseColor field to <0 0 0>, set emissiveColor field to // <1 1 1>, then edit the (SoSFColor) emissiveColor field. // // Note: If either Appearance or Material node is null then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // 4) Else Appearance and Material nodes exist, so edit the // Material node's (SoSFColor) diffuseColor field. // // First check geometry. // Get ptr to geometry node, then see if it's one of the ones // that has a color field (IndexedFaceSet, IndexedLineSet, // PointSet, ElevationGrid) Unfortunately we have to check each // one separately because currently there is no common ancestor // class that contains the color field. SoNode geoNode = shapeNode.geometry.Value; if (geoNode != null) { SoSFNode clrNodeField = null; if (geoNode is SoVRMLVertexShape) clrNodeField = (geoNode as SoVRMLVertexShape).color; else if (geoNode is SoVRMLVertexPoint) clrNodeField = (geoNode as SoVRMLVertexPoint).color; else if (geoNode is SoVRMLVertexLine) clrNodeField = (geoNode as SoVRMLVertexLine).color; else if (geoNode is SoVRMLGridShape) clrNodeField = (geoNode as SoVRMLGridShape).color; // If the geometry has a color node if (clrNodeField != null) { // and it contains a Color node, attach to color field SoVRMLColor clrNode = clrNodeField.Value as SoVRMLColor; if (clrNode != null) { colorEditor.Attach(clrNode.color, 0, clrNode); colorEditor.Show(); return; } } } // Now check Appearance/Material SoVRMLAppearance appNode = shapeNode.appearance.Value as SoVRMLAppearance; SoVRMLMaterial matNode = null; SoSFColor clrField = null; // If no Appearance node, create one // (Created node's material field will contain null of course.) if (appNode == null) { appNode = new SoVRMLAppearance(); shapeNode.appearance.Value = appNode; } // Similarly, if no Material node, create one so we can edit it. // Set diffuse and emissive colors to simulate unlit appearance, // (see note above) then attach to emissiveColor field. You // might want to attach to the diffuseColor field anyway... matNode = appNode.material.Value as SoVRMLMaterial; if (matNode == null) { matNode = new SoVRMLMaterial(); appNode.material.Value = matNode; matNode.diffuseColor.SetValue(0f, 0f, 0f); matNode.emissiveColor.SetValue(1f, 1f, 1f); clrField = matNode.emissiveColor; } // Else both Appearance and Material nodes *do exist, // so attach to diffuse color field. else { clrField = matNode.diffuseColor; } // Do the actual attach and display the color editor colorEditor.Attach(clrField, matNode); colorEditor.Show(); }
SoPath selectionPath SwColorEditor colorEditor = new SwColorEditor(); SoNode tailNode = selectionPath.regular.getTail(); if ( tailNode instanceof SoVRMLShape ) { SoVRMLShape shapeNode = (SoVRMLShape) tailNode; // Note: We're only interested in editing the diffuseColor and // that simplifies the problem somewhat -- // // 1) If geometry contains a color field and that field contains // a Color node (ie. is not null), edit the first value in the // Color node's (SoMFColor) color field. DONE. // // 2) Else, if Appearance node is null, create a Appearance node // (created node will have a null Material node of course.) // // 3) If Material node is null, create a Material node, set the // diffuseColor field to <0 0 0>, set emissiveColor field to // <1 1 1>, then edit the (SoSFColor) emissiveColor field. // // Note: If either Appearance or Material node is null then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // 4) Else Appearance and Material nodes exist, so edit the // Material node's (SoSFColor) diffuseColor field. // // First check geometry. // Get ptr to geometry node, then see if it's one of the ones // that has a color field (IndexedFaceSet, IndexedLineSet, // PointSet, ElevationGrid) Unfortunately we have to check each // one separately because currently there is no common ancestor // class that contains the color field. SoNode geoNode = shapeNode.geometry.getValue(); if ( geoNode != null ) { SoSFNode clrNodeField = null; if ( geoNode instanceof SoVRMLVertexShape ) clrNodeField = (((SoVRMLVertexShape) geoNode).color); else if ( geoNode instanceof SoVRMLVertexPoint ) clrNodeField = (((SoVRMLVertexPoint) geoNode).color); else if ( geoNode instanceof SoVRMLVertexLine ) clrNodeField = (((SoVRMLVertexLine) geoNode).color); else if ( geoNode instanceof SoVRMLGridShape ) clrNodeField = (((SoVRMLGridShape) geoNode).color); // If the geometry has a color node if ( clrNodeField != null ) { // and it contains a Color node, attach to color field SoVRMLColor clrNode = (SoVRMLColor) clrNodeField.getValue(); if ( clrNode != null ) { colorEditor.attach(clrNode.color, 0, clrNode); colorEditor.setVisible(true); return; } } } // Now check Appearance/Material SoVRMLAppearance appNode = (SoVRMLAppearance) shapeNode.appearance.getValue(); SoVRMLMaterial matNode = null; SoSFColor clrField = null; // If no Appearance node, create one // (Created node's material field will contain null of course.) if ( appNode == null ) { appNode = new SoVRMLAppearance(); shapeNode.appearance.setValue(appNode); } // Similarly, if no Material node, create one so we can edit it. // Set diffuse and emissive colors to simulate unlit appearance, // (see note above) then attach to emissiveColor field. You // might want to attach to the diffuseColor field anyway... matNode = (SoVRMLMaterial) appNode.material.getValue(); if ( matNode == null ) { matNode = new SoVRMLMaterial(); appNode.material.setValue(matNode); matNode.diffuseColor.setValue(0f, 0f, 0f); matNode.emissiveColor.setValue(1f, 1f, 1f); clrField = matNode.emissiveColor; } // Else both Appearance and Material nodes *do exist, // so attach to diffuse color field. else { clrField = matNode.diffuseColor; } // Do the actual attach and display the color editor colorEditor.attach(clrField, matNode); colorEditor.setVisible(true); }
Example 22.10. How to attach a Material Editor
Handle special case of VRML shape.
SoPath *selectionPath SoXtMaterialEditor *materialEditor SoNode *pTailNode = selectionPath->getTail(); if (pTailNode->isOfType(SoVRMLShape::getClassTypeId())) { SoVRMLShape *pShapeNode = (SoVRMLShape*)pTailNode; // Now check Appearance/Material SoVRMLAppearance *pAppNode = (SoVRMLAppearance*)pShapeNode->appearance.getValue(); SoVRMLMaterial *pMatNode = NULL; // If no Appearance node, create one (so we can create Mat node). // (Created node's material field will contain NULL of course.) if (pAppNode == NULL) { pAppNode = new SoVRMLAppearance; pShapeNode->appearance.setValue(pAppNode); } // Similarly, if no Material node, create one so we can edit it. // // Note: If either Appearance or Material node is NULL then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // Set diffuse and emissive colors to simulate unlit appearance, // then attach to emissiveColor field. You may wish to attach // to the diffuseColor field anyway... // pMatNode = (SoVRMLMaterial*)pAppNode->material.getValue(); if (pMatNode == NULL) { pMatNode = new SoVRMLMaterial; pAppNode->material.setValue(pMatNode); pMatNode->diffuseColor.setValue(0.,0.,0.); pMatNode->emissiveColor.setValue(1.,1.,1.); } // At this point the material node should exist. // Do the actual attach and display the material editor materialEditor->attach(pMatNode); return; }
SoPath selectionPath SoWinMaterialEditor materialEditor = new SoWinMaterialEditor(); SoNode tailNode = selectionPath.GetTail(); if (tailNode is SoVRMLShape) { SoVRMLShape shapeNode = (SoVRMLShape)tailNode; // Now check Appearance/Material SoVRMLAppearance appNode = (SoVRMLAppearance)shapeNode.appearance.GetValue(); SoVRMLMaterial matNode = null; // If no Appearance node, create one (so we can create Mat node). // (Created node's material field will contain null of course.) if (appNode == null) { appNode = new SoVRMLAppearance(); shapeNode.appearance.SetValue(appNode); } // Similarly, if no Material node, create one so we can edit it. // // Note: If either Appearance or Material node is null then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // Set diffuse and emissive colors to simulate unlit appearance, // then attach to emissiveColor field. You may wish to attach // to the diffuseColor field anyway... // matNode = (SoVRMLMaterial)appNode.material.GetValue(); if (matNode == null) { matNode = new SoVRMLMaterial(); appNode.material.SetValue(matNode); matNode.diffuseColor.SetValue(0f, 0f, 0f); matNode.emissiveColor.SetValue(1f, 1, 1f); } // At this point the material node should exist. // Do the actual attach and display the material editor materialEditor.Attach(matNode); }
SoPath selectionPath SwMaterialEditor materialEditor = new SwMaterialEditor(); SoNode tailNode = selectionPath.regular.getTail(); if ( tailNode instanceof SoVRMLShape ) { SoVRMLShape shapeNode = (SoVRMLShape) tailNode; // Now check Appearance/Material SoVRMLAppearance appNode = (SoVRMLAppearance) shapeNode.appearance.getValue(); SoVRMLMaterial matNode = null; // If no Appearance node, create one (so we can create Mat node). // (Created node's material field will contain null of course.) if ( appNode == null ) { appNode = new SoVRMLAppearance(); shapeNode.appearance.setValue(appNode); } // Similarly, if no Material node, create one so we can edit it. // // Note: If either Appearance or Material node is null then // VRML2 spec says the object is unlit and has color <1 1 1>. // Since we need to create a Material node in order to edit // the object's color, we set up its fields to simulate the // unlit appearance. We can only approximate this since VRML // does not have any way to specify the object color and // turn off lighting. // // Set diffuse and emissive colors to simulate unlit appearance, // then attach to emissiveColor field. You may wish to attach // to the diffuseColor field anyway... // matNode = (SoVRMLMaterial) appNode.material.getValue(); if ( matNode == null ) { matNode = new SoVRMLMaterial(); appNode.material.setValue(matNode); matNode.diffuseColor.setValue(0f, 0f, 0f); matNode.emissiveColor.setValue(1f, 1, 1f); } // At this point the material node should exist. // Do the actual attach and display the material editor materialEditor.attach(matNode); materialEditor.setVisible(true); }
Example 22.11. How to attach a Manipulator
Handle special case of VRML shape.
SoPath *selectionPath // Strategy for VRML2 // On attach: // if tailnode is a Shape { // if numKids == 1 and parent is a VRMLTransform // insert SoTransform as first kid // else { // create an SoVRMLTransform // replace tailnode with VRMLTransform // make tailnode a child of VRMLTransform // insert SoTransform as first child of new VRMLTransform // } // } // // On detach: // Manip should always be first child of a VRMLTransform // remove manip from scene graph // concatenate manip values into VRMLTransform SoPath *xfPath = NULL; // Get shape node SoNode *pTailNode = selectionPath->getTail(); if (pTailNode->isOfType(SoVRMLShape::getClassTypeId())) { // Get shape's parent SoNode *pNode = pSelectedPath->getNodeFromTail(1); if (pNode->isOfType(SoVRMLParent::getClassTypeId())) { SoVRMLShape *pShapeNode = (SoVRMLShape*)pTailNode; SoVRMLParent *pParentNode = (SoVRMLParent*)pNode; SoTransform *pXform; // Case of only one child AND parent is a VRMLTransform // (if parent was not a VRMLTransform then we wouldn't have // a place to put final transform values when we're done!) if (pParentNode->getNumChildren() == 1 && pParentNode->isOfType(SoVRMLTransform::getClassTypeId())) { // In this case make manipulator first child // of existing parent node. // Copy and pop the shape node off the path xfPath = pSelectedPath->copy(); xfPath->ref(); xfPath->pop(); // Insert SoTransform (for manip to replace) as 1st child pXform = new SoTransform; pParentNode->insertChild(pXform, 0); // Append the SoTransform to the manip path xfPath->append(0); } // Else push geometry down one level so it is the only child // of a VRMLTransform node (where we will put final xform). else { // Copy and pop the shape node off the path xfPath = pSelectedPath->copy(); xfPath->ref(); xfPath->pop(); // Replace geometry with a new VRMLTransform node pShapeNode->ref(); SoVRMLTransform *pVXNode = new SoVRMLTransform; pParentNode->replaceChild(pShapeNode, pVXNode); // Insert SoTransform (for manip to replace) as 1st child pXform = new SoTransform; pVXNode->addChild(pXform); pVXNode->addChild(pShapeNode); pShapeNode->unref(); // Append new VRMLTransform and SoTransform to manip path xfPath->append(pVXNode); xfPath->append(pXform); } // Note: The following is the same as code in SceneViewer // function findTransformForAttach used in non-VRML case. // Since we created transform node, we will set the 'center' // field based on the geometric center. { // First, find 'applyPath' by popping nodes off the path // until you reach a separator. This path will contain all // nodes affected by transform at the end of 'pathToManip' SoFullPath *applyPath = (SoFullPath *) xfPath->copy(); applyPath->ref(); for (int i = (applyPath->getLength() - 1); i >0; i--) { if (applyPath->getNode(i)->isOfType(SoSeparator::getClassTypeId())|| applyPath->getNode(i)->isOfType(SoVRMLParent::getClassTypeId())) break; applyPath->pop(); } // Next, apply a bounding box action to applyPath, and // reset the bounding box just before the tail of // 'pathToXform' (which is just the editXform). This will // assure that the only things included in the resulting // bbox will be those affected by the editXform. SoGetBoundingBoxAction bboxAction(currentViewer->getViewportRegion()); bboxAction.setResetPath(xfPath,TRUE,SoGetBoundingBoxAction::BBOX); bboxAction.apply(applyPath); applyPath->unref(); // Get the center of the bbox in world space... SbVec3f worldBoxCenter = bboxAction.getBoundingBox().getCenter(); // Convert it into local space of the transform... SbVec3f localBoxCenter; SoGetMatrixAction ma(currentViewer->getViewportRegion()); ma.apply(xfPath); ma.getInverse().multVecMatrix(worldBoxCenter, localBoxCenter); // Finally, set the center value... pXform->center.setValue(localBoxCenter); } } } // If not VRML case, do the traditional Inventor thing if (xfPath == NULL) { xfPath = findTransformForAttach(selectedPath); if (xfPath == NULL) return; xfPath->ref(); } // Create manipulator (substitute whatever type you want) // // Note: In an actual application you will probably want to create // the manipulator *once* and keep it in a global, ie. re-use the // same manipulator for different geometry. SoTransformerManip *theXfManip = new SoTransformerManip; theXfManip->ref(); // Replace the SoTransform with our manipulator theXfManip->replaceNode(xfPath); // Cleanup theXfManip->unref(); xfPath->unref(); return;
SoPath selectionPath // Strategy for VRML2 // On attach: // if tailnode is a Shape { // if numKids == 1 and parent is a VRMLTransform // insert SoTransform as first kid // else { // create an SoVRMLTransform // replace tailnode with VRMLTransform // make tailnode a child of VRMLTransform // insert SoTransform as first child of new VRMLTransform // } // } // // On detach: // Manip should always be first child of a VRMLTransform // remove manip from scene graph // concatenate manip values into VRMLTransform xfPath = null; // Get shape node SoNode tailNode = selectionPath.GetTail(); if (tailNode is SoVRMLShape) { // Get shape's parent SoNode node = selectionPath.GetNodeFromTail(1); if (node is SoVRMLParent) { SoVRMLShape shapeNode = (SoVRMLShape)tailNode; SoVRMLParent parentNode = (SoVRMLParent)node; SoTransform xform; // Case of only one child AND parent is a VRMLTransform // (if parent was not a VRMLTransform then we wouldn't have // a place to put final transform values when we're done!) if (parentNode.GetNumChildren() == 1 && parentNode is SoVRMLTransform) { // In this case make manipulator first child // of existing parent node. // Copy and pop the shape node off the path xfPath = selectionPath.Copy(); xfPath.Pop(); // Insert SoTransform (for manip to replace) as 1st child xform = new SoTransform(); parentNode.InsertChild(xform, 0); // Append the SoTransform to the manip path xfPath.Append(0); } // Else push geometry down one level so it is the only child // of a VRMLTransform node (where we will put final xform). else { // Copy and pop the shape node off the path xfPath = selectionPath.Copy(); xfPath.Pop(); // Replace geometry with a new VRMLTransform node SoVRMLTransform vXNode = new SoVRMLTransform(); parentNode.ReplaceChild(shapeNode, vXNode); // Insert SoTransform (for manip to replace) as 1st child xform = new SoTransform(); vXNode.AddChild(xform); vXNode.AddChild(shapeNode); // Append new VRMLTransform and SoTransform to manip path xfPath.Append(vXNode); xfPath.Append(xform); } // Note: The following is the same as code in SceneViewer // function findTransformForAttach used in non-VRML case. // Since we created transform node, we will set the 'center' // field based on the geometric center. { // First, find 'applyPath' by popping nodes off the path // until you reach a separator. This path will contain all // nodes affected by transform at the end of 'pathToManip' SoPath applyPath = xfPath.Copy(); for (int i = (applyPath.GetLength() - 1); i >0; i--) { if (applyPath.GetNode(i) is SoSeparator || applyPath.GetNode(i) is SoVRMLParent) break; applyPath.Pop(); } // Next, apply a bounding box action to applyPath, and // reset the bounding box just before the tail of // 'pathToXform' (which is just the editXform). This will // assure that the only things included in the resulting // bbox will be those affected by the editXform. SoGetBoundingBoxAction bboxAction = new SoGetBoundingBoxAction(myViewer.GetViewportRegion()); bboxAction.SetResetPath(xfPath,true,SoGetBoundingBoxAction.ResetTypes.BBOX); bboxAction.Apply(applyPath); // Get the center of the bbox in world space... SbVec3f worldBoxCenter = bboxAction.GetBoundingBox().GetCenter(); // Convert it into local space of the transform... SbVec3f localBoxCenter; SoGetMatrixAction ma = new SoGetMatrixAction(myViewer.GetViewportRegion()); ma.Apply(xfPath); ma.GetInverse().MultVecMatrix(ref worldBoxCenter, out localBoxCenter); // Finally, set the center value... xform.center.SetValue(localBoxCenter); } } } // If not VRML case, do the traditional Inventor thing if (xfPath == null) { xfPath = findTransformForAttach(selectionPath); if (xfPath == null) return; } // Create manipulator (substitute whatever type you want) // // Note: In an actual application you will probably want to create // the manipulator *once and keep it in a global, ie. re-use the // same manipulator for different geometry. SoTransformerManip theXfManip = new SoTransformerManip(); // Replace the SoTransform with our manipulator theXfManip.ReplaceNode(xfPath);
SoPath selectionPath // Strategy for VRML2 // On attach: // if tailnode instanceof a Shape { // if numKids == 1 and parent instanceof a VRMLTransform // insert SoTransform as first kid // else { // create an SoVRMLTransform // replace tailnode with VRMLTransform // make tailnode a child of VRMLTransform // insert SoTransform as first child of new VRMLTransform // } // } // // On detach: // Manip should always be first child of a VRMLTransform // remove manip from scene graph // concatenate manip values into VRMLTransform xfPath = null; // Get shape node SoNode tailNode = selectionPath.regular.getTail(); if ( tailNode instanceof SoVRMLShape ) { // Get shape's parent SoNode node = selectionPath.regular.getNodeFromTail(1); if ( node instanceof SoVRMLParent ) { SoVRMLShape shapeNode = (SoVRMLShape) tailNode; SoVRMLParent parentNode = (SoVRMLParent) node; SoTransform xform; // Case of only one child AND parent instanceof a VRMLTransform // (if parent was not a VRMLTransform then we wouldn't have // a place to put final transform values when we're done!) if ( parentNode.getNumChildren() == 1 && parentNode instanceof SoVRMLTransform ) { // In this case make manipulator first child // of existing parent node. // Copy and pop the shape node off the path xfPath = selectionPath.regular.copy(0); xfPath.regular.pop(); // Insert SoTransform (for manip to replace) as 1st child xform = new SoTransform(); parentNode.insertChild(xform, 0); // Append the SoTransform to the manip path xfPath.regular.append(0); } // Else push geometry down one level so it instanceof the only child // of a VRMLTransform node (where we will put final xform). else { // Copy and pop the shape node off the path xfPath = selectionPath.regular.copy(0); xfPath.regular.pop(); // Replace geometry with a new VRMLTransform node SoVRMLTransform vXNode = new SoVRMLTransform(); parentNode.replaceChild(shapeNode, vXNode); // Insert SoTransform (for manip to replace) as 1st child xform = new SoTransform(); vXNode.addChild(xform); vXNode.addChild(shapeNode); // Append new VRMLTransform and SoTransform to manip path xfPath.regular.append(vXNode); xfPath.regular.append(xform); } // Note: The following instanceof the same as code in SceneViewer // function findTransformForAttach used in non-VRML case. // Since we created transform node, we will set the 'center' // field based on the geometric center. { // First, find 'applyPath' by popping nodes off the path // until you reach a separator. This path will contain all // nodes affected by transform at the end of 'pathToManip' SoPath applyPath = xfPath.regular.copy(0); for ( int i = (applyPath.regular.getLength() - 1); i > 0; i-- ) { if ( applyPath.regular.getNode(i) instanceof SoSeparator || applyPath.regular.getNode(i) instanceof SoVRMLParent ) break; applyPath.regular.pop(); } // Next, apply a bounding box action to applyPath, and // reset the bounding box just before the tail of // 'pathToXform' (which instanceof just the editXform). This will // assure that the only things included in the resulting // bbox will be those affected by the editXform. SoGetBoundingBoxAction bboxAction = new SoGetBoundingBoxAction(myViewer.getArea().getViewportRegion()); bboxAction.setResetPath(xfPath, true, SoGetBoundingBoxAction.ResetTypes.BBOX); bboxAction.apply(applyPath); // Get the center of the bbox in world space... SbVec3f worldBoxCenter = bboxAction.getBoundingBox().getCenter(); // Convert it into local space of the transform... SbVec3f localBoxCenter; SoGetMatrixAction ma = new SoGetMatrixAction(myViewer.getArea().getViewportRegion()); ma.apply(xfPath); localBoxCenter = ma.getInverse().multVecMatrix(worldBoxCenter); // Finally, set the center value... xform.center.setValue(localBoxCenter); } } } // If not VRML case, do the traditional Inventor thing if ( xfPath == null ) { xfPath = findTransformForAttach(selectionPath); if ( xfPath == null ) return; } // Create manipulator (substitute whatever type you want) // // Note: In an actual application you will probably want to create // the manipulator *once and keep it in a global, ie. re-use the // same manipulator for different geometry. SoTransformerManip theXfManip = new SoTransformerManip(); // Replace the SoTransform with our manipulator theXfManip.replaceNode(xfPath);
Example 22.12. How to detach a Manipulator
Handle special case for VRML
SoPath *xfPath -- The path to the manipulator // Strategy for VRML // This should be the case where, at attach time we had a Shape node // with a VRMLTransform as its parent (we forced this case if // necessary). If so then, at the tail of the path, we have a // VRMLTransform with at least two children: a manipulator and a // VRMLShape. We will remove the manipulator and combine its // transformation into the VRMLTransform node, neatly leaving // the scene graph legal VRML (if it was before). // Just to be safe though, check that the path is long enough, the // parent is the right type and there are the right number of // children. If anything looks funny we'll just do a classic // Inventor detach using replaceManip. This will leave an SoTransform // in the graph but functionally that's OK. if (xfPath->getLength() > 1) { // Get parent of manipulator SoNode *pNode = xfPath->getNodeFromTail(1); if (pNode->isOfType(SoVRMLTransform::getClassTypeId())) { SoVRMLTransform *pParentNode = (SoVRMLTransform*)pNode; if (pParentNode->getNumChildren() == 2) { SoTransformManip *pManip = (SoTransformManip*)xfPath->getTail(); // Create a temporary SoVRMLTransform and copy field // values from manip SoVRMLTransform *pTempNode = new SoVRMLTransform; pTempNode->ref(); pTempNode->rotation = pManip->rotation.getValue(); pTempNode->translation = pManip->translation.getValue(); pTempNode->scale = pManip->scaleFactor.getValue(); pTempNode->center = pManip->center.getValue(); pTempNode->scaleOrientation = pManip->scaleOrientation.getValue(); // Fold the new transformation into the existing one // (in the parent node) pParentNode->combineLeft(pTempNode); // We're done with the manip and the temporary node now pParentNode->removeChild(pManip); pTempNode->unref(); return; } } }
SoPath xfPath -- The path to the manipulator // Strategy for VRML // This should be the case where, at attach time we had a Shape node // with a VRMLTransform as its parent (we forced this case if // necessary). If so then, at the tail of the path, we have a // VRMLTransform with at least two children: a manipulator and a // VRMLShape. We will remove the manipulator and combine its // transformation into the VRMLTransform node, neatly leaving // the scene graph legal VRML (if it was before). // Just to be safe though, check that the path is long enough, the // parent is the right type and there are the right number of // children. If anything looks funny we'll just do a classic // Inventor detach using replaceManip. This will leave an SoTransform // in the graph but functionally that's OK. if (xfPath.GetLength() > 1) { // Get parent of manipulator SoNode node = xfPath.GetNodeFromTail(1); if (node is SoVRMLTransform) { SoVRMLTransform parentNode = (SoVRMLTransform)node; if (parentNode.GetNumChildren() == 2) { SoTransformManip manip = (SoTransformManip)xfPath.GetTail(); // Create a temporary SoVRMLTransform and copy field values from manip SoVRMLTransform tempNode = new SoVRMLTransform(); tempNode.rotation.SetValue(manip.rotation.GetValue()); tempNode.translation.SetValue(manip.translation.GetValue()); tempNode.scale.SetValue(manip.scaleFactor.GetValue()); tempNode.center.SetValue(manip.center.GetValue()); tempNode.scaleOrientation.SetValue(manip.scaleOrientation.GetValue()); // Fold the new transformation into the existing one (in the parent node) parentNode.CombineLeft(tempNode); // We're done with the manip and the temporary node now parentNode.RemoveChild(manip); } } }
SoPath xfPath -- The path to the manipulator // Strategy for VRML // This should be the case where, at attach time we had a Shape node // with a VRMLTransform as its parent (we forced this case if // necessary). If so then, at the tail of the path, we have a // VRMLTransform with at least two children: a manipulator and a // VRMLShape. We will remove the manipulator and combine its // transformation into the VRMLTransform node, neatly leaving // the scene graph legal VRML (if it was before). // Just to be safe though, check that the path instanceof long enough, // the // parent instanceof the right type and there are the right number of // children. If anything looks funny we'll just do a classic // Inventor detach using replaceManip. This will leave an SoTransform // in the graph but functionally that's OK. if ( xfPath.regular.getLength() > 1 ) { // Get parent of manipulator SoNode node = xfPath.regular.getNodeFromTail(1); if ( node instanceof SoVRMLTransform ) { SoVRMLTransform parentNode = (SoVRMLTransform) node; if ( parentNode.getNumChildren() == 2 ) { SoTransformManip manip = (SoTransformManip) xfPath.regular.getTail(); // Create a temporary SoVRMLTransform and copy field values from // manip SoVRMLTransform tempNode = new SoVRMLTransform(); tempNode.rotation.setValue(manip.rotation.getValue()); tempNode.translation.setValue(manip.translation.getValue()); tempNode.scale.setValue(manip.scaleFactor.getValue()); tempNode.center.setValue(manip.center.getValue()); tempNode.scaleOrientation.setValue(manip.scaleOrientation.getValue()); // Fold the new transformation into the existing one (in the // parent node) parentNode.combineLeft(tempNode); // We're done with the manip and the temporary node now parentNode.removeChild(manip); } } }