javax.media.j3d.doc-files.SceneGraphSharing.html Maven / Gradle / Ivy
Show all versions of j3dcore Show documentation
Java 3D API - Reusing Scene Graphs
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.
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.
A SharedGroup node allows multiple Link leaf nodes to share its
subgraph as shown in Figure
1 below.
Figure 1 – Sharing a Subgraph
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.
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
2
shows two instances of NodeComponent objects that are shared and one
NodeComponent element that is duplicated for the cloned subgraph.
Figure 2 – Referenced and Duplicated
NodeComponent Objects
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
3).
To handle these ambiguities, a callback mechanism is provided.
Figure 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
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
4).
Figure 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.
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
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 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
5.
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.
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.
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.
}
// 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);
}
}