You can use manipulators in your application in various ways:
You can use the replaceNode() method to replace certain kinds of nodes in the scene graph with an editable version. When the user is finished manipulating the node, use the replaceManip() method to restore the original node to the scene graph.
You can write your own callback functions to use the field values of the manipulator. The callback functions described in the section called “Callback Functions” can be used for any manipulator. (Recall that these functions belong to the dragger, so you need to call getDragger() before using them.)
You can also combine use of these two techniques. For example, you can use replaceNode() to replace an SoTransform( C++ | Java | .NET ) with a manipulator. Then you can use a value-changed callback to notify the application when any of the manipulator's dragger fields changes, and the application can use this new value, if desired.
The following sections describe both of these techniques in more detail.
To use any manipulator in an application, follow these basic steps:
Construct the manipulator.
Reference it if you plan on reusing it.
Replace the node in the scene graph with the manipulator. Manipulators derived from SoTransform( C++ | Java | .NET ), such as the handle box and trackball, replace an SoTransform( C++ | Java | .NET ) node. An SoDirectionalLight- Manip replaces an SoDirectionalLight( C++ | Java | .NET ) node, an SoPointLightManip( C++ | Java | .NET ) replaces an SoPointLight( C++ | Java | .NET ) node, and so on.
The replaceNode() method takes a path as an argument:
replaceNode(SoPath *p)
ReplaceNode(SoPath p)
replaceNode(SoPath p)
The path is supplied by the application. For example, Figure 17.8, “ Specifying the Path to the Target Node ” shows the path to a target SoTransform( C++ | Java | .NET ) node. When a transform manipulator replaces this node, editing the manipulator will affect cube2 in the scene graph.
Manipulators subclassed from SoTransformManip( C++ | Java | .NET ) use special nodes to maintain their shape (so that the trackball remains spherical, for example) and to ensure that they surround the shape objects they affect. These nodes are described in The Inventor Toolmaker.
To remove the manipulator from the scene graph:
Because the manipulator methods replaceManip() and replaceNode() exchange the new node for the tail of the given path, you can reuse the path for subsequent calls to these methods.
For example, if we begin with:
myManip = new SoTrackballManip; myPathToTransform = createPathtoTransform(pickPath);
SoTrackballManip myManip = new SoTrackballManip(); SoPath myPathToTransform = createPathtoTransform(pickPath);
SoTrackballManip myManip = new SoTrackballManip(); SoPath myPathToTransform = createPathtoTransform(pickPath);
Then we can call:
myManip->replaceNode(myPathToTransform);
myManip.ReplaceNode(myPathToTransform);
myManip.replaceNode(myPathToTransform);
to put the manipulator at the end of the path.
Later, we can call
myManip->replaceManip(myPathToTransform, new SoTransform);
myManip.ReplaceManip(myPathToTransform, new SoTransform());
myManip.replaceManip(myPathToTransform, new SoTransform());
to remove the manipulator and replace it with a transform.
Example 17.3, “ Using Manipulators to Transform Objects ” displays a cube, a sphere, and a lamp. The lamp is read from a file and inserted as the “contents” part of an SoWrapperKit( C++ | Java | .NET ). When the user picks the cube, a trackball replaces the transform node that affects the cube. When the user picks the sphere, a handle box replaces the transform node that affects the sphere. When the user picks the lamp, a transform box replaces the “transform” part of the wrapper kit containing the lamp. Figure 17.9, “ Adding Manipulators to a Scene ” shows an image created by this program. This example shows the following techniques:
Using replaceNode() and replaceManip() to make certain nodes in the scene graph editable and to restore the original nodes when manipulation finishes
Using selection callbacks (see Chapter 10, Handling Events and Selection)
Example 17.3. Using Manipulators to Transform Objects
// Note that for illustration purposes, the // cube and SoWrapperKit already have transform nodes // associated with them; the sphere does not. In all cases, // the routine createTransformPath() is used to find the // transform node that affects the picked object. #include <Inventor/SoDB.h> #include <Inventor/SoInput.h> #include <Inventor/manips/SoHandleBoxManip.h> #include <Inventor/manips/SoTrackballManip.h> #include <Inventor/manips/SoTransformBoxManip.h> #include <Inventor/nodekits/SoWrapperKit.h> #include <Inventor/nodes/SoCamera.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoGroup.h> #include <Inventor/nodes/SoLight.h>
#include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoSelection.h> #include <Inventor/nodes/SoSphere.h> #include <Inventor/nodes/SoTransform.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> // function prototypes void selectionCallback(void *, SoPath *); void deselectionCallback(void *, SoPath *); void dragStartCallback(void *, SoDragger *); void dragFinishCallback(void *, SoDragger *); // global data SoSeparator *root; SoHandleBoxManip *myHandleBox; SoTrackballManip *myTrackball; SoTransformBoxManip *myTransformBox; SoPath *handleBoxPath = NULL; SoPath *trackballPath = NULL; SoPath *transformBoxPath = NULL; main(int, char **argv) { // Initialize Inventor and Xt Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); // Create and set up the selection node SoSelection *selectionRoot = new SoSelection; selectionRoot->ref(); selectionRoot-> addSelectionCallback(selectionCallback, NULL); selectionRoot-> addDeselectionCallback(deselectionCallback, NULL); // Create the scene graph root = new SoSeparator; selectionRoot->addChild(root); // Read a file into contents of SoWrapperKit // Translate it to the right. SoWrapperKit *myWrapperKit = new SoWrapperKit; root->addChild(myWrapperKit); SoInput myInput; if (!myInput.openFile("luxo.iv")) return (1); SoSeparator *objectFromFile = SoDB::readAll(&myInput); if (objectFromFile == NULL) return (1); myWrapperKit->setPart("contents",objectFromFile); myWrapperKit->set("transform { translation 3 -1 0 }"); SoMaterial *wrapperMat = (SoMaterial *) myWrapperKit->getPart("material",TRUE); wrapperMat->diffuseColor.setValue(.8, .8, .8); // Create a cube with its own transform. SoSeparator *cubeRoot = new SoSeparator; SoTransform *cubeXform = new SoTransform; cubeXform->translation.setValue(-4, 0, 0); root->addChild(cubeRoot); cubeRoot->addChild(cubeXform); SoMaterial *cubeMat = new SoMaterial; cubeMat->diffuseColor.setValue(.8, .8, .8); cubeRoot->addChild(cubeMat); cubeRoot->addChild(new SoCube); // Add a sphere node without a transform // (one will be added when we attach the manipulator) SoSeparator *sphereRoot = new SoSeparator; SoMaterial *sphereMat = new SoMaterial; root->addChild(sphereRoot); sphereRoot->addChild(sphereMat); sphereRoot->addChild(new SoSphere); sphereMat->diffuseColor.setValue(.8, .8, .8); // Create the manipulators myHandleBox = new SoHandleBoxManip; myHandleBox->ref(); myTrackball = new SoTrackballManip; myTrackball->ref(); myTransformBox = new SoTransformBoxManip; myTransformBox->ref(); // Get the draggers and add callbacks to them. Note // that you don't put callbacks on manipulators. You put // them on the draggers which handle events for them. SoDragger *myDragger; myDragger = myTrackball->getDragger(); myDragger->addStartCallback(dragStartCallback,cubeMat); myDragger->addFinishCallback(dragFinishCallback,cubeMat); myDragger = myHandleBox->getDragger(); myDragger->addStartCallback(dragStartCallback,sphereMat); myDragger->addFinishCallback(dragFinishCallback,sphereMat); myDragger = myTransformBox->getDragger(); myDragger->addStartCallback(dragStartCallback,wrapperMat); myDragger->addFinishCallback(dragFinishCallback,wrapperMat); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(selectionRoot); myViewer->setTitle("Attaching Manipulators"); myViewer->show(); myViewer->viewAll(); SoXt::show(myWindow); SoXt::mainLoop(); } // Is this node of a type that is influenced by transforms? SbBool isTransformable(SoNode *myNode) { if (myNode->isOfType(SoGroup::getClassTypeId()) || myNode->isOfType(SoShape::getClassTypeId()) || myNode->isOfType(SoCamera::getClassTypeId()) || myNode->isOfType(SoLight::getClassTypeId())) return TRUE; else return FALSE; } // Create a path to the transform node that affects the tail // of the input path. Three possible cases: // [1] The path-tail is a node kit. Just ask the node kit for // a path to the part called "transform" // [2] The path-tail is NOT a group. Search siblings of path // tail from right to left until you find a transform. If // none is found, or if another transformable object is // found (shape,group,light,or camera), then insert a // transform just to the left of the tail. This way, the // manipulator only affects the selected object. // [3] The path-tail IS a group. Search its children left to // right until a transform is found. If a transformable // node is found first, insert a transform just left of // that node. This way the manip will affect all nodes // in the group. SoPath * createTransformPath(SoPath *inputPath) { int pathLength = inputPath->getLength(); if (pathLength < 2) // Won't be able to get parent of tail return NULL; SoNode *tail = inputPath->getTail(); // CASE 1: The tail is a node kit. // Nodekits have built in policy for creating parts. // The kit copies inputPath, then extends it past the // kit all the way down to the transform. It creates the // transform if necessary. if (tail->isOfType(SoBaseKit::getClassTypeId())) { SoBaseKit *kit = (SoBaseKit *) tail; return kit->createPathToPart("transform",TRUE,inputPath); } SoTransform *editXf = NULL; SoGroup *parent; SbBool existedBefore = FALSE; // CASE 2: The tail is not a group. SbBool isTailGroup; isTailGroup = tail->isOfType(SoGroup::getClassTypeId()); if (!isTailGroup) { // 'parent' is node above tail. Search under parent right // to left for a transform. If we find a 'movable' node // insert a transform just left of tail. parent = (SoGroup *) inputPath->getNode(pathLength - 2); int tailIndx = parent->findChild(tail); for (int i = tailIndx; (i >= 0) && (editXf == NULL);i--){ SoNode *myNode = parent->getChild(i); if (myNode->isOfType(SoTransform::getClassTypeId())) editXf = (SoTransform *) myNode; else if (i != tailIndx && (isTransformable(myNode))) break; } if (editXf == NULL) { existedBefore = FALSE; editXf = new SoTransform; parent->insertChild(editXf, tailIndx); } else existedBefore = TRUE; } // CASE 3: The tail is a group. else { // Search the children from left to right for transform // nodes. Stop the search if we come to a movable node // and insert a transform before it. parent = (SoGroup *) tail; for (int i = 0; (i < parent->getNumChildren()) && (editXf == NULL); i++) { SoNode *myNode = parent->getChild(i); if (myNode->isOfType(SoTransform::getClassTypeId())) editXf = (SoTransform *) myNode; else if (isTransformable(myNode)) break; } if (editXf == NULL) { existedBefore = FALSE; editXf = new SoTransform; parent->insertChild(editXf, i); } else existedBefore = TRUE; } // Create 'pathToXform.' Copy inputPath, then make last // node be editXf. SoPath *pathToXform = NULL; pathToXform = inputPath->copy(); pathToXform->ref(); if (!isTailGroup) // pop off the last entry. pathToXform->pop(); // add editXf to the end int xfIndex = parent->findChild(editXf); pathToXform->append(xfIndex); pathToXform->unrefNoDelete(); return(pathToXform); } // This routine is called when an object // gets selected. We determine which object // was selected, then call replaceNode() // to replace the object's transform with // a manipulator. void selectionCallback( void *, // user data is not used SoPath *selectionPath) { // Attach the manipulator. // Use the convenience routine to get a path to // the transform that affects the selected object. SoPath *xformPath = createTransformPath(selectionPath); if (xformPath == NULL) return; xformPath->ref(); // Attach the handle box to the sphere, // the trackball to the cube // or the transformBox to the wrapperKit if (selectionPath->getTail()->isOfType( SoSphere::getClassTypeId())) { handleBoxPath = xformPath; myHandleBox->replaceNode(xformPath); } else if (selectionPath->getTail()-> isOfType(SoCube::getClassTypeId())) { trackballPath = xformPath; myTrackball->replaceNode(xformPath); } else if (selectionPath->getTail()-> isOfType(SoWrapperKit::getClassTypeId())) { transformBoxPath = xformPath; myTransformBox->replaceNode(xformPath); } } // This routine is called whenever an object gets // deselected. It detaches the manipulator from // the transform node, and removes it from the // scene graph that will not be visible. void deselectionCallback( void *, // user data is not used SoPath *deselectionPath) { if (deselectionPath->getTail()-> isOfType(SoSphere::getClassTypeId())) { myHandleBox->replaceManip(handleBoxPath,NULL); handleBoxPath->unref(); } else if (deselectionPath->getTail()-> isOfType(SoCube::getClassTypeId())) { myTrackball->replaceManip(trackballPath,NULL); trackballPath->unref(); } else if (deselectionPath->getTail()-> isOfType(SoWrapperKit::getClassTypeId())) { myTransformBox->replaceManip(transformBoxPath,NULL); transformBoxPath->unref(); } } // This is called when a manipulator is // about to begin manipulation. void dragStartCallback( void *myMaterial, // user data SoDragger *) // callback data not used { ((SoMaterial *) myMaterial)->diffuseColor=SbColor(1,.2,.2); } // This is called when a manipulator is // done manipulating. void dragFinishCallback( void *myMaterial, // user data SoDragger *) // callback data not used { ((SoMaterial *) myMaterial)->diffuseColor=SbColor(.8,.8,.8); }
package inventor.mentor.attachManip; import tools.*; import com.openinventor.inventor.nodes.*; import com.openinventor.inventor.manips.*; import com.openinventor.inventor.*; import com.openinventor.inventor.draggers.*; import com.openinventor.inventor.misc.callbacks.*; import com.openinventor.inventor.awt.*; import java.awt.*; public class Main extends DemoInventor { SoHandleBoxManip myHandleBox = new SoHandleBoxManip(); SoTrackballManip myTrackball = new SoTrackballManip(); SoTransformBoxManip myTransformBox = new SoTransformBoxManip(); SoPath handleBoxPath = null; SoPath trackballPath = null; SoPath transformBoxPath = null; SoMaterial cubeMat = new SoMaterial(); SoMaterial sphereMat = new SoMaterial(); SoMaterial coneMat = new SoMaterial(); public static void main(String[] args) { Main applet = new Main(); DemoInventor.isAnApplet = false; applet.start(); demoMain(applet, "Attaching Manipulators"); } public void start() { super.start(); // create and set up the selection node SoSelection selectionRoot = new SoSelection(); // Create a cone SoSeparator coneRoot = new SoSeparator(); SoTransform coneTrans = new SoTransform(); coneTrans.translation.setValue(3, 0, 0); coneMat.diffuseColor.setValue(0.8f, 0.8f, 0.8f); { coneRoot.addChild(coneTrans); coneRoot.addChild(coneMat); coneRoot.addChild(new SoCone()); } // Create a cube with its own transform. SoSeparator cubeRoot = new SoSeparator(); SoTransform cubeXform = new SoTransform(); cubeXform.translation.setValue(-3.0f, 0.0f, 0.0f); cubeMat.diffuseColor.setValue(0.8f, 0.8f, 0.8f); { cubeRoot.addChild(cubeXform); cubeRoot.addChild(cubeMat); cubeRoot.addChild(new SoCube()); } // add a sphere node without a transform // (one will be added when we attach the manipulator) SoSeparator sphereRoot = new SoSeparator(); sphereMat.diffuseColor.setValue(0.8f, 0.8f, 0.8f); { sphereRoot.addChild(sphereMat); sphereRoot.addChild(new SoSphere()); } // create the scene graph SoSeparator root = new SoSeparator(); { // assemble scene graph root.addChild(coneRoot); root.addChild(cubeRoot); root.addChild(sphereRoot); } // Get the draggers and add callbacks to them. Note // that you don't put callbacks on manipulators. You put // them on the draggers which handle events for them. SoDragger myDragger = myTrackball.getDragger(); myDragger.addStartCallback(new DragStartCallback(cubeMat), null); myDragger.addFinishCallback(new DragFinishCallback(cubeMat), null); myDragger = myHandleBox.getDragger(); myDragger.addStartCallback(new DragStartCallback(sphereMat), null); myDragger.addFinishCallback(new DragFinishCallback(sphereMat), null); myDragger = myTransformBox.getDragger(); myDragger.addStartCallback(new DragStartCallback(coneMat), null); myDragger.addFinishCallback(new DragFinishCallback(coneMat), null); { // Assemble scene graph selectionRoot.addSelectionCallback(new SelectionCallback(), null); selectionRoot.addDeselectionCallback(new DeselectionCallback(), null); selectionRoot.addChild(root); } SwSimpleViewer myViewer = new SwSimpleViewer(); myViewer.setSceneGraph(selectionRoot); setLayout(new BorderLayout()); add(myViewer, BorderLayout.CENTER); } // Is this node of a type that is influenced by transforms? boolean isTransformable(SoNode myNode) { if (myNode instanceof SoGroup || myNode instanceof SoShape || myNode instanceof SoCamera || myNode instanceof SoLight) return true; else return false; } SoPath createTransformPath(SoPath inputPath) { int pathLength = inputPath.regular.getLength(); if (pathLength < 2) // Won't be able to get parent of tail return null; SoNode tail = inputPath.regular.getTail(); /* * // CASE 1: The tail is a node kit. // Nodekits have built in policy for * creating parts. // The kit copies inputPath, then extends it past the // * kit all the way down to the transform. It creates the // transform if * necessary. if (tail instanceof SoBaseKit) { SoBaseKit kit = * (SoBaseKit)tail; return kit.createPathToPart("transform", true, * inputPath); } */ SoTransform editXf = null; SoGroup parent; boolean existedBefore = false; // CASE 2: The tail is not a group. boolean isTailGroup; isTailGroup = tail instanceof SoGroup; if (!isTailGroup) { // 'parent' is node above tail. Search under parent right // to left for a transform. If we find a 'movable' node // insert a transform just left of tail. parent = (SoGroup) inputPath.regular.getNode(pathLength - 2); int tailIndx = parent.findChild(tail); for (int i = tailIndx; (i >= 0) && (editXf == null); i--) { SoNode myNode = parent.getChild(i); if (myNode instanceof SoTransform) editXf = (SoTransform) myNode; else if (i != tailIndx && (isTransformable(myNode))) break; } if (editXf == null) { existedBefore = false; editXf = new SoTransform(); parent.insertChild(editXf, tailIndx); } else existedBefore = true; } // CASE 3: The tail is a group. else { // Search the children from left to right for transform // nodes. Stop the search if we come to a movable node. // and insert a transform before it. int i; parent = (SoGroup) tail; for (i = 0; (i < parent.getNumChildren()) && (editXf == null); i++) { SoNode myNode = parent.getChild(i); if (myNode instanceof SoTransform) editXf = (SoTransform) myNode; else if (isTransformable(myNode)) break; } if (editXf == null) { existedBefore = false; editXf = new SoTransform(); parent.insertChild(editXf, i); } else existedBefore = true; } // Create 'pathToXform.' Copy inputPath, then make last // node be editXf. SoPath pathToXform = null; pathToXform = inputPath.regular.copy(0); if (!isTailGroup) // pop off the last entry. pathToXform.regular.pop(); // add editXf to the end int xfIndex = parent.findChild(editXf); pathToXform.regular.append(xfIndex); return (pathToXform); } class SelectionCallback extends SoSelectionPathCB { public void invoke(SoPath path) { // Attach the manipulator. // Use the convenience routine to get a path to // the transform that effects the selected object. SoPath xformPath = createTransformPath(path); if (xformPath == null) return; // Attach the handle box to the sphere, // the trackball to the cube // or the transformBox to the wrapperKit if (path.regular.getTail() instanceof SoSphere) { handleBoxPath = xformPath; myHandleBox.replaceNode(xformPath); } else if (path.regular.getTail() instanceof SoCube) { trackballPath = xformPath; myTrackball.replaceNode(xformPath); } else if (path.regular.getTail() instanceof SoCone) { transformBoxPath = xformPath; myTransformBox.replaceNode(xformPath); } } } class DeselectionCallback extends SoSelectionPathCB { public void invoke(SoPath path) { if (path.regular.getTail() instanceof SoSphere) myHandleBox.replaceManip(handleBoxPath, new SoTransform()); else if (path.regular.getTail() instanceof SoCube) myTrackball.replaceManip(trackballPath, new SoTransform()); else if (path.regular.getTail() instanceof SoCone) myTransformBox.replaceManip(transformBoxPath, new SoTransform()); } } // This is called when a manipulator is // about to begin manipulation. class DragStartCallback extends SoDraggerCB { SoMaterial m_material; public DragStartCallback(SoMaterial mat) { m_material = mat; } public void invoke(SoDragger d) { m_material.diffuseColor.setValue(new SbColor(1.0f, 0.2f, 0.2f)); } } // This is called when a manipulator is // done manipulating. class DragFinishCallback extends SoDraggerCB { SoMaterial m_material; public DragFinishCallback(SoMaterial mat) { m_material = mat; } public void invoke(SoDragger d) { m_material.diffuseColor.setValue(new SbColor(0.8f, 0.8f, 0.8f)); } } }
The SoClipPlaneManip( C++ | Java | .NET )manipulator was developed to allow easy interactive manipulation of the clipping plane (see SoClipPlane( C++ | Java | .NET ) ). The SoClipPlaneManip( C++ | Java | .NET )utilizes an SoJackDragger( C++ | Java | .NET )to provide a 3D interface for moving the clip plane. This manipulator derives from SoClipPlane( C++ | Java | .NET ) and maintains, like all other manipulators, consistency between the fields of the dragger and its own fields. Please read Chapter 17, Draggers and Manipulators of The Inventor Mentor for more information about manipulators.
Example 17.4. How to use SoClipPlaneManip
This example reads an Inventor file and displays an SoClipPlaneManip( C++ | Java | .NET ) at the center of the scene graph. The program source code is available in:
$OIVHOME/src/Inventor/examples/Features/
ClipPlaneManip/ClipPlaneManip.cxx.
int main(int argc, char **argv) { // Initialize Inventor and Xt Widget myWindow = SoXt::init(argv[0]); const char *filename = "../../../../data/models/slotMachine.iv"; if (argc < 2) printf("Usage : %s <file.iv>\n", argv[0]); if (argc >=2) filename = argv[1]; // Read the file SoSeparator *scene = readFile(filename); scene->ref(); // Compute the bounding box of the scene graph. SoGetBoundingBoxAction bboxAction(SbViewportRegion(100, 100)); bboxAction.apply(scene); // Inserting the manipulator at the center of the scene graph // and in the plane YZ. SoClipPlaneManip *clipPlaneManip = new SoClipPlaneManip; clipPlaneManip->setValue(bboxAction.getBoundingBox(),SbVec3f(1, 0, 0), 0.1); scene->insertChild(clipPlaneManip, 0); // Create a viewer SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); // Attach and show viewer myViewer->setSceneGraph(scene); myViewer->setTitle("File Reader"); myViewer->show(); // Loop forever SoXt::show(myWindow); SoXt::mainLoop(); return 0; }
string filename = "$OIVNETHOME/data/models/slotMachine.iv"; string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) Console.WriteLine("Usage : {0} <file.iv>\n", Application.ExecutablePath); if (args.Length >= 2) filename = args[1]; // Read the file SoSeparator scene = ReadFile(filename); // Compute the bounding box of the scene graph. SoGetBoundingBoxAction bboxAction = new SoGetBoundingBoxAction(new SbViewportRegion(100, 100)); bboxAction.Apply(scene); // Inserting the manipulator at the center of the scene graph and in the plane YZ. SoClipPlaneManip clipPlaneManip = new SoClipPlaneManip(); clipPlaneManip.SetValue(bboxAction.GetBoundingBox(), new SbVec3f(1, 0, 0), 0.1f); scene.InsertChild(clipPlaneManip, 0); // Create a viewer SoWinExaminerViewer myViewer = new SoWinExaminerViewer(this, "", true, SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER); // attach and show viewer myViewer.SetSceneGraph(scene); myViewer.SetTitle("Clip Plane Manipulator"); myViewer.Show();
public void start() { super.start(); setLayout(new BorderLayout()); Panel panel = new Panel(new BorderLayout()); myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER); String filename = "../../../../data/models/slotMachine.iv"; if (args.length < 1) System.out.println("Use arg <file.iv>\n"); if (args.length >= 1) filename = args[0]; // Read the file SoSeparator scene = readFile(filename); // Compute the bounding box of the scene graph. SoGetBoundingBoxAction bboxAction = new SoGetBoundingBoxAction(new SbViewportRegion((short)100, (short)100)); bboxAction.apply(scene); // Inserting the manipulator at the center of the scene graph and in the plane YZ. SoClipPlaneManip clipPlaneManip = new SoClipPlaneManip(); clipPlaneManip.setValue(bboxAction.getBoundingBox(), new SbVec3f(1, 0, 0), 0.1f); scene.insertChild(clipPlaneManip, 0); // Create a viewer myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER); // attach and show viewer myViewer.setSceneGraph(scene); panel.add(myViewer); add(panel); }