001: /*
002: * $RCSfile: WakeupOnCollisionMovement.java,v $
003: *
004: * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.5 $
028: * $Date: 2008/02/28 20:17:33 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import java.util.*;
035:
036: /**
037: * Class specifying a wakeup when the specified object
038: * moves while in collision with any other object in the scene graph.
039: */
040: public final class WakeupOnCollisionMovement extends WakeupCriterion {
041:
042: // different types of WakeupIndexedList that use in GeometryStructure
043: static final int COND_IN_GS_LIST = 0;
044: static final int COLLIDEMOVE_IN_BS_LIST = 1;
045:
046: // total number of different IndexedUnorderedSet types
047: static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;
048:
049: /**
050: * Use geometry in computing collisions.
051: */
052: public static final int USE_GEOMETRY = WakeupOnCollisionEntry.USE_GEOMETRY;
053:
054: /**
055: * Use geometric bounds as an approximation in computing collisions.
056: */
057: public static final int USE_BOUNDS = WakeupOnCollisionEntry.USE_BOUNDS;
058:
059: /**
060: * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
061: */
062: int accuracyMode;
063:
064: // Cached the arming Node being used when it is not BOUND
065: NodeRetained armingNode;
066:
067: // transformed Bounds of Group/Bounds, use by
068: // BOUND, BOUNDINGLEAF, GROUP
069: Bounds vwcBounds;
070:
071: // use by GROUP to cache local bounds
072: Bounds localBounds = null;
073:
074: // source bound when collision occur last time
075: // These three variables are used to keep track of duplicate
076: // wakupOnMovement event
077: Bounds lastSrcBounds = null;
078: Bounds lastDstBounds = null;
079: boolean duplicateEvent = false;
080:
081: // Use by BoundingLeaf, point to mirror BoundingLeaf
082: // transformedRegion under this leaf is used.
083: BoundingLeafRetained boundingLeaf = null;
084:
085: /**
086: * Geometry atoms that this wakeup condition refer to.
087: * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE
088: */
089: UnorderList geometryAtoms = null;
090:
091: // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND
092: int nodeType;
093:
094: SceneGraphPath armingPath = null;
095: Bounds armingBounds = null;
096:
097: // the following two references are set only after a collision
098: // has occurred
099: SceneGraphPath collidingPath = null;
100: Bounds collidingBounds = null;
101:
102: /**
103: * Constructs a new WakeupOnCollisionMovement criterion.
104: * @param armingPath the path used to <em>arm</em> collision
105: * detection
106: * @exception IllegalArgumentException if object associated with the
107: * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
108: */
109: public WakeupOnCollisionMovement(SceneGraphPath armingPath) {
110: this (armingPath, USE_BOUNDS);
111: }
112:
113: /**
114: * Constructs a new WakeupOnCollisionMovement criterion.
115: * @param armingPath the path used to <em>arm</em> collision
116: * detection
117: * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
118: * accurately Java 3D will perform collision detection
119: * @exception IllegalArgumentException if hint is not one of
120: * USE_GEOMETRY or USE_BOUNDS.
121: * @exception IllegalArgumentException if object associated with the
122: * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
123: */
124: public WakeupOnCollisionMovement(SceneGraphPath armingPath,
125: int speedHint) {
126: this (new SceneGraphPath(armingPath), speedHint, null);
127: }
128:
129: /**
130: * Constructs a new WakeupOnCollisionMovement criterion.
131: * @param armingNode the Group, Shape, or Morph node used to
132: * <em>arm</em> collision detection
133: * @exception IllegalArgumentException if object is under a
134: * SharedGroup node or object is other than a Group, Shape3D,
135: * Morph or BoundingLeaf node.
136: */
137: public WakeupOnCollisionMovement(Node armingNode) {
138: this (armingNode, USE_BOUNDS);
139: }
140:
141: /**
142: * Constructs a new WakeupOnCollisionMovement criterion.
143: * @param armingNode the Group, Shape, or Morph node used to
144: * <em>arm</em> collision detection
145: * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
146: * accurately Java 3D will perform collision detection
147: * @exception IllegalArgumentException if hint is not one of
148: * USE_GEOMETRY or USE_BOUNDS.
149: * @exception IllegalArgumentException if object is under a
150: * SharedGroup node or object is other than a Group, Shape3D,
151: * Morph or BoundingLeaf node.
152: */
153: public WakeupOnCollisionMovement(Node armingNode, int speedHint) {
154: this (new SceneGraphPath(null, armingNode), speedHint, null);
155: }
156:
157: /**
158: * Constructs a new WakeupOnCollisionMovement criterion.
159: * @param armingBounds the bounds object used to <em>arm</em> collision
160: * detection
161: */
162: public WakeupOnCollisionMovement(Bounds armingBounds) {
163: this (null, USE_BOUNDS, (Bounds) armingBounds.clone());
164: }
165:
166: /**
167: * Constructs a new WakeupOnCollisionMovement criterion.
168: * @param armingPath the path used to <em>arm</em> collision
169: * detection
170: * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
171: * accurately Java 3D will perform collision detection
172: * @param armingBounds the bounds object used to <em>arm</em> collision
173: * detection
174: * @exception IllegalArgumentException if hint is not one of
175: * USE_GEOMETRY or USE_BOUNDS.
176: * @exception IllegalArgumentException if object associated with the
177: * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
178: */
179: WakeupOnCollisionMovement(SceneGraphPath armingPath, int speedHint,
180: Bounds armingBounds) {
181: if (armingPath != null) {
182: this .armingNode = (NodeRetained) armingPath.getObject().retained;
183: nodeType = WakeupOnCollisionEntry.getNodeType(armingNode,
184: armingPath, "WakeupOnCollisionMovement");
185: this .armingPath = armingPath;
186: WakeupOnCollisionEntry.validateSpeedHint(speedHint,
187: "WakeupOnCollisionMovement4");
188: } else {
189: this .armingBounds = armingBounds;
190: nodeType = WakeupOnCollisionEntry.BOUND;
191: }
192: accuracyMode = speedHint;
193: WakeupIndexedList.init(this , TOTAL_INDEXED_UNORDER_SET_TYPES);
194: }
195:
196: /**
197: * Returns the path used in specifying the collision condition.
198: * @return the SceneGraphPath object generated when arming this
199: * criterion---null implies that a bounds object armed this criteria
200: */
201: public SceneGraphPath getArmingPath() {
202: return (armingPath != null ? new SceneGraphPath(armingPath)
203: : null);
204: }
205:
206: /**
207: * Returns the bounds object used in specifying the collision condition.
208: * @return the Bounds object generated when arming this
209: * criterion---null implies that a SceneGraphPath armed this criteria
210: */
211: public Bounds getArmingBounds() {
212: return (armingBounds != null ? (Bounds) armingBounds.clone()
213: : null);
214: }
215:
216: /**
217: * Retrieves the path describing the object causing the collision.
218: * @return the SceneGraphPath that describes the triggering object.
219: * @exception IllegalStateException if not called from within the
220: * a behavior's processStimulus method which was awoken by a collision.
221: */
222: public SceneGraphPath getTriggeringPath() {
223: if (behav == null) {
224: throw new IllegalStateException(J3dI18N
225: .getString("WakeupOnCollisionMovement5"));
226: }
227:
228: synchronized (behav) {
229: if (!behav.inCallback) {
230: throw new IllegalStateException(J3dI18N
231: .getString("WakeupOnCollisionMovement5"));
232: }
233: }
234: return (collidingPath != null ? new SceneGraphPath(
235: collidingPath) : null);
236: }
237:
238: /**
239: * Retrieves the Bounds object that caused the collision
240: * @return the colliding Bounds object.
241: * @exception IllegalStateException if not called from within the
242: * a behavior's processStimulus method which was awoken by a collision.
243: */
244: public Bounds getTriggeringBounds() {
245: if (behav == null) {
246: throw new IllegalStateException(J3dI18N
247: .getString("WakeupOnCollisionMovement6"));
248: }
249:
250: synchronized (behav) {
251: if (!behav.inCallback) {
252: throw new IllegalStateException(J3dI18N
253: .getString("WakeupOnCollisionMovement6"));
254: }
255: }
256: return (collidingBounds != null ? (Bounds) (collidingBounds
257: .clone()) : null);
258: }
259:
260: /**
261: * This is a callback from BehaviorStructure. It is
262: * used to add wakeupCondition to behavior structure.
263: */
264: void addBehaviorCondition(BehaviorStructure bs) {
265:
266: switch (nodeType) {
267: case WakeupOnCollisionEntry.SHAPE: // Use geometryAtoms[].collisionBounds
268: case WakeupOnCollisionEntry.ORIENTEDSHAPE3D:
269: if (!armingNode.source.isLive()) {
270: return;
271: }
272: if (geometryAtoms == null) {
273: geometryAtoms = new UnorderList(1, GeometryAtom.class);
274: }
275: Shape3DRetained shape = (Shape3DRetained) armingNode;
276: geometryAtoms.add(Shape3DRetained.getGeomAtom(shape
277: .getMirrorShape(armingPath)));
278: break;
279: case WakeupOnCollisionEntry.MORPH: // Use geometryAtoms[].collisionBounds
280: if (!armingNode.source.isLive()) {
281: return;
282: }
283: if (geometryAtoms == null) {
284: geometryAtoms = new UnorderList(1, GeometryAtom.class);
285: }
286: MorphRetained morph = (MorphRetained) armingNode;
287: geometryAtoms.add(Shape3DRetained.getGeomAtom(morph
288: .getMirrorShape(armingPath)));
289: break;
290: case WakeupOnCollisionEntry.BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
291: if (!armingNode.source.isLive()) {
292: return;
293: }
294: this .boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
295: break;
296: case WakeupOnCollisionEntry.BOUND: // use this.vwcBounds
297: vwcBounds = (Bounds) armingBounds.clone();
298: this .armingNode = behav;
299: break;
300: case WakeupOnCollisionEntry.GROUP:
301: if (!armingNode.source.isLive()) {
302: return;
303: }
304: if (accuracyMode == USE_GEOMETRY) {
305: if (geometryAtoms == null) {
306: geometryAtoms = new UnorderList(1,
307: GeometryAtom.class);
308: }
309: ((GroupRetained) armingNode)
310: .searchGeometryAtoms(geometryAtoms);
311: }
312: // else use this.vwcBounds
313: default:
314: }
315:
316: behav.universe.geometryStructure.addWakeupOnCollision(this );
317: }
318:
319: /**
320: * This is a callback from BehaviorStructure. It is
321: * used to remove wakeupCondition from behavior structure.
322: */
323: void removeBehaviorCondition(BehaviorStructure bs) {
324: vwcBounds = null;
325: if (geometryAtoms != null) {
326: geometryAtoms.clear();
327: }
328: boundingLeaf = null;
329: behav.universe.geometryStructure.removeWakeupOnCollision(this );
330: }
331:
332: // Set collidingPath & collidingBounds
333: void setTarget(BHLeafInterface leaf) {
334: SceneGraphPath path;
335: Bounds bound;
336:
337: if (leaf instanceof GeometryAtom) {
338: // Find the triggered Path & Bounds for this geometry Atom
339: GeometryAtom geomAtom = (GeometryAtom) leaf;
340: Shape3DRetained shape = geomAtom.source;
341: path = WakeupOnCollisionEntry.getSceneGraphPath(
342: shape.sourceNode, shape.key, shape
343: .getCurrentLocalToVworld(0));
344: bound = WakeupOnCollisionEntry.getTriggeringBounds(shape);
345:
346: } else {
347: // Find the triggered Path & Bounds for this alternative
348: // collision target
349: GroupRetained group = (GroupRetained) leaf;
350: path = WakeupOnCollisionEntry.getSceneGraphPath(group);
351: bound = WakeupOnCollisionEntry.getTriggeringBounds(group);
352: }
353:
354: if (path != null) {
355: // colliding path may be null when branch detach before
356: // user behavior retrieve the previous colliding path
357: collidingPath = path;
358: collidingBounds = bound;
359: }
360: }
361:
362: // Invoke from GeometryStructure to update vwcBounds of GROUP
363: void updateCollisionBounds(boolean reEvaluateGAs) {
364: if (nodeType == WakeupOnCollisionEntry.GROUP) {
365: GroupRetained group = (GroupRetained) armingNode;
366: if (group.collisionBound != null) {
367: vwcBounds = (Bounds) group.collisionBound.clone();
368: } else {
369: // this may involve recursive tree traverse if
370: // BoundsAutoCompute is true, we can't avoid
371: // since the bound under it may change by transform
372: vwcBounds = group.getEffectiveBounds();
373: }
374: group.transformBounds(armingPath, vwcBounds);
375: } else if (nodeType == WakeupOnCollisionEntry.BOUND) {
376: vwcBounds.transform(armingBounds, behav
377: .getCurrentLocalToVworld());
378: }
379:
380: if (reEvaluateGAs && (nodeType == WakeupOnCollisionEntry.GROUP)
381: && (accuracyMode == USE_GEOMETRY)) {
382: geometryAtoms.clear();
383: ((GroupRetained) armingNode)
384: .searchGeometryAtoms(geometryAtoms);
385: }
386: }
387:
388: void setTriggered() {
389: // if path not set, probably the branch is just detach.
390: if (collidingPath != null) {
391: super .setTriggered();
392: }
393: }
394:
395: /**
396: * Perform task in addBehaviorCondition() that has to be
397: * set every time the condition met.
398: */
399: void resetBehaviorCondition(BehaviorStructure bs) {
400: // The reference geometryAtom will not change once
401: // Shape3D create so there is no need to set this.
402: }
403:
404: }
|