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 */
32package com.jme3.math;
33
34import com.jme3.bounding.BoundingVolume;
35import com.jme3.collision.Collidable;
36import com.jme3.collision.CollisionResult;
37import com.jme3.collision.CollisionResults;
38import com.jme3.collision.UnsupportedCollisionException;
39import com.jme3.export.*;
40import com.jme3.util.TempVars;
41import 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 */
51public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
489    public void set(Ray source) {
490        origin.set(source.getOrigin());
491        direction.set(source.getDirection());
492    }
493
494    public String toString() {
495        return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]";
496    }
497
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
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
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