The Java 3D API Specification |

C H A P T E R 7

Reusing Scene Graphs

> JAVA 3D provides application programmers with two different means for reusing scene graphs. First, multiple scene graphs can share a common subgraph. Second, the node hierarchy of a common subgraph can be cloned, while still sharing large component objects such as geometry and texture objects. In the first case, changes in the shared subgraph affect all scene graphs that refer to the shared subgraph. In the second case, each instance is unique-a change in one instance does not affect any other instance.

> ## 7.1 Sharing Subgraphs

> An application that wishes to share a subgraph from multiple places in a scene graph must do so through the use of the Link leaf node and an associated SharedGroup node. The SharedGroup node serves as the root of the shared subgraph. The Link leaf node refers to the SharedGroup node. It does not incorporate the shared scene graph directly into its scene graph.

> ### 7.1.1 SharedGroup Node

> A SharedGroup node allows multiple Link leaf nodes to share its subgraph (see Figure 7-1) according to the following semantics:

> - A SharedGroup may be referenced by one or more Link leaf nodes. Any runtime changes to a node or component object in this shared subgraph affect all graphs that refer to this subgraph.

> - A SharedGroup may be compiled by calling its `compile` method prior to being referenced by any Link leaf nodes.

> - Only Link leaf nodes may refer to SharedGroup nodes. A SharedGroup node cannot have parents or be attached to a Locale.

> Figure 7-1 Sharing a Subgraph

> A shared subgraph may contain any group node, except an embedded SharedGroup node (SharedGroup nodes cannot have parents). However, only the following leaf nodes may appear in a shared subgraph:

> - Light

> - Link

> - Morph

> - Shape

> - Sound

> An `IllegalSharingException` is thrown if any of the following leaf nodes appear in a shared subgraph:

> - AlternateAppearance

> - Background

> - BoundingLeaf

> - Behavior

> - Clip

> - Fog

> - ModelClip

> - Soundscape

> - ViewPlatform

> ##### Constructors

>     public SharedGroup()

> Constructs and initializes a new SharedGroup node object.

> ##### Methods

> The SharedGroup node defines the following methods:

>     public void compile()

> This method compiles the source SharedGroup associated with this object and creates and caches a newly compiled scene graph.

> ### 7.1.2 Link Leaf Node

> The Link leaf node allows an application to reference a shared graph, rooted by a SharedGroup node, from within a branch graph or another shared graph. See Figure 7-1. Any number of Link nodes can refer to the same SharedGroup node.

> ##### Constants

> The Link node object defines two flags.

>     public static final int ALLOW_SHARED_GROUP_READ
>     public static final int ALLOW_SHARED_GROUP_WRITE

> These flags, when enabled using the `setCapability` method, allow an application to invoke methods that respectively read and write the SharedGroup node pointed to by this Link node. These capability flags are enforced only when the node is part of a live or compiled scene graph.

> ##### Constructors

> The Link node object defines two constructors.

>     public Link()
>     public Link(SharedGroup sharedGroup)

> The first form constructs a Link node object that does not yet point to a SharedGroup node. The second form constructs a Link node object that points to the specified SharedGroup node.

> ##### Methods

> The Link node object defines two methods.

>     public void setSharedGroup(SharedGroup sharedGroup)
>     public SharedGroup getSharedGroup()

> These methods access and modify the SharedGroup node associated with this Link leaf node.

> ## 7.2 Cloning Subgraphs

> An application developer may wish to reuse a common subgraph without completely sharing that subgraph. For example, the developer may wish to create a parking lot scene consisting of multiple cars, each with a different color. The developer might define three basic types of cars, such as convertible, truck, and sedan. To create the parking lot scene, the application will instantiate each type of car several times. Then the application can change the color of the various instances to create more variety in the scene. Unlike shared subgraphs, each instance is a separate copy of the scene graph definition: Changes to one instance do not affect any other instance.

> Java 3D provides the `cloneTree` method for this purpose. The `cloneTree` method allows the programmer to change some attributes (NodeComponent objects) in a scene graph, while at the same time sharing the majority of the scene graph data-the geometry.

> ##### Methods

