5.5. Transformations

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.

Cumulative Effect of Transformation Nodes

Figure 5.25.  Cumulative Effect of Transformation Nodes


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]

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


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

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

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