CHAPTER 5

Scenegraph node reference

5.1 Scenegraph compilation

5.2 Node

5.3 Bounds and CollisionBounds

5.4 Group

5.5 Switch

5.6 BranchGroup

5.7 OrderedGroup

5.8 SharedGroup and link

5.9 Primitive

5.10 TransformGroup

5.11 Summary

It is time to look at usage examples and implementation hints and tips for the most commonly used scenegraph elements available in Java 3D. This chapter is intended to supplement the Sun Java 3D API documentation. Whether you are browsing or reading the book chapter by chapter, you may want to skim the detailed implementation advice and move ahead.

5.1 Scenegraph compilation

The Java 3D system supports the concept of a “compiled” scenegraph. Compilation is typically carried out after the scenegraph structure has been built, but before it is displayed. During compilation Java 3D analyzes the elements in the scenegraph, as well as the scenegraph structure, and attempts to perform optimizations to improve rendering time and scenegraph traversal.

Java 3D uses the capability bits that have been set on the scenegraph elements to identify which optimizations can be applied to the scenegraph. The capability bits that have been set for an object defines a contract between you (the application developer) and the Java 3D system. If, for example, you have not set the ALLOW_TRANSFORM_WRITE capability on a TransformGroup, the Java 3D system could use the fact that the 4 × 4 transformation matrix within the TransformGroup remains constant to optimize rendering.

The use of capability bits is a powerful mechanism for adding complex optimizations in future versions of Java 3D. At present two main scenegraph optimizations have been implemented: attribute merging/sorting and geometry merging.

To take advantage of the benefits of compilation, you must override some of the default settings of the Shape3D-derived objects in your scenegraph. In particular, the pickable property is set by default for Shape3D objects; objects with the pickable property will not be optimized during the compile process.

5.1.1 Appearance merging and sorting

The Appearances assigned to Shape3D objects in the scenegraph are computationally quite expensive for the scenegraph renderer. They take up memory, and when the renderer hits an Appearance, it must make a long and complex series of OpenGL or DirectX calls to the rendering subsystem to add the information within the Appearance to the rendering pipeline. Two optimizations can take place: duplicate Appearance objects can be removed and replaced with a reference to a unique Appearance object, and Shape3D objects can be rendered in an order such that changes in Appearance state are minimized. These optimizations reduce memory consumption and save on calls to the rendering subsystem. Obviously, only Appearance objects that do not have any WRITE capability bits set can be optimized using scenegraph compilation.

5.1.2 Geometry merging

Geometry merging attempts to minimize the number of discrete Shape3D objects in the scenegraph. A scenegraph will benefit from geometry merging if the Shape3D objects fulfill the following criteria:

The compilation process will sort and attempt to merge the static subgraphs in the scenegraph. Dynamic subgraphs, that is, Nodes with writable attributes (such as Group.ALLOW_CHILDREN_WRITE), will have their static child Nodes processed while ignoring the dynamic child Nodes.

5.2 Node

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node

Node is an abstract base class for Group and Leaf Nodes. It defines methods to control bounding volumes (through the Bounds class), automatic computation of Bounds, collision detection and picking (mouse selection). Most important, it allows each Node to have a single parent Node. The parent Node allows arbitrarily complex scenegraph structures to be defined.

Group-derived Nodes have the ability to manage a Collection of Node-derived child objects, while Leaf-derived Nodes define the leaves of the scenegraph tree. In other words, Leaf Nodes cannot have child Nodes.

5.3 Bounds and CollisionBounds

Java 3D maintains object boundary information for the Nodes in the scenegraph. Every Node in the scenegraph contains a Bounds field that stores the geometric extent of the Node. Java 3D uses the Node Bounds information extensively, for everything from visibility testing to Behavior scheduling.

In addition, Shape3D- and Group-derived objects in the scenegraph (i.e., all geometric objects and geometric container objects) contain the CollisionBounds field. The Java 3D collision detection engine makes use of the CollisionBounds field. A simplistic (and hence poor) collision detection algorithm would iterate through the objects in the scenegraph and test for an intersection between the CollisionBounds for a Shape3D object and the CollisionBounds of every other Shape3D object in the scenegraph.

