Open Inventor allows you to bevel all Open Inventor shapes except NURBS and sphere. The beveling is performed using an SoBevelAction( C++ | Java | .NET ) , which traverses a scene graph looking for shapes to bevel.
Shapes can be selected by adding SoBevelProperty( C++ | Java | .NET )and/or SoEdgeFlag( C++ | Java | .NET )and/or SoVertexFlag( C++ | Java | .NET )nodes in the scene graph before the shapes to be beveled.
Five kinds of parameters control the beveling:
An angle: All edges whose adjacent face normals make an angle greater than the specified angle will be beveled (see field angle of SoBevelProperty( C++ | Java | .NET )or method setAngle() of SoBevelAction) .
The bevel radius: The radius specifies the size of the rounded edge or corner (see field radius of SoBevelProperty( C++ | Java | .NET )or method setRadius() of SoBevelAction( C++ | Java | .NET ) ). This radius can be absolute or relative to the value max where max is the length of the longest edge of the current shape to be beveled. For a relative radius, the radius value must be between 0 and 1. The real radius is max * radius (see field absoluteRadius of SoBevelProperty( C++ | Java | .NET )or method enableAbsoluteRadius() of SoBevelAction( C++ | Java | .NET ) ).
Edge marks: Indicate which edges must be beveled and which must not be (SoEdgeFlag( C++ | Java | .NET ) ).
Vertex marks: The same as edge marks but for the vertices (SoVertexFlag( C++ | Java | .NET ) ).
The beveling precision: This depends on the SoComplexity( C++ | Java | .NET )node.
The methods setAngle(), setRadius(), and enableAbsoluteRadius() are available in SoBevelAction( C++ | Java | .NET )as well as SoBevelProperty( C++ | Java | .NET ) . The SoBevelAction( C++ | Java | .NET )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( C++ | Java | .NET )node, they are used to override the global properties locally.
Different effects are possible by combining these parameters different ways:
|
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( C++ | Java | .NET ) , and the bitmask field testsBeforeBevel of SoBevelProperty( C++ | Java | .NET ) . 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.
The action is applied in the same manner as any other action and generates a scene graph containing beveled shapes.
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( C++ | Java | .NET )and SoIndexedFaceSet( C++ | Java | .NET ).
SoBevelAction( C++ | Java | .NET )does not take into consideration PER_FACE or PER_VERTEX materials. Therefore, the shapes to be beveled should have a uniform material (SoMaterialBinding::OVERALL). |
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( C++ | Java | .NET )shape or SoIndexedTriangleStripSet( C++ | Java | .NET )shape, along one, two, or three dimensions. The splitting is performed using SoSplitGeometryAction( C++ | Java | .NET ) , 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 question is: why is the split geometry action useful?
The Open Inventor SoSplitGeometryAction( C++ | Java | .NET )is most useful when you need to visualize large data sets where IndexedFaceSet and/or IndexedTriangleStripSet nodes define most of the geometry. |
Figure 8.10, “ Split geometry action”, displays a large surface described by only one SoIndexedTriangleStripSet( C++ | Java | .NET )node. If you insert an octree at the top of your scene graph, the optimization offered by the octree will not be very efficient because there is only one object in the scene. This means that each render thread must still render the whole surface, which is a lot of redundant computation.
If you apply an SoSplitGeometryAction( C++ | Java | .NET )to this node to split it into 16 new SoIndexedTriangleStripSets as shown in the example (of course, you can split a node into as many pieces as your geometry allows), the Open Inventor rendering process can divide the work between threads and thus improve performance.
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( C++ | Java | .NET )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.
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. |
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. |
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. .NET public delegate void PreCallback( SoSplitGeometryAction action, SoNode objectToSplit, int objectId, int divAlongX, int divAlongY, int divAlongZ ) |
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.
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( C++ | Java | .NET ) :
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); }
SoSplitGeometryAction( C++ | Java | .NET )splits every SoIndexedShape( C++ | Java | .NET ) it finds during traversal of the scene graph. This means that if you apply the action to a scene graph containing several SoIndexedShape( C++ | Java | .NET )nodes, each of the indexed shapes will be divided into the requested number of subdivisions (see figure above). |
Currently this action affects only SoIndexedFaceSet( C++ | Java | .NET )and SoIndexedTriangleStripSet( C++ | Java | .NET )nodes. The new object is the same type of node as the original. |
Texture Mapping: If the original geometry has explicit texture coordinates, the results will be correct. If texture coordinates are computed by Open Inventor, the texture will be applied separately to each new object. |
The first LMV (Large Model Visualization) algorithm Open Inventor includes is geometry simplification. The purpose of this algorithm is to represent the geometry with a smaller number of triangles that approximate the original geometry. After simplification, we can choose between the original geometry and one or more simplified versions of the geometry. Therefore, we have some control over the rendering cost.
Open Inventor integrates mesh decimation through two components: a simplifier and a simplify action. The simplify action can be applied to a scene graph, or some part of it, to produce a simpler representation that will be used for subsequent redraws. A new node, SoLevelOfSimplification( C++ | Java | .NET ) , has been added to group these representations and to keep track of their complexity levels, represented by a percentage of the original number of primitives.
Objects derived from the virtual class SoSimplifier( C++ | Java | .NET )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( C++ | Java | .NET )object derived from SoSimplifier( C++ | Java | .NET ), 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( C++ | Java | .NET ).
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( C++ | Java | .NET )which is used for simplifying the geometry. This simplifier can be an SoDecimator( C++ | Java | .NET )or any class derived from SoSimplifier( C++ | Java | .NET ) by the application.
Several traversal methods are available:
SoGlobalSimplifyAction( C++ | Java | .NET )
SoGlobalSimplifyAction( C++ | Java | .NET ) traverses the scene graph and collects all the triangles in a single list. This list is then simplified and the result is stored in a new scene graph.
The SoGlobalSimplifyAction( C++ | Java | .NET ) provides two simplification strategies:
SIMPLIFY_GLOBALLY: All the triangles found in the scene graph are collected in a single object. Then this object is simplified.
SIMPLIFY_BY_SUBGROUP: Triangles are collected until a material change or a separator is found.
The generateNormals flag can be used to force per-vertex normal computation during an SoGlobalSimplifyAction( C++ | Java | .NET ). When not set, objects may appear faceted rather than smooth. Setting the addShapeHintsNode flag to TRUE tells Open Inventor to build normals at run-time.
By default the SoGlobalSimplifyAction( C++ | Java | .NET ) collects all the triangles even if they are part of a simple shape (Cube, Cone, Sphere, Text3, etc.). Setting the setCatchAllShapesFlag flag to FALSE tells the action to collect triangles only if they belong to a complex shape.
The getSimplifiedSceneGraph() method returns the root of the new scene graph after applying the SoGlobalSimplifyAction( C++ | Java | .NET )
SoShapeSimplifyAction( C++ | Java | .NET )
SoShapeSimplifyAction( C++ | Java | .NET ) traverses the scene graph and replaces every complex shape with its simplified version.
// the Simplifier object: uses geometric simplification SoDecimator *decimer = new SoDecimator; // the simplification action: uses the global simplification. SoShapeSimplifyAction simplify(decimer); int numLevels = 3; static float levels[5] = {1.0f, 0.3f, 0.1f}; simplify.setSimplificationLevels(numLevels,levels); simplify.setMinTriangles(20); simplify.apply(selection); delete decimer;
// the Simplifier object: uses geometric simplification SoDecimator decimer = new SoDecimator(); // the simplification action: uses the global simplification. SoShapeSimplifyAction simplify = new SoShapeSimplifyAction(decimer); int numLevels = 3; float[] levels = new float[] {1.0f, 0.3f, 0.1f}; simplify.SetSimplificationLevels(levels); simplify.SetMinTriangles(20); simplify.Apply(selection);
// the Simplifier object: uses geometric simplification SoDecimator decimer = new SoDecimator(); // the simplification action: uses the global simplification. SoShapeSimplifyAction simplify = new SoShapeSimplifyAction(decimer); int numLevels = 3; float[] levels = new float[] {1.0f, 0.3f, 0.1f}; simplify.setSimplificationLevels(levels); simplify.setMinTriangles(20); simplify.apply(selection);
SoReorganizeAction( C++ | Java | .NET )
SoReorganizeAction( C++ | Java | .NET ) reorganizes the scene graph by grouping shapes with common properties, then groups these shapes into a single shape and runs the simplifier on it. If a simplifier is not provided, it creates an IndexedTriangleStripSet from this shape. This action is similar to the ivfix utility program.
// Replace scene with reorganized scene graph: int numLevels = 3; static float levels[5] = {1.0f, 0.3f, 0.1f}; SoReorganizeAction *simplify; if (doSimplify) { SoDecimator *decimer = new SoDecimator; simplify = new SoReorganizeAction(decimer); simplify->setSimplificationLevels(numLevels,levels); simplify->setSizeFactor(4.); simplify->setMinTriangles(20); } else { simplify = new SoReorganizeAction; simplify->generateNormals(TRUE); simplify->generateTriangleStrips(TRUE); } simplify->apply(sep); sep->unref(); if (simplify->getSimplifiedSceneGraph() != NULL) { SoNode *result = simplify->getSimplifiedSceneGraph(); ... } delete simplify;
// Replace scene with reorganized scene graph: int numLevels = 3; float[] levels = new float[] {1.0f, 0.3f, 0.1f}; SoReorganizeAction simplify; if (doSimplify) { SoDecimator decimer = new SoDecimator(); simplify = new SoReorganizeAction(decimer); simplify.SetSimplificationLevels(levels); simplify.SetSizeFactor(4f); simplify.SetMinTriangles(20); } else { simplify = new SoReorganizeAction(); simplify.GenerateNormals(true); simplify.GenerateTriangleStrips(true); } simplify.Apply(sep); if (simplify.GetSimplifiedSceneGraph() != null) { SoNode result = simplify.GetSimplifiedSceneGraph(); ... }
// Replace scene with reorganized scene graph: int numLevels = 3; float[] levels = new float[] {1.0f, 0.3f, 0.1f}; SoReorganizeAction simplify; if (doSimplify) { SoDecimator decimer = new SoDecimator(); simplify = new SoReorganizeAction(decimer); simplify.setSimplificationLevels(levels); simplify.setSizeFactor(4f); simplify.setMinTriangles(20); } else { simplify = new SoReorganizeAction(); simplify.generateNormals(true); simplify.generateTriangleStrips(true); } simplify.apply(sep); if (simplify.getSimplifiedSceneGraph() != null) { SoNode result = simplify.getSimplifiedSceneGraph(); ... }
Each simplified sub-level can be stored in regular separators or in WWWInline nodes. In this case, the urlName field specifies the full url to use for each WWWInline. For instance if urlName = “dir/file”, the action will generate “dir/file_1.wrl”, ”dir/file_2.wrl”, and so on.
When a simplify action generates SoLevelOfSimplification( C++ | Java | .NET )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.