3.5. Shared Instancing of Nodes

You can add any node to more than one group. A bicycle, for example, might use the same basic wheel group for both the front and rear wheels, with slight modifications for size and location of the two wheels. The term shared instancing refers to such cases, where a single node has more than one parent.

The robot example can instance the leg group twice to form a left and right leg, as shown in Figure 3.10, “ Scene Graph Showing Shared Instancing of the Leg Group. The basic leg group contains nodes for a cylinder (the thigh), a transformed cylinder (the calf), and a transformed cube (the foot). The left and right leg groups (the parents: rightLeg and leftLeg) each contain an additional SoTransform SoTransform SoTransform node to position the complete legs correctly onto the robot's body.

Any change made within the leg group is reflected in all instances of it. Here, for example, if the height of the cube in the foot node is doubled, both the left and right feet double in height.

Shared instancing offers database and program economy, since objects can be reused without duplicating them. You save both time and space by reusing nodes (and groups) when possible.

Do not, however, create cycles within a given scene graph. A node can connect to multiple parents but should not be a child of itself or any of its descendants.

Scene Graph Showing Shared Instancing of the Leg Group

Figure 3.10.  Scene Graph Showing Shared Instancing of the Leg Group



Example 3.2, “ Robot.c++ shows the code for the robot as described up to this point. The rendered image is shown in Figure 3.11, “ Rendered Image of the Robot.

Example 3.2.  Robot.c++

// Robot with legs

// Construct parts for legs (thigh, calf and foot)
SoCube *thigh = new SoCube;     
thigh->width = 1.2;
thigh->height = 2.2;
thigh->depth = 1.1;

SoTransform *calfTransform = new SoTransform;
calfTransform->translation.setValue(0, -2.25, 0.0);

SoCube *calf = new SoCube;
calf->width = 1;
calf->height = 2.2;
calf->depth = 1;

SoTransform *footTransform = new SoTransform;
footTransform->translation.setValue(0, -2, .5);

SoCube *foot = new SoCube;
foot->width = 0.8;
foot->height = 0.8;
foot->depth = 2;

// Put leg parts together
SoGroup *leg = new SoGroup;      
leg->addChild(thigh);
leg->addChild(calfTransform);
leg->addChild(calf);
leg->addChild(footTransform);
leg->addChild(foot);

SoTransform *leftTransform = new SoTransform;
leftTransform->translation = SbVec3f(1, -4.25, 0);

// Left leg
SoSeparator *leftLeg = new SoSeparator;   
leftLeg->addChild(leftTransform);
leftLeg->addChild(leg);

SoTransform *rightTransform = new SoTransform;
rightTransform->translation.setValue(-1, -4.25, 0);

// Right leg
SoSeparator *rightLeg = new SoSeparator;   
rightLeg->addChild(rightTransform);
rightLeg->addChild(leg);

// Parts for body
SoTransform *bodyTransform = new SoTransform;    
bodyTransform->translation.setValue(0.0, 3.0, 0.0);

SoMaterial *bronze = new SoMaterial;
bronze->ambientColor.setValue(.33, .22, .27);
bronze->diffuseColor.setValue(.78, .57, .11);
bronze->specularColor.setValue(.99, .94, .81);
bronze->shininess = .28;

SoCylinder *bodyCylinder = new SoCylinder;
bodyCylinder->radius = 2.5;
bodyCylinder->height = 6;

// Construct body out of parts 
SoSeparator *body = new SoSeparator;  
body->addChild(bodyTransform);      
body->addChild(bronze);
body->addChild(bodyCylinder);
body->addChild(leftLeg);
body->addChild(rightLeg);

// Head parts
SoTransform *headTransform = new SoTransform;   
headTransform->translation.setValue(0, 7.5, 0);
headTransform->scaleFactor.setValue(1.5, 1.5, 1.5);

SoMaterial *silver = new SoMaterial;
silver->ambientColor.setValue(.2, .2, .2);
silver->diffuseColor.setValue(.6, .6, .6);
silver->specularColor.setValue(.5, .5, .5);
silver->shininess = .5;

SoSphere *headSphere = new SoSphere;

// Construct head
SoSeparator *head = new SoSeparator;      
head->addChild(headTransform);
head->addChild(silver);
head->addChild(headSphere);
   
// Robot is just head and body
SoSeparator *robot = new SoSeparator;  
robot->addChild(body);               
robot->addChild(head);
    
// Robot with legs

// Construct parts for legs (thigh, calf and foot)
SoCube thigh = new SoCube();
thigh.width.Value = 1.2f;
thigh.height.Value = 2.2f;
thigh.depth.Value = 1.1f;

SoTransform calfTransform = new SoTransform();
calfTransform.translation.SetValue(0.0f, -2.25f, 0.0f);

SoCube calf = new SoCube();
calf.width.Value = 1.0f;
calf.height.Value = 2.2f;
calf.depth.Value = 1.0f;

SoTransform footTransform = new SoTransform();
footTransform.translation.SetValue(0.0f, -1.5f, 0.5f);

SoCube foot = new SoCube();
foot.width.Value = 0.8f;
foot.height.Value = 0.8f;
foot.depth.Value = 2.0f;

// Put leg parts together
SoGroup leg = new SoGroup();
leg.AddChild(thigh);
leg.AddChild(calfTransform);
leg.AddChild(calf);
leg.AddChild(footTransform);
leg.AddChild(foot);

SoTransform leftTransform = new SoTransform();
leftTransform.translation.Value = new SbVec3f(1.0f, -4.25f, 0.0f);