There are three classes derived from Bounds: BoundingBox, BoundingSphere, and BoundingPolytope. BoundingBox defines a cuboidal volume of space; BoundingSphere, a spherical volume of space; and BoundingPolytope, a set of intersecting planes that define a closed, convex volume.

Node-derived classes also have the option to autocompute their Bounds. This option is enabled by default and allows geometric objects in the scenegraph, as well as their parents, to compute the Bounds field based upon the positions of the geometric primitives (points, lines) from which they are composed.

Consult the following code snippets for the effects of creating an object with various combinations of Bounds and/or CollisionBounds.

From BoundsTest.java
//use the defaults
ColorCube cube0 = new ColorCube( 1.0 );

RESULTS
 
 
 
 
BoundsAutoCompute: true
Collidable: true
Pickable: true
Bounds: Bounding box: Lower=–1.0 –1.0 –1.0 Upper=1.0 1.0 1.0
CollisionBounds: null
By default, Shape3D objects are created as Collidable and Pickable, and they autocompute their Bounds. No CollisionBounds are assigned, so if collision detection functionality is required, the collision mode USE_GEOMETRY should be used.
//explicitly set the Bounds  using a BoundingBox
ColorCube cube1 = new ColorCube( 2.0 );
cube1.setBoundsAutoCompute( false );
Bounds bounds = new BoundingBox( new Point3d( –2, –2, –2),
  new Point3d( 2, 2, 2 ) );
cube1.setBounds( bounds );
cube1.setCollisionBounds( bounds );
RESULTS
 
 
 
 
BoundsAutoCompute: false
Collidable: true
Pickable: true
Bounds: Bounding box: Lower = –2.0 –2.0 –2.0 Upper = 2.0 2.0 2.0
CollisionBounds: Bounding box: Lower = –2.0 –2.0 –2.0 Upper = 2.0 2.0 2.0
By calling setBoundsAutoCompute( false ), the Bounds and CollisionBounds for the Shape3D object can be manually specified, as one would expect.
//explicitly set the Bounds using a BoundingSphere
ColorCube cube2 = new ColorCube( 4.0 );
cube2.setBoundsAutoCompute( false );
bounds = new BoundingSphere( new Point3d( 0, 0, 0 ), 4 );
cube2.setBounds( bounds );
cube2.setCollisionBounds( bounds );
RESULTS
 
 
 
 
BoundsAutoCompute: false
Collidable: true
Pickable: true
Bounds: Bounding box: Lower = –4.0 –4.0 –4.0 Upper = 4.0 4.0 4.0
CollisionBounds: Center = (0.0, 0.0, 0.0) Radius = 4.0
Surprisingly, if a BoundingSphere is used to specify the Bounds and CollisionBounds for the Shape3D object, the BoundingSphere will be internally converted to a BoundingBox and used for the Bounds. The CollisionBounds uses the original BoundingSphere, however.
//auto compute, manual collision
ColorCube cube3 = new ColorCube( 6.0 );
cube3.setBoundsAutoCompute( true );
bounds = new BoundingBox( new Point3d( –10, –10, –10 ), 
  new Point3d( 10, 10, 10 ) );
cube3.setCollisionBounds( bounds );
RESULTS
 
 
 
 
BoundsAutoCompute: true
Collidable: true
Pickable: true
Bounds: Bounding box: Lower = –6.0 –6.0 –6.0 Upper = 6.0 6.0 6.0
CollisionBounds: Bounding box: Lower = –10.0 –10.0 –10.0 Upper = 10.0 10.0 10.0
//auto compute both
ColorCube cube4 = new ColorCube( 6.0 );
cube4.setBoundsAutoCompute( true );
RESULTS
 
 
 
 
BoundsAutoCompute: true
Collidable: true
Pickable: true
Bounds: Bounding box: Lower = –6.0 –6.0 –6.0 Upper = 6.0 6.0 6.0
CollisionBounds: null

5.3.1 Bounds and CollisionBounds propagation

There is a final piece to the Bounds story. The scenegraph is a hierarchical data structure, so it makes sense for the Bounds of a parent object to automatically encompass a volume large enough to hold all of its child objects. Java 3D can perform these calculations automatically, as table 5.1 illustrates (from BoundsTest.java).

