Unlike other property nodes, transformation nodes do not replace the current geometric transformation element in the action state. Instead, they have a cumulative effect on the current geometric transformation. In Figure 5.25, “ Cumulative Effect of Transformation Nodes ”, for example, the transformations in node xfm1 are applied first, followed by the transformations in node xfm2.
The cube is affected by only the transformation in xfm1. The sphere, however, is affected by both xfm1 and xfm2.
An SoTransform( C++ | Java | .NET ) node includes the following fields:
Tip: If you are using only one of the fields in an SoTransform( C++ | Java | .NET ) node, you can substitute the corresponding “lightweight” version. For rotations, useSoRotation( C++ | Java | .NET ) orSoRotationXYZ( C++ | Java | .NET ); for translations, use SoTranslation( C++ | Java | .NET ); and for scaling, use SoScale( C++ | Java | .NET ). |
Within each SoTransform( C++ | Java | .NET ) node, the fields are applied so that the last field in the node (the center) affects the shape object first. The order is first the center, followed by the scale orientation, the scaling factor, the rotation, and the translation.
Figure 5.26, “ Two Groups with Transformations in Different Order ” and Figure 5.27, “ Effects of Ordering Transformation Fields” show how different ordering of transformations produces different results. At the left of Figure 5.27, “ Effects of Ordering Transformation Fields”, the temple is scaled, rotated, and then translated. The transform node closest to the shape object affects the object first. You thus need to read backward through the code to see how the effects of the transformations are felt. At the right of Figure 5.27, “ Effects of Ordering Transformation Fields”, the temple is rotated, then scaled and translated. Example 5.9, “ Changing the Order of Transformations ” shows the code for the two sets of transformations.
Example 5.9. Changing the Order of Transformations
#include <Inventor/Xt/SoXt.h> #include <Inventor/Xt/viewers/SoXtExaminerViewer.h> #include <Inventor/SoDB.h> #include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoRotationXYZ.h> #include <Inventor/nodes/SoScale.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoTranslation.h> main(int, char **argv) { // Initialize Inventor and Xt Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoSeparator *root = new SoSeparator; root->ref(); // Create two separators, for left and right objects. SoSeparator *leftSep = new SoSeparator; SoSeparator *rightSep = new SoSeparator; root->addChild(leftSep); root->addChild(rightSep); // Create the transformation nodes. SoTranslation *leftTranslation = new SoTranslation; SoTranslation *rightTranslation = new SoTranslation; SoRotationXYZ *myRotation = new SoRotationXYZ; SoScale *myScale = new SoScale; // Fill in the values. leftTranslation->translation.setValue(-1.0, 0.0, 0.0); rightTranslation->translation.setValue(1.0, 0.0, 0.0); myRotation->angle = M_PI/2; // 90 degrees myRotation->axis = SoRotationXYZ::X; myScale->scaleFactor.setValue(2., 1., 3.); // Add transforms to the scene. leftSep->addChild(leftTranslation); // left graph leftSep->addChild(myRotation); // then rotated leftSep->addChild(myScale); // first scaled rightSep->addChild(rightTranslation); // right graph rightSep->addChild(myScale); // then scaled rightSep->addChild(myRotation); // first rotated // Read an object from file. (as in example 4.2.Lights) SoInput myInput; if (!myInput.openFile("temple.iv")) return (1); SoSeparator *fileContents = SoDB::readAll(&myInput); if (fileContents == NULL) return (1); // Add an instance of the object under each separator. leftSep->addChild(fileContents); rightSep->addChild(fileContents); // Construct a renderArea and display the scene. SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); myViewer->setSceneGraph(root); myViewer->setTitle("Transform Ordering"); myViewer->viewAll(); myViewer->show(); SoXt::show(myWindow); SoXt::mainLoop(); }
using OIV.Inventor.Nodes; using OIV.Inventor.Win; using OIV.Inventor.Win.Viewers; using OIV.Inventor; namespace _05_6_TransformOrdering { public partial class MainForm : Form { SoWinExaminerViewer myViewer; public MainForm() { InitializeComponent(); CreateSample(); } public void CreateSample() { SoSeparator root = new SoSeparator(); // Create two separators, for left and right objects. SoSeparator leftSep = new SoSeparator(); SoSeparator rightSep = new SoSeparator(); root.AddChild(leftSep); root.AddChild(rightSep); // Create the transformation nodes SoTranslation leftTranslation = new SoTranslation(); SoTranslation rightTranslation = new SoTranslation(); SoRotationXYZ myRotation = new SoRotationXYZ(); SoScale myScale = new SoScale(); // Fill in the values leftTranslation.translation.Value = new SbVec3f(-1.0f, 0.0f, 0.0f); rightTranslation.translation.Value = new SbVec3f(1.0f, 0.0f, 0.0f); myRotation.angle.Value = (float) (Math.PI / 2.0f); // 90 degrees myRotation.axis.Value = SoRotationXYZ.AxisType.X; myScale.scaleFactor.Value = new SbVec3f(2.0f, 1.0f, 3.0f); // Add transforms to the scene. leftSep.AddChild(leftTranslation); // left graph leftSep.AddChild(myRotation); // then rotated leftSep.AddChild(myScale); // first scaled rightSep.AddChild(rightTranslation); // right graph rightSep.AddChild(myScale); // then scaled rightSep.AddChild(myRotation); // first rotated // Read an object from file. (as in example 4.2.Lights) SoInput myInput = new SoInput(); myInput.OpenFile("../../../../../data/temple.iv"); SoSeparator fileContents = SoDB.ReadAll(myInput); // Add an instance of the object under each separator. leftSep.AddChild(fileContents); rightSep.AddChild(fileContents); // Construct a renderArea and display the scene. myViewer = new SoWinExaminerViewer(this, "", true, SoWinFullViewer.BuildFlags.BUILD_ALL, SoWinViewer.Types.BROWSER); myViewer.SetSceneGraph(root); myViewer.SetTitle("Transform Ordering"); myViewer.ViewAll(); } } }
import tools.*; import com.openinventor.inventor.nodes.*; import com.openinventor.inventor.*; import com.openinventor.inventor.awt.*; import java.awt.*; public class Main extends DemoInventor { public static void main(String[] args) { Main applet = new Main(); DemoInventor.isAnApplet = false; applet.start(); demoMain(applet, "Transform Ordering"); } public void start() { super.start(); // Create two separators, for left and right objects. SoSeparator leftSep = new SoSeparator(); SoSeparator rightSep = new SoSeparator(); // Create the transformation nodes SoTranslation leftTranslation = new SoTranslation(); SoTranslation rightTranslation = new SoTranslation(); SoRotationXYZ myRotation = new SoRotationXYZ(); SoScale myScale = new SoScale(); // Fill in the values leftTranslation.translation.setValue(-1.0f, 0.0f, 0.0f); rightTranslation.translation.setValue(1.0f, 0.0f, 0.0f); myRotation.angle.setValue((float) java.lang.Math.PI / 2.0f); // 90 degrees myRotation.axis.setValue(SoRotationXYZ.X); myScale.scaleFactor.setValue(2.0f, 1.0f, 3.0f); // Read an object from file. (as in example 4.2.Lights) SoInput myInput = new SoInput(); if (!myInput.openFile(m_prefix + "../../../../data/models/temple.iv")) System.exit(1); SoSeparator fileContents = SoDB.readAll(myInput); if (fileContents == null) System.exit(1); SoSeparator root = new SoSeparator(); { // Assemble scene graph root.addChild(leftSep); root.addChild(rightSep); // Add transforms to the scene. leftSep.addChild(leftTranslation); // left graph leftSep.addChild(myRotation); // then rotated leftSep.addChild(myScale); // first scaled rightSep.addChild(rightTranslation); // right graph rightSep.addChild(myScale); // then scaled rightSep.addChild(myRotation); // first rotated // Add an instance of the object under each separator. leftSep.addChild(fileContents); rightSep.addChild(fileContents); } // Construct a renderArea and display the scene. SwSimpleViewer myViewer = new SwSimpleViewer(); myViewer.setSceneGraph(root); setLayout(new BorderLayout()); add(myViewer, BorderLayout.CENTER); } }