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 */
32package com.jme3.bounding;
33
34import com.jme3.collision.Collidable;
35import com.jme3.collision.CollisionResult;
36import com.jme3.collision.CollisionResults;
37import com.jme3.collision.UnsupportedCollisionException;
38import com.jme3.export.JmeExporter;
39import com.jme3.export.JmeImporter;
40import com.jme3.math.*;
41import com.jme3.util.BufferUtils;
42import com.jme3.util.TempVars;
43import java.io.IOException;
44import java.nio.FloatBuffer;
45import java.util.logging.Level;
46import 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 */
60public 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}