/* * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.util.Enumeration; import java.util.Hashtable; /** * The Node class provides an abstract class for all Group and Leaf Nodes. * It provides a common framework for constructing a Java 3D scene graph, * specifically bounding volumes. * *
* For more information, see the * Introduction to the Java 3D API. * *
* NOTE: Applications should not extend this class directly. */ public abstract class Node extends SceneGraphObject { /** * Specifies that this Node will be reported in the pick * SceneGraphPath if a pick occurs. This capability is only * specifiable for Group nodes; it is ignored for leaf nodes. * The default for Group nodes is false. All interior nodes not * needed for uniqueness in a SceneGraphPath that don't have * ENABLE_PICK_REPORTING set to true will not be reported in the * SceneGraphPath. * @see SceneGraphPath */ public static final int ENABLE_PICK_REPORTING = CapabilityBits.NODE_ENABLE_PICK_REPORTING; /** * Specifies that this Node will be reported in the collision * SceneGraphPath if a collision occurs. This capability is only * specifiable for Group nodes; it is ignored for leaf nodes. * The default for Group nodes is false. All interior nodes not * needed for uniqueness in a SceneGraphPath that don't have * ENABLE_COLLISION_REPORTING set to true will not be reported * in the SceneGraphPath. * @see SceneGraphPath */ public static final int ENABLE_COLLISION_REPORTING = CapabilityBits.NODE_ENABLE_COLLISION_REPORTING; /** * Specifies that this Node allows read access to its bounds * information. */ public static final int ALLOW_BOUNDS_READ = CapabilityBits.NODE_ALLOW_BOUNDS_READ; /** * Specifies that this Node allows write access to its bounds * information. */ public static final int ALLOW_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_BOUNDS_WRITE; /** * Specifies that this Node allows reading its pickability state. */ public static final int ALLOW_PICKABLE_READ = CapabilityBits.NODE_ALLOW_PICKABLE_READ; /** * Specifies that this Node allows write access its pickability state. */ public static final int ALLOW_PICKABLE_WRITE = CapabilityBits.NODE_ALLOW_PICKABLE_WRITE; /** * Specifies that this Node allows reading its collidability state. */ public static final int ALLOW_COLLIDABLE_READ = CapabilityBits.NODE_ALLOW_COLLIDABLE_READ; /** * Specifies that this Node allows write access its collidability state. */ public static final int ALLOW_COLLIDABLE_WRITE = CapabilityBits.NODE_ALLOW_COLLIDABLE_WRITE; /** * Specifies that this Node allows read access to its bounds * auto compute information. */ public static final int ALLOW_AUTO_COMPUTE_BOUNDS_READ = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_READ; /** * Specifies that this Node allows write access to its bounds * auto compute information. */ public static final int ALLOW_AUTO_COMPUTE_BOUNDS_WRITE = CapabilityBits.NODE_ALLOW_AUTO_COMPUTE_BOUNDS_WRITE; /** * Specifies that this Node allows read access to its local * coordinates to virtual world (Vworld) coordinates transform. */ public static final int ALLOW_LOCAL_TO_VWORLD_READ = CapabilityBits.NODE_ALLOW_LOCAL_TO_VWORLD_READ; /** * Specifies that this Node allows read access to its parent Group node. * * @since Java 3D 1.4 */ public static final int ALLOW_PARENT_READ = CapabilityBits.NODE_ALLOW_PARENT_READ; /** * Specifies that this Node allows read access to its Locale. * * @since Java 3D 1.4 */ public static final int ALLOW_LOCALE_READ = CapabilityBits.NODE_ALLOW_LOCALE_READ; // Array for setting default read capabilities private static final int[] readCapabilities = { ALLOW_BOUNDS_READ, ALLOW_PICKABLE_READ, ALLOW_COLLIDABLE_READ, ALLOW_AUTO_COMPUTE_BOUNDS_READ, ALLOW_LOCAL_TO_VWORLD_READ, ALLOW_PARENT_READ, ALLOW_LOCALE_READ }; // for checking for cycles private boolean visited = false; /** * Constructs a Node object with default parameters. The default * values are as follows: *
this
node. It is only valid
* for nodes that are part of a live scene graph.
* If the node is not part of a live scene graph then the coordinates are
* calculated as if the graph was attached at the origin of a locale.
* @param t the object that will receive the local coordinates to
* Vworld coordinates transform.
* @exception RestrictedAccessException if the node is compiled but not
* part of a live scene graph
* @exception CapabilityNotSetException if appropriate capability is
* not set and this node is part of live or compiled scene graph
* @exception IllegalSharingException if the node is a descendant
* of a SharedGroup node.
*/
public void getLocalToVworld(Transform3D t) {
if (isLiveOrCompiled()) {
if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
}
if (!isLive()) {
// TODO Support compiled graphs
if (isCompiled())
throw new RestrictedAccessException(J3dI18N.getString("Node7"));
// In 1.4 we support getLocalToVworld for non live nodes
((NodeRetained)this.retained).computeNonLiveLocalToVworld(t, this);
//throw new RestrictedAccessException(J3dI18N.getString("Node7"));
} else {
((NodeRetained)this.retained).getLocalToVworld(t);
}
}
/**
* Retrieves the local coordinates to virtual world coordinates
* transform for the particular path in the scene graph ending with
* this node. This is the composite
* of all transforms in the scene graph from the root down to
* this
node via the specified Link nodes. It is
* only valid for nodes that are part of a live scene graph.
* @param path the specific path from the node to the Locale
* @param t the object that will receive the local coordinates to
* Vworld coordinates transform.
* @exception RestrictedAccessException if the node is not
* part of a live scene graph
* @exception CapabilityNotSetException if appropriate capability is
* not set and this node is part of live scene graph
* @exception IllegalArgumentException if the specified path does
* not contain a valid Locale, or if the last node in the path is
* different from this node
* @exception IllegalSharingException if the node is not a descendant
* of a SharedGroup node.
*/
public void getLocalToVworld(SceneGraphPath path, Transform3D t) {
if (!isLive()) {
throw new RestrictedAccessException(J3dI18N.getString("Node7"));
}
if(!this.getCapability(ALLOW_LOCAL_TO_VWORLD_READ))
throw new CapabilityNotSetException(J3dI18N.getString("Node8"));
((NodeRetained)this.retained).getLocalToVworld(path,t);
}
/**
* Retrieves the locale to which this node is attached. If the
* node is not part of a live scene graph, null is returned.
*
* @return the locale to which this node is attached.
*
* @exception CapabilityNotSetException if appropriate capability is
* not set and this node is part of live scene graph
* @exception IllegalSharingException if the node is a descendant
* of a SharedGroup node.
*
* @since Java 3D 1.4
*/
public Locale getLocale() {
if (!isLive()) {
return null;
}
if(!this.getCapability(ALLOW_LOCALE_READ)) {
throw new CapabilityNotSetException(J3dI18N.getString("Node17"));
}
return ((NodeRetained)this.retained).getLocale();
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
* and then cloneTree
* is called for each child node. For Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
* @return a reference to the cloned sub-graph.
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneTree() {
return cloneTree(new NodeReferenceTable(), false, false);
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
* and then cloneTree
is called for each child node.
* For Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
determines whether data is
* duplicated or copied.
* @return a reference to the cloned scene graph.
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneTree(boolean forceDuplicate) {
return cloneTree(new NodeReferenceTable(), forceDuplicate, false);
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
and
* then cloneTree
is called for each child node. For
* Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
*
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
* flag to be ignored. When false
, the value of each node's
* duplicateOnCloneTree
determines whether data is
* duplicated or copied.
*
* @param allowDanglingReferences when set to true
allows
* the cloneTree
* method to complete even whan a dangling reference is discovered. When
* this parameter is false
a
* DanglingReferenceException
is generated as
* soon as cloneTree detects this situation.
*
* @return a reference to the cloned scene graph.
*
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation and the
* allowDanglingReference
parameter is false.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
*
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneTree(boolean forceDuplicate,
boolean allowDanglingReferences) {
return cloneTree(new NodeReferenceTable(),
forceDuplicate, allowDanglingReferences);
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
* and then cloneTree
* is called for each child node. For Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
* @param referenceTable table that stores the mapping between
* original and cloned nodes. All previous values in the
* referenceTable will be cleared before the clone is made.
* @return a reference to the cloned sub-graph.
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
* @see NodeComponent#setDuplicateOnCloneTree
* @since Java 3D 1.2
*/
public Node cloneTree(NodeReferenceTable referenceTable) {
return cloneTree(referenceTable, false, false);
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
* and then cloneTree
is called for each child node.
* For Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
* @param referenceTable table that stores the mapping between
* original and cloned nodes. All previous values in the
* referenceTable will be cleared before the clone is made.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
determines whether data is
* duplicated or copied.
* @return a reference to the cloned scene graph.
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
* @see NodeComponent#setDuplicateOnCloneTree
* @since Java 3D 1.2
*/
public Node cloneTree(NodeReferenceTable referenceTable,
boolean forceDuplicate) {
return cloneTree(referenceTable, forceDuplicate, false);
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
* and then cloneTree
is called for each child node.
* For Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
* @param referenceTable table that stores the mapping between
* original and cloned nodes. All previous values in the
* referenceTable will be cleared before the clone is made.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
determines whether data is
* duplicated or copied.
*
* @param allowDanglingReferences when set to true
allows
* the cloneTree
* method to complete even whan a dangling reference is discovered. When
* this parameter is false
a
* DanglingReferenceException
is generated as
* soon as cloneTree detects this situation.
*
* @return a reference to the cloned scene graph.
* @exception DanglingReferenceException When a dangling reference is
* discovered during the cloneTree operation.
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
* @exception SceneGraphCycleException if there is a cycle in the
* scene graph
* @see NodeComponent#setDuplicateOnCloneTree
* @since Java 3D 1.2
*/
public Node cloneTree(NodeReferenceTable referenceTable,
boolean forceDuplicate,
boolean allowDanglingReferences) {
if (!isLiveOrCompiled()) {
// this will throw a SceneGraphCycleException if there is
// a cycle
checkForCycle();
}
referenceTable.set(allowDanglingReferences, new Hashtable());
Node n = cloneTree(forceDuplicate, referenceTable.objectHashtable);
// go through hash table looking for Leaf nodes.
// call updateNodeReferences for each.
Enumeration e = referenceTable.objectHashtable.elements();
while (e.hasMoreElements()) {
SceneGraphObject o = (SceneGraphObject) e.nextElement();
o.updateNodeReferences(referenceTable);
}
return n;
}
/**
* Duplicates all the nodes of the specified sub-graph. For Group Nodes
* the group node is duplicated via a call to cloneNode
and
* then cloneTree
is called for each child node. For
* Leaf Nodes, component
* data can either be duplicated or be made a reference to the original
* data. Leaf Node cloneTree behavior is determined by the
* duplicateOnCloneTree
flag found in every Leaf Node's
* component data class and by the forceDuplicate
paramter.
*
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
* flag to be ignored. When false
, the value of each node's
* duplicateOnCloneTree
determines whether data is
* duplicated or copied.
*
* @param nodeHashtable a hashtable used to map orignal node references to
* their cloned counterpart.
*
* @return a reference to the cloned scene graph.
*
* @see NodeComponent#setDuplicateOnCloneTree
*/
Node cloneTree(boolean forceDuplicate, Hashtable nodeHashtable) {
Node l;
this.nodeHashtable = nodeHashtable;
try {
l = cloneNode(forceDuplicate);
} catch (RuntimeException e) {
this.nodeHashtable = null;
throw e;
}
// must reset to null so that we can tell whether the call is from user
// or cloneTree
this.nodeHashtable = null;
nodeHashtable.put(this, l);
return l;
}
/**
* Used to create a new instance of the node. This routine is called
* by cloneTree
to duplicate the current node.
* cloneNode
should be overridden by any user subclassed
* objects. All subclasses must have their cloneNode
* method consist of the following lines:
* * NOTE: Applications should not call this method directly. * It should only be called by the cloneTree method. * * @param forceDuplicate when set to* public Node cloneNode(boolean forceDuplicate) { * UserSubClass usc = new UserSubClass(); * usc.duplicateNode(this, forceDuplicate); * return usc; * } *
true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
variable determines whether
* NodeComponent data is duplicated or copied.
*
* @exception RestrictedAccessException if this object is part of live
* or compiled scene graph
*
* @see Node#cloneTree
* @see Node#duplicateNode
* @see NodeComponent#setDuplicateOnCloneTree
*/
public Node cloneNode(boolean forceDuplicate) {
throw new RuntimeException(J3dI18N.getString("Node12"));
}
/**
* Copies all node information from originalNode
into
* the current node. This method is called from the
* cloneNode
method which is, in turn, called by the
* cloneTree
method.
*
* For any NodeComponent
objects
* contained by the object being duplicated, each NodeComponent
* object's duplicateOnCloneTree
value is used to determine
* whether the NodeComponent
should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate
parameter in the cloneTree
* method to true
.
*
*
* NOTE: Applications should not call this method directly.
* It should only be called by the cloneNode method.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Group#cloneNode
* @see Node#duplicateNode
* @see Node#cloneTree
* @see NodeComponent#setDuplicateOnCloneTree
*/
public void duplicateNode(Node originalNode,
boolean forceDuplicate) {
duplicateAttributes(originalNode, forceDuplicate);
}
/**
* Copies all node information from originalNode
into
* the current node. This method is called from subclass of
* duplicateNode
method which is, in turn, called by the
* cloneNode
method.
*
* For any NodeComponent objects
* contained by the object being duplicated, each NodeComponent
* object's duplicateOnCloneTree
value is used to determine
* whether the NodeComponent should be duplicated in the new node
* or if just a reference to the current node should be placed in the
* new node. This flag can be overridden by setting the
* forceDuplicate
parameter in the cloneTree
* method to true
.
*
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
variable determines whether
* NodeComponent data is duplicated or copied.
*
* @see Group#cloneNode
* @see Node#duplicateNode
* @see Node#cloneTree
* @see NodeComponent#setDuplicateOnCloneTree
*/
final void checkDuplicateNode(Node originalNode,
boolean forceDuplicate) {
if (originalNode.nodeHashtable != null) {
duplicateAttributes(originalNode, forceDuplicate);
} else {
// user call cloneNode() or duplicateNode() directly
// instead of via cloneTree()
originalNode.nodeHashtable = new Hashtable();
duplicateAttributes(originalNode, forceDuplicate);
originalNode.nodeHashtable = null;
}
}
/**
* Copies all Node information from
* originalNode
into
* the current node. This method is called from the
* cloneNode
method which is, in turn, called by the
* cloneTree
method.
*
* @param originalNode the original node to duplicate.
* @param forceDuplicate when set to true
, causes the
* duplicateOnCloneTree
flag to be ignored. When
* false
, the value of each node's
* duplicateOnCloneTree
variable determines whether
* NodeComponent data is duplicated or copied.
*
* @exception RestrictedAccessException if originalNode object is part of a live
* or compiled scenegraph.
*
* @see Node#duplicateNode
* @see Node#cloneTree
* @see NodeComponent#setDuplicateOnCloneTree
*/
void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
if (originalNode.isLiveOrCompiled()) {
throw new RestrictedAccessException(J3dI18N.getString("Node13"));
}
super.duplicateSceneGraphObject(originalNode);
NodeRetained attr = (NodeRetained) originalNode.retained;
NodeRetained rt = (NodeRetained) retained;
rt.setPickable(attr.getPickable());
rt.setCollidable(attr.getCollidable());
}
/**
* When set to true
this Node
can be Picked.
* Setting to false indicates that this node and it's children
* are ALL unpickable.
*
* @param pickable Indicates if this node should be pickable or not
*/
public void setPickable( boolean pickable ) {
if (isLiveOrCompiled())
if(!this.getCapability(ALLOW_PICKABLE_WRITE))
throw new CapabilityNotSetException(J3dI18N.getString("Node14"));
((NodeRetained)retained).setPickable(pickable);
}
/**
* Returns true if this Node
is pickable,
* false if it is not pickable.
*/
public boolean getPickable() {
if (isLiveOrCompiled())
if(!this.getCapability(ALLOW_PICKABLE_READ))
throw new CapabilityNotSetException(J3dI18N.getString("Node3"));
return ((NodeRetained)retained).getPickable();
}
/**
* checks for cycles in the scene graph
*/
void checkForCycle() {
if (visited) {
throw new SceneGraphCycleException(J3dI18N.getString("Node15"));
}
visited = true;
Node parent = getParent();
if (parent != null) {
parent.checkForCycle();
}
visited = false;
}
}