From cebf822f0ea8c5fac29352a4f74f5ae49ac1e81b Mon Sep 17 00:00:00 2001 From: "Sha..om" Date: Sun, 4 Dec 2011 21:48:23 +0000 Subject: [PATCH] * Added check for unit vector in Ray.setDirection() * Changed Ray constructor to set direction to 0, 0, 1 to satisfy above requirement * Ray no longer steals references git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8859 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- engine/src/core/com/jme3/math/Ray.java | 1043 ++++++++++++------------ 1 file changed, 523 insertions(+), 520 deletions(-) diff --git a/engine/src/core/com/jme3/math/Ray.java b/engine/src/core/com/jme3/math/Ray.java index cfac314c0..faefcb0ec 100644 --- a/engine/src/core/com/jme3/math/Ray.java +++ b/engine/src/core/com/jme3/math/Ray.java @@ -1,520 +1,523 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.math; - -import com.jme3.bounding.BoundingVolume; -import com.jme3.collision.Collidable; -import com.jme3.collision.CollisionResult; -import com.jme3.collision.CollisionResults; -import com.jme3.collision.UnsupportedCollisionException; -import com.jme3.export.*; -import com.jme3.util.TempVars; -import java.io.IOException; - -/** - * Ray defines a line segment which has an origin and a direction. - * That is, a point and an infinite ray is cast from this point. The ray is - * defined by the following equation: R(t) = origin + t*direction for t >= 0. - * - * @author Mark Powell - * @author Joshua Slack - */ -public final class Ray implements Savable, Cloneable, Collidable, java.io.Serializable { - - static final long serialVersionUID = 1; - - //todo: merge with Line? - /** The ray's begining point. */ - public Vector3f origin; - /** The direction of the ray. */ - public Vector3f direction; - public float limit = Float.POSITIVE_INFINITY; - -// protected static final Vector3f tempVa=new Vector3f(); -// protected static final Vector3f tempVb=new Vector3f(); -// protected static final Vector3f tempVc=new Vector3f(); -// protected static final Vector3f tempVd=new Vector3f(); - /** - * Constructor instantiates a new Ray object. As default, the - * origin is (0,0,0) and the direction is (0,0,0). - * - */ - public Ray() { - origin = new Vector3f(); - direction = new Vector3f(); - } - - /** - * Constructor instantiates a new Ray object. The origin and - * direction are given. - * @param origin the origin of the ray. - * @param direction the direction the ray travels in. - */ - public Ray(Vector3f origin, Vector3f direction) { - this.origin = origin; - this.direction = direction; - } - - /** - * intersect determines if the Ray intersects a triangle. - * @param t the Triangle to test against. - * @return true if the ray collides. - */ -// public boolean intersect(Triangle t) { -// return intersect(t.get(0), t.get(1), t.get(2)); -// } - /** - * intersect determines if the Ray intersects a triangle - * defined by the specified points. - * - * @param v0 - * first point of the triangle. - * @param v1 - * second point of the triangle. - * @param v2 - * third point of the triangle. - * @return true if the ray collides. - */ -// public boolean intersect(Vector3f v0,Vector3f v1,Vector3f v2){ -// return intersectWhere(v0, v1, v2, null); -// } - /** - * intersectWhere determines if the Ray intersects a triangle. It then - * stores the point of intersection in the given loc vector - * @param t the Triangle to test against. - * @param loc - * storage vector to save the collision point in (if the ray - * collides) - * @return true if the ray collides. - */ - public boolean intersectWhere(Triangle t, Vector3f loc) { - return intersectWhere(t.get(0), t.get(1), t.get(2), loc); - } - - /** - * intersectWhere determines if the Ray intersects a triangle - * defined by the specified points and if so it stores the point of - * intersection in the given loc vector. - * - * @param v0 - * first point of the triangle. - * @param v1 - * second point of the triangle. - * @param v2 - * third point of the triangle. - * @param loc - * storage vector to save the collision point in (if the ray - * collides) if null, only boolean is calculated. - * @return true if the ray collides. - */ - public boolean intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2, - Vector3f loc) { - return intersects(v0, v1, v2, loc, false, false); - } - - /** - * intersectWherePlanar determines if the Ray intersects a - * triangle and if so it stores the point of - * intersection in the given loc vector as t, u, v where t is the distance - * from the origin to the point of intersection and u,v is the intersection - * point in terms of the triangle plane. - * - * @param t the Triangle to test against. - * @param loc - * storage vector to save the collision point in (if the ray - * collides) as t, u, v - * @return true if the ray collides. - */ - public boolean intersectWherePlanar(Triangle t, Vector3f loc) { - return intersectWherePlanar(t.get(0), t.get(1), t.get(2), loc); - } - - /** - * intersectWherePlanar determines if the Ray intersects a - * triangle defined by the specified points and if so it stores the point of - * intersection in the given loc vector as t, u, v where t is the distance - * from the origin to the point of intersection and u,v is the intersection - * point in terms of the triangle plane. - * - * @param v0 - * first point of the triangle. - * @param v1 - * second point of the triangle. - * @param v2 - * third point of the triangle. - * @param loc - * storage vector to save the collision point in (if the ray - * collides) as t, u, v - * @return true if the ray collides. - */ - public boolean intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2, - Vector3f loc) { - return intersects(v0, v1, v2, loc, true, false); - } - - /** - * intersects does the actual intersection work. - * - * @param v0 - * first point of the triangle. - * @param v1 - * second point of the triangle. - * @param v2 - * third point of the triangle. - * @param store - * storage vector - if null, no intersection is calc'd - * @param doPlanar - * true if we are calcing planar results. - * @param quad - * @return true if ray intersects triangle - */ - private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2, - Vector3f store, boolean doPlanar, boolean quad) { - TempVars vars = TempVars.get(); - - Vector3f tempVa = vars.vect1, - tempVb = vars.vect2, - tempVc = vars.vect3, - tempVd = vars.vect4; - - Vector3f diff = origin.subtract(v0, tempVa); - Vector3f edge1 = v1.subtract(v0, tempVb); - Vector3f edge2 = v2.subtract(v0, tempVc); - Vector3f norm = edge1.cross(edge2, tempVd); - - float dirDotNorm = direction.dot(norm); - float sign; - if (dirDotNorm > FastMath.FLT_EPSILON) { - sign = 1; - } else if (dirDotNorm < -FastMath.FLT_EPSILON) { - sign = -1f; - dirDotNorm = -dirDotNorm; - } else { - // ray and triangle/quad are parallel - vars.release(); - return false; - } - - float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2)); - if (dirDotDiffxEdge2 >= 0.0f) { - float dirDotEdge1xDiff = sign - * direction.dot(edge1.crossLocal(diff)); - - if (dirDotEdge1xDiff >= 0.0f) { - if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) { - float diffDotNorm = -sign * diff.dot(norm); - if (diffDotNorm >= 0.0f) { - // this method always returns - vars.release(); - - // ray intersects triangle - // if storage vector is null, just return true, - if (store == null) { - return true; - } - - // else fill in. - float inv = 1f / dirDotNorm; - float t = diffDotNorm * inv; - if (!doPlanar) { - store.set(origin).addLocal(direction.x * t, - direction.y * t, direction.z * t); - } else { - // these weights can be used to determine - // interpolated values, such as texture coord. - // eg. texcoord s,t at intersection point: - // s = w0*s0 + w1*s1 + w2*s2; - // t = w0*t0 + w1*t1 + w2*t2; - float w1 = dirDotDiffxEdge2 * inv; - float w2 = dirDotEdge1xDiff * inv; - //float w0 = 1.0f - w1 - w2; - store.set(t, w1, w2); - } - return true; - } - } - } - } - vars.release(); - return false; - } - - public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) { - float edge1X = v1.x - v0.x; - float edge1Y = v1.y - v0.y; - float edge1Z = v1.z - v0.z; - - float edge2X = v2.x - v0.x; - float edge2Y = v2.y - v0.y; - float edge2Z = v2.z - v0.z; - - float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y)); - float normY = ((edge1Z * edge2X) - (edge1X * edge2Z)); - float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X)); - - float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ; - - float diffX = origin.x - v0.x; - float diffY = origin.y - v0.y; - float diffZ = origin.z - v0.z; - - float sign; - if (dirDotNorm > FastMath.FLT_EPSILON) { - sign = 1; - } else if (dirDotNorm < -FastMath.FLT_EPSILON) { - sign = -1f; - dirDotNorm = -dirDotNorm; - } else { - // ray and triangle/quad are parallel - return Float.POSITIVE_INFINITY; - } - - float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y)); - float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z)); - float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X)); - - float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X - + direction.y * diffEdge2Y - + direction.z * diffEdge2Z); - - if (dirDotDiffxEdge2 >= 0.0f) { - diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY)); - diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ)); - diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX)); - - float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X - + direction.y * diffEdge2Y - + direction.z * diffEdge2Z); - - if (dirDotEdge1xDiff >= 0.0f) { - if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) { - float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ); - if (diffDotNorm >= 0.0f) { - // ray intersects triangle - // fill in. - float inv = 1f / dirDotNorm; - float t = diffDotNorm * inv; - return t; - } - } - } - } - - return Float.POSITIVE_INFINITY; - } - - /** - * intersectWherePlanar determines if the Ray intersects a - * quad defined by the specified points and if so it stores the point of - * intersection in the given loc vector as t, u, v where t is the distance - * from the origin to the point of intersection and u,v is the intersection - * point in terms of the quad plane. - * One edge of the quad is [v0,v1], another one [v0,v2]. The behaviour thus is like - * {@link #intersectWherePlanar(Vector3f, Vector3f, Vector3f, Vector3f)} except for - * the extended area, which is equivalent to the union of the triangles [v0,v1,v2] - * and [-v0+v1+v2,v1,v2]. - * - * @param v0 - * top left point of the quad. - * @param v1 - * top right point of the quad. - * @param v2 - * bottom left point of the quad. - * @param loc - * storage vector to save the collision point in (if the ray - * collides) as t, u, v - * @return true if the ray collides with the quad. - */ - public boolean intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2, - Vector3f loc) { - return intersects(v0, v1, v2, loc, true, true); - } - - /** - * - * @param p - * @param loc - * @return true if the ray collides with the given Plane - */ - public boolean intersectsWherePlane(Plane p, Vector3f loc) { - float denominator = p.getNormal().dot(direction); - - if (denominator > -FastMath.FLT_EPSILON && denominator < FastMath.FLT_EPSILON) { - return false; // coplanar - } - float numerator = -(p.getNormal().dot(origin) - p.getConstant()); - float ratio = numerator / denominator; - - if (ratio < FastMath.FLT_EPSILON) { - return false; // intersects behind origin - } - loc.set(direction).multLocal(ratio).addLocal(origin); - - return true; - } - - public int collideWith(Collidable other, CollisionResults results) { - if (other instanceof BoundingVolume) { - BoundingVolume bv = (BoundingVolume) other; - return bv.collideWith(this, results); - } else if (other instanceof AbstractTriangle) { - AbstractTriangle tri = (AbstractTriangle) other; - float d = intersects(tri.get1(), tri.get2(), tri.get3()); - if (Float.isInfinite(d) || Float.isNaN(d)) { - return 0; - } - - Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin); - results.addCollision(new CollisionResult(point, d)); - return 1; - } else { - throw new UnsupportedCollisionException(); - } - } - - public float distanceSquared(Vector3f point) { - TempVars vars = TempVars.get(); - - Vector3f tempVa = vars.vect1, - tempVb = vars.vect2; - - point.subtract(origin, tempVa); - float rayParam = direction.dot(tempVa); - if (rayParam > 0) { - origin.add(direction.mult(rayParam, tempVb), tempVb); - } else { - tempVb.set(origin); - rayParam = 0.0f; - } - - tempVb.subtract(point, tempVa); - float len = tempVa.lengthSquared(); - vars.release(); - return len; - } - - /** - * - * getOrigin retrieves the origin point of the ray. - * - * @return the origin of the ray. - */ - public Vector3f getOrigin() { - return origin; - } - - /** - * - * setOrigin sets the origin of the ray. - * @param origin the origin of the ray. - */ - public void setOrigin(Vector3f origin) { - this.origin.set(origin); - } - - /** - * getLimit returns the limit or the ray, aka the length. - * If the limit is not infinity, then this ray is a line with length - * limit. - * - * @return the limit or the ray, aka the length. - */ - public float getLimit() { - return limit; - } - - /** - * setLimit sets the limit of the ray. - * @param limit the limit of the ray. - * @see Ray#getLimit() - */ - public void setLimit(float limit) { - this.limit = limit; - } - - /** - * - * getDirection retrieves the direction vector of the ray. - * @return the direction of the ray. - */ - public Vector3f getDirection() { - return direction; - } - - /** - * - * setDirection sets the direction vector of the ray. - * @param direction the direction of the ray. - */ - public void setDirection(Vector3f direction) { - this.direction.set(direction); - } - - /** - * Copies information from a source ray into this ray. - * - * @param source - * the ray to copy information from - */ - public void set(Ray source) { - origin.set(source.getOrigin()); - direction.set(source.getDirection()); - } - - public String toString() { - return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]"; - } - - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(origin, "origin", Vector3f.ZERO); - capsule.write(direction, "direction", Vector3f.ZERO); - } - - public void read(JmeImporter e) throws IOException { - InputCapsule capsule = e.getCapsule(this); - origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone()); - direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone()); - } - - @Override - public Ray clone() { - try { - Ray r = (Ray) super.clone(); - r.direction = direction.clone(); - r.origin = origin.clone(); - return r; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.math; + +import com.jme3.bounding.BoundingVolume; +import com.jme3.collision.Collidable; +import com.jme3.collision.CollisionResult; +import com.jme3.collision.CollisionResults; +import com.jme3.collision.UnsupportedCollisionException; +import com.jme3.export.*; +import com.jme3.util.TempVars; +import java.io.IOException; + +/** + * Ray defines a line segment which has an origin and a direction. + * That is, a point and an infinite ray is cast from this point. The ray is + * defined by the following equation: R(t) = origin + t*direction for t >= 0. + * + * @author Mark Powell + * @author Joshua Slack + */ +public final class Ray implements Savable, Cloneable, Collidable, java.io.Serializable { + + static final long serialVersionUID = 1; + + /** + * The ray's begining point. + */ + public Vector3f origin = new Vector3f(); + + /** + * The direction of the ray. + */ + public Vector3f direction = new Vector3f(0, 0, 1); + + + public float limit = Float.POSITIVE_INFINITY; + + /** + * Constructor instantiates a new Ray object. As default, the + * origin is (0,0,0) and the direction is (0,0,1). + * + */ + public Ray() { + } + + /** + * Constructor instantiates a new Ray object. The origin and + * direction are given. + * @param origin the origin of the ray. + * @param direction the direction the ray travels in. + */ + public Ray(Vector3f origin, Vector3f direction) { + setOrigin(origin) + setDirection(direction); + } + + /** + * intersect determines if the Ray intersects a triangle. + * @param t the Triangle to test against. + * @return true if the ray collides. + */ +// public boolean intersect(Triangle t) { +// return intersect(t.get(0), t.get(1), t.get(2)); +// } + /** + * intersect determines if the Ray intersects a triangle + * defined by the specified points. + * + * @param v0 + * first point of the triangle. + * @param v1 + * second point of the triangle. + * @param v2 + * third point of the triangle. + * @return true if the ray collides. + */ +// public boolean intersect(Vector3f v0,Vector3f v1,Vector3f v2){ +// return intersectWhere(v0, v1, v2, null); +// } + /** + * intersectWhere determines if the Ray intersects a triangle. It then + * stores the point of intersection in the given loc vector + * @param t the Triangle to test against. + * @param loc + * storage vector to save the collision point in (if the ray + * collides) + * @return true if the ray collides. + */ + public boolean intersectWhere(Triangle t, Vector3f loc) { + return intersectWhere(t.get(0), t.get(1), t.get(2), loc); + } + + /** + * intersectWhere determines if the Ray intersects a triangle + * defined by the specified points and if so it stores the point of + * intersection in the given loc vector. + * + * @param v0 + * first point of the triangle. + * @param v1 + * second point of the triangle. + * @param v2 + * third point of the triangle. + * @param loc + * storage vector to save the collision point in (if the ray + * collides) if null, only boolean is calculated. + * @return true if the ray collides. + */ + public boolean intersectWhere(Vector3f v0, Vector3f v1, Vector3f v2, + Vector3f loc) { + return intersects(v0, v1, v2, loc, false, false); + } + + /** + * intersectWherePlanar determines if the Ray intersects a + * triangle and if so it stores the point of + * intersection in the given loc vector as t, u, v where t is the distance + * from the origin to the point of intersection and u,v is the intersection + * point in terms of the triangle plane. + * + * @param t the Triangle to test against. + * @param loc + * storage vector to save the collision point in (if the ray + * collides) as t, u, v + * @return true if the ray collides. + */ + public boolean intersectWherePlanar(Triangle t, Vector3f loc) { + return intersectWherePlanar(t.get(0), t.get(1), t.get(2), loc); + } + + /** + * intersectWherePlanar determines if the Ray intersects a + * triangle defined by the specified points and if so it stores the point of + * intersection in the given loc vector as t, u, v where t is the distance + * from the origin to the point of intersection and u,v is the intersection + * point in terms of the triangle plane. + * + * @param v0 + * first point of the triangle. + * @param v1 + * second point of the triangle. + * @param v2 + * third point of the triangle. + * @param loc + * storage vector to save the collision point in (if the ray + * collides) as t, u, v + * @return true if the ray collides. + */ + public boolean intersectWherePlanar(Vector3f v0, Vector3f v1, Vector3f v2, + Vector3f loc) { + return intersects(v0, v1, v2, loc, true, false); + } + + /** + * intersects does the actual intersection work. + * + * @param v0 + * first point of the triangle. + * @param v1 + * second point of the triangle. + * @param v2 + * third point of the triangle. + * @param store + * storage vector - if null, no intersection is calc'd + * @param doPlanar + * true if we are calcing planar results. + * @param quad + * @return true if ray intersects triangle + */ + private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2, + Vector3f store, boolean doPlanar, boolean quad) { + TempVars vars = TempVars.get(); + + Vector3f tempVa = vars.vect1, + tempVb = vars.vect2, + tempVc = vars.vect3, + tempVd = vars.vect4; + + Vector3f diff = origin.subtract(v0, tempVa); + Vector3f edge1 = v1.subtract(v0, tempVb); + Vector3f edge2 = v2.subtract(v0, tempVc); + Vector3f norm = edge1.cross(edge2, tempVd); + + float dirDotNorm = direction.dot(norm); + float sign; + if (dirDotNorm > FastMath.FLT_EPSILON) { + sign = 1; + } else if (dirDotNorm < -FastMath.FLT_EPSILON) { + sign = -1f; + dirDotNorm = -dirDotNorm; + } else { + // ray and triangle/quad are parallel + vars.release(); + return false; + } + + float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2)); + if (dirDotDiffxEdge2 >= 0.0f) { + float dirDotEdge1xDiff = sign + * direction.dot(edge1.crossLocal(diff)); + + if (dirDotEdge1xDiff >= 0.0f) { + if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) { + float diffDotNorm = -sign * diff.dot(norm); + if (diffDotNorm >= 0.0f) { + // this method always returns + vars.release(); + + // ray intersects triangle + // if storage vector is null, just return true, + if (store == null) { + return true; + } + + // else fill in. + float inv = 1f / dirDotNorm; + float t = diffDotNorm * inv; + if (!doPlanar) { + store.set(origin).addLocal(direction.x * t, + direction.y * t, direction.z * t); + } else { + // these weights can be used to determine + // interpolated values, such as texture coord. + // eg. texcoord s,t at intersection point: + // s = w0*s0 + w1*s1 + w2*s2; + // t = w0*t0 + w1*t1 + w2*t2; + float w1 = dirDotDiffxEdge2 * inv; + float w2 = dirDotEdge1xDiff * inv; + //float w0 = 1.0f - w1 - w2; + store.set(t, w1, w2); + } + return true; + } + } + } + } + vars.release(); + return false; + } + + public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) { + float edge1X = v1.x - v0.x; + float edge1Y = v1.y - v0.y; + float edge1Z = v1.z - v0.z; + + float edge2X = v2.x - v0.x; + float edge2Y = v2.y - v0.y; + float edge2Z = v2.z - v0.z; + + float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y)); + float normY = ((edge1Z * edge2X) - (edge1X * edge2Z)); + float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X)); + + float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ; + + float diffX = origin.x - v0.x; + float diffY = origin.y - v0.y; + float diffZ = origin.z - v0.z; + + float sign; + if (dirDotNorm > FastMath.FLT_EPSILON) { + sign = 1; + } else if (dirDotNorm < -FastMath.FLT_EPSILON) { + sign = -1f; + dirDotNorm = -dirDotNorm; + } else { + // ray and triangle/quad are parallel + return Float.POSITIVE_INFINITY; + } + + float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y)); + float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z)); + float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X)); + + float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X + + direction.y * diffEdge2Y + + direction.z * diffEdge2Z); + + if (dirDotDiffxEdge2 >= 0.0f) { + diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY)); + diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ)); + diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX)); + + float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X + + direction.y * diffEdge2Y + + direction.z * diffEdge2Z); + + if (dirDotEdge1xDiff >= 0.0f) { + if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) { + float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ); + if (diffDotNorm >= 0.0f) { + // ray intersects triangle + // fill in. + float inv = 1f / dirDotNorm; + float t = diffDotNorm * inv; + return t; + } + } + } + } + + return Float.POSITIVE_INFINITY; + } + + /** + * intersectWherePlanar determines if the Ray intersects a + * quad defined by the specified points and if so it stores the point of + * intersection in the given loc vector as t, u, v where t is the distance + * from the origin to the point of intersection and u,v is the intersection + * point in terms of the quad plane. + * One edge of the quad is [v0,v1], another one [v0,v2]. The behaviour thus is like + * {@link #intersectWherePlanar(Vector3f, Vector3f, Vector3f, Vector3f)} except for + * the extended area, which is equivalent to the union of the triangles [v0,v1,v2] + * and [-v0+v1+v2,v1,v2]. + * + * @param v0 + * top left point of the quad. + * @param v1 + * top right point of the quad. + * @param v2 + * bottom left point of the quad. + * @param loc + * storage vector to save the collision point in (if the ray + * collides) as t, u, v + * @return true if the ray collides with the quad. + */ + public boolean intersectWherePlanarQuad(Vector3f v0, Vector3f v1, Vector3f v2, + Vector3f loc) { + return intersects(v0, v1, v2, loc, true, true); + } + + /** + * + * @param p + * @param loc + * @return true if the ray collides with the given Plane + */ + public boolean intersectsWherePlane(Plane p, Vector3f loc) { + float denominator = p.getNormal().dot(direction); + + if (denominator > -FastMath.FLT_EPSILON && denominator < FastMath.FLT_EPSILON) { + return false; // coplanar + } + float numerator = -(p.getNormal().dot(origin) - p.getConstant()); + float ratio = numerator / denominator; + + if (ratio < FastMath.FLT_EPSILON) { + return false; // intersects behind origin + } + loc.set(direction).multLocal(ratio).addLocal(origin); + + return true; + } + + public int collideWith(Collidable other, CollisionResults results) { + if (other instanceof BoundingVolume) { + BoundingVolume bv = (BoundingVolume) other; + return bv.collideWith(this, results); + } else if (other instanceof AbstractTriangle) { + AbstractTriangle tri = (AbstractTriangle) other; + float d = intersects(tri.get1(), tri.get2(), tri.get3()); + if (Float.isInfinite(d) || Float.isNaN(d)) { + return 0; + } + + Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin); + results.addCollision(new CollisionResult(point, d)); + return 1; + } else { + throw new UnsupportedCollisionException(); + } + } + + public float distanceSquared(Vector3f point) { + TempVars vars = TempVars.get(); + + Vector3f tempVa = vars.vect1, + tempVb = vars.vect2; + + point.subtract(origin, tempVa); + float rayParam = direction.dot(tempVa); + if (rayParam > 0) { + origin.add(direction.mult(rayParam, tempVb), tempVb); + } else { + tempVb.set(origin); + rayParam = 0.0f; + } + + tempVb.subtract(point, tempVa); + float len = tempVa.lengthSquared(); + vars.release(); + return len; + } + + /** + * + * getOrigin retrieves the origin point of the ray. + * + * @return the origin of the ray. + */ + public Vector3f getOrigin() { + return origin; + } + + /** + * + * setOrigin sets the origin of the ray. + * @param origin the origin of the ray. + */ + public void setOrigin(Vector3f origin) { + this.origin.set(origin); + } + + /** + * getLimit returns the limit or the ray, aka the length. + * If the limit is not infinity, then this ray is a line with length + * limit. + * + * @return the limit or the ray, aka the length. + */ + public float getLimit() { + return limit; + } + + /** + * setLimit sets the limit of the ray. + * @param limit the limit of the ray. + * @see Ray#getLimit() + */ + public void setLimit(float limit) { + this.limit = limit; + } + + /** + * + * getDirection retrieves the direction vector of the ray. + * @return the direction of the ray. + */ + public Vector3f getDirection() { + return direction; + } + + /** + * + * setDirection sets the direction vector of the ray. + * @param direction the direction of the ray. + */ + public void setDirection(Vector3f direction) { + if (!direction.isUnitVector()) { + throw new IllegalArgumentException("direction must be a unit vector"); + } + this.direction.set(direction); + } + + /** + * Copies information from a source ray into this ray. + * + * @param source + * the ray to copy information from + */ + public void set(Ray source) { + origin.set(source.getOrigin()); + direction.set(source.getDirection()); + } + + public String toString() { + return getClass().getSimpleName() + " [Origin: " + origin + ", Direction: " + direction + "]"; + } + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(origin, "origin", Vector3f.ZERO); + capsule.write(direction, "direction", Vector3f.ZERO); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone()); + direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone()); + } + + @Override + public Ray clone() { + try { + Ray r = (Ray) super.clone(); + r.direction = direction.clone(); + r.origin = origin.clone(); + return r; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +}