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.JmeExporter; 39 import com.jme3.export.JmeImporter; 40 import com.jme3.math.*; 41 import com.jme3.util.BufferUtils; 42 import com.jme3.util.TempVars; 43 import java.io.IOException; 44 import java.nio.FloatBuffer; 45 import java.util.logging.Level; 46 import java.util.logging.Logger; 47 48 /** 49 * <code>BoundingSphere</code> defines a sphere that defines a container for a 50 * group of vertices of a particular piece of geometry. This sphere defines a 51 * radius and a center. <br> 52 * <br> 53 * A typical usage is to allow the class define the center and radius by calling 54 * either <code>containAABB</code> or <code>averagePoints</code>. A call to 55 * <code>computeFramePoint</code> in turn calls <code>containAABB</code>. 56 * 57 * @author Mark Powell 58 * @version $Id: BoundingSphere.java,v 1.59 2007/08/17 10:34:26 rherlitz Exp $ 59 */ 60 public class BoundingSphere extends BoundingVolume { 61 62 private static final Logger logger = 63 Logger.getLogger(BoundingSphere.class.getName()); 64 float radius; 65 private static final float RADIUS_EPSILON = 1f + 0.00001f; 66 67 /** 68 * Default contstructor instantiates a new <code>BoundingSphere</code> 69 * object. 70 */ BoundingSphere()71 public BoundingSphere() { 72 } 73 74 /** 75 * Constructor instantiates a new <code>BoundingSphere</code> object. 76 * 77 * @param r 78 * the radius of the sphere. 79 * @param c 80 * the center of the sphere. 81 */ BoundingSphere(float r, Vector3f c)82 public BoundingSphere(float r, Vector3f c) { 83 this.center.set(c); 84 this.radius = r; 85 } 86 getType()87 public Type getType() { 88 return Type.Sphere; 89 } 90 91 /** 92 * <code>getRadius</code> returns the radius of the bounding sphere. 93 * 94 * @return the radius of the bounding sphere. 95 */ getRadius()96 public float getRadius() { 97 return radius; 98 } 99 100 /** 101 * <code>setRadius</code> sets the radius of this bounding sphere. 102 * 103 * @param radius 104 * the new radius of the bounding sphere. 105 */ setRadius(float radius)106 public void setRadius(float radius) { 107 this.radius = radius; 108 } 109 110 /** 111 * <code>computeFromPoints</code> creates a new Bounding Sphere from a 112 * given set of points. It uses the <code>calcWelzl</code> method as 113 * default. 114 * 115 * @param points 116 * the points to contain. 117 */ computeFromPoints(FloatBuffer points)118 public void computeFromPoints(FloatBuffer points) { 119 calcWelzl(points); 120 } 121 122 /** 123 * <code>computeFromTris</code> creates a new Bounding Box from a given 124 * set of triangles. It is used in OBBTree calculations. 125 * 126 * @param tris 127 * @param start 128 * @param end 129 */ computeFromTris(Triangle[] tris, int start, int end)130 public void computeFromTris(Triangle[] tris, int start, int end) { 131 if (end - start <= 0) { 132 return; 133 } 134 135 Vector3f[] vertList = new Vector3f[(end - start) * 3]; 136 137 int count = 0; 138 for (int i = start; i < end; i++) { 139 vertList[count++] = tris[i].get(0); 140 vertList[count++] = tris[i].get(1); 141 vertList[count++] = tris[i].get(2); 142 } 143 averagePoints(vertList); 144 } 145 // 146 // /** 147 // * <code>computeFromTris</code> creates a new Bounding Box from a given 148 // * set of triangles. It is used in OBBTree calculations. 149 // * 150 // * @param indices 151 // * @param mesh 152 // * @param start 153 // * @param end 154 // */ 155 // public void computeFromTris(int[] indices, Mesh mesh, int start, int end) { 156 // if (end - start <= 0) { 157 // return; 158 // } 159 // 160 // Vector3f[] vertList = new Vector3f[(end - start) * 3]; 161 // 162 // int count = 0; 163 // for (int i = start; i < end; i++) { 164 // mesh.getTriangle(indices[i], verts); 165 // vertList[count++] = new Vector3f(verts[0]); 166 // vertList[count++] = new Vector3f(verts[1]); 167 // vertList[count++] = new Vector3f(verts[2]); 168 // } 169 // 170 // averagePoints(vertList); 171 // } 172 173 /** 174 * Calculates a minimum bounding sphere for the set of points. The algorithm 175 * was originally found in C++ at 176 * <p><a href="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1"> 177 * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1</a><br><strong>broken link</strong></p> 178 * <p>and translated to java by Cep21</p> 179 * 180 * @param points 181 * The points to calculate the minimum bounds from. 182 */ calcWelzl(FloatBuffer points)183 public void calcWelzl(FloatBuffer points) { 184 if (center == null) { 185 center = new Vector3f(); 186 } 187 FloatBuffer buf = BufferUtils.createFloatBuffer(points.limit()); 188 points.rewind(); 189 buf.put(points); 190 buf.flip(); 191 recurseMini(buf, buf.limit() / 3, 0, 0); 192 } 193 194 /** 195 * Used from calcWelzl. This function recurses to calculate a minimum 196 * bounding sphere a few points at a time. 197 * 198 * @param points 199 * The array of points to look through. 200 * @param p 201 * The size of the list to be used. 202 * @param b 203 * The number of points currently considering to include with the 204 * sphere. 205 * @param ap 206 * A variable simulating pointer arithmatic from C++, and offset 207 * in <code>points</code>. 208 */ recurseMini(FloatBuffer points, int p, int b, int ap)209 private void recurseMini(FloatBuffer points, int p, int b, int ap) { 210 //TempVars vars = TempVars.get(); 211 212 Vector3f tempA = new Vector3f(); //vars.vect1; 213 Vector3f tempB = new Vector3f(); //vars.vect2; 214 Vector3f tempC = new Vector3f(); //vars.vect3; 215 Vector3f tempD = new Vector3f(); //vars.vect4; 216 217 switch (b) { 218 case 0: 219 this.radius = 0; 220 this.center.set(0, 0, 0); 221 break; 222 case 1: 223 this.radius = 1f - RADIUS_EPSILON; 224 BufferUtils.populateFromBuffer(center, points, ap - 1); 225 break; 226 case 2: 227 BufferUtils.populateFromBuffer(tempA, points, ap - 1); 228 BufferUtils.populateFromBuffer(tempB, points, ap - 2); 229 setSphere(tempA, tempB); 230 break; 231 case 3: 232 BufferUtils.populateFromBuffer(tempA, points, ap - 1); 233 BufferUtils.populateFromBuffer(tempB, points, ap - 2); 234 BufferUtils.populateFromBuffer(tempC, points, ap - 3); 235 setSphere(tempA, tempB, tempC); 236 break; 237 case 4: 238 BufferUtils.populateFromBuffer(tempA, points, ap - 1); 239 BufferUtils.populateFromBuffer(tempB, points, ap - 2); 240 BufferUtils.populateFromBuffer(tempC, points, ap - 3); 241 BufferUtils.populateFromBuffer(tempD, points, ap - 4); 242 setSphere(tempA, tempB, tempC, tempD); 243 //vars.release(); 244 return; 245 } 246 for (int i = 0; i < p; i++) { 247 BufferUtils.populateFromBuffer(tempA, points, i + ap); 248 if (tempA.distanceSquared(center) - (radius * radius) > RADIUS_EPSILON - 1f) { 249 for (int j = i; j > 0; j--) { 250 BufferUtils.populateFromBuffer(tempB, points, j + ap); 251 BufferUtils.populateFromBuffer(tempC, points, j - 1 + ap); 252 BufferUtils.setInBuffer(tempC, points, j + ap); 253 BufferUtils.setInBuffer(tempB, points, j - 1 + ap); 254 } 255 recurseMini(points, i, b + 1, ap + 1); 256 } 257 } 258 //vars.release(); 259 } 260 261 /** 262 * Calculates the minimum bounding sphere of 4 points. Used in welzl's 263 * algorithm. 264 * 265 * @param O 266 * The 1st point inside the sphere. 267 * @param A 268 * The 2nd point inside the sphere. 269 * @param B 270 * The 3rd point inside the sphere. 271 * @param C 272 * The 4th point inside the sphere. 273 * @see #calcWelzl(java.nio.FloatBuffer) 274 */ setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C)275 private void setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C) { 276 Vector3f a = A.subtract(O); 277 Vector3f b = B.subtract(O); 278 Vector3f c = C.subtract(O); 279 280 float Denominator = 2.0f * (a.x * (b.y * c.z - c.y * b.z) - b.x 281 * (a.y * c.z - c.y * a.z) + c.x * (a.y * b.z - b.y * a.z)); 282 if (Denominator == 0) { 283 center.set(0, 0, 0); 284 radius = 0; 285 } else { 286 Vector3f o = a.cross(b).multLocal(c.lengthSquared()).addLocal( 287 c.cross(a).multLocal(b.lengthSquared())).addLocal( 288 b.cross(c).multLocal(a.lengthSquared())).divideLocal( 289 Denominator); 290 291 radius = o.length() * RADIUS_EPSILON; 292 O.add(o, center); 293 } 294 } 295 296 /** 297 * Calculates the minimum bounding sphere of 3 points. Used in welzl's 298 * algorithm. 299 * 300 * @param O 301 * The 1st point inside the sphere. 302 * @param A 303 * The 2nd point inside the sphere. 304 * @param B 305 * The 3rd point inside the sphere. 306 * @see #calcWelzl(java.nio.FloatBuffer) 307 */ setSphere(Vector3f O, Vector3f A, Vector3f B)308 private void setSphere(Vector3f O, Vector3f A, Vector3f B) { 309 Vector3f a = A.subtract(O); 310 Vector3f b = B.subtract(O); 311 Vector3f acrossB = a.cross(b); 312 313 float Denominator = 2.0f * acrossB.dot(acrossB); 314 315 if (Denominator == 0) { 316 center.set(0, 0, 0); 317 radius = 0; 318 } else { 319 320 Vector3f o = acrossB.cross(a).multLocal(b.lengthSquared()).addLocal(b.cross(acrossB).multLocal(a.lengthSquared())).divideLocal(Denominator); 321 radius = o.length() * RADIUS_EPSILON; 322 O.add(o, center); 323 } 324 } 325 326 /** 327 * Calculates the minimum bounding sphere of 2 points. Used in welzl's 328 * algorithm. 329 * 330 * @param O 331 * The 1st point inside the sphere. 332 * @param A 333 * The 2nd point inside the sphere. 334 * @see #calcWelzl(java.nio.FloatBuffer) 335 */ setSphere(Vector3f O, Vector3f A)336 private void setSphere(Vector3f O, Vector3f A) { 337 radius = FastMath.sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y) 338 * (A.y - O.y) + (A.z - O.z) * (A.z - O.z)) / 4f) + RADIUS_EPSILON - 1f; 339 center.interpolate(O, A, .5f); 340 } 341 342 /** 343 * <code>averagePoints</code> selects the sphere center to be the average 344 * of the points and the sphere radius to be the smallest value to enclose 345 * all points. 346 * 347 * @param points 348 * the list of points to contain. 349 */ averagePoints(Vector3f[] points)350 public void averagePoints(Vector3f[] points) { 351 logger.info("Bounding Sphere calculated using average points."); 352 center = points[0]; 353 354 for (int i = 1; i < points.length; i++) { 355 center.addLocal(points[i]); 356 } 357 358 float quantity = 1.0f / points.length; 359 center.multLocal(quantity); 360 361 float maxRadiusSqr = 0; 362 for (int i = 0; i < points.length; i++) { 363 Vector3f diff = points[i].subtract(center); 364 float radiusSqr = diff.lengthSquared(); 365 if (radiusSqr > maxRadiusSqr) { 366 maxRadiusSqr = radiusSqr; 367 } 368 } 369 370 radius = (float) Math.sqrt(maxRadiusSqr) + RADIUS_EPSILON - 1f; 371 372 } 373 374 /** 375 * <code>transform</code> modifies the center of the sphere to reflect the 376 * change made via a rotation, translation and scale. 377 * 378 * @param trans 379 * the transform to apply 380 * @param store 381 * sphere to store result in 382 * @return BoundingVolume 383 * @return ref 384 */ transform(Transform trans, BoundingVolume store)385 public BoundingVolume transform(Transform trans, BoundingVolume store) { 386 BoundingSphere sphere; 387 if (store == null || store.getType() != BoundingVolume.Type.Sphere) { 388 sphere = new BoundingSphere(1, new Vector3f(0, 0, 0)); 389 } else { 390 sphere = (BoundingSphere) store; 391 } 392 393 center.mult(trans.getScale(), sphere.center); 394 trans.getRotation().mult(sphere.center, sphere.center); 395 sphere.center.addLocal(trans.getTranslation()); 396 sphere.radius = FastMath.abs(getMaxAxis(trans.getScale()) * radius) + RADIUS_EPSILON - 1f; 397 return sphere; 398 } 399 transform(Matrix4f trans, BoundingVolume store)400 public BoundingVolume transform(Matrix4f trans, BoundingVolume store) { 401 BoundingSphere sphere; 402 if (store == null || store.getType() != BoundingVolume.Type.Sphere) { 403 sphere = new BoundingSphere(1, new Vector3f(0, 0, 0)); 404 } else { 405 sphere = (BoundingSphere) store; 406 } 407 408 trans.mult(center, sphere.center); 409 Vector3f axes = new Vector3f(1, 1, 1); 410 trans.mult(axes, axes); 411 float ax = getMaxAxis(axes); 412 sphere.radius = FastMath.abs(ax * radius) + RADIUS_EPSILON - 1f; 413 return sphere; 414 } 415 getMaxAxis(Vector3f scale)416 private float getMaxAxis(Vector3f scale) { 417 float x = FastMath.abs(scale.x); 418 float y = FastMath.abs(scale.y); 419 float z = FastMath.abs(scale.z); 420 421 if (x >= y) { 422 if (x >= z) { 423 return x; 424 } 425 return z; 426 } 427 428 if (y >= z) { 429 return y; 430 } 431 432 return z; 433 } 434 435 /** 436 * <code>whichSide</code> takes a plane (typically provided by a view 437 * frustum) to determine which side this bound is on. 438 * 439 * @param plane 440 * the plane to check against. 441 * @return side 442 */ whichSide(Plane plane)443 public Plane.Side whichSide(Plane plane) { 444 float distance = plane.pseudoDistance(center); 445 446 if (distance <= -radius) { 447 return Plane.Side.Negative; 448 } else if (distance >= radius) { 449 return Plane.Side.Positive; 450 } else { 451 return Plane.Side.None; 452 } 453 } 454 455 /** 456 * <code>merge</code> combines this sphere with a second bounding sphere. 457 * This new sphere contains both bounding spheres and is returned. 458 * 459 * @param volume 460 * the sphere to combine with this sphere. 461 * @return a new sphere 462 */ merge(BoundingVolume volume)463 public BoundingVolume merge(BoundingVolume volume) { 464 if (volume == null) { 465 return this; 466 } 467 468 switch (volume.getType()) { 469 470 case Sphere: { 471 BoundingSphere sphere = (BoundingSphere) volume; 472 float temp_radius = sphere.getRadius(); 473 Vector3f temp_center = sphere.center; 474 BoundingSphere rVal = new BoundingSphere(); 475 return merge(temp_radius, temp_center, rVal); 476 } 477 478 case AABB: { 479 BoundingBox box = (BoundingBox) volume; 480 Vector3f radVect = new Vector3f(box.xExtent, box.yExtent, 481 box.zExtent); 482 Vector3f temp_center = box.center; 483 BoundingSphere rVal = new BoundingSphere(); 484 return merge(radVect.length(), temp_center, rVal); 485 } 486 487 // case OBB: { 488 // OrientedBoundingBox box = (OrientedBoundingBox) volume; 489 // BoundingSphere rVal = (BoundingSphere) this.clone(null); 490 // return rVal.mergeOBB(box); 491 // } 492 493 default: 494 return null; 495 496 } 497 } 498 499 /** 500 * <code>mergeLocal</code> combines this sphere with a second bounding 501 * sphere locally. Altering this sphere to contain both the original and the 502 * additional sphere volumes; 503 * 504 * @param volume 505 * the sphere to combine with this sphere. 506 * @return this 507 */ mergeLocal(BoundingVolume volume)508 public BoundingVolume mergeLocal(BoundingVolume volume) { 509 if (volume == null) { 510 return this; 511 } 512 513 switch (volume.getType()) { 514 515 case Sphere: { 516 BoundingSphere sphere = (BoundingSphere) volume; 517 float temp_radius = sphere.getRadius(); 518 Vector3f temp_center = sphere.center; 519 return merge(temp_radius, temp_center, this); 520 } 521 522 case AABB: { 523 BoundingBox box = (BoundingBox) volume; 524 TempVars vars = TempVars.get(); 525 Vector3f radVect = vars.vect1; 526 radVect.set(box.xExtent, box.yExtent, box.zExtent); 527 Vector3f temp_center = box.center; 528 float len = radVect.length(); 529 vars.release(); 530 return merge(len, temp_center, this); 531 } 532 533 // case OBB: { 534 // return mergeOBB((OrientedBoundingBox) volume); 535 // } 536 537 default: 538 return null; 539 } 540 } 541 542 // /** 543 // * Merges this sphere with the given OBB. 544 // * 545 // * @param volume 546 // * The OBB to merge. 547 // * @return This sphere, after merging. 548 // */ 549 // private BoundingSphere mergeOBB(OrientedBoundingBox volume) { 550 // // compute edge points from the obb 551 // if (!volume.correctCorners) 552 // volume.computeCorners(); 553 // _mergeBuf.rewind(); 554 // for (int i = 0; i < 8; i++) { 555 // _mergeBuf.put(volume.vectorStore[i].x); 556 // _mergeBuf.put(volume.vectorStore[i].y); 557 // _mergeBuf.put(volume.vectorStore[i].z); 558 // } 559 // 560 // // remember old radius and center 561 // float oldRadius = radius; 562 // Vector3f oldCenter = _compVect2.set( center ); 563 // 564 // // compute new radius and center from obb points 565 // computeFromPoints(_mergeBuf); 566 // Vector3f newCenter = _compVect3.set( center ); 567 // float newRadius = radius; 568 // 569 // // restore old center and radius 570 // center.set( oldCenter ); 571 // radius = oldRadius; 572 // 573 // //merge obb points result 574 // merge( newRadius, newCenter, this ); 575 // 576 // return this; 577 // } merge(float temp_radius, Vector3f temp_center, BoundingSphere rVal)578 private BoundingVolume merge(float temp_radius, Vector3f temp_center, 579 BoundingSphere rVal) { 580 TempVars vars = TempVars.get(); 581 582 Vector3f diff = temp_center.subtract(center, vars.vect1); 583 float lengthSquared = diff.lengthSquared(); 584 float radiusDiff = temp_radius - radius; 585 586 float fRDiffSqr = radiusDiff * radiusDiff; 587 588 if (fRDiffSqr >= lengthSquared) { 589 if (radiusDiff <= 0.0f) { 590 vars.release(); 591 return this; 592 } 593 594 Vector3f rCenter = rVal.center; 595 if (rCenter == null) { 596 rVal.setCenter(rCenter = new Vector3f()); 597 } 598 rCenter.set(temp_center); 599 rVal.setRadius(temp_radius); 600 vars.release(); 601 return rVal; 602 } 603 604 float length = (float) Math.sqrt(lengthSquared); 605 606 Vector3f rCenter = rVal.center; 607 if (rCenter == null) { 608 rVal.setCenter(rCenter = new Vector3f()); 609 } 610 if (length > RADIUS_EPSILON) { 611 float coeff = (length + radiusDiff) / (2.0f * length); 612 rCenter.set(center.addLocal(diff.multLocal(coeff))); 613 } else { 614 rCenter.set(center); 615 } 616 617 rVal.setRadius(0.5f * (length + radius + temp_radius)); 618 vars.release(); 619 return rVal; 620 } 621 622 /** 623 * <code>clone</code> creates a new BoundingSphere object containing the 624 * same data as this one. 625 * 626 * @param store 627 * where to store the cloned information. if null or wrong class, 628 * a new store is created. 629 * @return the new BoundingSphere 630 */ clone(BoundingVolume store)631 public BoundingVolume clone(BoundingVolume store) { 632 if (store != null && store.getType() == Type.Sphere) { 633 BoundingSphere rVal = (BoundingSphere) store; 634 if (null == rVal.center) { 635 rVal.center = new Vector3f(); 636 } 637 rVal.center.set(center); 638 rVal.radius = radius; 639 rVal.checkPlane = checkPlane; 640 return rVal; 641 } 642 643 return new BoundingSphere(radius, 644 (center != null ? (Vector3f) center.clone() : null)); 645 } 646 647 /** 648 * <code>toString</code> returns the string representation of this object. 649 * The form is: "Radius: RRR.SSSS Center: <Vector>". 650 * 651 * @return the string representation of this. 652 */ 653 @Override toString()654 public String toString() { 655 return getClass().getSimpleName() + " [Radius: " + radius + " Center: " 656 + center + "]"; 657 } 658 659 /* 660 * (non-Javadoc) 661 * 662 * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume) 663 */ intersects(BoundingVolume bv)664 public boolean intersects(BoundingVolume bv) { 665 return bv.intersectsSphere(this); 666 } 667 668 /* 669 * (non-Javadoc) 670 * 671 * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere) 672 */ intersectsSphere(BoundingSphere bs)673 public boolean intersectsSphere(BoundingSphere bs) { 674 assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); 675 676 TempVars vars = TempVars.get(); 677 678 Vector3f diff = center.subtract(bs.center, vars.vect1); 679 float rsum = getRadius() + bs.getRadius(); 680 boolean eq = (diff.dot(diff) <= rsum * rsum); 681 vars.release(); 682 return eq; 683 } 684 685 /* 686 * (non-Javadoc) 687 * 688 * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox) 689 */ intersectsBoundingBox(BoundingBox bb)690 public boolean intersectsBoundingBox(BoundingBox bb) { 691 assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); 692 693 if (FastMath.abs(bb.center.x - center.x) < getRadius() 694 + bb.xExtent 695 && FastMath.abs(bb.center.y - center.y) < getRadius() 696 + bb.yExtent 697 && FastMath.abs(bb.center.z - center.z) < getRadius() 698 + bb.zExtent) { 699 return true; 700 } 701 702 return false; 703 } 704 705 /* 706 * (non-Javadoc) 707 * 708 * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox) 709 */ 710 // public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) { 711 // return obb.intersectsSphere(this); 712 // } 713 714 /* 715 * (non-Javadoc) 716 * 717 * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray) 718 */ intersects(Ray ray)719 public boolean intersects(Ray ray) { 720 assert Vector3f.isValidVector(center); 721 722 TempVars vars = TempVars.get(); 723 724 Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal(center); 725 float radiusSquared = getRadius() * getRadius(); 726 float a = diff.dot(diff) - radiusSquared; 727 if (a <= 0.0) { 728 // in sphere 729 return true; 730 } 731 732 // outside sphere 733 float b = ray.getDirection().dot(diff); 734 vars.release(); 735 if (b >= 0.0) { 736 return false; 737 } 738 return b * b >= a; 739 } 740 741 /* 742 * (non-Javadoc) 743 * 744 * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray) 745 */ collideWithRay(Ray ray, CollisionResults results)746 private int collideWithRay(Ray ray, CollisionResults results) { 747 TempVars vars = TempVars.get(); 748 749 Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal( 750 center); 751 float a = diff.dot(diff) - (getRadius() * getRadius()); 752 float a1, discr, root; 753 if (a <= 0.0) { 754 // inside sphere 755 a1 = ray.direction.dot(diff); 756 discr = (a1 * a1) - a; 757 root = FastMath.sqrt(discr); 758 759 float distance = root - a1; 760 Vector3f point = new Vector3f(ray.direction).multLocal(distance).addLocal(ray.origin); 761 762 CollisionResult result = new CollisionResult(point, distance); 763 results.addCollision(result); 764 vars.release(); 765 return 1; 766 } 767 768 a1 = ray.direction.dot(diff); 769 vars.release(); 770 if (a1 >= 0.0) { 771 return 0; 772 } 773 774 discr = a1 * a1 - a; 775 if (discr < 0.0) { 776 return 0; 777 } else if (discr >= FastMath.ZERO_TOLERANCE) { 778 root = FastMath.sqrt(discr); 779 float dist = -a1 - root; 780 Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin); 781 results.addCollision(new CollisionResult(point, dist)); 782 783 dist = -a1 + root; 784 point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin); 785 results.addCollision(new CollisionResult(point, dist)); 786 return 2; 787 } else { 788 float dist = -a1; 789 Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin); 790 results.addCollision(new CollisionResult(point, dist)); 791 return 1; 792 } 793 } 794 collideWith(Collidable other, CollisionResults results)795 public int collideWith(Collidable other, CollisionResults results) { 796 if (other instanceof Ray) { 797 Ray ray = (Ray) other; 798 return collideWithRay(ray, results); 799 } else if (other instanceof Triangle){ 800 Triangle t = (Triangle) other; 801 802 float r2 = radius * radius; 803 float d1 = center.distanceSquared(t.get1()); 804 float d2 = center.distanceSquared(t.get2()); 805 float d3 = center.distanceSquared(t.get3()); 806 807 if (d1 <= r2 || d2 <= r2 || d3 <= r2) { 808 CollisionResult r = new CollisionResult(); 809 r.setDistance(FastMath.sqrt(Math.min(Math.min(d1, d2), d3)) - radius); 810 results.addCollision(r); 811 return 1; 812 } 813 814 return 0; 815 } else { 816 throw new UnsupportedCollisionException(); 817 } 818 } 819 820 @Override contains(Vector3f point)821 public boolean contains(Vector3f point) { 822 return center.distanceSquared(point) < (getRadius() * getRadius()); 823 } 824 825 @Override intersects(Vector3f point)826 public boolean intersects(Vector3f point) { 827 return center.distanceSquared(point) <= (getRadius() * getRadius()); 828 } 829 distanceToEdge(Vector3f point)830 public float distanceToEdge(Vector3f point) { 831 return center.distance(point) - radius; 832 } 833 834 @Override write(JmeExporter e)835 public void write(JmeExporter e) throws IOException { 836 super.write(e); 837 try { 838 e.getCapsule(this).write(radius, "radius", 0); 839 } catch (IOException ex) { 840 logger.logp(Level.SEVERE, this.getClass().toString(), "write(JMEExporter)", "Exception", ex); 841 } 842 } 843 844 @Override read(JmeImporter e)845 public void read(JmeImporter e) throws IOException { 846 super.read(e); 847 try { 848 radius = e.getCapsule(this).readFloat("radius", 0); 849 } catch (IOException ex) { 850 logger.logp(Level.SEVERE, this.getClass().toString(), "read(JMEImporter)", "Exception", ex); 851 } 852 } 853 854 @Override getVolume()855 public float getVolume() { 856 return 4 * FastMath.ONE_THIRD * FastMath.PI * radius * radius * radius; 857 } 858 }