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