Table 5.1 Bounds propagation within a branch of the scenegraph
Scenegraph item Bounds Autocompute
  BranchGroup null false
  TransformGroup Bounding Sphere: Center = (0.477, 0.34, 0.23) Radius = 11.00 true
  RotationInterpolator null true
  Group1 Center = (0.0,0.0, 0.0) Radius = 10.39 true
  Cube1 Bounding box: Lower = –1.0 –1.0 –1.0 Upper = 1.0 1.0 1.0 true
  Cube2 Bounding box: Lower = –2.0 –2.0 –2.0 Upper = 2.0 2.0 2.0 true
  Cube3 Bounding box: Lower = –4.0 –4.0 –4.0 Upper = 4.0 4.0 4.0 true
  Cube4 Bounding box: Lower = –6.0 –6.0 –6.0 Upper = 6.0 6.0 6.0 true
  Cube5 Bounding box: Lower = –6.0 –6.0 –6.0 Upper = 6.0 6.0 6.0 true
  Group2 Bounding Sphere: Center = (–0.01,–0.01,0.00) Radius = 8.62 true
  PointsArray Bounding box: Lower = –4.97 –4.98 –5.00 Upper = 4.95 4.96 4.97 true

Group1 contains the five ColorCubes, as created earlier in the section. The largest ColorCube has a BoundingBox of (–6,–6,–6) –> (6,6,6). The radius of the smallest BoundingSphere to enclose the largest ColorCube is therefore radius = sqrt( 62 + 62 + 62) = 10.392. This BoundingSphere is automatically created by Java 3D and assigned to the parent Group (Group1) of the ColorCubes. Note that Group1 has the property setBoundsAutoCompute( true ).

Group2 contains a Shape3D object composed from 200 random points in a PointArray (positioned between –5 and 5 in the x-, y-, and z-axes). Java 3D automatically creates a BoundingBox to enclose the points composing the Shape3D object—approximately: (–5,–5,–5) –> (5,5,5). The BoundingBox is automatically assigned to the Shape3D object containing the PointArray. The Bounds for the Shape3D object are propagated up the scenegraph hierarchy as a BoundingSphere and assigned to Group2. The center of the BoundingSphere is positioned to minimize the radius (in this case approximately 0,0,0). The radius of the BoundingSphere is approximately computed from radius = sqrt( 52 + 52 + 52) = 8.660.

The parent of Group1, Group2, and RotationInterpolator is TransformGroup. TransformGroup combines the Bounds objects for its children to compute its own Bounds. In this case, as the Bounds of the children are all approximately centered at (0,0,0), which is equal to the Bounds of Group1 (which is the largest).

NOTE The top-level parent BranchGroup node has the attribute setBoundsAutoCompute( false ).
//routine to create a Shape3D object made from a point cloud 
//of 200 random points
protected Group createPoints()
{
 Group group = new Group();
final int kNumPoints = 200; final double kRadius = 10.0; Point3d points[] = new Point3d[kNumPoints];
for( int n = 0; n < kNumPoints; n++ ) { double randX = (java.lang.Math.random() * kRadius ) – kRadius/2; double randY = (java.lang.Math.random() * kRadius ) – kRadius/2; double randZ = (java.lang.Math.random() * kRadius ) – kRadius/2;
points[n] = new Point3d( randX, randY, randZ ); }
PointArray pointArray = new PointArray( points.length, GeometryArray.COLOR_4 | GeometryArray.COORDINATES ); pointArray.setCoordinates( 0, points );
Shape3D shapePoints = new Shape3D( pointArray, new Appearance() );
group.addChild( shapePoints ); return group; }

Note that the propagation of Bounds up the scenegraph hierarchy (from child to parent) does not occur with CollisionBounds. Cube4 has CollisionBounds of (–10,–10,–10) –> (10,10,10) but these do not influence the Bounds of the parent Group1. Surprisingly, the CollisionBounds of Cube4 do not influence the CollisionBounds of the parent Group1 either. It appears that the application programmer is responsible for manually propagating CollisionBounds from child to parent Nodes.

5.4 Group

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group

Group defines a scenegraph Node that contains a collection of child Nodes. It defines the following child Node management methods:

void addChild(Node child)
java.util.Enumeration getAllChildren()
Node getChild(int index)
void insertChild(Node child, int index)
void moveTo(BranchGroup branchGroup)
int numChildren()
void removeChild(int index)
void setChild(Node child, int index)

These methods essentially delegate, in obvious ways, to the internal collection that manages the child Nodes within the Group. Table 5.2 shows the capabilities defined by Group.

Table 5.2 Capabilities defined by Group
Capability bit Description
ALLOW_CHILDREN_EXTEND Allows child Nodes to be added to the Group
ALLOW_CHILDREN_READ Allows reading of child Nodes (e.g., getChild method)
ALLOW_CHILDREN_WRITE Allows writing of child Nodes (e.g., setChild method)
ALLOW_COLLISION_BOUNDS_READ Allows reading of collision Bounds
ALLOW_COLLISION_BOUNDS_WRITE Allows writing of collision Bounds

Group is an important base class for the Java 3D Node management classes, and it can also be instantiated in its own right. For increased flexibility, however, I recommend BranchGroup Nodes because they can be dynamically added or removed from the scenegraph. The classes derived from Group are shown in table 5.3.

Table 5.3 Classes derived from Group
Class Description
BranchGroup A dynamically insertable and removable Group
OrderedGroup A Group that renders its children in a defined order, irrespective of location
Primitive A geometric Group used to manage geometry in the utils package
SharedGroup A Group that can be reused across the scenegraph and can be attached to multiple parents
Switch A Group that can conditionally display its child Nodes
TransformGroup A Group that has an associated geometric transformation containing rotation, translation, and scale information that is applied to its child Nodes before rendering

Note that an instance of any of the Group-derived classes, including SharedGroup, can only be added to a single location within the scenegraph. Attempting to add a scenegraph node to a scenegraph that already has an assigned parent (i.e., a node that has already been added to the scenegraph) will result in a run-time exception. I discuss reusing scenegraph branches using a SharedGroup and a Link later in this chapter.

5.4.1 Remove a child Node by reference

It is useful to be able to remove a child Node from its parent Group without knowing the child Node’s index. Unfortunately, because scenegraph Nodes are removed from a Group using void removeChild(int index), there is no easy way to remove a Shape3D object from a Group if you do not know the index at which it was originally inserted. In the following example, I remove a Shape3D object that corresponds to the internal, application-specific data structure. By storing the application-specific data structure in the UserData field of the Shape3D, I can retrieve the index of the Shape3D and remove it from its parent Group object.

ClassificationObject is an application-specific data structure that is stored in each child Node to identify it. To store the ClassificationObject in the Node, use

node.setUserData( classificationObject );
public void removeChildObject( ClassificationObject targetObj ) { //we need to remove the object by index, so we have to iterate //through our objects to find it.
//get an enumeration containing all the child nodes Enumeration enum = getAllChildren();
int nIndex = 0; Node obj = null;
//scan through the child nodes until we find the one that //corresponds to our data structure. while( enum.hasMoreElements() != false ) { obj = (Node) enum.nextElement();
if( targetObj != obj.getUserData() ) nIndex++; else break; }
//if we found the object, we can now remove it by index. if( nIndex < numChildren() ) removeChild( nIndex ); else System.out.println( "Failed to find child object during remove operation." ); }

Note that in the preceding example, the implicit this is an instance of a class derived from Group that has the capability to remove child Nodes based on an internal data structure.

5.5 Switch

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.Switch

The Switch Node provides the facility to define a Group Node that can conditionally display or hide its child Nodes (see table 5.4).

Table 5.4 Switch Node modes
Effect Usage example
All child Nodes new Switch( Switch.CHILD_ALL )
No child Nodes new Switch( Switch.CHILD_NONE )
A single child Node switchNode.setWhichChild( nIndex )
Specifiable child Nodes new Switch( Switch.CHILD_MASK ), switchNode.setChildMask( java.util.BitSet childMask )

For example, to create a Switch Node that displays child Nodes at index 3, 6, and 7 use the following:

