Home | History | Annotate | Download | only in math
      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      */
     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