diff options
Diffstat (limited to 'src/javax/media/j3d/WakeupOnCollisionEntry.java')
-rw-r--r-- | src/javax/media/j3d/WakeupOnCollisionEntry.java | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/src/javax/media/j3d/WakeupOnCollisionEntry.java b/src/javax/media/j3d/WakeupOnCollisionEntry.java new file mode 100644 index 0000000..27fcd63 --- /dev/null +++ b/src/javax/media/j3d/WakeupOnCollisionEntry.java @@ -0,0 +1,595 @@ +/* + * Copyright 1997-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.Vector; + +/** + * Class specifying a wakeup when the specified object + * collides with any other object in the scene graph. + * + */ +public final class WakeupOnCollisionEntry extends WakeupCriterion { + + // different types of WakeupIndexedList that use in GeometryStructure + static final int COND_IN_GS_LIST = 0; + static final int COLLIDEENTRY_IN_BS_LIST = 1; + + // total number of different IndexedUnorderedSet types + static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2; + + /** + * Use geometry in computing collisions. + */ + public static final int USE_GEOMETRY = 10; + + /** + * Use geometric bounds as an approximation in computing collisions. + */ + public static final int USE_BOUNDS = 11; + + static final int GROUP = NodeRetained.GROUP; + static final int BOUNDINGLEAF = NodeRetained.BOUNDINGLEAF; + static final int SHAPE = NodeRetained.SHAPE; + static final int MORPH = NodeRetained.MORPH; + static final int ORIENTEDSHAPE3D = NodeRetained.ORIENTEDSHAPE3D; + static final int BOUND = 0; + + /** + * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS + */ + int accuracyMode; + + // Cached the arming Node being used when it is not BOUND + NodeRetained armingNode; + + // A transformed Bounds of Group/Bounds, use by + // BOUND, GROUP + Bounds vwcBounds = null; + + // Use by BoundingLeaf, point to mirror BoundingLeaf + // transformedRegion under this leaf is used. + BoundingLeafRetained boundingLeaf = null; + + /** + * Geometry atoms that this wakeup condition refer to. + * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE + */ + UnorderList geometryAtoms = null; + + // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND + int nodeType; + + SceneGraphPath armingPath = null; + Bounds armingBounds = null; + + // the following two references are set only after a collision + // has occurred + Bounds collidingBounds = null; + SceneGraphPath collidingPath = null; + + /** + * Constructs a new WakeupOnCollisionEntry criterion with + * USE_BOUNDS for a speed hint. + * @param armingPath the path used to <em>arm</em> collision + * detection + * @exception IllegalArgumentException if object associated with the + * SceneGraphPath is other than a Group, Shape3D, Morph, or + * BoundingLeaf node. + */ + public WakeupOnCollisionEntry(SceneGraphPath armingPath) { + this(armingPath, USE_BOUNDS); + } + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingPath the path used to <em>arm</em> collision + * detection + * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how + * accurately Java 3D will perform collision detection + * @exception IllegalArgumentException if hint is not one of + * USE_GEOMETRY or USE_BOUNDS. + * @exception IllegalArgumentException if object associated with the + * SceneGraphPath is other than a Group, Shape3D, Morph, or + * BoundingLeaf node. + */ + public WakeupOnCollisionEntry(SceneGraphPath armingPath, + int speedHint) { + this(new SceneGraphPath(armingPath), speedHint, null); + } + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingNode the Group, Shape, or Morph node used to + * <em>arm</em> collision detection + * @exception IllegalArgumentException if object is under a + * SharedGroup node or object is other than a Group, Shape3D, + * Morph or BoundingLeaf node. + */ + public WakeupOnCollisionEntry(Node armingNode) { + this(armingNode, USE_BOUNDS); + } + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingNode the Group, Shape, or Morph node used to + * <em>arm</em> collision detection + * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how + * accurately Java 3D will perform collision detection + * @exception IllegalArgumentException if hint is not one of + * USE_GEOMETRY or USE_BOUNDS. + * @exception IllegalArgumentException if object is under a + * SharedGroup node or object is other than a Group, Shape3D, + * Morph or BoundingLeaf node. + */ + public WakeupOnCollisionEntry(Node armingNode, int speedHint) { + this(new SceneGraphPath(null, armingNode), speedHint, null); + } + + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingBounds the bounds object used to <em>arm</em> collision + * detection + */ + public WakeupOnCollisionEntry(Bounds armingBounds) { + this(null, USE_BOUNDS, (Bounds) armingBounds.clone()); + } + + /** + * Constructs a new WakeupOnCollisionEntry criterion. + * @param armingPath the path used to <em>arm</em> collision + * detection + * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how + * accurately Java 3D will perform collision detection + * @param armingBounds the bounds object used to <em>arm</em> collision + * detection + * @exception IllegalArgumentException if hint is not one of + * USE_GEOMETRY or USE_BOUNDS. + * @exception IllegalArgumentException if object associated with the + * SceneGraphPath is other than a Group, Shape3D, Morph, or + * BoundingLeaf node. + */ + WakeupOnCollisionEntry(SceneGraphPath armingPath, + int speedHint, Bounds armingBounds) { + if (armingPath != null) { + this.armingNode = (NodeRetained) armingPath.getObject().retained; + nodeType = getNodeType(armingNode, armingPath, + "WakeupOnCollisionEntry"); + this.armingPath = armingPath; + validateSpeedHint(speedHint, "WakeupOnCollisionEntry4"); + } else { + this.armingBounds = armingBounds; + nodeType = BOUND; + } + accuracyMode = speedHint; + WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES); + } + + /** + * Returns the path used in specifying the collision condition. + * @return the SceneGraphPath object generated when arming this + * criterion---null implies that a bounds object armed this criteria + */ + public SceneGraphPath getArmingPath() { + return (armingPath != null ? + new SceneGraphPath(armingPath) : null); + } + + /** + * Returns the bounds object used in specifying the collision condition. + * @return the Bounds object generated when arming this + * criterion---null implies that a SceneGraphPath armed this criteria + */ + public Bounds getArmingBounds() { + return (armingBounds != null ? + (Bounds)armingBounds.clone() : null); + } + + /** + * Retrieves the path describing the object causing the collision. + * @return the SceneGraphPath that describes the triggering object. + * @exception IllegalStateException if not called from within the + * a behavior's processStimulus method which was awoken by a collision. + */ + public SceneGraphPath getTriggeringPath() { + if (behav == null) { + throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionEntry5")); + } + + synchronized (behav) { + if (!behav.inCallback) { + throw new IllegalStateException + (J3dI18N.getString("WakeupOnCollisionEntry5")); + } + } + return (collidingPath != null ? + new SceneGraphPath(collidingPath): null); + } + + /** + * Retrieves the Bounds object that caused the collision + * @return the colliding Bounds object. + * @exception IllegalStateException if not called from within the + * a behavior's processStimulus method which was awoken by a collision. + */ + public Bounds getTriggeringBounds() { + if (behav == null) { + throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionEntry6")); + } + + synchronized (behav) { + if (!behav.inCallback) { + throw new IllegalStateException + (J3dI18N.getString("WakeupOnCollisionEntry6")); + } + } + return (collidingBounds != null ? + (Bounds)(collidingBounds.clone()): null); + } + + + /** + * Node legality checker + * throw Exception if node is not legal. + * @return nodeType + */ + static int getNodeType(NodeRetained armingNode, + SceneGraphPath armingPath, String s) + throws IllegalArgumentException { + + // check if SceneGraphPath is unique + // Note that graph may not live at this point so we + // can't use node.inSharedGroup. + if (!armingPath.validate()) { + throw new IllegalArgumentException(J3dI18N.getString(s + "7")); + } + + if (armingNode.inBackgroundGroup) { + throw new IllegalArgumentException(J3dI18N.getString(s + "1")); + } + + // This should come before Shape3DRetained check + if (armingNode instanceof OrientedShape3DRetained) { + return ORIENTEDSHAPE3D; + } + + if (armingNode instanceof Shape3DRetained) { + return SHAPE; + } + + if (armingNode instanceof MorphRetained) { + return MORPH; + } + + if (armingNode instanceof GroupRetained) { + return GROUP; + } + + if (armingNode instanceof BoundingLeafRetained) { + return BOUNDINGLEAF; + } + + throw new IllegalArgumentException(J3dI18N.getString(s + "0")); + } + + /** + * speedHint legality checker + * throw Exception if speedHint is not legal + */ + static void validateSpeedHint(int speedHint, String s) + throws IllegalArgumentException { + if ((speedHint != USE_GEOMETRY) && (speedHint != USE_BOUNDS)) { + throw new IllegalArgumentException(J3dI18N.getString(s)); + } + + } + + + /** + * This is a callback from BehaviorStructure. It is + * used to add wakeupCondition to behavior structure. + */ + @Override + void addBehaviorCondition(BehaviorStructure bs) { + + switch (nodeType) { + case SHAPE: // Use geometryAtoms[].collisionBounds + case ORIENTEDSHAPE3D: + if (!armingNode.source.isLive()) { + return; + } + if (geometryAtoms == null) { + geometryAtoms = new UnorderList(1, GeometryAtom.class); + } + Shape3DRetained shape = (Shape3DRetained) armingNode; + geometryAtoms.add(Shape3DRetained.getGeomAtom(shape.getMirrorShape(armingPath))); + break; + case MORPH: // Use geometryAtoms[].collisionBounds + if (!armingNode.source.isLive()) { + return; + } + if (geometryAtoms == null) { + geometryAtoms = new UnorderList(1, GeometryAtom.class); + } + MorphRetained morph = (MorphRetained) armingNode; + geometryAtoms.add(Shape3DRetained.getGeomAtom(morph.getMirrorShape(armingPath))); + break; + case BOUNDINGLEAF: // use BoundingLeaf.transformedRegion + if (!armingNode.source.isLive()) { + return; + } + this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf; + break; + case BOUND: // use this.vwcBounds + vwcBounds = (Bounds) armingBounds.clone(); + this.armingNode = behav; + break; + case GROUP: + if (!armingNode.source.isLive()) { + return; + } + if (accuracyMode == USE_GEOMETRY) { + if (geometryAtoms == null) { + geometryAtoms = new UnorderList(1, GeometryAtom.class); + } + ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms); + } + // else use this.vwcBounds + default: + } + + behav.universe.geometryStructure.addWakeupOnCollision(this); + } + + /** + * This is a callback from BehaviorStructure. It is + * used to remove wakeupCondition from behavior structure. + */ + @Override + void removeBehaviorCondition(BehaviorStructure bs) { + vwcBounds = null; + if (geometryAtoms != null) { + geometryAtoms.clear(); + } + boundingLeaf = null; + behav.universe.geometryStructure.removeWakeupOnCollision(this); + } + + + // Set collidingPath & collidingBounds + void setTarget(BHLeafInterface leaf) { + SceneGraphPath path; + Bounds bound; + + if (leaf instanceof GeometryAtom) { + // Find the triggered Path & Bounds for this geometry Atom + GeometryAtom geomAtom = (GeometryAtom) leaf; + Shape3DRetained shape = geomAtom.source; + + path = getSceneGraphPath(shape.sourceNode, + shape.key, + shape.getCurrentLocalToVworld(0)); + bound = getTriggeringBounds(shape); + + } else { + // Find the triggered Path & Bounds for this alternative + // collision target + GroupRetained group = (GroupRetained) leaf; + path = getSceneGraphPath(group); + bound = getTriggeringBounds(group); + } + + if (path != null) { + // colliding path may be null when branch detach before + // user behavior retrieve the previous colliding path + collidingPath = path; + collidingBounds = bound; + } + } + + + // Invoke from GeometryStructure to update vwcBounds of GROUP + void updateCollisionBounds(boolean reEvaluateGAs){ + if (nodeType == GROUP) { + GroupRetained group = (GroupRetained) armingNode; + if (group.collisionBound != null) { + vwcBounds = (Bounds) group.collisionBound.clone(); + } else { + // this may involve recursive tree traverse if + // BoundsAutoCompute is true, we can't avoid + // since the bound under it may change by transform + vwcBounds = group.getEffectiveBounds(); + } + group.transformBounds(armingPath, vwcBounds); + } else if (nodeType == BOUND) { + vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld()); + } + + if (reEvaluateGAs && + (nodeType == GROUP) && + (accuracyMode == USE_GEOMETRY)) { + geometryAtoms.clear(); + ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms); + } + } + + + /** + * Return the TriggeringBounds for node + */ + static Bounds getTriggeringBounds(Shape3DRetained mirrorShape) { + NodeRetained node = mirrorShape.sourceNode; + + if (node instanceof Shape3DRetained) { + Shape3DRetained shape = (Shape3DRetained) node; + if (shape.collisionBound == null) { + // TODO: get bounds by copy + return shape.getEffectiveBounds(); + } + return shape.collisionBound; + } + + + MorphRetained morph = (MorphRetained) node; + if (morph.collisionBound == null) { + // TODO: get bounds by copy + return morph.getEffectiveBounds(); + } + return morph.collisionBound; + } + + + /** + * Return the TriggeringBounds for node + */ + static Bounds getTriggeringBounds(GroupRetained group) { + if (group.collisionBound == null) { + // TODO: get bounds by copy + return group.getEffectiveBounds(); + } + return group.collisionBound; + } + + static SceneGraphPath getSceneGraphPath(GroupRetained group) { + // Find the transform base on the key + Transform3D transform = null; + GroupRetained srcGroup = group.sourceNode; + + synchronized (srcGroup.universe.sceneGraphLock) { + if (group.key == null) { + transform = srcGroup.getCurrentLocalToVworld(); + } else { + HashKey keys[] = srcGroup.localToVworldKeys; + if (keys == null) { + // the branch is already detach when + // Collision got this message + return null; + } + transform = srcGroup.getCurrentLocalToVworld(group.key); + } + return getSceneGraphPath(srcGroup, group.key, transform); + } + + } + + /** + * return the SceneGraphPath of the geomAtom. + * Find the alternative Collision target closest to the locale. + */ + static SceneGraphPath getSceneGraphPath(NodeRetained startNode, + HashKey key, + Transform3D transform) { + synchronized (startNode.universe.sceneGraphLock) { + NodeRetained target = startNode; + + UnorderList path = new UnorderList(5, Node.class); + NodeRetained nodeR = target; + Locale locale = nodeR.locale; + String nodeId; + + if (nodeR.inSharedGroup) { + // getlastNodeId() will destroy this key + if (key != null) { + key = new HashKey(key); + } else { + key = new HashKey(startNode.localToVworldKeys[0]); + } + } + + do { + if (nodeR.source.getCapability(Node.ENABLE_COLLISION_REPORTING)){ + path.add(nodeR.source); + } + + if (nodeR instanceof SharedGroupRetained) { + + // retrieve the last node ID + nodeId = key.getLastNodeId(); + Vector<NodeRetained> parents = ((SharedGroupRetained)nodeR).parents; + NodeRetained prevNodeR = nodeR; + for(int i=parents.size()-1; i >=0; i--) { + NodeRetained linkR = parents.get(i); + if (linkR.nodeId.equals(nodeId)) { + nodeR = linkR; + break; + } + } + if (nodeR == prevNodeR) { + // the branch is already detach when + // Collision got this message + return null; + } + } else if ((nodeR instanceof GroupRetained) && + ((GroupRetained) nodeR).collisionTarget) { + // we need to find the collision target closest to the + // root of tree + target = nodeR; + + if (key == null) { + transform = nodeR.getCurrentLocalToVworld(null); + } else { + transform = nodeR.getCurrentLocalToVworld(key); + } + } + nodeR = nodeR.parent; + } while (nodeR != null); // reach Locale + + Node nodes[]; + if (target == startNode) { // in most case + nodes = (Node []) path.toArray(false); + } else { // alternativeCollisionTarget is set + nodes = (Node []) path.toArray(target); + } + SceneGraphPath sgpath = new SceneGraphPath(locale, + nodes, + (Node) target.source); + sgpath.setTransform(transform); + return sgpath; + } + } + + + @Override + void setTriggered(){ + // if path not set, probably the branch is just detach. + if (collidingPath != null) { + super.setTriggered(); + } + } + + + /** + * Perform task in addBehaviorCondition() that has to be + * set every time the condition met. + */ + @Override + void resetBehaviorCondition(BehaviorStructure bs) { + // The reference geometryAtom will not change once + // Shape3D create so there is no need to set this. + } +} |