8.10. Modify Geometry


Open Inventor allows you to bevel all Open Inventor shapes except NURBS and sphere. The beveling is performed using an SoBevelAction SoBevelAction SoBevelAction , which traverses a scene graph looking for shapes to bevel.


Shapes can be selected by adding SoBevelProperty SoBevelProperty SoBevelProperty and/or SoEdgeFlag SoEdgeFlag SoEdgeFlag and/or SoVertexFlag SoVertexFlag SoVertexFlag nodes in the scene graph before the shapes to be beveled.

Five kinds of parameters control the beveling:

The methods setAngle(), setRadius(), and enableAbsoluteRadius() are available in SoBevelAction SoBevelAction SoBevelAction as well as SoBevelProperty SoBevelProperty SoBevelProperty . The SoBevelAction SoBevelAction SoBevelAction methods set the global default bevel properties that will apply to beveled shapes. However, if bevel property values have also been set by the SoBevelProperty SoBevelProperty SoBevelProperty node, they are used to override the global properties locally.

[Tip]

Different effects are possible by combining these parameters different ways:

  • A rounded corner: The vertex is marked, but none of the adjacent edges are marked.

  • Rounded edges: There are two types of rounded edges:

    • If both vertices of the edge are unmarked, the rounded edge has a cylindrical cross section.

    • If only one of the two vertices of the edge is marked, the rounded edge looks like a portion of a cone, where the cone vertex is the marked vertex.

Other combinations of parameters are useful for marking vertices and edges easily. For example, we can mark all edges with adjacent face normals making an angle greater than 33 degrees, except the first and the second edge.

Furthermore, some tests can be applied in order to “clean up” the shape before it is beveled. This is the purpose of the methods enableCoplanarTest(), enableDuplicateTest(), and enableOrderingTest() of SoBevelAction SoBevelAction SoBevelAction , and the bitmask field testsBeforeBevel of SoBevelProperty SoBevelProperty SoBevelProperty . The coplanar test, which eliminates coplanar faces, and the duplicate test, which eliminates duplicated faces, are time consuming but are necessary for shapes with coplanar faces or duplicated faces because the bevel action does not work properly with such cases.

As a result of the beveling, another scene graph is created, and can be obtained with the method getSceneGraph(). Each selected shape is beveled, and appears as a scene graph containing SoCoordinate3 SoCoordinate3 SoCoordinate3 and SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet .

Example 8.4. How to use SoBevelAction

This example, which bevels a cube with a bevel radius equal to 20% of the cube size, can be found in: $OIVHOME/src/Inventor/examples/Features/BevelAction/BevelAction.cxx.

void
main(int, char **argv)                      

// Initialize Inventor and Xt 
Widget myWindow = SoXt::init(argv[0]); 
if (myWindow == NULL) exit(1);
  {
    SoInput *input = new SoInput();

    if (!input->openFile("$OIVNETHOME/data/models/simple/cube.iv"))
    exit(0);

    SoSeparator *root = SoDB::readAll(input);
    root->ref();

    SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow);
    myViewer->setBackgroundColor(SbColor(1.0, 1.0, 1.0));

    SoSeparator *result1 = new SoSeparator();
    result1->ref();
    SoComplexity *myComplexity = new SoComplexity();
    myComplexity->value = 1.f;
    result1->addChild(myComplexity);

    SoBevelProperty *prop = new SoBevelProperty();
    prop->radius.setValue(0.2f);
    result1->addChild(prop);
    result1->addChild(root);

    root->unref();

    SoBevelAction baction;
    baction.apply(result1);
    SoGroup *result2 = baction.getSceneGraph();
    result2->ref();

    myViewer->setSceneGraph(result2);
    myViewer->setTitle("BevelAction");
    myViewer->show();
    myViewer->viewAll();

    SoWriteAction myAction;
    myAction.getOutput()->openFile("./bevel.iv");
    myAction.apply(result2);
    myAction.getOutput()->closeFile();

    SoXt::show(myWindow);
    SoXt::mainLoop();
  }
   
private void CreateSample()
{
  SoInput input = new SoInput();

  input.OpenFile("$OIVNETHOME/src/Inventor/examples/data/BevelAction/cube.iv");

  SoSeparator root = SoDB.ReadAll(input);

  SoWinExaminerViewer myViewer = new SoWinExaminerViewer(this, "", true,
        SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.EDITOR);
  myViewer.SetBackgroundColor(new SbColor(1f, 1f, 1f));

  SoSeparator result1 = new SoSeparator();
  SoComplexity myComplexity = new SoComplexity();
  myComplexity.value.Value = 1.0f;
  result1.AddChild(myComplexity);

  SoBevelProperty prop = new SoBevelProperty();
  prop.radius.Value = 0.2f;
  result1.AddChild(prop);
  result1.AddChild(root);

  SoBevelAction baction = new SoBevelAction();
  baction.Apply(result1);
  SoGroup result2 = baction.GetSceneGraph();

  myViewer.SetSceneGraph(result2);
  myViewer.SetTitle("BevelAction");
  myViewer.Show();
  myViewer.ViewAll();

  SoWriteAction myAction = new SoWriteAction();
  myAction.GetOutput().OpenFile("./bevel.iv");
  myAction.Apply(result2);
  myAction.GetOutput().CloseFile();
}
   
