001: /*
002: * $Header: /cvs/j3dfly/J3dFly/src/org/jdesktop/j3dfly/utils/vpbehaviors/VPDefaultCollision.java,v 1.1 2005/04/20 21:05:15 paulby Exp $
003: *
004: * Sun Public License Notice
005: *
006: * The contents of this file are subject to the Sun Public License Version
007: * 1.0 (the "License"). You may not use this file except in compliance with
008: * the License. A copy of the License is available at http://www.sun.com/
009: *
010: * The Original Code is Java 3D(tm) Fly Through.
011: * The Initial Developer of the Original Code is Paul Byrne.
012: * Portions created by Paul Byrne are Copyright (C) 2002.
013: * All Rights Reserved.
014: *
015: * Contributor(s): Paul Byrne.
016: *
017: **/
018: package org.jdesktop.j3dfly.utils.vpbehaviors;
019:
020: import java.util.ArrayList;
021:
022: import javax.media.j3d.BranchGroup;
023: import javax.media.j3d.Transform3D;
024: import javax.media.j3d.Locale;
025: import javax.media.j3d.Node;
026: import javax.media.j3d.Shape3D;
027:
028: import javax.vecmath.Point3d;
029: import javax.vecmath.Vector3f;
030:
031: import com.sun.j3d.utils.picking.PickTool;
032: import com.sun.j3d.utils.picking.PickResult;
033: import org.jdesktop.j3d.utils.scenegraph.traverser.TreeScan;
034: import org.jdesktop.j3d.utils.scenegraph.traverser.NodeChangeProcessor;
035:
036: /**
037: * A simple collision implementation that uses Picking to determine if
038: * a collision will occurr.
039: *
040: * The rays of a SweptVolume are cast between the current viewPlatform and
041: * next viewPlatform transform. Only the rays for the non-zero velocity
042: * directions are cast. For example if the VP is moving forward (-ve Z) then
043: * frontRays are checked.
044: *
045: * This opimization means that this collision implementation is not appropriate for
046: * checking for collision between the viewPlatform and fast moving animated geometry.
047: * This feature will be added in a future version of the API.
048: *
049: * @author Paul Byrne
050: * @version 1.11, 01/18/02
051: */
052: public class VPDefaultCollision implements VPCollisionInterface {
053:
054: protected ArrayList branchGroups;
055: protected PickTool pickTool = null;
056:
057: private Point3d segmentEnd = new Point3d();
058: private Point3d segmentStart = new Point3d();
059:
060: protected SweptVolume sweptVolume;
061:
062: /**
063: * Return minimal amount of information of collision
064: * which is the Shape3D and the RayIndex and Direction
065: */
066: public static final int ALLOW_COLLISION_SHAPE = 0x00;
067:
068: /**
069: * Return the point3d for each collision
070: */
071: public static final int ALLOW_COLLISION_POINT = 0x01;
072:
073: /**
074: * Return the geometry for each collision
075: */
076: public static final int ALLOW_COLLISION_GEOMETRY = 0x02;
077:
078: private int mode;
079:
080: public VPDefaultCollision(int mode) {
081: this .mode = mode;
082: branchGroups = new ArrayList();
083: }
084:
085: /**
086: * Creates a default VPDefaultCollision with a mode of
087: * ALLOW_COLLISION
088: */
089: public VPDefaultCollision() {
090: this (ALLOW_COLLISION_SHAPE);
091: }
092:
093: /**
094: * Add a BranchGroup to check for collision
095: *
096: * Current implementation only support a single BranchGroup
097: * @see setCollisionLocale
098: *
099: * @param bg BranchGroup to add to set of BranchGroups checked for
100: * collision
101: */
102: public void addCollisionBG(BranchGroup bg) {
103: if (pickTool == null) {
104: pickTool = new PickTool(bg);
105: } else {
106: throw new RuntimeException(
107: "Implementation only supports one CollisionBG");
108: }
109:
110: branchGroups.add(bg);
111:
112: if (mode > ALLOW_COLLISION_SHAPE)
113: pickTool.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
114: else
115: pickTool.setMode(PickTool.GEOMETRY);
116: }
117:
118: /**
119: * Check for collision with all geometry in <code>locale</code>
120: */
121: public void setCollisionLocale(Locale locale) {
122: pickTool = new PickTool(locale);
123: if (mode > ALLOW_COLLISION_SHAPE)
124: pickTool.setMode(PickTool.GEOMETRY_INTERSECT_INFO);
125: else
126: pickTool.setMode(PickTool.GEOMETRY);
127: }
128:
129: /**
130: * Get the number of collision BranchGroups that are checked for
131: * collision
132: */
133: public int getCollisionBGCount() {
134: return branchGroups.size();
135: }
136:
137: /**
138: * Get the BranchGroup at the specified index that is used for
139: * collision detection
140: */
141: public BranchGroup getCollisionBG(int index) {
142: return (BranchGroup) branchGroups.get(index);
143: }
144:
145: /**
146: * Check for collision when casting the swept bounds from currentLocation to
147: * nextLocation. The orientation of the swept bounds is set to the orientation at
148: * nextLocation.
149: *
150: * This implementation only processes the first ray in each direction. It
151: * also requires that a ray exists in each direction.
152: *
153: * @param currentLocation The current location of the view
154: * @param nextLocation Will contain the next non collision location of the view on return
155: * @param velocity The current velocity vector for the view
156: * @param roll The change in roll for this frame
157: * @param pitch The change in pitch for this frame
158: * @param yaw The change in yaw for this frame
159: */
160: public SweptVolumeCollision getCollisions(
161: Transform3D currentLocation, Transform3D nextLocation,
162: Vector3f velocity, float roll, float pitch, float yaw) {
163:
164: boolean collision = false;
165: SweptVolumeCollision ret = new SweptVolumeCollision();
166:
167: Transform3D yawT = new Transform3D();
168: Transform3D pitchT = new Transform3D();
169: Transform3D rollT = new Transform3D();
170: Transform3D velocityT = new Transform3D();
171: Transform3D destination = new Transform3D();
172:
173: yawT.rotY(yaw);
174: pitchT.rotX(pitch);
175: rollT.rotZ(roll);
176:
177: velocityT.set(velocity);
178: velocityT.mul(yawT);
179: velocityT.mul(pitchT);
180: velocityT.mul(rollT);
181:
182: //System.out.println(velocity+" "+acceleration);
183:
184: destination.set(currentLocation);
185:
186: destination.mul(yawT);
187: destination.mul(pitchT);
188: destination.mul(rollT);
189: destination.mul(velocityT);
190:
191: collision |= checkZDirection(currentLocation, destination,
192: velocity, ret);
193: collision |= checkXDirection(currentLocation, destination,
194: velocity, ret);
195: collision |= checkYDirection(currentLocation, destination,
196: velocity, ret);
197:
198: // If the volume is in collision return the original transform, ie
199: // don't move.
200: // An alternative approach would be to calculate the 'best' move from
201: // the current location, ie move as far as possible without causing
202: // collision.
203: if (collision) {
204: nextLocation.set(currentLocation);
205: return null;
206: } else {
207: nextLocation.set(destination);
208: return ret;
209: }
210: }
211:
212: protected boolean checkZDirection(Transform3D currentLocation,
213: Transform3D destination, Vector3f velocity,
214: SweptVolumeCollision ret) {
215: boolean collision = false;
216: if (velocity.z < 0f) { // Check on Z axis
217: PickResult result = checkDirection(currentLocation,
218: destination, sweptVolume.frontRays[0],
219: sweptVolume.frontRays[1], velocity.z * -1f);
220: if (result != null) {
221: collision |= true;
222: RayCollision ray = new RayCollision();
223: ray.rayDirection = RayCollision.FRONT;
224: ray.rayIndex = 0;
225: ray.collisionShape = (Shape3D) result.getObject();
226: if (mode > ALLOW_COLLISION_SHAPE) {
227: ray.collisionGeometryArray = result
228: .getIntersection(0).getGeometryArray();
229: ray.collisionPoint = result.getIntersection(0)
230: .getPointCoordinatesVW();
231: }
232: ret.addRayCollision(ray);
233: }
234: } else if (velocity.z > 0f) {
235: PickResult result = checkDirection(currentLocation,
236: destination, sweptVolume.backRays[0],
237: sweptVolume.backRays[1], velocity.z);
238: if (result != null) {
239: collision |= true;
240: RayCollision ray = new RayCollision();
241: ray.rayDirection = RayCollision.BACK;
242: ray.rayIndex = 0;
243: ray.collisionShape = (Shape3D) result.getObject();
244: if (mode > ALLOW_COLLISION_SHAPE) {
245: ray.collisionGeometryArray = result
246: .getIntersection(0).getGeometryArray();
247: ray.collisionPoint = result.getIntersection(0)
248: .getPointCoordinatesVW();
249: ray.collisionNormal = result.getIntersection(0)
250: .getPointNormal();
251: }
252: ret.addRayCollision(ray);
253: }
254: }
255:
256: return collision;
257: }
258:
259: protected boolean checkXDirection(Transform3D currentLocation,
260: Transform3D destination, Vector3f velocity,
261: SweptVolumeCollision ret) {
262: boolean collision = false;
263: if (velocity.x > 0f) { // Check on X axis
264: PickResult result = checkDirection(currentLocation,
265: destination, sweptVolume.rightRays[0],
266: sweptVolume.rightRays[1], velocity.x);
267: if (result != null) {
268: collision |= true;
269: RayCollision ray = new RayCollision();
270: ray.rayDirection = RayCollision.RIGHT;
271: ray.rayIndex = 0;
272: ray.collisionShape = (Shape3D) result.getObject();
273: if (mode > ALLOW_COLLISION_SHAPE) {
274: ray.collisionGeometryArray = result
275: .getIntersection(0).getGeometryArray();
276: ray.collisionPoint = result.getIntersection(0)
277: .getPointCoordinatesVW();
278: }
279: ret.addRayCollision(ray);
280: }
281: } else if (velocity.x < 0f) {
282: PickResult result = checkDirection(currentLocation,
283: destination, sweptVolume.leftRays[0],
284: sweptVolume.leftRays[1], velocity.x * -1f);
285: if (result != null) {
286: collision |= true;
287: RayCollision ray = new RayCollision();
288: ray.rayDirection = RayCollision.LEFT;
289: ray.rayIndex = 0;
290: ray.collisionShape = (Shape3D) result.getObject();
291: if (mode > ALLOW_COLLISION_SHAPE) {
292: ray.collisionGeometryArray = result
293: .getIntersection(0).getGeometryArray();
294: ray.collisionPoint = result.getIntersection(0)
295: .getPointCoordinatesVW();
296: ray.collisionNormal = result.getIntersection(0)
297: .getPointNormal();
298: }
299: ret.addRayCollision(ray);
300: }
301: }
302:
303: return collision;
304: }
305:
306: protected boolean checkYDirection(Transform3D currentLocation,
307: Transform3D destination, Vector3f velocity,
308: SweptVolumeCollision ret) {
309: boolean collision = false;
310: if (velocity.y > 0f) { // Check on Y axis
311: PickResult result = checkDirection(currentLocation,
312: destination, sweptVolume.upRays[0],
313: sweptVolume.upRays[1], velocity.y);
314: if (result != null) {
315: collision |= true;
316: RayCollision ray = new RayCollision();
317: ray.rayDirection = RayCollision.UP;
318: ray.rayIndex = 0;
319: ray.collisionShape = (Shape3D) result.getObject();
320: if (mode > ALLOW_COLLISION_SHAPE) {
321: ray.collisionGeometryArray = result
322: .getIntersection(0).getGeometryArray();
323: ray.collisionPoint = result.getIntersection(0)
324: .getPointCoordinatesVW();
325: }
326: ret.addRayCollision(ray);
327: }
328: } else if (velocity.y < 0f) {
329: PickResult result = checkDirection(currentLocation,
330: destination, sweptVolume.downRays[0],
331: sweptVolume.downRays[1], velocity.y * -1f);
332: if (result != null) {
333: collision |= true;
334: RayCollision ray = new RayCollision();
335: ray.rayDirection = RayCollision.DOWN;
336: ray.rayIndex = 0;
337: ray.collisionShape = (Shape3D) result.getObject();
338: if (mode > ALLOW_COLLISION_SHAPE) {
339: ray.collisionGeometryArray = result
340: .getIntersection(0).getGeometryArray();
341: ray.collisionPoint = result.getIntersection(0)
342: .getPointCoordinatesVW();
343: ray.collisionNormal = result.getIntersection(0)
344: .getPointNormal();
345: }
346: ret.addRayCollision(ray);
347: }
348: }
349:
350: return collision;
351: }
352:
353: protected PickResult checkDirection(Transform3D currentLocation,
354: Transform3D nextLocation, Vector3f rayStart,
355: Vector3f rayEnd, float stepSize) {
356:
357: if (stepSize < 1f)
358: stepSize = 1f;
359:
360: segmentEnd.x = rayEnd.x * stepSize;
361: segmentEnd.y = rayEnd.y * stepSize;
362: segmentEnd.z = rayEnd.z * stepSize;
363: segmentStart.x = rayStart.x;
364: segmentStart.y = rayStart.y;
365: segmentStart.z = rayStart.z;
366:
367: nextLocation.transform(segmentEnd);
368: currentLocation.transform(segmentStart);
369:
370: //javax.vecmath.Vector3d loc = new javax.vecmath.Vector3d();
371: //currentLocation.get( loc );
372: //System.out.println("Location "+loc);
373: //System.out.println("Seg "+segmentStart+" "+segmentEnd);
374:
375: pickTool.setShapeSegment(segmentStart, segmentEnd);
376: return pickTool.pickClosest();
377: }
378:
379: /**
380: * Set the capabilities in the BranchGroup to allow this collision
381: * detection to work
382: */
383: public void setCapabilities(BranchGroup branchGroup) {
384: final int m = this .mode;
385: NodeChangeProcessor proc = new NodeChangeProcessor() {
386: public boolean changeNode(Node node) {
387: if (m > ALLOW_COLLISION_SHAPE)
388: pickTool.setCapabilities(node,
389: PickTool.INTERSECT_FULL);
390: else
391: pickTool.setCapabilities(node,
392: PickTool.INTERSECT_TEST);
393: return true;
394: }
395: };
396:
397: TreeScan.findNode(branchGroup, new Class[] {
398: javax.media.j3d.Shape3D.class,
399: javax.media.j3d.Morph.class }, proc, false, true);
400: }
401:
402: /** Set the SweptsweptVolume to be used in collision calculations
403: */
404: public void setVPSweptVolume(SweptVolume volume) {
405: sweptVolume = volume;
406: }
407:
408: /** Get the SweptVolume used in collision calculations
409: */
410: public SweptVolume getVPSweptVolume() {
411: return sweptVolume;
412: }
413:
414: }
|