From SwitchTest.java
//create the Switch Node
Switch switchGroup = new Switch( Switch.CHILD_MASK );
switchGroup.setCapability( Switch.ALLOW_SWITCH_WRITE );
switchGroup.addChild( createLabel( "Child Node 1", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 2", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 3", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 4", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 5", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 6", labelScale ) ); switchGroup.addChild( createLabel( "Child Node 7", labelScale ) );
//create the logical mask to control Node visibility java.util.BitSet visibleNodes = new java.util.BitSet( switchGroup.numChildren() );
//make the third, sixth and seventh nodes visible visibleNodes.set( 2 ); visibleNodes.set( 5 ); visibleNodes.set( 6 );
//assign the visibility mask to the Switch switchGroup.setChildMask( visibleNodes );

The output of the SwitchTest example is shown in figure 5.1.

Figure 5.1

Figure 5.1 The effect of using a BitSet mask and a Switch Node. On the left, the Switch Node has been created with the Switch.CHILD_ALL attribute. On the right, a BitSet has been created to display the third, sixth, and seventh Node through a call to setChildMask. The child elements being controlled by the Switch Node are Text2D objects

The Switch Node can be used to implement modal static scenegraphs, that is, scenegraphs that are essentially of a fixed structure but which the user can influence through adding or removing prebuilt sections.

Switch Nodes can also be used to implement simple animation using the SwitchInterpolator Behavior (figure 5.2). The SwitchInterpolator attaches to a Switch Node and cycles the active child of the Switch Node using an Alpha object. For example, a simple 3D “flip-book” style animation could be achieved by adding several versions of a 3D model to a Switch Node and triggering a SwitchInterpolator to cycle from one model to another.

Figure 5.2

Figure 5.2 Seven frames from the NodesTest example. A Switch Node is created with seven child elements, and a SwitchInterpolator is used to cycle between them. Note that the items at index 3 (row 2, column 1) and 6 (row 3, column 1) use a Link Node to share a single SharedGroup
BEWARE
 
If you choose to use the SwitchInterpolator class, do not add the Interpolator as a child of the Switch Node itself, or the moment the Interpolator is activated it will deactivate itself, hence stopping the Interpolator.

5.6 BranchGroup

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.BranchGroup

BranchGroup defines a Group Node that can be dynamically inserted or removed from a parent Group at runtime. In addition the root parent Group of the scenegraph must be a BranchGroup since only BranchGroups can be inserted into a Locale.

To allow a BranchGroup to be dynamically removed from its parent, the BranchGroup must have the BranchGroup.ALLOW_DETACH capability set. To allow a BranchGroup to be dynamically inserted (or attached) into a parent Group the Group must have the capability Group.ALLOW_CHILDREN_EXTEND. Why these capabilities have been divided between the Group and BranchGroup classes is not quite clear.

BranchGroups are typically used as return values by the methods that build the various components of an application scenegraph. For example, an F1 racing application might define the following abstract class:

abstract class F1Object extends Object
{
 BranchGroup   m_BranchGroup = null;
public F1Object(); protected BranchGroup createBranchGroup();
public getBranchGroup() { if( m_BranchGroup == null ) m_BranchGroup = createBranchGroup();
return m_BranchGroup; } }

The F1Object abstract class forces the application developer to define the createBranchGroup virtual method in a derived class. The createBranchGroup method is responsible for creating a BranchGroup containing the child Nodes required to define the Geometry and Behaviors for the F1Object being created. This very simple OO design allows an F1Object Manager class to be defined that maintains a Vector (or List or Map) of generic F1Objects and is responsible for their creation and removal from the applications scenegraph. The objects derived from F1Object are themselves easily reusable because their geometry generation and management routines are all packages with the class itself. The setUserData method (defined in the section on Group Nodes) can be used to track F1Object instances once they have been inserted into the scenegraph.

5.7 OrderedGroup

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.OrderedGroup

The OrderedGroup Node is a less commonly used scenegraph element. It allows an application developer to have basic control over the rendering order of the children of a Group Node.

An OrderedGroup (or DecalGroup which is derived from OrderedGroup) can be useful for defining the rendering order for essentially coplanar surfaces, such as a road lying on terrain, or a cloth covering a table (figure 5.3). Note that both of these examples make implicit assumptions about the range of possible viewing angles for the objects, if the viewer can sit underneath the table, the tablecloth should not be rendered before the table! Other uses for an OrderedGroup might be to implement signs or labels for geometric objects—the labels should always be rendered on top of the objects they are labeling, irrespective of the viewer’s position.

Figure 5.3

Figure 5.3 The effect of rendering three coplanar child Nodes using an OrderedGroup. The item at index 0 (a Text2D object with the text “3. OrderedGroup”), overlaps the item at index 1 (Child 1), which overlaps the item at index 2 (Child 2)
From NodesTest.java
OrderedGroup orderedGroup = new OrderedGroup();
orderedGroup.addChild
  ( createLabel( "3. OrderedGroup", labelScale ) );
orderedGroup.addChild( createLabel( "Child 1", labelScale ) );
orderedGroup.addChild( createLabel( "Child 2", labelScale ) );

5.8 SharedGroup and link

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.SharedGroup

The SharedGroup Node defines a scenegraph management Node that can be attached to several parent Nodes. The SharedGroup itself can be arbitrarily complex but must occur (as a whole) as a Leaf Node within the scenegraph. A SharedGroup must be wrapped in an instance of a Link object before being added to the scenegraph. The Link must have one unique parent.

SharedGroups cannot contain the following Nodes:

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Leaf
                    |
                    +--javax.media.j3d.Link

The Link Node is used in association with a SharedGroup. Since the SharedGroup can appear in several locations in the scenegraph, and a Node object can only have a single parent Node, a unique Link Node must be used as a placeholder within the scenegraph for the SharedGroup Node. When the scenegraph is traversed, and the Link Node is encountered, the traversal algorithm will step into the SharedGroup that is internally referenced by the Link Node (figure 5.4).

Figure 5.4

Figure 5.4 A Switch Node with two child Link Nodes. Both Link Nodes reference the same SharedGroup (SG). This type of structure is not possible without using Link Nodes as a single Node can only have one parent Node

From NodesTest.java

//create the SharedGroup
SharedGroup sharedGroup1 = new SharedGroup();
//add geometry to the SharedGroup sharedGroup1.addChild ( createLabel( "4. Shared Group 1", labelScale ) );
//add the first Link for the SharedGroup switchGroup.addChild( new Link( sharedGroup1 ) );
//add the second Link for the SharedGroup switchGroup.addChild( new Link( sharedGroup1 ) );
//Note. The InterpolatorTest example also uses SharedGroups //and Link Nodes in a more substantial manner.

SharedGroups can be very useful in minimizing the memory footprint of an application because a single SharedGroup can be referenced hundreds of times within a scenegraph to position repeating geometry within a scene.

5.9 Primitive

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--com.sun.j3d.utils.geometry.Primitive

Primitive is a Group-derived class that serves as the base class for the geometric primitives (Box, Sphere, Cone, etc.) that are defined in the Java 3D utils package. Objects of the Primitive class should not be instantiated directly, and the Primitive class is very difficult to extend usefully. For a full discussion of the Primitive class, see chapter 8.

5.10 TransformGroup

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.Node
              |
              +--javax.media.j3d.Group
                    |
                    +--javax.media.j3d.TransformGroup

The TransformGroup Node is central to almost all scenegraph designs. A TransformGroup incorporates the rotation, translation, and scaling information that is applied to all of its child Nodes. Without the TransformGroup, scenegraphs would be static, always positioned at 0,0,0, uniformly unit scaled, and without rotation about any of the axes. Not a very interesting interactive scene.

The TranformGroup controls orientation of its child Nodes through the Transform3D object that it encapsulates (figure 5.5). The Transform3D object represents such transformations using a typical 4 × 4 transformation matrix. The 4 × 4 matrix of double precision numbers allows scaling, rotation, and translation information to be stored and applied by a single matrix.

Figure 5.5

Figure 5.5 A TransformGroup is used to rotate a ColorCube and Text2D label to a desired orientation

A detailed understanding of the mathematics behind the Transform3D object is very useful, but is beyond the scope of this book. Some useful references for mathematics for 3D graphics are presented in appendix B. Transform3D includes methods that allow application developers to apply transformations while remaining largely ignorant of the underlying implementation.

5.11 Summary

This chapter has introduced many of the Node types available in Java 3D. The Java 3D Nodes cover the basic requirements of most scenegraphs:

Armed with the information in chapters 4 and 5 you should be able to tackle the high-level scenegraph design for your application. The chapters to come will also be very useful, as we start to discuss how the scenegraph fits into the Java 3D VirtualUniverse as well as the rendering model (chapter 6), the data model for your application (chapter 7), and Java 3D’s geometry description capabilities (chapter 8).

[previous]  |  [main]  |  [next]