• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.math;
33 
34 import com.jme3.bounding.BoundingVolume;
35 import com.jme3.collision.Collidable;
36 import com.jme3.collision.CollisionResult;
37 import com.jme3.collision.CollisionResults;
38 import com.jme3.collision.UnsupportedCollisionException;
39 import com.jme3.export.*;
40 import com.jme3.util.TempVars;
41 import java.io.IOException;
42 
43 /**
44  * <code>Ray</code> defines a line segment which has an origin and a direction.
45  * That is, a point and an infinite ray is cast from this point. The ray is
46  * defined by the following equation: R(t) = origin + t*direction for t >= 0.
47  *
48  * @author Mark Powell
49  * @author Joshua Slack
50  */
51 public final class Ray implements Savable, Cloneable, Collidable, java.io.Serializable {
52 
53     static final long serialVersionUID = 1;
54 
55     /**
56      * The ray's begining point.
57      */
58     public Vector3f origin = new Vector3f();
59 
60     /**
61      * The direction of the ray.
62      */
63     public Vector3f direction = new Vector3f(0, 0, 1);
64 
65 
66     public float limit = Float.POSITIVE_INFINITY;
67 
68     /**
69      * Constructor instantiates a new <code>Ray</code> object. As default, the
70      * origin is (0,0,0) and the direction is (0,0,1).
71      *
72      */
Ray()73     public Ray() {
74     }
75 
76     /**
77      * Constructor instantiates a new <code>Ray</code> object. The origin and
78      * direction are given.
79      * @param origin the origin of the ray.
80      * @param direction the direction the ray travels in.
81      */
Ray(Vector3f origin, Vector3f direction)82     public Ray(Vector3f origin, Vector3f direction) {
83         setOrigin(origin);
84         setDirection(direction);
85     }
86 
87     /**
88      * <code>intersect</code> determines if the Ray intersects a triangle.
89      * @param t the Triangle to test against.
90      * @return true if the ray collides.
91      */
92 //    public boolean intersect(Triangle t) {
93 //        return intersect(t.get(0), t.get(1), t.get(2));
94 //    }
95     /**
96      * <code>intersect</code> determines if the Ray intersects a triangle
97      * defined by the specified points.
98      *
99      * @param v0
100      *            first point of the triangle.
101      * @param v1
102      *            second point of the triangle.
103      * @param v2
104      *            third point of the triangle.
105      * @return true if the ray collides.
106      */
107 //    public boolean intersect(Vector3f v0,Vector3f v1,Vector3f v2){
108 //        return intersectWhere(v0, v1, v2, null);
109 //    }
110     /**
111      * <code>intersectWhere</code> determines if the Ray intersects a triangle. It then
112      * stores the point of intersection in the given loc vector
113      * @param t the Triangle to test against.
114      * @param loc
115      *            storage vector to save the collision point in (if the ray
116      *            collides)
117      * @return true if the ray collides.
118      */
intersectWhere(Triangle t, Vector3f loc)119     public boolean intersectWhere(Triangle t, Vector3f loc) {
120         return intersectWhere(t.get(0), t.get(1), t.get(2), loc);
121     }
122 
123     /**
124      * <code>intersectWhere</code> determines if the Ray intersects a triangle
125      * defined by the specified points and if so it stores the point of
126      * intersection in the given loc vector.
127      *
128      * @param v0
129      *            first point of the triangle.
130      * @param v1
131      *            second point of the triangle.
132      * @param v2
133      *            third point of the triangle.
134      * @param loc
135      *            storage vector to save the collision point in (if the ray
136      *            collides)  if null, only boolean is calculated.
137      * @return true if the ray collides.
138      */
intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2, Vector3f loc)139     public boolean intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2,
140             Vector3f loc) {
141         return intersects(v0, v1, v2, loc, false, false);
142     }
143 
144     /**
145      * <code>intersectWherePlanar</code> determines if the Ray intersects a
146      * triangle and if so it stores the point of
147      * intersection in the given loc vector as t, u, v where t is the distance
148      * from the origin to the point of intersection and u,v is the intersection
149      * point in terms of the triangle plane.
150      *
151      * @param t the Triangle to test against.
152      * @param loc
153      *            storage vector to save the collision point in (if the ray
154      *            collides) as t, u, v
155      * @return true if the ray collides.
156      */
intersectWherePlanar(Triangle t, Vector3f loc)157     public boolean intersectWherePlanar(Triangle t, Vector3f loc) {
158         return intersectWherePlanar(t.get(0), t.get(1), t.get(2), loc);
159     }
160 
161     /**
162      * <code>intersectWherePlanar</code> determines if the Ray intersects a
163      * triangle defined by the specified points and if so it stores the point of
164      * intersection in the given loc vector as t, u, v where t is the distance
165      * from the origin to the point of intersection and u,v is the intersection
166      * point in terms of the triangle plane.
167      *
168      * @param v0
169      *            first point of the triangle.
170      * @param v1
171      *            second point of the triangle.
172      * @param v2
173      *            third point of the triangle.
174      * @param loc
175      *            storage vector to save the collision point in (if the ray
176      *            collides) as t, u, v
177      * @return true if the ray collides.
178      */
intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2, Vector3f loc)179     public boolean intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2,
180             Vector3f loc) {
181         return intersects(v0, v1, v2, loc, true, false);
182     }
183 
184     /**
185      * <code>intersects</code> does the actual intersection work.
186      *
187      * @param v0
188      *            first point of the triangle.
189      * @param v1
190      *            second point of the triangle.
191      * @param v2
192      *            third point of the triangle.
193      * @param store
194      *            storage vector - if null, no intersection is calc'd
195      * @param doPlanar
196      *            true if we are calcing planar results.
197      * @param quad
198      * @return true if ray intersects triangle
199      */
intersects(Vector3f v0, Vector3f v1, Vector3f v2, Vector3f store, boolean doPlanar, boolean quad)200     private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2,
201             Vector3f store, boolean doPlanar, boolean quad) {
202         TempVars vars = TempVars.get();
203 
204         Vector3f tempVa = vars.vect1,
205                 tempVb = vars.vect2,
206                 tempVc = vars.vect3,
207                 tempVd = vars.vect4;
208 
209         Vector3f diff = origin.subtract(v0, tempVa);
210         Vector3f edge1 = v1.subtract(v0, tempVb);
211         Vector3f edge2 = v2.subtract(v0, tempVc);
212         Vector3f norm = edge1.cross(edge2, tempVd);
213 
214         float dirDotNorm = direction.dot(norm);
215         float sign;
216         if (dirDotNorm > FastMath.FLT_EPSILON) {
217             sign = 1;
218         } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
219             sign = -1f;
220             dirDotNorm = -dirDotNorm;
221         } else {
222             // ray and triangle/quad are parallel
223             vars.release();
224             return false;
225         }
226 
227         float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2));
228         if (dirDotDiffxEdge2 >= 0.0f) {
229             float dirDotEdge1xDiff = sign
230                     * direction.dot(edge1.crossLocal(diff));
231 
232             if (dirDotEdge1xDiff >= 0.0f) {
233                 if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) {
234                     float diffDotNorm = -sign * diff.dot(norm);
235                     if (diffDotNorm >= 0.0f) {
236                         // this method always returns
237                         vars.release();
238 
239                         // ray intersects triangle
240                         // if storage vector is null, just return true,
241                         if (store == null) {
242                             return true;
243                         }
244 
245                         // else fill in.
246                         float inv = 1f / dirDotNorm;
247                         float t = diffDotNorm * inv;
248                         if (!doPlanar) {
249                             store.set(origin).addLocal(direction.x * t,
250                                     direction.y * t, direction.z * t);
251                         } else {
252                             // these weights can be used to determine
253                             // interpolated values, such as texture coord.
254                             // eg. texcoord s,t at intersection point:
255                             // s = w0*s0 + w1*s1 + w2*s2;
256                             // t = w0*t0 + w1*t1 + w2*t2;
257                             float w1 = dirDotDiffxEdge2 * inv;
258                             float w2 = dirDotEdge1xDiff * inv;
259                             //float w0 = 1.0f - w1 - w2;
260                             store.set(t, w1, w2);
261                         }
262                         return true;
263                     }
264                 }
265             }
266         }
267         vars.release();
268         return false;
269     }
270 
intersects(Vector3f v0, Vector3f v1, Vector3f v2)271     public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) {
272         float edge1X = v1.x - v0.x;
273         float edge1Y = v1.y - v0.y;
274         float edge1Z = v1.z - v0.z;
275 
276         float edge2X = v2.x - v0.x;
277         float edge2Y = v2.y - v0.y;
278         float edge2Z = v2.z - v0.z;
279 
280         float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y));
281         float normY = ((edge1Z * edge2X) - (edge1X * edge2Z));
282         float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X));
283 
284         float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ;
285 
286         float diffX = origin.x - v0.x;
287         float diffY = origin.y - v0.y;
288         float diffZ = origin.z - v0.z;
289 
290         float sign;
291         if (dirDotNorm > FastMath.FLT_EPSILON) {
292             sign = 1;
293         } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
294             sign = -1f;
295             dirDotNorm = -dirDotNorm;
296         } else {
297             // ray and triangle/quad are parallel
298             return Float.POSITIVE_INFINITY;
299         }
300 
301         float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y));
302         float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z));
303         float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X));
304 
305         float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X
306                 + direction.y * diffEdge2Y
307                 + direction.z * diffEdge2Z);
308 
309         if (dirDotDiffxEdge2 >= 0.0f) {
310             diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY));
311             diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ));
312             diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX));
313 
314             float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X
315                     + direction.y * diffEdge2Y
316                     + direction.z * diffEdge2Z);
317 
318             if (dirDotEdge1xDiff >= 0.0f) {
319                 if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
320                     float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ);
321                     if (diffDotNorm >= 0.0f) {
322                         // ray intersects triangle
323                         // fill in.
324                         float inv = 1f / dirDotNorm;
325                         float t = diffDotNorm * inv;
326                         return t;
327                     }
328                 }
329             }
330         }
331 
332         return Float.POSITIVE_INFINITY;
333     }
334 
335     /**
336      * <code>intersectWherePlanar</code> determines if the Ray intersects a
337      * quad defined by the specified points and if so it stores the point of
338      * intersection in the given loc vector as t, u, v where t is the distance
339      * from the origin to the point of intersection and u,v is the intersection
340      * point in terms of the quad plane.
341      * One edge of the quad is [v0,v1], another one [v0,v2]. The behaviour thus is like
342      * {@link #intersectWherePlanar(Vector3f, Vector3f, Vector3f, Vector3f)} except for
343      * the extended area, which is equivalent to the union of the triangles [v0,v1,v2]
344      * and [-v0+v1+v2,v1,v2].
345      *
346      * @param v0
347      *            top left point of the quad.
348      * @param v1
349      *            top right point of the quad.
350      * @param v2
351      *            bottom left point of the quad.
352      * @param loc
353      *            storage vector to save the collision point in (if the ray
354      *            collides) as t, u, v
355      * @return true if the ray collides with the quad.
356      */
intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2, Vector3f loc)357     public boolean intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2,
358             Vector3f loc) {
359         return intersects(v0, v1, v2, loc, true, true);
360     }
361 
362     /**
363      *
364      * @param p
365      * @param loc
366      * @return true if the ray collides with the given Plane
367      */
intersectsWherePlane(Plane p, Vector3f loc)368     public boolean intersectsWherePlane(Plane p, Vector3f loc) {
369         float denominator = p.getNormal().dot(direction);
370 
371         if (denominator > -FastMath.FLT_EPSILON && denominator < FastMath.FLT_EPSILON) {
372             return false; // coplanar
373         }
374         float numerator = -(p.getNormal().dot(origin) - p.getConstant());
375         float ratio = numerator / denominator;
376 
377         if (ratio < FastMath.FLT_EPSILON) {
378             return false; // intersects behind origin
379         }
380         loc.set(direction).multLocal(ratio).addLocal(origin);
381 
382         return true;
383     }
384 
collideWith(Collidable other, CollisionResults results)385     public int collideWith(Collidable other, CollisionResults results) {
386         if (other instanceof BoundingVolume) {
387             BoundingVolume bv = (BoundingVolume) other;
388             return bv.collideWith(this, results);
389         } else if (other instanceof AbstractTriangle) {
390             AbstractTriangle tri = (AbstractTriangle) other;
391             float d = intersects(tri.get1(), tri.get2(), tri.get3());
392             if (Float.isInfinite(d) || Float.isNaN(d)) {
393                 return 0;
394             }
395 
396             Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
397             results.addCollision(new CollisionResult(point, d));
398             return 1;
399         } else {
400             throw new UnsupportedCollisionException();
401         }
402     }
403 
distanceSquared(Vector3f point)404     public float distanceSquared(Vector3f point) {
405         TempVars vars = TempVars.get();
406 
407         Vector3f tempVa = vars.vect1,
408                 tempVb = vars.vect2;
409 
410         point.subtract(origin, tempVa);
411         float rayParam = direction.dot(tempVa);
412         if (rayParam > 0) {
413             origin.add(direction.mult(rayParam, tempVb), tempVb);
414         } else {
415             tempVb.set(origin);
416             rayParam = 0.0f;
417         }
418 
419         tempVb.subtract(point, tempVa);
420         float len = tempVa.lengthSquared();
421         vars.release();
422         return len;
423     }
424 
425     /**
426      *
427      * <code>getOrigin</code> retrieves the origin point of the ray.
428      *
429      * @return the origin of the ray.
430      */
getOrigin()431     public Vector3f getOrigin() {
432         return origin;
433     }
434 
435     /**
436      *
437      * <code>setOrigin</code> sets the origin of the ray.
438      * @param origin the origin of the ray.
439      */
setOrigin(Vector3f origin)440     public void setOrigin(Vector3f origin) {
441         this.origin.set(origin);
442     }
443 
444     /**
445      * <code>getLimit</code> returns the limit of the ray, aka the length.
446      * If the limit is not infinity, then this ray is a line with length <code>
447      * limit</code>.
448      *
449      * @return the limit of the ray, aka the length.
450      */
getLimit()451     public float getLimit() {
452         return limit;
453     }
454 
455     /**
456      * <code>setLimit</code> sets the limit of the ray.
457      * @param limit the limit of the ray.
458      * @see Ray#getLimit()
459      */
setLimit(float limit)460     public void setLimit(float limit) {
461         this.limit = limit;
462     }
463 
464     /**
465      *
466      * <code>getDirection</code> retrieves the direction vector of the ray.
467      * @return the direction of the ray.
468      */
getDirection()469     public Vector3f getDirection() {
470         return direction;
471     }
472 
473     /**
474      *
475      * <code>setDirection</code> sets the direction vector of the ray.
476      * @param direction the direction of the ray.
477      */
setDirection(Vector3f direction)478     public void setDirection(Vector3f direction) {
479         assert direction.isUnitVector();
480         this.direction.set(direction);
481     }
482 
483     /**
484      * Copies information from a source ray into this ray.
485      *
486      * @param source
487      *            the ray to copy information from
488      */
set(Ray source)489     public void set(Ray source) {
490         origin.set(source.getOrigin());
491         direction.set(source.getDirection());
492     }
493 
toString()494     public String toString() {
495         return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]";
496     }
497 
write(JmeExporter e)498     public void write(JmeExporter e) throws IOException {
499         OutputCapsule capsule = e.getCapsule(this);
500         capsule.write(origin, "origin", Vector3f.ZERO);
501         capsule.write(direction, "direction", Vector3f.ZERO);
502     }
503 
read(JmeImporter e)504     public void read(JmeImporter e) throws IOException {
505         InputCapsule capsule = e.getCapsule(this);
506         origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
507         direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
508     }
509 
510     @Override
clone()511     public Ray clone() {
512         try {
513             Ray r = (Ray) super.clone();
514             r.direction = direction.clone();
515             r.origin = origin.clone();
516             return r;
517         } catch (CloneNotSupportedException e) {
518             throw new AssertionError();
519         }
520     }
521 }
522