Home | History | Annotate | Download | only in bounding
      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      */
     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      */
     82     public BoundingSphere(float r, Vector3f c) {
     83         this.center.set(c);
     84         this.radius = r;
     85     }
     86 
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    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 //    }
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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
    821     public boolean contains(Vector3f point) {
    822         return center.distanceSquared(point) < (getRadius() * getRadius());
    823     }
    824 
    825     @Override
    826     public boolean intersects(Vector3f point) {
    827         return center.distanceSquared(point) <= (getRadius() * getRadius());
    828     }
    829 
    830     public float distanceToEdge(Vector3f point) {
    831         return center.distance(point) - radius;
    832     }
    833 
    834     @Override
    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
    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
    855     public float getVolume() {
    856         return 4 * FastMath.ONE_THIRD * FastMath.PI * radius * radius * radius;
    857     }
    858 }