Open Inventor Release 2024.2.0
 
Loading...
Searching...
No Matches
Modify Geometry

Bevel Action

"Tip"

  • 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, and the bitmask field testsBeforeBevel of 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.

Applying the action

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

Obtaining the results

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 and SoIndexedFaceSet.

SoBevelAction 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 : How to use SoBevelAction

C++ : 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.

C++ :

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();
}

C# :

privatevoid
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();
}

Java :

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 );
}

Split Geometry Action

Split geometry action

The question is: why is the split geometry action useful?

The Open Inventor SoSplitGeometryAction is most useful when you need to visualize large data
sets where IndexedFaceSet and/or IndexedTriangleStripSet nodes define most of the
geometry.

Split geometry action on the Harley model, displays a large surface described by only one SoIndexedTriangleStripSet 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 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.

Specify Options

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 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:

C++ :

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

C# :

SoNode mySwitch = SoNode.GetByName( "switch0" );

Java :

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.

C++ :

To specifiy a pre-split callback function to call when a node of the given type is encountered during traversal, use setSplitGeometryActionPreCB(SoSplitGeometryActionCB *userCB, void *userData). The pre-callback is called 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. SoSplitGeometryActionCB is a defined type:C++ :

typedef void SoSplitGeometryActionCB( const SoSplitGeometryAction* action,
<br /> SoNode* objectToSplit,
const int objectId,
int divAlongX,
int divAlongY,
int divAlongZ,
void* userData );

C# :

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.**C# :**

publicdelegate void
PreCallback( <br /> SoSplitGeometryAction action,
<br /> SoNode objectToSplit,
<br /> int objectId,
<br /> int divAlongX,
<br /> int divAlongY,
<br /> int divAlongZ<br /> )

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.

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 : How to use SoSplitGeometryAction This example splits a model into four pieces using SoSplitGeometryAction :

C++ :

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;
}

C# :

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();
}

Java :

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 );
}

Remarks

Split geometry action on the Harley model

Currently this action affects only SoIndexedFaceSet and SoIndexedTriangleStripSet 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.

Simplify Action

Geometry Simplification

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, 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.

Simplifiers

Objects derived from the virtual class 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 object derived from 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.

Simplifier schema

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 which is used for simplifying the geometry. This simplifier can be an SoDecimator or any class derived from SoSimplifier by the application.

Several traversal methods are available:

  • SoGlobalSimplifyAction 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 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**. 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** 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**

@anchor SoGlobalSimplifyAction_schema
SoGlobalSimplifyAction schema

C++ :

// 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;

C# :

// 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 );

Java :

// 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 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.

C++ :

// 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;

C# :

// 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();
...
}

Java :

// 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 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.