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 SoRotor SoRotor is a transformation node that spins the rotation angle while keeping the axis constant.
SoPendulum SoPendulum SoPendulum is a transformation node that oscillates between two rotations.
SoShuttle SoShuttle SoShuttle is a transformation node that oscillates between two translations.
SoBlinker SoBlinker SoBlinker is a switch node that cycles through its children.
Let's look at examples of rotor and blinker nodes.
The SoRotor SoRotor SoRotor node, derived from SoRotation SoRotation SoRotation , changes the angle of rotation at a specified speed. You can use an SoRotor SoRotor SoRotor node any place you would use an SoRotation SoRotation 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. Example 15.7, “ 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 Figure 15.19, “ 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 15.7. A Spinning Windmill Using an SoRotor Node
#include <Inventor/SoDB.h> #include <Inventor/SoInput.h> #include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoRotor.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; }
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; }
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; }
// 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(); }
// 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"); } } }
The SoBlinker SoBlinker SoBlinker node, derived from SoSwitch SoSwitch 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 SoBlinker SoBlinker cycles between that child (0) and SO_SWITCH_NONE. Example 15.8, “ 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.
Example 15.8. Using a Blinker Node to Make a Sign Flash
// 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);
// 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);
// 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);