>     public Node cloneTree()
>     public Node cloneTree(boolean forceDuplicate)
>     public Node cloneTree(boolean forceDuplicate,
>            boolean  allowDanglingReferences)
>     public Node cloneTree(NodeReferenceTable referenceTable)
>     public Node cloneTree(NodeReferenceTable referenceTable,
>            boolean  forceDuplicate)
>     public Node cloneTree(NodeReferenceTable referenceTable,
>            boolean  forceDuplicate, boolean allowDanglingReferences)

> These methods start the cloning of the subgraph. The optional `forceDuplicate` parameter, when set to `true`, causes leaf NodeComponent objects to ignore their `duplicateOnCloneTree` value and always be duplicated (see Section 7.2.1, "References to Node Component Objects"). The `allowDanglingReferences` parameter, when set to `true`, will permit the cloning of a subgraph even when a dangling reference is generated (see Section 7.2.3, "Dangling References"). Setting `forceDuplicate` and `allowDanglingReferences` to `false` is the equivalent of calling `cloneTree` without any parameters. This will result in NodeComponent objects being either duplicated or referenced in the cloned node, based on their `duplicateOnCloneTree` value. A `DanglingReferenceException` will be thrown if a dangling reference is encountered.

> When the `cloneTree` method is called on a node, that node is duplicated along with its entire internal state. If the node is a Group node, `cloneTree` is then called on each of the node's children.

> The `cloneTree` method cannot be called on a live or compiled scene graph.

> ### 7.2.1 References to Node Component Objects

> When `cloneTree` reaches a leaf node, there are two possible actions for handling the leaf node's NodeComponent objects (such as Material, Texture, and so forth). First, the cloned leaf node can reference the original leaf node's NodeComponent object-the NodeComponent object itself is not duplicated. Since the cloned leaf node shares the NodeComponent object with the original leaf node, changing the data in the NodeComponent object will effect a change in both nodes. This mode would also be used for objects that are read-only at run time.

> Alternatively, the NodeComponent object can be duplicated, in which case the new leaf node would reference the duplicated object. This mode allows data referenced by the newly created leaf node to be modified without that modification affecting the original leaf node.

> Figure 7-2 shows two instances of NodeComponent objects that are shared and one NodeComponent element that is duplicated for the cloned subgraph.

> Figure 7-2 Referenced and Duplicated NodeComponent Objects

> ##### Methods

>     public void setDuplicateOnCloneTree(boolean)
>     public void getDuplicateOnCloneTree()

> These methods set a flag that controls whether a NodeComponent object is duplicated or referenced on a call to `cloneTree`. By default this flag is `false`, meaning that the NodeComponent object will not be duplicated on a call to `cloneTree`-newly created leaf nodes will refer to the original NodeComponent object instead.

> If the `cloneTree` method is called with the `forceDuplicate` parameter set to `true`, the `duplicateOnCloneTree` flag is ignored and the entire scene graph is duplicated.

> ### 7.2.2 References to Other Scene Graph Nodes

> Leaf nodes that contain references to other nodes (for example, Light nodes reference a Group node) can create a problem for the `cloneTree` method. After the `cloneTree` operation is performed, the reference in the cloned leaf node will still refer to the node in the original subgraph-a situation that is most likely incorrect (see Figure 7-3).

> To handle these ambiguities, a callback mechanism is provided.

> Figure 7-3 References to Other Scene Graph Nodes

> A leaf node that needs to update referenced nodes upon being duplicated by a call to `cloneTree` must implement the `updateNodeReferences` method. By using this method, the cloned leaf node can determine if any nodes referenced by it have been duplicated and, if so, update the appropriate references to their cloned counterparts.

> Suppose, for instance, that the leaf node Lf1 in Figure 7-3 implemented the `updateNodeReferences` method. Once all nodes had been duplicated, the `clone-Tree` method would then call each cloned leaf's node `updateNodeReferences` method. When cloned leaf node Lf2's method was called, Lf2 could ask if the node N1 had been duplicated during the `cloneTree` operation. If the node had been duplicated, leaf Lf2 could then update its internal state with the cloned node, N2 (see Figure 7-4).

> Figure 7-4 Updated Subgraph after `updateNodeReferences` Call

