aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/bounding/BoundingSphere.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/bounding/BoundingSphere.java')
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingSphere.java858
1 files changed, 858 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/bounding/BoundingSphere.java b/engine/src/core/com/jme3/bounding/BoundingSphere.java
new file mode 100644
index 0000000..12be035
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingSphere.java
@@ -0,0 +1,858 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.bounding;
+
+import com.jme3.collision.Collidable;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.collision.UnsupportedCollisionException;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>BoundingSphere</code> defines a sphere that defines a container for a
+ * group of vertices of a particular piece of geometry. This sphere defines a
+ * radius and a center. <br>
+ * <br>
+ * A typical usage is to allow the class define the center and radius by calling
+ * either <code>containAABB</code> or <code>averagePoints</code>. A call to
+ * <code>computeFramePoint</code> in turn calls <code>containAABB</code>.
+ *
+ * @author Mark Powell
+ * @version $Id: BoundingSphere.java,v 1.59 2007/08/17 10:34:26 rherlitz Exp $
+ */
+public class BoundingSphere extends BoundingVolume {
+
+ private static final Logger logger =
+ Logger.getLogger(BoundingSphere.class.getName());
+ float radius;
+ private static final float RADIUS_EPSILON = 1f + 0.00001f;
+
+ /**
+ * Default contstructor instantiates a new <code>BoundingSphere</code>
+ * object.
+ */
+ public BoundingSphere() {
+ }
+
+ /**
+ * Constructor instantiates a new <code>BoundingSphere</code> object.
+ *
+ * @param r
+ * the radius of the sphere.
+ * @param c
+ * the center of the sphere.
+ */
+ public BoundingSphere(float r, Vector3f c) {
+ this.center.set(c);
+ this.radius = r;
+ }
+
+ public Type getType() {
+ return Type.Sphere;
+ }
+
+ /**
+ * <code>getRadius</code> returns the radius of the bounding sphere.
+ *
+ * @return the radius of the bounding sphere.
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+ /**
+ * <code>setRadius</code> sets the radius of this bounding sphere.
+ *
+ * @param radius
+ * the new radius of the bounding sphere.
+ */
+ public void setRadius(float radius) {
+ this.radius = radius;
+ }
+
+ /**
+ * <code>computeFromPoints</code> creates a new Bounding Sphere from a
+ * given set of points. It uses the <code>calcWelzl</code> method as
+ * default.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public void computeFromPoints(FloatBuffer points) {
+ calcWelzl(points);
+ }
+
+ /**
+ * <code>computeFromTris</code> creates a new Bounding Box from a given
+ * set of triangles. It is used in OBBTree calculations.
+ *
+ * @param tris
+ * @param start
+ * @param end
+ */
+ public void computeFromTris(Triangle[] tris, int start, int end) {
+ if (end - start <= 0) {
+ return;
+ }
+
+ Vector3f[] vertList = new Vector3f[(end - start) * 3];
+
+ int count = 0;
+ for (int i = start; i < end; i++) {
+ vertList[count++] = tris[i].get(0);
+ vertList[count++] = tris[i].get(1);
+ vertList[count++] = tris[i].get(2);
+ }
+ averagePoints(vertList);
+ }
+//
+// /**
+// * <code>computeFromTris</code> creates a new Bounding Box from a given
+// * set of triangles. It is used in OBBTree calculations.
+// *
+// * @param indices
+// * @param mesh
+// * @param start
+// * @param end
+// */
+// public void computeFromTris(int[] indices, Mesh mesh, int start, int end) {
+// if (end - start <= 0) {
+// return;
+// }
+//
+// Vector3f[] vertList = new Vector3f[(end - start) * 3];
+//
+// int count = 0;
+// for (int i = start; i < end; i++) {
+// mesh.getTriangle(indices[i], verts);
+// vertList[count++] = new Vector3f(verts[0]);
+// vertList[count++] = new Vector3f(verts[1]);
+// vertList[count++] = new Vector3f(verts[2]);
+// }
+//
+// averagePoints(vertList);
+// }
+
+ /**
+ * Calculates a minimum bounding sphere for the set of points. The algorithm
+ * was originally found in C++ at
+ * <p><a href="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1">
+ * http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1</a><br><strong>broken link</strong></p>
+ * <p>and translated to java by Cep21</p>
+ *
+ * @param points
+ * The points to calculate the minimum bounds from.
+ */
+ public void calcWelzl(FloatBuffer points) {
+ if (center == null) {
+ center = new Vector3f();
+ }
+ FloatBuffer buf = BufferUtils.createFloatBuffer(points.limit());
+ points.rewind();
+ buf.put(points);
+ buf.flip();
+ recurseMini(buf, buf.limit() / 3, 0, 0);
+ }
+
+ /**
+ * Used from calcWelzl. This function recurses to calculate a minimum
+ * bounding sphere a few points at a time.
+ *
+ * @param points
+ * The array of points to look through.
+ * @param p
+ * The size of the list to be used.
+ * @param b
+ * The number of points currently considering to include with the
+ * sphere.
+ * @param ap
+ * A variable simulating pointer arithmatic from C++, and offset
+ * in <code>points</code>.
+ */
+ private void recurseMini(FloatBuffer points, int p, int b, int ap) {
+ //TempVars vars = TempVars.get();
+
+ Vector3f tempA = new Vector3f(); //vars.vect1;
+ Vector3f tempB = new Vector3f(); //vars.vect2;
+ Vector3f tempC = new Vector3f(); //vars.vect3;
+ Vector3f tempD = new Vector3f(); //vars.vect4;
+
+ switch (b) {
+ case 0:
+ this.radius = 0;
+ this.center.set(0, 0, 0);
+ break;
+ case 1:
+ this.radius = 1f - RADIUS_EPSILON;
+ BufferUtils.populateFromBuffer(center, points, ap - 1);
+ break;
+ case 2:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ setSphere(tempA, tempB);
+ break;
+ case 3:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ BufferUtils.populateFromBuffer(tempC, points, ap - 3);
+ setSphere(tempA, tempB, tempC);
+ break;
+ case 4:
+ BufferUtils.populateFromBuffer(tempA, points, ap - 1);
+ BufferUtils.populateFromBuffer(tempB, points, ap - 2);
+ BufferUtils.populateFromBuffer(tempC, points, ap - 3);
+ BufferUtils.populateFromBuffer(tempD, points, ap - 4);
+ setSphere(tempA, tempB, tempC, tempD);
+ //vars.release();
+ return;
+ }
+ for (int i = 0; i < p; i++) {
+ BufferUtils.populateFromBuffer(tempA, points, i + ap);
+ if (tempA.distanceSquared(center) - (radius * radius) > RADIUS_EPSILON - 1f) {
+ for (int j = i; j > 0; j--) {
+ BufferUtils.populateFromBuffer(tempB, points, j + ap);
+ BufferUtils.populateFromBuffer(tempC, points, j - 1 + ap);
+ BufferUtils.setInBuffer(tempC, points, j + ap);
+ BufferUtils.setInBuffer(tempB, points, j - 1 + ap);
+ }
+ recurseMini(points, i, b + 1, ap + 1);
+ }
+ }
+ //vars.release();
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 4 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @param B
+ * The 3rd point inside the sphere.
+ * @param C
+ * The 4th point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C) {
+ Vector3f a = A.subtract(O);
+ Vector3f b = B.subtract(O);
+ Vector3f c = C.subtract(O);
+
+ float Denominator = 2.0f * (a.x * (b.y * c.z - c.y * b.z) - b.x
+ * (a.y * c.z - c.y * a.z) + c.x * (a.y * b.z - b.y * a.z));
+ if (Denominator == 0) {
+ center.set(0, 0, 0);
+ radius = 0;
+ } else {
+ Vector3f o = a.cross(b).multLocal(c.lengthSquared()).addLocal(
+ c.cross(a).multLocal(b.lengthSquared())).addLocal(
+ b.cross(c).multLocal(a.lengthSquared())).divideLocal(
+ Denominator);
+
+ radius = o.length() * RADIUS_EPSILON;
+ O.add(o, center);
+ }
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 3 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @param B
+ * The 3rd point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A, Vector3f B) {
+ Vector3f a = A.subtract(O);
+ Vector3f b = B.subtract(O);
+ Vector3f acrossB = a.cross(b);
+
+ float Denominator = 2.0f * acrossB.dot(acrossB);
+
+ if (Denominator == 0) {
+ center.set(0, 0, 0);
+ radius = 0;
+ } else {
+
+ Vector3f o = acrossB.cross(a).multLocal(b.lengthSquared()).addLocal(b.cross(acrossB).multLocal(a.lengthSquared())).divideLocal(Denominator);
+ radius = o.length() * RADIUS_EPSILON;
+ O.add(o, center);
+ }
+ }
+
+ /**
+ * Calculates the minimum bounding sphere of 2 points. Used in welzl's
+ * algorithm.
+ *
+ * @param O
+ * The 1st point inside the sphere.
+ * @param A
+ * The 2nd point inside the sphere.
+ * @see #calcWelzl(java.nio.FloatBuffer)
+ */
+ private void setSphere(Vector3f O, Vector3f A) {
+ radius = FastMath.sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y)
+ * (A.y - O.y) + (A.z - O.z) * (A.z - O.z)) / 4f) + RADIUS_EPSILON - 1f;
+ center.interpolate(O, A, .5f);
+ }
+
+ /**
+ * <code>averagePoints</code> selects the sphere center to be the average
+ * of the points and the sphere radius to be the smallest value to enclose
+ * all points.
+ *
+ * @param points
+ * the list of points to contain.
+ */
+ public void averagePoints(Vector3f[] points) {
+ logger.info("Bounding Sphere calculated using average points.");
+ center = points[0];
+
+ for (int i = 1; i < points.length; i++) {
+ center.addLocal(points[i]);
+ }
+
+ float quantity = 1.0f / points.length;
+ center.multLocal(quantity);
+
+ float maxRadiusSqr = 0;
+ for (int i = 0; i < points.length; i++) {
+ Vector3f diff = points[i].subtract(center);
+ float radiusSqr = diff.lengthSquared();
+ if (radiusSqr > maxRadiusSqr) {
+ maxRadiusSqr = radiusSqr;
+ }
+ }
+
+ radius = (float) Math.sqrt(maxRadiusSqr) + RADIUS_EPSILON - 1f;
+
+ }
+
+ /**
+ * <code>transform</code> modifies the center of the sphere to reflect the
+ * change made via a rotation, translation and scale.
+ *
+ * @param trans
+ * the transform to apply
+ * @param store
+ * sphere to store result in
+ * @return BoundingVolume
+ * @return ref
+ */
+ public BoundingVolume transform(Transform trans, BoundingVolume store) {
+ BoundingSphere sphere;
+ if (store == null || store.getType() != BoundingVolume.Type.Sphere) {
+ sphere = new BoundingSphere(1, new Vector3f(0, 0, 0));
+ } else {
+ sphere = (BoundingSphere) store;
+ }
+
+ center.mult(trans.getScale(), sphere.center);
+ trans.getRotation().mult(sphere.center, sphere.center);
+ sphere.center.addLocal(trans.getTranslation());
+ sphere.radius = FastMath.abs(getMaxAxis(trans.getScale()) * radius) + RADIUS_EPSILON - 1f;
+ return sphere;
+ }
+
+ public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
+ BoundingSphere sphere;
+ if (store == null || store.getType() != BoundingVolume.Type.Sphere) {
+ sphere = new BoundingSphere(1, new Vector3f(0, 0, 0));
+ } else {
+ sphere = (BoundingSphere) store;
+ }
+
+ trans.mult(center, sphere.center);
+ Vector3f axes = new Vector3f(1, 1, 1);
+ trans.mult(axes, axes);
+ float ax = getMaxAxis(axes);
+ sphere.radius = FastMath.abs(ax * radius) + RADIUS_EPSILON - 1f;
+ return sphere;
+ }
+
+ private float getMaxAxis(Vector3f scale) {
+ float x = FastMath.abs(scale.x);
+ float y = FastMath.abs(scale.y);
+ float z = FastMath.abs(scale.z);
+
+ if (x >= y) {
+ if (x >= z) {
+ return x;
+ }
+ return z;
+ }
+
+ if (y >= z) {
+ return y;
+ }
+
+ return z;
+ }
+
+ /**
+ * <code>whichSide</code> takes a plane (typically provided by a view
+ * frustum) to determine which side this bound is on.
+ *
+ * @param plane
+ * the plane to check against.
+ * @return side
+ */
+ public Plane.Side whichSide(Plane plane) {
+ float distance = plane.pseudoDistance(center);
+
+ if (distance <= -radius) {
+ return Plane.Side.Negative;
+ } else if (distance >= radius) {
+ return Plane.Side.Positive;
+ } else {
+ return Plane.Side.None;
+ }
+ }
+
+ /**
+ * <code>merge</code> combines this sphere with a second bounding sphere.
+ * This new sphere contains both bounding spheres and is returned.
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return a new sphere
+ */
+ public BoundingVolume merge(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+
+ case Sphere: {
+ BoundingSphere sphere = (BoundingSphere) volume;
+ float temp_radius = sphere.getRadius();
+ Vector3f temp_center = sphere.center;
+ BoundingSphere rVal = new BoundingSphere();
+ return merge(temp_radius, temp_center, rVal);
+ }
+
+ case AABB: {
+ BoundingBox box = (BoundingBox) volume;
+ Vector3f radVect = new Vector3f(box.xExtent, box.yExtent,
+ box.zExtent);
+ Vector3f temp_center = box.center;
+ BoundingSphere rVal = new BoundingSphere();
+ return merge(radVect.length(), temp_center, rVal);
+ }
+
+// case OBB: {
+// OrientedBoundingBox box = (OrientedBoundingBox) volume;
+// BoundingSphere rVal = (BoundingSphere) this.clone(null);
+// return rVal.mergeOBB(box);
+// }
+
+ default:
+ return null;
+
+ }
+ }
+
+ /**
+ * <code>mergeLocal</code> combines this sphere with a second bounding
+ * sphere locally. Altering this sphere to contain both the original and the
+ * additional sphere volumes;
+ *
+ * @param volume
+ * the sphere to combine with this sphere.
+ * @return this
+ */
+ public BoundingVolume mergeLocal(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+
+ case Sphere: {
+ BoundingSphere sphere = (BoundingSphere) volume;
+ float temp_radius = sphere.getRadius();
+ Vector3f temp_center = sphere.center;
+ return merge(temp_radius, temp_center, this);
+ }
+
+ case AABB: {
+ BoundingBox box = (BoundingBox) volume;
+ TempVars vars = TempVars.get();
+ Vector3f radVect = vars.vect1;
+ radVect.set(box.xExtent, box.yExtent, box.zExtent);
+ Vector3f temp_center = box.center;
+ float len = radVect.length();
+ vars.release();
+ return merge(len, temp_center, this);
+ }
+
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+
+ default:
+ return null;
+ }
+ }
+
+// /**
+// * Merges this sphere with the given OBB.
+// *
+// * @param volume
+// * The OBB to merge.
+// * @return This sphere, after merging.
+// */
+// private BoundingSphere mergeOBB(OrientedBoundingBox volume) {
+// // compute edge points from the obb
+// if (!volume.correctCorners)
+// volume.computeCorners();
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(volume.vectorStore[i].x);
+// _mergeBuf.put(volume.vectorStore[i].y);
+// _mergeBuf.put(volume.vectorStore[i].z);
+// }
+//
+// // remember old radius and center
+// float oldRadius = radius;
+// Vector3f oldCenter = _compVect2.set( center );
+//
+// // compute new radius and center from obb points
+// computeFromPoints(_mergeBuf);
+// Vector3f newCenter = _compVect3.set( center );
+// float newRadius = radius;
+//
+// // restore old center and radius
+// center.set( oldCenter );
+// radius = oldRadius;
+//
+// //merge obb points result
+// merge( newRadius, newCenter, this );
+//
+// return this;
+// }
+ private BoundingVolume merge(float temp_radius, Vector3f temp_center,
+ BoundingSphere rVal) {
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = temp_center.subtract(center, vars.vect1);
+ float lengthSquared = diff.lengthSquared();
+ float radiusDiff = temp_radius - radius;
+
+ float fRDiffSqr = radiusDiff * radiusDiff;
+
+ if (fRDiffSqr >= lengthSquared) {
+ if (radiusDiff <= 0.0f) {
+ vars.release();
+ return this;
+ }
+
+ Vector3f rCenter = rVal.center;
+ if (rCenter == null) {
+ rVal.setCenter(rCenter = new Vector3f());
+ }
+ rCenter.set(temp_center);
+ rVal.setRadius(temp_radius);
+ vars.release();
+ return rVal;
+ }
+
+ float length = (float) Math.sqrt(lengthSquared);
+
+ Vector3f rCenter = rVal.center;
+ if (rCenter == null) {
+ rVal.setCenter(rCenter = new Vector3f());
+ }
+ if (length > RADIUS_EPSILON) {
+ float coeff = (length + radiusDiff) / (2.0f * length);
+ rCenter.set(center.addLocal(diff.multLocal(coeff)));
+ } else {
+ rCenter.set(center);
+ }
+
+ rVal.setRadius(0.5f * (length + radius + temp_radius));
+ vars.release();
+ return rVal;
+ }
+
+ /**
+ * <code>clone</code> creates a new BoundingSphere object containing the
+ * same data as this one.
+ *
+ * @param store
+ * where to store the cloned information. if null or wrong class,
+ * a new store is created.
+ * @return the new BoundingSphere
+ */
+ public BoundingVolume clone(BoundingVolume store) {
+ if (store != null && store.getType() == Type.Sphere) {
+ BoundingSphere rVal = (BoundingSphere) store;
+ if (null == rVal.center) {
+ rVal.center = new Vector3f();
+ }
+ rVal.center.set(center);
+ rVal.radius = radius;
+ rVal.checkPlane = checkPlane;
+ return rVal;
+ }
+
+ return new BoundingSphere(radius,
+ (center != null ? (Vector3f) center.clone() : null));
+ }
+
+ /**
+ * <code>toString</code> returns the string representation of this object.
+ * The form is: "Radius: RRR.SSSS Center: <Vector>".
+ *
+ * @return the string representation of this.
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [Radius: " + radius + " Center: "
+ + center + "]";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume)
+ */
+ public boolean intersects(BoundingVolume bv) {
+ return bv.intersectsSphere(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
+ */
+ public boolean intersectsSphere(BoundingSphere bs) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = center.subtract(bs.center, vars.vect1);
+ float rsum = getRadius() + bs.getRadius();
+ boolean eq = (diff.dot(diff) <= rsum * rsum);
+ vars.release();
+ return eq;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
+ */
+ public boolean intersectsBoundingBox(BoundingBox bb) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
+
+ if (FastMath.abs(bb.center.x - center.x) < getRadius()
+ + bb.xExtent
+ && FastMath.abs(bb.center.y - center.y) < getRadius()
+ + bb.yExtent
+ && FastMath.abs(bb.center.z - center.z) < getRadius()
+ + bb.zExtent) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
+ */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// return obb.intersectsSphere(this);
+// }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
+ */
+ public boolean intersects(Ray ray) {
+ assert Vector3f.isValidVector(center);
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal(center);
+ float radiusSquared = getRadius() * getRadius();
+ float a = diff.dot(diff) - radiusSquared;
+ if (a <= 0.0) {
+ // in sphere
+ return true;
+ }
+
+ // outside sphere
+ float b = ray.getDirection().dot(diff);
+ vars.release();
+ if (b >= 0.0) {
+ return false;
+ }
+ return b * b >= a;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
+ */
+ private int collideWithRay(Ray ray, CollisionResults results) {
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal(
+ center);
+ float a = diff.dot(diff) - (getRadius() * getRadius());
+ float a1, discr, root;
+ if (a <= 0.0) {
+ // inside sphere
+ a1 = ray.direction.dot(diff);
+ discr = (a1 * a1) - a;
+ root = FastMath.sqrt(discr);
+
+ float distance = root - a1;
+ Vector3f point = new Vector3f(ray.direction).multLocal(distance).addLocal(ray.origin);
+
+ CollisionResult result = new CollisionResult(point, distance);
+ results.addCollision(result);
+ vars.release();
+ return 1;
+ }
+
+ a1 = ray.direction.dot(diff);
+ vars.release();
+ if (a1 >= 0.0) {
+ return 0;
+ }
+
+ discr = a1 * a1 - a;
+ if (discr < 0.0) {
+ return 0;
+ } else if (discr >= FastMath.ZERO_TOLERANCE) {
+ root = FastMath.sqrt(discr);
+ float dist = -a1 - root;
+ Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+
+ dist = -a1 + root;
+ point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+ return 2;
+ } else {
+ float dist = -a1;
+ Vector3f point = new Vector3f(ray.direction).multLocal(dist).addLocal(ray.origin);
+ results.addCollision(new CollisionResult(point, dist));
+ return 1;
+ }
+ }
+
+ public int collideWith(Collidable other, CollisionResults results) {
+ if (other instanceof Ray) {
+ Ray ray = (Ray) other;
+ return collideWithRay(ray, results);
+ } else if (other instanceof Triangle){
+ Triangle t = (Triangle) other;
+
+ float r2 = radius * radius;
+ float d1 = center.distanceSquared(t.get1());
+ float d2 = center.distanceSquared(t.get2());
+ float d3 = center.distanceSquared(t.get3());
+
+ if (d1 <= r2 || d2 <= r2 || d3 <= r2) {
+ CollisionResult r = new CollisionResult();
+ r.setDistance(FastMath.sqrt(Math.min(Math.min(d1, d2), d3)) - radius);
+ results.addCollision(r);
+ return 1;
+ }
+
+ return 0;
+ } else {
+ throw new UnsupportedCollisionException();
+ }
+ }
+
+ @Override
+ public boolean contains(Vector3f point) {
+ return center.distanceSquared(point) < (getRadius() * getRadius());
+ }
+
+ @Override
+ public boolean intersects(Vector3f point) {
+ return center.distanceSquared(point) <= (getRadius() * getRadius());
+ }
+
+ public float distanceToEdge(Vector3f point) {
+ return center.distance(point) - radius;
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ try {
+ e.getCapsule(this).write(radius, "radius", 0);
+ } catch (IOException ex) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "write(JMEExporter)", "Exception", ex);
+ }
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ try {
+ radius = e.getCapsule(this).readFloat("radius", 0);
+ } catch (IOException ex) {
+ logger.logp(Level.SEVERE, this.getClass().toString(), "read(JMEImporter)", "Exception", ex);
+ }
+ }
+
+ @Override
+ public float getVolume() {
+ return 4 * FastMath.ONE_THIRD * FastMath.PI * radius * radius * radius;
+ }
+} \ No newline at end of file