15.13. Nodes Used for Animation

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:

Let's look at examples of rotor and blinker nodes.

Rotor Node

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


Scene Graph for Rotor Node Example

Figure 15.19.  Scene Graph for Rotor Node Example



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

Blinker Node

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.

Flashing Sign Controlled by a Blinker Node

Figure 15.20.  Flashing Sign Controlled by a Blinker Node



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