> All predefined Java 3D nodes will automatically have their `updateNodeReferences` method defined. Only subclassed nodes that reference other nodes need to have this method overridden by the user.

> ##### Methods

>     public void updateNodeReferences(NodeReferenceTable
>            referenceTable)

> This SceneGraphObject node method is called by the `cloneTree` method after all nodes in the subgraph have been cloned. The user can query the NodeReferenceTable object (see Section 7.2.5, "NodeReferenceTable Object") to determine if any nodes that the SceneGraphObject node references have been duplicated by the `cloneTree` call and, if so, what the corresponding node is in the new subgraph. If a user extends a predefined Java 3D object and adds a reference to another node, this method must be defined in order to ensure proper operation of the `cloneTree` method. The first statement in the user's `updateNodeReferences` method must be `super.updateNodeReferences(referenceTable)`. For predefined Java 3D nodes, this method will be implemented automatically.

> The NodeReferenceTable object is passed to the `updateNodeReferences` method and allows references from the old subgraph to be translated into references in the cloned subgraph. The translation is performed by the `getNew-NodeReference` method.

>     public final SceneGraphObject
>            getNewObjectReference(SceneGraphObject oldReference)

> This method takes a reference to the node in the original subgraph as an input parameter and returns a reference to the equivalent node in the just-cloned subgraph. If the equivalent node in the cloned subgraph does not exist, either an exception is thrown or a reference to the original node is returned (see Section 7.2.3, "Dangling References").

> ### 7.2.3 Dangling References

> Because `cloneTree` is able to start the cloning operation from any node, there is a potential for creating dangling references. A dangling reference can occur only when a leaf node that contains a reference to another scene graph node is cloned. If the referenced node is not cloned, a dangling reference situation exists: There are now two leaf nodes that access the same node (Figure 7-5). A dangling reference is discovered when a leaf node's `updateNodeReferences` method calls the `getNewNodeReference` method and the cloned subgraph does not contain a counterpart to the node being looked up.

> Figure 7-5 Dangling Reference: Bold Nodes Are Being Cloned

> When a dangling reference is discovered, `cloneTree` can handle it in one of two ways. If `cloneTree` is called without the `allowDanglingReferences` parameter set to `true`, a dangling reference will result in a `DanglingReferenceException` being thrown. The user can catch this exception if desired. If `cloneTree` is called with the `allowDanglingReferences` parameter set to `true`, the `update-NodeReferences` method will return a reference to the same object passed into the `getNewNodeReference` method. This will result in the `cloneTree` operation completing with dangling references, as in Figure 7-5.

> ### 7.2.4 Subclassing Nodes

> All Java 3D predefined nodes (for example, Interpolators and LOD nodes) automatically handle all node reference and duplication operations. When a user subclasses a Leaf object or a NodeComponent object, certain methods must be provided in order to ensure the proper operation of `cloneTree`.

> Leaf node subclasses (for example, Behaviors) that contain any user node-specific data that needs to be duplicated during a `cloneTree` operation must define the following two methods:

>     Node cloneNode(boolean forceDuplicate);
>     void duplicateNode(Node n, boolean forceDuplicate)

> The `cloneNode` method consists of three lines:

>     UserSubClass usc = new UserSubClass();
>     usc.duplicateNode(this, forceDuplicate);
>     return usc;

> The `duplicateNode` method must first call `super.duplicateNode` before duplicating any necessary user-specific data or setting any user-specific state.

> NodeComponent subclasses that contain any user node-specific data must define the following two methods:

>     NodeComponent cloneNodeComponent();
>     void duplicateNodeComponent(NodeComponent nc,
>            boolean  forceDuplicate);

> The `cloneNodeComponent` method consists of three lines:

>     UserNodeComponent unc = new UserNodeComponent();
>     unc.duplicateNodeComponent(this, forceDuplicate);
>     return un;

> The `duplicateNodeComponent` must first call `super.duplicateNodeComponent` and then can duplicate any user-specific data or set any user-specific state as necessary.

> ### 7.2.5 NodeReferenceTable Object