// Left leg
SoSeparator leftLeg = new SoSeparator();
leftLeg.AddChild(leftTransform);
leftLeg.AddChild(leg);

SoTransform rightTransform = new SoTransform();
rightTransform.translation.SetValue(-1.0f, -4.25f, 0.0f);

// Right leg
SoSeparator rightLeg = new SoSeparator();
rightLeg.AddChild(rightTransform);
rightLeg.AddChild(leg);

// Parts for body
SoTransform bodyTransform = new SoTransform();
bodyTransform.translation.SetValue(0.0f, 3.0f, 0.0f);

SoMaterial bronze = new SoMaterial();
bronze.ambientColor.SetValue(0.33f, 0.22f, 0.27f);
bronze.diffuseColor.SetValue(0.78f, 0.57f, 0.11f);
bronze.specularColor.SetValue(0.99f, 0.94f, 0.81f);
bronze.shininess.SetValue(0.28f);

SoCylinder bodyCylinder = new SoCylinder();
bodyCylinder.radius.Value = 2.5f;
bodyCylinder.height.alue = 6.0f;

// Construct body out of parts 
SoSeparator body = new SoSeparator();
body.AddChild(bodyTransform);
body.AddChild(bronze);
body.AddChild(bodyCylinder);
body.AddChild(leftLeg);
body.AddChild(rightLeg);

// Head parts
SoTransform headTransform = new SoTransform();
headTransform.translation.SetValue(0.0f, 7.5f, 0.0f);
headTransform.scaleFactor.SetValue(1.5f, 1.5f, 1.5f);

SoMaterial silver = new SoMaterial();
silver.ambientColor.SetValue(0.2f, 0.2f, 0.2f);
silver.diffuseColor.SetValue(0.6f, 0.6f, 0.6f);
silver.specularColor.SetValue(0.5f, 0.5f, 0.5f);
silver.shininess.SetValue(0.5f);

SoSphere headSphere = new SoSphere();

// Construct head
SoSeparator head = new SoSeparator();
head.AddChild(headTransform);
head.AddChild(silver);
head.AddChild(headSphere);

// Robot is just head and body
SoSeparator robot = new SoSeparator();
robot.AddChild(body);
robot.AddChild(head);
  
// Robot with legs

// Construct parts for legs (thigh, calf and foot)
SoCube thigh = new SoCube();
thigh.width.setValue(1.2F);
thigh.height.setValue(2.2F);
thigh.depth.setValue(1.1F);

SoTransform calfTransform = new SoTransform();
calfTransform.translation.setValue(0, -2.25F, 0);

SoCube calf = new SoCube();
calf.width.setValue(1);
calf.height.setValue(2.2F);
calf.depth.setValue(1);

SoTransform footTransform = new SoTransform();
footTransform.translation.setValue(0, -1.5F, .5F);

SoCube foot = new SoCube();
foot.width.setValue(0.8F);
foot.height.setValue(0.8F);
foot.depth.setValue(2);

// Put leg parts together
SoGroup leg = new SoGroup();
leg.addChild(thigh);
leg.addChild(calfTransform);
leg.addChild(calf);
leg.addChild(footTransform);
leg.addChild(foot);

SoTransform leftTransform = new SoTransform();
leftTransform.translation.setValue(new SbVec3f(1, -4.25F, 0));

// Left leg
SoSeparator leftLeg = new SoSeparator();
leftLeg.addChild(leftTransform);
leftLeg.addChild(leg);

SoTransform rightTransform = new SoTransform();
rightTransform.translation.setValue(-1, -4.25F, 0);

// Right leg
SoSeparator rightLeg = new SoSeparator();
rightLeg.addChild(rightTransform);
rightLeg.addChild(leg);

// Parts for body
SoTransform bodyTransform = new SoTransform();
bodyTransform.translation.setValue(0.0F, 3.0F, 0.0F);

SoMaterial bronze = new SoMaterial();
bronze.ambientColor.setValue(.33F, .22F, .27F);
bronze.diffuseColor.setValue(.78F, .57F, .11F);
bronze.specularColor.setValue(.99F, .94F, .81F);
bronze.shininess.setValue(.28F);

SoCylinder bodyCylinder = new SoCylinder();
bodyCylinder.radius.setValue(2.5F);
bodyCylinder.height.setValue(6);

// Construct body out of parts
SoSeparator body = new SoSeparator();
body.addChild(bodyTransform);
body.addChild(bronze);
body.addChild(bodyCylinder);
body.addChild(leftLeg);
body.addChild(rightLeg);

// Head parts
SoTransform headTransform = new SoTransform();
headTransform.translation.setValue(0, 7.5F, 0);
headTransform.scaleFactor.setValue(1.5F, 1.5F, 1.5F);

SoMaterial silver = new SoMaterial();
silver.ambientColor.setValue(.2F, .2F, .2F);
silver.diffuseColor.setValue(.6F, .6F, .6F);
silver.specularColor.setValue(.5F, .5F, .5F);
silver.shininess.setValue(.5F);

SoSphere headSphere = new SoSphere();

// Construct head
SoSeparator head = new SoSeparator();
head.addChild(headTransform);
head.addChild(silver);
head.addChild(headSphere);

// Robot is just head and body
SoSeparator robot = new SoSeparator();
robot.addChild(body);
robot.addChild(head);
  


Tip: When constructing a complicated scene graph, you may want to define the graph using the Inventor file format (see Chapter 12, Importing data) and read the graph from a file or from a string in memory. This approach can be easier and less error-prone than constructing the scene graph programmatically.

Rendered Image of the Robot

Figure 3.11.  Rendered Image of the Robot