Engines are usually connected to nodes. You can, though, create a node class that has built-in engines automatically connected to it. Here are some examples that Inventor provides. These nodes provide a convenient mechanism for adding animation to a scene graph:
- SoRotor is a transformation node that spins the rotation angle while keeping the axis constant.
- SoPendulum is a transformation node that oscillates between two rotations.
- SoShuttle is a transformation node that oscillates between two translations.
- SoBlinker is a switch node that cycles through its children. Let's look at examples of rotor and blinker nodes.
Rotor Node
The SoRotor node, derived from SoRotation, changes the angle of rotation at a specified speed. You can use an SoRotor node any place you would use an SoRotation. It has these fields:
| |
| rotation (SoSFRotation) | specifies the rotation (axis and initial angle). The angle changes when the rotor spins. |
| speed (SoSFFloat) | specifies the number of cycles per second. |
| on (SoSFBool) | TRUE to run, FALSE to stop. The default is TRUE. |
The number of times a second it is updated depends on the application. This node contains an engine that is connected to the real-time global field. A Spinning Windmill Using an SoRotor Node illustrates how you could use this node to rotate the vanes of a windmill. It specifies the rotation and speed for the rotor node and adds it to the scene graph before the windmill vanes, as shown in Scene Graph for Rotor Node Example . The rotation axis of the windmill vanes is (0.0, 0.0, 1.0) and the initial angle is 0.0. This rotation angle is updated automatically by the rotor node.
Example : A Spinning Windmill Using an SoRotor Node
C++ :
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoRotor.h>
#include <Inventor/nodes/SoSeparator.h>
SoSeparator*
readFile( const char* filename )
{
// Open the input file
SoInput mySceneInput;
if ( !mySceneInput.openFile( filename ) )
{
fprintf( stderr, "Cannot open file %s\n", filename );
return NULL;
}
C# :
using System;
using System.Windows.Forms;
using OIV.Inventor.Nodes;
using OIV.Inventor.Win.Viewers;
using OIV.Inventor;
namespace _13_7_Rotor {
public partial class MainForm : Form
{
SoWinExaminerViewer myViewer;
public MainForm()
{
InitializeComponent();
CreateSample();
}
SoSeparator
readFile( String filename )
{
// Open the input file
SoInput mySceneInput = new SoInput();
if ( !mySceneInput.OpenFile( filename ) )
{
Console.WriteLine( "Cannot open file " + filename );
return null;
}
Java :
import tools.*;
import com.openinventor.inventor.awt.*;
import com.openinventor.inventor.nodes.*;
import com.openinventor.inventor.*;
import java.awt.*;
SoSeparator
readFile( String filename )
{
// Open the input file
SoInput mySceneInput = new SoInput();
if ( !mySceneInput.openFile( filename ) )
{
System.err.println( "Cannot open file " + filename );
return null;
}
Scene Graph for Rotor Node Example
C++ :
// Read the whole file into the database
SoSeparator* myGraph = SoDB::readAll( &mySceneInput );
if ( myGraph == NULL )
{
fprintf( stderr, "Problem reading file\n" );
return NULL;
}
mySceneInput.closeFile();
return myGraph;
}
main( int, char*\* argv )
{
// Initialize Inventor and Xt
Widget myWindow = SoXt::init( argv[0] );
SoSeparator* root = new SoSeparator;
root->ref();
// Read in the data for the windmill tower
SoSeparator* windmillTower = readFile( "windmillTower.iv" );
root->addChild( windmillTower );
// Add a rotor node to spin the vanes
SoRotor* myRotor = new SoRotor;
myRotor->rotation.setValue( SbVec3f( 0, 0, 1 ), 0 ); // z axis
myRotor->speed = 0.2;
root->addChild( myRotor );
// Read in the data for the windmill vanes
SoSeparator* windmillVanes = readFile( "windmillVanes.iv" );
root->addChild( windmillVanes );
// Create a viewer
SoXtExaminerViewer* myViewer = new SoXtExaminerViewer( myWindow );
// Attach and show viewer
myViewer->setSceneGraph( root );
myViewer->setTitle( "Windmill" );
myViewer->show();
// Loop forever
SoXt::show( myWindow );
SoXt::mainLoop();
}
C# :
// Read the whole file into the database
SoSeparator myGraph = SoDB.ReadAll( mySceneInput );
if ( myGraph == null )
{
Console.WriteLine( "Problem reading file\n" );
return null;
}
mySceneInput.CloseFile();
return myGraph;
}
public void CreateSample()
{
SoSeparator root = new SoSeparator();
// Read in the data for the windmill tower
SoSeparator windmillTower = readFile( "../../../../../data/windmillTower.iv" );
root.AddChild( windmillTower );
// Add a rotor node to spin the vanes
SoRotor myRotor = new SoRotor();
myRotor.rotation.SetValue( new SbVec3f( 0.0f, 0.0f, 1.0f ), ( float )( Math.PI / 32.0f ) ); // z axis
myRotor.speed.Value = ( 0.1f );
root.AddChild( myRotor );
// Read in the data for the windmill vanes
SoSeparator windmillVanes = readFile( "../../../../../data/windmillVanes.iv" );
root.AddChild( windmillVanes );
// Create a viewer
myViewer = new SoWinExaminerViewer( this, "", true, SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER );
// attach and show viewer
myViewer.SetSceneGraph( root );
myViewer.SetTitle( "Windmill" );
}
}
}
Java :
Blinker Node
The SoBlinker node, derived from SoSwitch, cycles among its children by changing the value of the whichChild field. This node has the following fields:
| |
| whichChild (SoSFLong) | index of the child to be traversed. |
| speed (SoSFFloat) | cycles per second. |
| on (SoSFBool) | TRUE to run, FALSE to stop. The default is TRUE. |
When it has only one child, SoBlinker cycles between that child (0) and SO_SWITCH_NONE. Using a Blinker Node to Make a Sign Flash shows how you could make the text string “Eat at Josie's” flash on and off.
Flashing Sign Controlled by a Blinker Node
Example : Using a Blinker Node to Make a Sign Flash
C++ :
// Add the non-blinking part of the sign to the root
root->addChild( eatAt );
// Add the fast-blinking part to a blinker node
SoBlinker* fastBlinker = new SoBlinker;
root->addChild( fastBlinker );
fastBlinker->speed = 2; // blinks 2 times a second
fastBlinker->addChild( josie );
// Add the slow-blinking part to another blinker node
SoBlinker* slowBlinker = new SoBlinker;
root->addChild( slowBlinker );
slowBlinker->speed = 0.5; // 2 secs per cycle; 1 on, 1 off
slowBlinker->addChild( frame );
C# :
// Add the non-blinking part of the sign to the root
root.AddChild( eatAt );
// Add the fast-blinking part to a blinker node
SoBlinker fastBlinker = new SoBlinker();
root.AddChild( fastBlinker );
fastBlinker.speed.Value = ( 2 ); // blinks 2 times a second
fastBlinker.AddChild( josie );
// Add the slow-blinking part to another blinker node
SoBlinker slowBlinker = new SoBlinker();
root.AddChild( slowBlinker );
slowBlinker.speed.Value = ( 0.5f ); // 2 secs per cycle; 1 on, 1 off
slowBlinker.AddChild( frame );
Java :
// Add the non-blinking part of the sign to the root
root.addChild( eatAt );
// Add the fast-blinking part to a blinker node
SoBlinker fastBlinker = new SoBlinker();
root.addChild( fastBlinker );
fastBlinker.speed.setValue( 2 ); // blinks 2 times a second
fastBlinker.addChild( josie );
// Add the slow-blinking part to another blinker node
SoBlinker slowBlinker = new SoBlinker();
root.addChild( slowBlinker );
slowBlinker.speed.setValue( 0.5f ); // 2 secs per cycle; 1 on, 1 off
slowBlinker.addChild( frame );