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.bounding; 33 34 import com.jme3.collision.Collidable; 35 import com.jme3.collision.CollisionResult; 36 import com.jme3.collision.CollisionResults; 37 import com.jme3.collision.UnsupportedCollisionException; 38 import com.jme3.export.InputCapsule; 39 import com.jme3.export.JmeExporter; 40 import com.jme3.export.JmeImporter; 41 import com.jme3.export.OutputCapsule; 42 import com.jme3.math.*; 43 import com.jme3.scene.Mesh; 44 import com.jme3.util.BufferUtils; 45 import com.jme3.util.TempVars; 46 import java.io.IOException; 47 import java.nio.FloatBuffer; 48 //import com.jme.scene.TriMesh; 49 50 /** 51 * <code>BoundingBox</code> defines an axis-aligned cube that defines a 52 * container for a group of vertices of a particular piece of geometry. This box 53 * defines a center and extents from that center along the x, y and z axis. <br> 54 * <br> 55 * A typical usage is to allow the class define the center and radius by calling 56 * either <code>containAABB</code> or <code>averagePoints</code>. A call to 57 * <code>computeFramePoint</code> in turn calls <code>containAABB</code>. 58 * 59 * @author Joshua Slack 60 * @version $Id: BoundingBox.java,v 1.50 2007/09/22 16:46:35 irrisor Exp $ 61 */ 62 public class BoundingBox extends BoundingVolume { 63 64 float xExtent, yExtent, zExtent; 65 66 /** 67 * Default constructor instantiates a new <code>BoundingBox</code> 68 * object. 69 */ BoundingBox()70 public BoundingBox() { 71 } 72 73 /** 74 * Contstructor instantiates a new <code>BoundingBox</code> object with 75 * given specs. 76 */ BoundingBox(Vector3f c, float x, float y, float z)77 public BoundingBox(Vector3f c, float x, float y, float z) { 78 this.center.set(c); 79 this.xExtent = x; 80 this.yExtent = y; 81 this.zExtent = z; 82 } 83 BoundingBox(BoundingBox source)84 public BoundingBox(BoundingBox source) { 85 this.center.set(source.center); 86 this.xExtent = source.xExtent; 87 this.yExtent = source.yExtent; 88 this.zExtent = source.zExtent; 89 } 90 BoundingBox(Vector3f min, Vector3f max)91 public BoundingBox(Vector3f min, Vector3f max) { 92 setMinMax(min, max); 93 } 94 getType()95 public Type getType() { 96 return Type.AABB; 97 } 98 99 /** 100 * <code>computeFromPoints</code> creates a new Bounding Box from a given 101 * set of points. It uses the <code>containAABB</code> method as default. 102 * 103 * @param points 104 * the points to contain. 105 */ computeFromPoints(FloatBuffer points)106 public void computeFromPoints(FloatBuffer points) { 107 containAABB(points); 108 } 109 110 /** 111 * <code>computeFromTris</code> creates a new Bounding Box from a given 112 * set of triangles. It is used in OBBTree calculations. 113 * 114 * @param tris 115 * @param start 116 * @param end 117 */ computeFromTris(Triangle[] tris, int start, int end)118 public void computeFromTris(Triangle[] tris, int start, int end) { 119 if (end - start <= 0) { 120 return; 121 } 122 123 TempVars vars = TempVars.get(); 124 125 Vector3f min = vars.vect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); 126 Vector3f max = vars.vect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); 127 128 Vector3f point; 129 for (int i = start; i < end; i++) { 130 point = tris[i].get(0); 131 checkMinMax(min, max, point); 132 point = tris[i].get(1); 133 checkMinMax(min, max, point); 134 point = tris[i].get(2); 135 checkMinMax(min, max, point); 136 } 137 138 center.set(min.addLocal(max)); 139 center.multLocal(0.5f); 140 141 xExtent = max.x - center.x; 142 yExtent = max.y - center.y; 143 zExtent = max.z - center.z; 144 145 vars.release(); 146 } 147 computeFromTris(int[] indices, Mesh mesh, int start, int end)148 public void computeFromTris(int[] indices, Mesh mesh, int start, int end) { 149 if (end - start <= 0) { 150 return; 151 } 152 153 TempVars vars = TempVars.get(); 154 155 Vector3f vect1 = vars.vect1; 156 Vector3f vect2 = vars.vect2; 157 Triangle triangle = vars.triangle; 158 159 Vector3f min = vect1.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); 160 Vector3f max = vect2.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 161 Vector3f point; 162 163 for (int i = start; i < end; i++) { 164 mesh.getTriangle(indices[i], triangle); 165 point = triangle.get(0); 166 checkMinMax(min, max, point); 167 point = triangle.get(1); 168 checkMinMax(min, max, point); 169 point = triangle.get(2); 170 checkMinMax(min, max, point); 171 } 172 173 center.set(min.addLocal(max)); 174 center.multLocal(0.5f); 175 176 xExtent = max.x - center.x; 177 yExtent = max.y - center.y; 178 zExtent = max.z - center.z; 179 180 vars.release(); 181 } 182 checkMinMax(Vector3f min, Vector3f max, Vector3f point)183 public static void checkMinMax(Vector3f min, Vector3f max, Vector3f point) { 184 if (point.x < min.x) { 185 min.x = point.x; 186 } 187 if (point.x > max.x) { 188 max.x = point.x; 189 } 190 if (point.y < min.y) { 191 min.y = point.y; 192 } 193 if (point.y > max.y) { 194 max.y = point.y; 195 } 196 if (point.z < min.z) { 197 min.z = point.z; 198 } 199 if (point.z > max.z) { 200 max.z = point.z; 201 } 202 } 203 204 /** 205 * <code>containAABB</code> creates a minimum-volume axis-aligned bounding 206 * box of the points, then selects the smallest enclosing sphere of the box 207 * with the sphere centered at the boxes center. 208 * 209 * @param points 210 * the list of points. 211 */ containAABB(FloatBuffer points)212 public void containAABB(FloatBuffer points) { 213 if (points == null) { 214 return; 215 } 216 217 points.rewind(); 218 if (points.remaining() <= 2) // we need at least a 3 float vector 219 { 220 return; 221 } 222 223 TempVars vars = TempVars.get(); 224 225 BufferUtils.populateFromBuffer(vars.vect1, points, 0); 226 float minX = vars.vect1.x, minY = vars.vect1.y, minZ = vars.vect1.z; 227 float maxX = vars.vect1.x, maxY = vars.vect1.y, maxZ = vars.vect1.z; 228 229 for (int i = 1, len = points.remaining() / 3; i < len; i++) { 230 BufferUtils.populateFromBuffer(vars.vect1, points, i); 231 232 if (vars.vect1.x < minX) { 233 minX = vars.vect1.x; 234 } else if (vars.vect1.x > maxX) { 235 maxX = vars.vect1.x; 236 } 237 238 if (vars.vect1.y < minY) { 239 minY = vars.vect1.y; 240 } else if (vars.vect1.y > maxY) { 241 maxY = vars.vect1.y; 242 } 243 244 if (vars.vect1.z < minZ) { 245 minZ = vars.vect1.z; 246 } else if (vars.vect1.z > maxZ) { 247 maxZ = vars.vect1.z; 248 } 249 } 250 251 vars.release(); 252 253 center.set(minX + maxX, minY + maxY, minZ + maxZ); 254 center.multLocal(0.5f); 255 256 xExtent = maxX - center.x; 257 yExtent = maxY - center.y; 258 zExtent = maxZ - center.z; 259 } 260 261 /** 262 * <code>transform</code> modifies the center of the box to reflect the 263 * change made via a rotation, translation and scale. 264 * 265 * @param trans 266 * the transform to apply 267 * @param store 268 * box to store result in 269 */ transform(Transform trans, BoundingVolume store)270 public BoundingVolume transform(Transform trans, BoundingVolume store) { 271 272 BoundingBox box; 273 if (store == null || store.getType() != Type.AABB) { 274 box = new BoundingBox(); 275 } else { 276 box = (BoundingBox) store; 277 } 278 279 center.mult(trans.getScale(), box.center); 280 trans.getRotation().mult(box.center, box.center); 281 box.center.addLocal(trans.getTranslation()); 282 283 TempVars vars = TempVars.get(); 284 285 Matrix3f transMatrix = vars.tempMat3; 286 transMatrix.set(trans.getRotation()); 287 // Make the rotation matrix all positive to get the maximum x/y/z extent 288 transMatrix.absoluteLocal(); 289 290 Vector3f scale = trans.getScale(); 291 vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z); 292 transMatrix.mult(vars.vect1, vars.vect2); 293 // Assign the biggest rotations after scales. 294 box.xExtent = FastMath.abs(vars.vect2.getX()); 295 box.yExtent = FastMath.abs(vars.vect2.getY()); 296 box.zExtent = FastMath.abs(vars.vect2.getZ()); 297 298 vars.release(); 299 300 return box; 301 } 302 transform(Matrix4f trans, BoundingVolume store)303 public BoundingVolume transform(Matrix4f trans, BoundingVolume store) { 304 BoundingBox box; 305 if (store == null || store.getType() != Type.AABB) { 306 box = new BoundingBox(); 307 } else { 308 box = (BoundingBox) store; 309 } 310 TempVars vars = TempVars.get(); 311 312 313 float w = trans.multProj(center, box.center); 314 box.center.divideLocal(w); 315 316 Matrix3f transMatrix = vars.tempMat3; 317 trans.toRotationMatrix(transMatrix); 318 319 // Make the rotation matrix all positive to get the maximum x/y/z extent 320 transMatrix.absoluteLocal(); 321 322 vars.vect1.set(xExtent, yExtent, zExtent); 323 transMatrix.mult(vars.vect1, vars.vect1); 324 325 // Assign the biggest rotations after scales. 326 box.xExtent = FastMath.abs(vars.vect1.getX()); 327 box.yExtent = FastMath.abs(vars.vect1.getY()); 328 box.zExtent = FastMath.abs(vars.vect1.getZ()); 329 330 vars.release(); 331 332 return box; 333 } 334 335 /** 336 * <code>whichSide</code> takes a plane (typically provided by a view 337 * frustum) to determine which side this bound is on. 338 * 339 * @param plane 340 * the plane to check against. 341 */ whichSide(Plane plane)342 public Plane.Side whichSide(Plane plane) { 343 float radius = FastMath.abs(xExtent * plane.getNormal().getX()) 344 + FastMath.abs(yExtent * plane.getNormal().getY()) 345 + FastMath.abs(zExtent * plane.getNormal().getZ()); 346 347 float distance = plane.pseudoDistance(center); 348 349 //changed to < and > to prevent floating point precision problems 350 if (distance < -radius) { 351 return Plane.Side.Negative; 352 } else if (distance > radius) { 353 return Plane.Side.Positive; 354 } else { 355 return Plane.Side.None; 356 } 357 } 358 359 /** 360 * <code>merge</code> combines this sphere with a second bounding sphere. 361 * This new sphere contains both bounding spheres and is returned. 362 * 363 * @param volume 364 * the sphere to combine with this sphere. 365 * @return the new sphere 366 */ merge(BoundingVolume volume)367 public BoundingVolume merge(BoundingVolume volume) { 368 if (volume == null) { 369 return this; 370 } 371 372 switch (volume.getType()) { 373 case AABB: { 374 BoundingBox vBox = (BoundingBox) volume; 375 return merge(vBox.center, vBox.xExtent, vBox.yExtent, 376 vBox.zExtent, new BoundingBox(new Vector3f(0, 0, 0), 0, 377 0, 0)); 378 } 379 380 case Sphere: { 381 BoundingSphere vSphere = (BoundingSphere) volume; 382 return merge(vSphere.center, vSphere.radius, vSphere.radius, 383 vSphere.radius, new BoundingBox(new Vector3f(0, 0, 0), 384 0, 0, 0)); 385 } 386 387 // case OBB: { 388 // OrientedBoundingBox box = (OrientedBoundingBox) volume; 389 // BoundingBox rVal = (BoundingBox) this.clone(null); 390 // return rVal.mergeOBB(box); 391 // } 392 393 default: 394 return null; 395 } 396 } 397 398 /** 399 * <code>mergeLocal</code> combines this sphere with a second bounding 400 * sphere locally. Altering this sphere to contain both the original and the 401 * additional sphere volumes; 402 * 403 * @param volume 404 * the sphere to combine with this sphere. 405 * @return this 406 */ mergeLocal(BoundingVolume volume)407 public BoundingVolume mergeLocal(BoundingVolume volume) { 408 if (volume == null) { 409 return this; 410 } 411 412 switch (volume.getType()) { 413 case AABB: { 414 BoundingBox vBox = (BoundingBox) volume; 415 return merge(vBox.center, vBox.xExtent, vBox.yExtent, 416 vBox.zExtent, this); 417 } 418 419 case Sphere: { 420 BoundingSphere vSphere = (BoundingSphere) volume; 421 return merge(vSphere.center, vSphere.radius, vSphere.radius, 422 vSphere.radius, this); 423 } 424 425 // case OBB: { 426 // return mergeOBB((OrientedBoundingBox) volume); 427 // } 428 429 default: 430 return null; 431 } 432 } 433 434 /** 435 * Merges this AABB with the given OBB. 436 * 437 * @param volume 438 * the OBB to merge this AABB with. 439 * @return This AABB extended to fit the given OBB. 440 */ 441 // private BoundingBox mergeOBB(OrientedBoundingBox volume) { 442 // if (!volume.correctCorners) 443 // volume.computeCorners(); 444 // 445 // TempVars vars = TempVars.get(); 446 // Vector3f min = vars.compVect1.set(center.x - xExtent, center.y - yExtent, 447 // center.z - zExtent); 448 // Vector3f max = vars.compVect2.set(center.x + xExtent, center.y + yExtent, 449 // center.z + zExtent); 450 // 451 // for (int i = 1; i < volume.vectorStore.length; i++) { 452 // Vector3f temp = volume.vectorStore[i]; 453 // if (temp.x < min.x) 454 // min.x = temp.x; 455 // else if (temp.x > max.x) 456 // max.x = temp.x; 457 // 458 // if (temp.y < min.y) 459 // min.y = temp.y; 460 // else if (temp.y > max.y) 461 // max.y = temp.y; 462 // 463 // if (temp.z < min.z) 464 // min.z = temp.z; 465 // else if (temp.z > max.z) 466 // max.z = temp.z; 467 // } 468 // 469 // center.set(min.addLocal(max)); 470 // center.multLocal(0.5f); 471 // 472 // xExtent = max.x - center.x; 473 // yExtent = max.y - center.y; 474 // zExtent = max.z - center.z; 475 // return this; 476 // } 477 /** 478 * <code>merge</code> combines this bounding box with another box which is 479 * defined by the center, x, y, z extents. 480 * 481 * @param boxCenter 482 * the center of the box to merge with 483 * @param boxX 484 * the x extent of the box to merge with. 485 * @param boxY 486 * the y extent of the box to merge with. 487 * @param boxZ 488 * the z extent of the box to merge with. 489 * @param rVal 490 * the resulting merged box. 491 * @return the resulting merged box. 492 */ merge(Vector3f boxCenter, float boxX, float boxY, float boxZ, BoundingBox rVal)493 private BoundingBox merge(Vector3f boxCenter, float boxX, float boxY, 494 float boxZ, BoundingBox rVal) { 495 496 TempVars vars = TempVars.get(); 497 498 vars.vect1.x = center.x - xExtent; 499 if (vars.vect1.x > boxCenter.x - boxX) { 500 vars.vect1.x = boxCenter.x - boxX; 501 } 502 vars.vect1.y = center.y - yExtent; 503 if (vars.vect1.y > boxCenter.y - boxY) { 504 vars.vect1.y = boxCenter.y - boxY; 505 } 506 vars.vect1.z = center.z - zExtent; 507 if (vars.vect1.z > boxCenter.z - boxZ) { 508 vars.vect1.z = boxCenter.z - boxZ; 509 } 510 511 vars.vect2.x = center.x + xExtent; 512 if (vars.vect2.x < boxCenter.x + boxX) { 513 vars.vect2.x = boxCenter.x + boxX; 514 } 515 vars.vect2.y = center.y + yExtent; 516 if (vars.vect2.y < boxCenter.y + boxY) { 517 vars.vect2.y = boxCenter.y + boxY; 518 } 519 vars.vect2.z = center.z + zExtent; 520 if (vars.vect2.z < boxCenter.z + boxZ) { 521 vars.vect2.z = boxCenter.z + boxZ; 522 } 523 524 center.set(vars.vect2).addLocal(vars.vect1).multLocal(0.5f); 525 526 xExtent = vars.vect2.x - center.x; 527 yExtent = vars.vect2.y - center.y; 528 zExtent = vars.vect2.z - center.z; 529 530 vars.release(); 531 532 return rVal; 533 } 534 535 /** 536 * <code>clone</code> creates a new BoundingBox object containing the same 537 * data as this one. 538 * 539 * @param store 540 * where to store the cloned information. if null or wrong class, 541 * a new store is created. 542 * @return the new BoundingBox 543 */ clone(BoundingVolume store)544 public BoundingVolume clone(BoundingVolume store) { 545 if (store != null && store.getType() == Type.AABB) { 546 BoundingBox rVal = (BoundingBox) store; 547 rVal.center.set(center); 548 rVal.xExtent = xExtent; 549 rVal.yExtent = yExtent; 550 rVal.zExtent = zExtent; 551 rVal.checkPlane = checkPlane; 552 return rVal; 553 } 554 555 BoundingBox rVal = new BoundingBox(center.clone(), 556 xExtent, yExtent, zExtent); 557 return rVal; 558 } 559 560 /** 561 * <code>toString</code> returns the string representation of this object. 562 * The form is: "Radius: RRR.SSSS Center: <Vector>". 563 * 564 * @return the string representation of this. 565 */ 566 @Override toString()567 public String toString() { 568 return getClass().getSimpleName() + " [Center: " + center + " xExtent: " 569 + xExtent + " yExtent: " + yExtent + " zExtent: " + zExtent 570 + "]"; 571 } 572 573 /** 574 * intersects determines if this Bounding Box intersects with another given 575 * bounding volume. If so, true is returned, otherwise, false is returned. 576 * 577 * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume) 578 */ intersects(BoundingVolume bv)579 public boolean intersects(BoundingVolume bv) { 580 return bv.intersectsBoundingBox(this); 581 } 582 583 /** 584 * determines if this bounding box intersects a given bounding sphere. 585 * 586 * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere) 587 */ intersectsSphere(BoundingSphere bs)588 public boolean intersectsSphere(BoundingSphere bs) { 589 assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); 590 591 if (FastMath.abs(center.x - bs.center.x) < bs.getRadius() 592 + xExtent 593 && FastMath.abs(center.y - bs.center.y) < bs.getRadius() 594 + yExtent 595 && FastMath.abs(center.z - bs.center.z) < bs.getRadius() 596 + zExtent) { 597 return true; 598 } 599 600 return false; 601 } 602 603 /** 604 * determines if this bounding box intersects a given bounding box. If the 605 * two boxes intersect in any way, true is returned. Otherwise, false is 606 * returned. 607 * 608 * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox) 609 */ intersectsBoundingBox(BoundingBox bb)610 public boolean intersectsBoundingBox(BoundingBox bb) { 611 assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); 612 613 if (center.x + xExtent < bb.center.x - bb.xExtent 614 || center.x - xExtent > bb.center.x + bb.xExtent) { 615 return false; 616 } else if (center.y + yExtent < bb.center.y - bb.yExtent 617 || center.y - yExtent > bb.center.y + bb.yExtent) { 618 return false; 619 } else if (center.z + zExtent < bb.center.z - bb.zExtent 620 || center.z - zExtent > bb.center.z + bb.zExtent) { 621 return false; 622 } else { 623 return true; 624 } 625 } 626 627 /** 628 * determines if this bounding box intersects with a given oriented bounding 629 * box. 630 * 631 * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox) 632 */ 633 // public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) { 634 // return obb.intersectsBoundingBox(this); 635 // } 636 /** 637 * determines if this bounding box intersects with a given ray object. If an 638 * intersection has occurred, true is returned, otherwise false is returned. 639 * 640 * @see BoundingVolume#intersects(com.jme3.math.Ray) 641 */ intersects(Ray ray)642 public boolean intersects(Ray ray) { 643 assert Vector3f.isValidVector(center); 644 645 float rhs; 646 647 TempVars vars = TempVars.get(); 648 649 Vector3f diff = ray.origin.subtract(getCenter(vars.vect2), vars.vect1); 650 651 final float[] fWdU = vars.fWdU; 652 final float[] fAWdU = vars.fAWdU; 653 final float[] fDdU = vars.fDdU; 654 final float[] fADdU = vars.fADdU; 655 final float[] fAWxDdU = vars.fAWxDdU; 656 657 fWdU[0] = ray.getDirection().dot(Vector3f.UNIT_X); 658 fAWdU[0] = FastMath.abs(fWdU[0]); 659 fDdU[0] = diff.dot(Vector3f.UNIT_X); 660 fADdU[0] = FastMath.abs(fDdU[0]); 661 if (fADdU[0] > xExtent && fDdU[0] * fWdU[0] >= 0.0) { 662 vars.release(); 663 return false; 664 } 665 666 fWdU[1] = ray.getDirection().dot(Vector3f.UNIT_Y); 667 fAWdU[1] = FastMath.abs(fWdU[1]); 668 fDdU[1] = diff.dot(Vector3f.UNIT_Y); 669 fADdU[1] = FastMath.abs(fDdU[1]); 670 if (fADdU[1] > yExtent && fDdU[1] * fWdU[1] >= 0.0) { 671 vars.release(); 672 return false; 673 } 674 675 fWdU[2] = ray.getDirection().dot(Vector3f.UNIT_Z); 676 fAWdU[2] = FastMath.abs(fWdU[2]); 677 fDdU[2] = diff.dot(Vector3f.UNIT_Z); 678 fADdU[2] = FastMath.abs(fDdU[2]); 679 if (fADdU[2] > zExtent && fDdU[2] * fWdU[2] >= 0.0) { 680 vars.release(); 681 return false; 682 } 683 684 Vector3f wCrossD = ray.getDirection().cross(diff, vars.vect2); 685 686 fAWxDdU[0] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_X)); 687 rhs = yExtent * fAWdU[2] + zExtent * fAWdU[1]; 688 if (fAWxDdU[0] > rhs) { 689 vars.release(); 690 return false; 691 } 692 693 fAWxDdU[1] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Y)); 694 rhs = xExtent * fAWdU[2] + zExtent * fAWdU[0]; 695 if (fAWxDdU[1] > rhs) { 696 vars.release(); 697 return false; 698 } 699 700 fAWxDdU[2] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Z)); 701 rhs = xExtent * fAWdU[1] + yExtent * fAWdU[0]; 702 if (fAWxDdU[2] > rhs) { 703 vars.release(); 704 return false; 705 } 706 707 vars.release(); 708 return true; 709 } 710 711 /** 712 * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray) 713 */ collideWithRay(Ray ray, CollisionResults results)714 private int collideWithRay(Ray ray, CollisionResults results) { 715 TempVars vars = TempVars.get(); 716 717 Vector3f diff = vars.vect1.set(ray.origin).subtractLocal(center); 718 Vector3f direction = vars.vect2.set(ray.direction); 719 720 float[] t = {0f, Float.POSITIVE_INFINITY}; 721 722 float saveT0 = t[0], saveT1 = t[1]; 723 boolean notEntirelyClipped = clip(+direction.x, -diff.x - xExtent, t) 724 && clip(-direction.x, +diff.x - xExtent, t) 725 && clip(+direction.y, -diff.y - yExtent, t) 726 && clip(-direction.y, +diff.y - yExtent, t) 727 && clip(+direction.z, -diff.z - zExtent, t) 728 && clip(-direction.z, +diff.z - zExtent, t); 729 vars.release(); 730 731 if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) { 732 if (t[1] > t[0]) { 733 float[] distances = t; 734 Vector3f[] points = new Vector3f[]{ 735 new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin), 736 new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin) 737 }; 738 739 CollisionResult result = new CollisionResult(points[0], distances[0]); 740 results.addCollision(result); 741 result = new CollisionResult(points[1], distances[1]); 742 results.addCollision(result); 743 return 2; 744 } 745 746 Vector3f point = new Vector3f(ray.direction).multLocal(t[0]).addLocal(ray.origin); 747 CollisionResult result = new CollisionResult(point, t[0]); 748 results.addCollision(result); 749 return 1; 750 } 751 return 0; 752 } 753 collideWith(Collidable other, CollisionResults results)754 public int collideWith(Collidable other, CollisionResults results) { 755 if (other instanceof Ray) { 756 Ray ray = (Ray) other; 757 return collideWithRay(ray, results); 758 } else if (other instanceof Triangle) { 759 Triangle t = (Triangle) other; 760 if (intersects(t.get1(), t.get2(), t.get3())) { 761 CollisionResult r = new CollisionResult(); 762 results.addCollision(r); 763 return 1; 764 } 765 return 0; 766 } else { 767 throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); 768 } 769 } 770 771 /** 772 * C code ported from <a href="http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt"> 773 * http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt</a> 774 * 775 * @param v1 The first point in the triangle 776 * @param v2 The second point in the triangle 777 * @param v3 The third point in the triangle 778 * @return True if the bounding box intersects the triangle, false 779 * otherwise. 780 */ intersects(Vector3f v1, Vector3f v2, Vector3f v3)781 public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3) { 782 return Intersection.intersect(this, v1, v2, v3); 783 } 784 785 @Override contains(Vector3f point)786 public boolean contains(Vector3f point) { 787 return FastMath.abs(center.x - point.x) < xExtent 788 && FastMath.abs(center.y - point.y) < yExtent 789 && FastMath.abs(center.z - point.z) < zExtent; 790 } 791 792 @Override intersects(Vector3f point)793 public boolean intersects(Vector3f point) { 794 return FastMath.abs(center.x - point.x) <= xExtent 795 && FastMath.abs(center.y - point.y) <= yExtent 796 && FastMath.abs(center.z - point.z) <= zExtent; 797 } 798 distanceToEdge(Vector3f point)799 public float distanceToEdge(Vector3f point) { 800 // compute coordinates of point in box coordinate system 801 TempVars vars= TempVars.get(); 802 Vector3f closest = vars.vect1; 803 804 point.subtract(center,closest); 805 806 // project test point onto box 807 float sqrDistance = 0.0f; 808 float delta; 809 810 if (closest.x < -xExtent) { 811 delta = closest.x + xExtent; 812 sqrDistance += delta * delta; 813 closest.x = -xExtent; 814 } else if (closest.x > xExtent) { 815 delta = closest.x - xExtent; 816 sqrDistance += delta * delta; 817 closest.x = xExtent; 818 } 819 820 if (closest.y < -yExtent) { 821 delta = closest.y + yExtent; 822 sqrDistance += delta * delta; 823 closest.y = -yExtent; 824 } else if (closest.y > yExtent) { 825 delta = closest.y - yExtent; 826 sqrDistance += delta * delta; 827 closest.y = yExtent; 828 } 829 830 if (closest.z < -zExtent) { 831 delta = closest.z + zExtent; 832 sqrDistance += delta * delta; 833 closest.z = -zExtent; 834 } else if (closest.z > zExtent) { 835 delta = closest.z - zExtent; 836 sqrDistance += delta * delta; 837 closest.z = zExtent; 838 } 839 840 vars.release(); 841 return FastMath.sqrt(sqrDistance); 842 } 843 844 /** 845 * <code>clip</code> determines if a line segment intersects the current 846 * test plane. 847 * 848 * @param denom 849 * the denominator of the line segment. 850 * @param numer 851 * the numerator of the line segment. 852 * @param t 853 * test values of the plane. 854 * @return true if the line segment intersects the plane, false otherwise. 855 */ clip(float denom, float numer, float[] t)856 private boolean clip(float denom, float numer, float[] t) { 857 // Return value is 'true' if line segment intersects the current test 858 // plane. Otherwise 'false' is returned in which case the line segment 859 // is entirely clipped. 860 if (denom > 0.0f) { 861 if (numer > denom * t[1]) { 862 return false; 863 } 864 if (numer > denom * t[0]) { 865 t[0] = numer / denom; 866 } 867 return true; 868 } else if (denom < 0.0f) { 869 if (numer > denom * t[0]) { 870 return false; 871 } 872 if (numer > denom * t[1]) { 873 t[1] = numer / denom; 874 } 875 return true; 876 } else { 877 return numer <= 0.0; 878 } 879 } 880 881 /** 882 * Query extent. 883 * 884 * @param store 885 * where extent gets stored - null to return a new vector 886 * @return store / new vector 887 */ getExtent(Vector3f store)888 public Vector3f getExtent(Vector3f store) { 889 if (store == null) { 890 store = new Vector3f(); 891 } 892 store.set(xExtent, yExtent, zExtent); 893 return store; 894 } 895 getXExtent()896 public float getXExtent() { 897 return xExtent; 898 } 899 getYExtent()900 public float getYExtent() { 901 return yExtent; 902 } 903 getZExtent()904 public float getZExtent() { 905 return zExtent; 906 } 907 setXExtent(float xExtent)908 public void setXExtent(float xExtent) { 909 if (xExtent < 0) { 910 throw new IllegalArgumentException(); 911 } 912 913 this.xExtent = xExtent; 914 } 915 setYExtent(float yExtent)916 public void setYExtent(float yExtent) { 917 if (yExtent < 0) { 918 throw new IllegalArgumentException(); 919 } 920 921 this.yExtent = yExtent; 922 } 923 setZExtent(float zExtent)924 public void setZExtent(float zExtent) { 925 if (zExtent < 0) { 926 throw new IllegalArgumentException(); 927 } 928 929 this.zExtent = zExtent; 930 } 931 getMin(Vector3f store)932 public Vector3f getMin(Vector3f store) { 933 if (store == null) { 934 store = new Vector3f(); 935 } 936 store.set(center).subtractLocal(xExtent, yExtent, zExtent); 937 return store; 938 } 939 getMax(Vector3f store)940 public Vector3f getMax(Vector3f store) { 941 if (store == null) { 942 store = new Vector3f(); 943 } 944 store.set(center).addLocal(xExtent, yExtent, zExtent); 945 return store; 946 } 947 setMinMax(Vector3f min, Vector3f max)948 public void setMinMax(Vector3f min, Vector3f max) { 949 this.center.set(max).addLocal(min).multLocal(0.5f); 950 xExtent = FastMath.abs(max.x - center.x); 951 yExtent = FastMath.abs(max.y - center.y); 952 zExtent = FastMath.abs(max.z - center.z); 953 } 954 955 @Override write(JmeExporter e)956 public void write(JmeExporter e) throws IOException { 957 super.write(e); 958 OutputCapsule capsule = e.getCapsule(this); 959 capsule.write(xExtent, "xExtent", 0); 960 capsule.write(yExtent, "yExtent", 0); 961 capsule.write(zExtent, "zExtent", 0); 962 } 963 964 @Override read(JmeImporter e)965 public void read(JmeImporter e) throws IOException { 966 super.read(e); 967 InputCapsule capsule = e.getCapsule(this); 968 xExtent = capsule.readFloat("xExtent", 0); 969 yExtent = capsule.readFloat("yExtent", 0); 970 zExtent = capsule.readFloat("zExtent", 0); 971 } 972 973 @Override getVolume()974 public float getVolume() { 975 return (8 * xExtent * yExtent * zExtent); 976 } 977 } 978