public void start()
{
  super.start();
  setLayout(new BorderLayout());
  Panel panel = new Panel(new BorderLayout());

  myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER);

  SoInput input = new SoInput();

  input.openFile("../../../../data/models/simple/cube.iv");

  SoSeparator root = SoDB.readAll(input);

  myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER);
  myViewer.setBackgroundColor(new SbColor(1f, 1f, 1f));

  SoSeparator result1 = new SoSeparator();
  SoComplexity myComplexity = new SoComplexity();
  myComplexity.value.setValue(1f);
  result1.addChild(myComplexity);

  SoBevelProperty prop = new SoBevelProperty();
  prop.radius.setValue(0.2f);
  result1.addChild(prop);
  result1.addChild(root);

  SoBevelAction baction = new SoBevelAction();
  baction.apply(result1);
  SoGroup result2 = baction.getSceneGraph();

  myViewer.setSceneGraph(result2);
  myViewer.viewAll();

  SoWriteAction myAction = new SoWriteAction();
  myAction.getOutput().openFile("./bevel.iv");
  myAction.apply(result2);
  myAction.getOutput().closeFile();

  panel.add(myViewer);
  add(panel);
}


Open Inventor allows you to split any kind of SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet shape or SoIndexedTriangleStripSet SoIndexedTriangleStripSet SoIndexedTriangleStripSet shape, along one, two, or three dimensions. The splitting is performed using SoSplitGeometryAction SoSplitGeometryAction SoSplitGeometryAction , which traverses a scene graph looking for shapes to split. The shapes are split into the number of x, y, and z subdivisions that you request.


The scene graph will be changed by this action. But you can specify how it will be changed.

If you don’t need to keep your original geometry, then the action can replace your original node with a separator containing all new nodes.

But if you want to keep your original geometry, use the setKeepOriginalGeometry()method. This will modify the behavior of the action and replace the original node with an SoSwitch SoSwitch SoSwitch node containing both the original and the new geometry. Each new switch node added to the scene graph is named “switchX” where X is the identifier of the object to split. For example, if this is the first object found by the action, the switch node will be called “switch0”. Use the root of your scene graph and the getByName() method to get the new switch node:

SoNode *mySwitch = myRoot->getByName("switch0");
        
SoNode mySwitch = SoNode.GetByName("switch0");
      
SoNode mySwitch = SoNode.getByName("switch0");

You can specify the desired number of subdivisions in the constructor. The first division value will be used to split the object along its x bounding box value, the second along y, and the third along z if you have requested splitting in three dimensions.

The above approach is suitable when you know the dimensions of your geometry in advance. However, if you have no prior knowledge of its dimensions, you can use the setSmartSplitting()method. A call to this method before applying the action will allow the action to split your geometry using the largest division value to split the object along its greatest bounding box value side, the next largest value to split it along the objects’s next largest bounding box value, and so on.

[Note]

If your scene graph contains switch nodes, you can specify to the action to traverse and split all children or just the active children using the setTraverseAllSoSwitchChildren() method.

[Note]

If you want to see the results of the action, a call to setDistinguishSplitParts()method will apply different materials to all each of the new objects.

[Note]

To specifiy a pre-split delegate to call when a node of the given type is encountered during traversal, use PreTraversal property. The delegate is invoked just before the node is traversed. For each node to split, you can customize how to split it by enabling or disabling options, changing the division values, etc.

public delegate void PreCallback(
	SoSplitGeometryAction action,
	SoNode objectToSplit,
	int objectId,
	int divAlongX,
	int divAlongY,
	int divAlongZ
)
          
[Warning]

The split geometry algorithm does not add, delete, or modify (e.g., split) individual triangles. It collects/sorts triangles into groups (subdivisions). If the geometry is very simple or the requested number of subdivisions is very large, it may not be possible to split the object into as many subdivisions as requested. If the requested number of subdivisions is greater than the maximum possible subdivisions, the object will be split into the maximum possible subdivisions.

A call to the setMaximumDivisionWarning() method enables Inventor warnings to inform you if you’ve requested more than the maximum possible number of subdivisions. Turning on warnings will slow traversal because, to determine if the subdivision value is valid, the average length of every primitive is calculated. This particular Inventor warning is Off by default.

[Warning]

All these options must be set before applying the action.

The action is applied in the same manner as any other action and generates a scene graph containing split shapes.

You can split your geometry along two dimensions, which is best for large flat surface visualizations, or along three dimensions, in which case you must be careful not to include flat indexed shapes in your scene graph or your application will generate an Open Inventor error. The number of divisions – two values to divide along two dimensions, three values to divide along three dimensions – can be specified in the constructor.

Example 8.5.  How to use SoSplitGeometryAction

This example splits a model into four pieces using SoSplitGeometryAction SoSplitGeometryAction SoSplitGeometryAction :

int
main(int argc, char **argv)
{
 //Create the window
 HWND myWindow = SoWin::init(argv[0]);
 if (myWindow == NULL) return;
 
 //Create the root :
 SoSeparator *myRoot = new SoSeparator;
 myRoot->ref();
 
 //Read a file containing a big SoIndexedFaceSet :
 SoSeparator *myScene = new SoSeparator;
 char *myFile = "crater.iv";
 if (myFile)
 {
   myScene = readFile(myFile);
   myScene->ref();
 }
 
 //create scene graph :
 myOctree->addChild(myScene);
 
 //apply the SoSplitGeometryAction to divide the model in 4 pieces:
 SoSplitGeometryAction mySplitAction(2,2);
 mySplitAction.setSmartSplitting();
 mySplitAction.setDistinguishSplitParts();
 mySplitAction.apply(myRoot);
 
 //create an Xt viewer :
 SoXtExaminerViewer *myViewer = new
 SoXtExaminerViewer(myWindow,"ivView");
 
 myViewer->setSceneGraph(myRoot);
 
 SoXt::show(myWindow);
 SoXt::mainLoop();
 return 0;
}
            
public void CreateSample()
{
  //Create the root :
  SoSeparator myRoot = new SoSeparator();
  
  //read the file
  SoSeparator myScene;
  
  myScene = readFile("$OIVNETHOME/src/Inventor/examples/data/crater.iv");
  
  //create the action... :
  SoSplitGeometryAction mySplitAction = new SoSplitGeometryAction(2, 2);
  mySplitAction.SetSmartSplitting(true);
  mySplitAction.SetDistinguishSplitParts(true);
  
  //... and split the scene :
  mySplitAction.Apply(myScene);
  
  //create scene graph :
  myOctree.AddChild(myScene);
  
  //create an Examiner Viewer :
  SoWinExaminerViewer myViewer = new SoWinExaminerViewer(this, "", true,
  SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER);
  
  myViewer.SetSceneGraph(myRoot);
  myViewer.SetTitle("OctreeAndSplitAction");
  myViewer.Show();
}
          
public void start()
{
  super.start();
  setLayout(new BorderLayout());
  Panel panel = new Panel(new BorderLayout());

  // Create the root :
  SoSeparator myRoot = new SoSeparator();

  // read the file
  SoSeparator myScene;

  myScene = readFile("../../../../data/models/wheel.iv");

  // create the action... :
  SoSplitGeometryAction mySplitAction = new SoSplitGeometryAction(2, 2);
  mySplitAction.setSmartSplitting(true);
  mySplitAction.setDistinguishSplitParts(true);

  // ... and split the scene :
  mySplitAction.apply(myScene);

  // create scene graph :
  myOctree.addChild(myScene);

  // create an Examiner Viewer :
  myViewer = new SwSimpleViewer(SwSimpleViewer.EXAMINER);
  myViewer.setSceneGraph(myRoot);

  panel.add(myViewer);
  add(panel);
}

Objects derived from the virtual class SoSimplifier SoSimplifier SoSimplifier receive a set of triangles and return an Open Inventor node representing a simplified version of the original geometry. Thus one can ask for a simplified version of a triangle list containing 50% of the original number of triangles.

Open Inventor contains an SoDecimator SoDecimator SoDecimator object derived from SoSimplifier SoSimplifier SoSimplifier , which implements a geometry simplification algorithm. This algorithm uses iterative contractions of vertex pairs to simplify models. Simplifiers are used internally by simplify actions but can also be used by any application to generate a simplified node from a given list of triangles not stored in an Open Inventor node. The programmer can implement new simplification methods by deriving from SoSimplifier SoSimplifier SoSimplifier .


Open Inventor has several simplify actions. Each action can be applied to a specific scene graph to create a simplified version of its geometry.

Each action’s constructor receives an SoSimplifier SoSimplifier SoSimplifier which is used for simplifying the geometry. This simplifier can be an SoDecimator SoDecimator SoDecimator or any class derived from SoSimplifier SoSimplifier SoSimplifier by the application.

Several traversal methods are available:

When a simplify action generates SoLevelOfSimplification SoLevelOfSimplification SoLevelOfSimplification nodes, the range field of each level of simplification node can be given globally in the range field of the simplify action. If the simplify action range field is not set, the simplify action computes it automatically for each level of simplification node. In this case, the sizeFactor field can be used to customize the range computation.

A minimum number of triangles can be set to stop the simplification when the object becomes too small.

Specifying 0% for the last level inserts an empty node in the scene graph. This allows you to completely remove objects that are too small or too far from the viewer.