> The NodeReferenceTable object is used by a leaf node's `updateNodeReferences` method called by the `cloneTree` operation. The NodeReferenceTable maps nodes from the original subgraph to the new nodes in the cloned subgraph. This information can than be used to update any cloned leaf node references to reference nodes in the cloned subgraph. This object can be created only by Java 3D.

> ##### Constructors

>     public NodeReferenceTable()

> Constructs an empty NodeReferenceTable.

> ##### Methods

>     public SceneGraphObject getNewObjectReference(SceneGraphObject
>            oldReference)

> This method takes a reference to the node in the original subgraph as an input parameter and returns a reference to the equivalent node in the just-cloned subgraph. If the equivalent node in the cloned subgraph does not exist, either an exception is thrown or a reference to the original node is returned (see Section 7.2.3, "Dangling References").

> ### 7.2.6 Example User Behavior Node

> The following is an example of a user-defined Behavior object to show properly how to define a node to be compatible with the `cloneTree` operation.

>     class RotationBehavior extends Behavior {
>         TransformGroup objectTransform;
>         WakeupOnElapsedFrames w;

>         Matrix4d rotMat = new Matrix4d();
>         Matrix4d objectMat = new Matrix4d();
>         Transform3D t = new Transform3D();

>         // Override Behavior's initialize method to set up wakeup
>         // criteria

>         public void initialize() {
>             // Establish initial wakeup criteria

>             wakeupOn(w);
>        }

>         // Override Behavior's stimulus method to handle the event

>         public void processStimulus(Enumeration criteria) {
>             // Rotate by another PI/120.0 radians

>             objectMat.mul(objectMat, rotMat);
>             t.set(objectMat);
>             objectTransform.setTransform(t);

>             // Set wakeup criteria for next time

>             wakeupOn(w);
>         }

>         // Constructor for rotation behavior.

>         public RotationBehavior(TransformGroup tg, int numFrames) {
>             w = new WakeupOnElapsedFrames(numFrames);
>             objectTransform = tg;
>             objectMat.setIdentity();

>             // Create a rotation matrix that rotates PI/120.0
>             // radians per frame
>             rotMat.rotX(Math.PI/120.0);

>             // Note: When this object is duplicated via cloneTree,
>             // the cloned RotationBehavior node needs to point to
>             // the TransformGroup in the just-cloned tree.
>         }

>         // Sets a new TransformGroup.

>         public void setTransformGroup(TransformGroup tg) {
>             objectTransform = tg;
>         }

>         // The next two methods are needed for cloneTree to operate
>         // correctly.
>         // cloneNode is needed to provide a new instance of the user
>         // derived subclass.

>         public Node cloneNode(boolean forceDuplicate) {
>             // Get all data from current node needed for
>             // the constructor
>             int numFrames = w.getElapsedFrameCount();

>             RotationBehavior r =
>                 new RotationBehavior(objectTransform, numFrames);
>             r.duplicateNode(this, forceDuplicate);
>             return r;
>         }
>         // duplicateNode is needed to duplicate all super class
>         // data as well as all user data.

>         public void duplicateNode(Node originalNode, boolean
>          forceDuplicate) {
>             super.duplicateNode(originalNode, forceDuplicate);
>             // Nothing to do here - all unique data was handled
>             // in the constructor in the cloneNode routine.
>         }

>         // duplicateNode is needed to duplicate all super class
>         // data as well as all user data.

>         public void duplicateNode(Node originalNode, boolean
>          forceDuplicate) {
>             super.duplicateNode(originalNode, forceDuplicate);
>             // Nothing to do here - all unique data was handled
>             // in the constructor in the cloneNode routine.

>         }

>         // Callback for when this leaf is cloned. For this object
>         // we want to find the cloned TransformGroup node that this
>         // clone Leaf node should reference.

>         public void updateNodeReferences(NodeReferenceTable t) {
>             super.updateNodeReferences(t);

>             // Update node's TransformGroup to proper reference

>             TransformGroup newTg =
>              (TransformGroup)t.getNewObjectReference(
>                 objectTransform);
>             setTransformGroup(newTg);
>         }
>     }

The Java 3D API Specification |

##### Copyright © 2000, Sun Microsystems, Inc. All rights reserved.