• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }