aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/bounding
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/core/com/jme3/bounding
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
Diffstat (limited to 'engine/src/core/com/jme3/bounding')
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingBox.java977
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingSphere.java858
-rw-r--r--engine/src/core/com/jme3/bounding/BoundingVolume.java329
-rw-r--r--engine/src/core/com/jme3/bounding/Intersection.java284
-rw-r--r--engine/src/core/com/jme3/bounding/OrientedBoundingBox.java1522
5 files changed, 3970 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/bounding/BoundingBox.java b/engine/src/core/com/jme3/bounding/BoundingBox.java
new file mode 100644
index 0000000..bc11411
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingBox.java
@@ -0,0 +1,977 @@
+/*
+ * 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.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.*;
+import com.jme3.scene.Mesh;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+//import com.jme.scene.TriMesh;
+
+/**
+ * <code>BoundingBox</code> defines an axis-aligned cube that defines a
+ * container for a group of vertices of a particular piece of geometry. This box
+ * defines a center and extents from that center along the x, y and z axis. <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 Joshua Slack
+ * @version $Id: BoundingBox.java,v 1.50 2007/09/22 16:46:35 irrisor Exp $
+ */
+public class BoundingBox extends BoundingVolume {
+
+ float xExtent, yExtent, zExtent;
+
+ /**
+ * Default constructor instantiates a new <code>BoundingBox</code>
+ * object.
+ */
+ public BoundingBox() {
+ }
+
+ /**
+ * Contstructor instantiates a new <code>BoundingBox</code> object with
+ * given specs.
+ */
+ public BoundingBox(Vector3f c, float x, float y, float z) {
+ this.center.set(c);
+ this.xExtent = x;
+ this.yExtent = y;
+ this.zExtent = z;
+ }
+
+ public BoundingBox(BoundingBox source) {
+ this.center.set(source.center);
+ this.xExtent = source.xExtent;
+ this.yExtent = source.yExtent;
+ this.zExtent = source.zExtent;
+ }
+
+ public BoundingBox(Vector3f min, Vector3f max) {
+ setMinMax(min, max);
+ }
+
+ public Type getType() {
+ return Type.AABB;
+ }
+
+ /**
+ * <code>computeFromPoints</code> creates a new Bounding Box from a given
+ * set of points. It uses the <code>containAABB</code> method as default.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public void computeFromPoints(FloatBuffer points) {
+ containAABB(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;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f min = vars.vect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+ Vector3f max = vars.vect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+
+ Vector3f point;
+ for (int i = start; i < end; i++) {
+ point = tris[i].get(0);
+ checkMinMax(min, max, point);
+ point = tris[i].get(1);
+ checkMinMax(min, max, point);
+ point = tris[i].get(2);
+ checkMinMax(min, max, point);
+ }
+
+ center.set(min.addLocal(max));
+ center.multLocal(0.5f);
+
+ xExtent = max.x - center.x;
+ yExtent = max.y - center.y;
+ zExtent = max.z - center.z;
+
+ vars.release();
+ }
+
+ public void computeFromTris(int[] indices, Mesh mesh, int start, int end) {
+ if (end - start <= 0) {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ Vector3f vect1 = vars.vect1;
+ Vector3f vect2 = vars.vect2;
+ Triangle triangle = vars.triangle;
+
+ Vector3f min = vect1.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+ Vector3f max = vect2.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ Vector3f point;
+
+ for (int i = start; i < end; i++) {
+ mesh.getTriangle(indices[i], triangle);
+ point = triangle.get(0);
+ checkMinMax(min, max, point);
+ point = triangle.get(1);
+ checkMinMax(min, max, point);
+ point = triangle.get(2);
+ checkMinMax(min, max, point);
+ }
+
+ center.set(min.addLocal(max));
+ center.multLocal(0.5f);
+
+ xExtent = max.x - center.x;
+ yExtent = max.y - center.y;
+ zExtent = max.z - center.z;
+
+ vars.release();
+ }
+
+ public static void checkMinMax(Vector3f min, Vector3f max, Vector3f point) {
+ if (point.x < min.x) {
+ min.x = point.x;
+ }
+ if (point.x > max.x) {
+ max.x = point.x;
+ }
+ if (point.y < min.y) {
+ min.y = point.y;
+ }
+ if (point.y > max.y) {
+ max.y = point.y;
+ }
+ if (point.z < min.z) {
+ min.z = point.z;
+ }
+ if (point.z > max.z) {
+ max.z = point.z;
+ }
+ }
+
+ /**
+ * <code>containAABB</code> creates a minimum-volume axis-aligned bounding
+ * box of the points, then selects the smallest enclosing sphere of the box
+ * with the sphere centered at the boxes center.
+ *
+ * @param points
+ * the list of points.
+ */
+ public void containAABB(FloatBuffer points) {
+ if (points == null) {
+ return;
+ }
+
+ points.rewind();
+ if (points.remaining() <= 2) // we need at least a 3 float vector
+ {
+ return;
+ }
+
+ TempVars vars = TempVars.get();
+
+ BufferUtils.populateFromBuffer(vars.vect1, points, 0);
+ float minX = vars.vect1.x, minY = vars.vect1.y, minZ = vars.vect1.z;
+ float maxX = vars.vect1.x, maxY = vars.vect1.y, maxZ = vars.vect1.z;
+
+ for (int i = 1, len = points.remaining() / 3; i < len; i++) {
+ BufferUtils.populateFromBuffer(vars.vect1, points, i);
+
+ if (vars.vect1.x < minX) {
+ minX = vars.vect1.x;
+ } else if (vars.vect1.x > maxX) {
+ maxX = vars.vect1.x;
+ }
+
+ if (vars.vect1.y < minY) {
+ minY = vars.vect1.y;
+ } else if (vars.vect1.y > maxY) {
+ maxY = vars.vect1.y;
+ }
+
+ if (vars.vect1.z < minZ) {
+ minZ = vars.vect1.z;
+ } else if (vars.vect1.z > maxZ) {
+ maxZ = vars.vect1.z;
+ }
+ }
+
+ vars.release();
+
+ center.set(minX + maxX, minY + maxY, minZ + maxZ);
+ center.multLocal(0.5f);
+
+ xExtent = maxX - center.x;
+ yExtent = maxY - center.y;
+ zExtent = maxZ - center.z;
+ }
+
+ /**
+ * <code>transform</code> modifies the center of the box to reflect the
+ * change made via a rotation, translation and scale.
+ *
+ * @param trans
+ * the transform to apply
+ * @param store
+ * box to store result in
+ */
+ public BoundingVolume transform(Transform trans, BoundingVolume store) {
+
+ BoundingBox box;
+ if (store == null || store.getType() != Type.AABB) {
+ box = new BoundingBox();
+ } else {
+ box = (BoundingBox) store;
+ }
+
+ center.mult(trans.getScale(), box.center);
+ trans.getRotation().mult(box.center, box.center);
+ box.center.addLocal(trans.getTranslation());
+
+ TempVars vars = TempVars.get();
+
+ Matrix3f transMatrix = vars.tempMat3;
+ transMatrix.set(trans.getRotation());
+ // Make the rotation matrix all positive to get the maximum x/y/z extent
+ transMatrix.absoluteLocal();
+
+ Vector3f scale = trans.getScale();
+ vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z);
+ transMatrix.mult(vars.vect1, vars.vect2);
+ // Assign the biggest rotations after scales.
+ box.xExtent = FastMath.abs(vars.vect2.getX());
+ box.yExtent = FastMath.abs(vars.vect2.getY());
+ box.zExtent = FastMath.abs(vars.vect2.getZ());
+
+ vars.release();
+
+ return box;
+ }
+
+ public BoundingVolume transform(Matrix4f trans, BoundingVolume store) {
+ BoundingBox box;
+ if (store == null || store.getType() != Type.AABB) {
+ box = new BoundingBox();
+ } else {
+ box = (BoundingBox) store;
+ }
+ TempVars vars = TempVars.get();
+
+
+ float w = trans.multProj(center, box.center);
+ box.center.divideLocal(w);
+
+ Matrix3f transMatrix = vars.tempMat3;
+ trans.toRotationMatrix(transMatrix);
+
+ // Make the rotation matrix all positive to get the maximum x/y/z extent
+ transMatrix.absoluteLocal();
+
+ vars.vect1.set(xExtent, yExtent, zExtent);
+ transMatrix.mult(vars.vect1, vars.vect1);
+
+ // Assign the biggest rotations after scales.
+ box.xExtent = FastMath.abs(vars.vect1.getX());
+ box.yExtent = FastMath.abs(vars.vect1.getY());
+ box.zExtent = FastMath.abs(vars.vect1.getZ());
+
+ vars.release();
+
+ return box;
+ }
+
+ /**
+ * <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.
+ */
+ public Plane.Side whichSide(Plane plane) {
+ float radius = FastMath.abs(xExtent * plane.getNormal().getX())
+ + FastMath.abs(yExtent * plane.getNormal().getY())
+ + FastMath.abs(zExtent * plane.getNormal().getZ());
+
+ float distance = plane.pseudoDistance(center);
+
+ //changed to < and > to prevent floating point precision problems
+ 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 the new sphere
+ */
+ public BoundingVolume merge(BoundingVolume volume) {
+ if (volume == null) {
+ return this;
+ }
+
+ switch (volume.getType()) {
+ case AABB: {
+ BoundingBox vBox = (BoundingBox) volume;
+ return merge(vBox.center, vBox.xExtent, vBox.yExtent,
+ vBox.zExtent, new BoundingBox(new Vector3f(0, 0, 0), 0,
+ 0, 0));
+ }
+
+ case Sphere: {
+ BoundingSphere vSphere = (BoundingSphere) volume;
+ return merge(vSphere.center, vSphere.radius, vSphere.radius,
+ vSphere.radius, new BoundingBox(new Vector3f(0, 0, 0),
+ 0, 0, 0));
+ }
+
+// case OBB: {
+// OrientedBoundingBox box = (OrientedBoundingBox) volume;
+// BoundingBox rVal = (BoundingBox) 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 AABB: {
+ BoundingBox vBox = (BoundingBox) volume;
+ return merge(vBox.center, vBox.xExtent, vBox.yExtent,
+ vBox.zExtent, this);
+ }
+
+ case Sphere: {
+ BoundingSphere vSphere = (BoundingSphere) volume;
+ return merge(vSphere.center, vSphere.radius, vSphere.radius,
+ vSphere.radius, this);
+ }
+
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Merges this AABB with the given OBB.
+ *
+ * @param volume
+ * the OBB to merge this AABB with.
+ * @return This AABB extended to fit the given OBB.
+ */
+// private BoundingBox mergeOBB(OrientedBoundingBox volume) {
+// if (!volume.correctCorners)
+// volume.computeCorners();
+//
+// TempVars vars = TempVars.get();
+// Vector3f min = vars.compVect1.set(center.x - xExtent, center.y - yExtent,
+// center.z - zExtent);
+// Vector3f max = vars.compVect2.set(center.x + xExtent, center.y + yExtent,
+// center.z + zExtent);
+//
+// for (int i = 1; i < volume.vectorStore.length; i++) {
+// Vector3f temp = volume.vectorStore[i];
+// if (temp.x < min.x)
+// min.x = temp.x;
+// else if (temp.x > max.x)
+// max.x = temp.x;
+//
+// if (temp.y < min.y)
+// min.y = temp.y;
+// else if (temp.y > max.y)
+// max.y = temp.y;
+//
+// if (temp.z < min.z)
+// min.z = temp.z;
+// else if (temp.z > max.z)
+// max.z = temp.z;
+// }
+//
+// center.set(min.addLocal(max));
+// center.multLocal(0.5f);
+//
+// xExtent = max.x - center.x;
+// yExtent = max.y - center.y;
+// zExtent = max.z - center.z;
+// return this;
+// }
+ /**
+ * <code>merge</code> combines this bounding box with another box which is
+ * defined by the center, x, y, z extents.
+ *
+ * @param boxCenter
+ * the center of the box to merge with
+ * @param boxX
+ * the x extent of the box to merge with.
+ * @param boxY
+ * the y extent of the box to merge with.
+ * @param boxZ
+ * the z extent of the box to merge with.
+ * @param rVal
+ * the resulting merged box.
+ * @return the resulting merged box.
+ */
+ private BoundingBox merge(Vector3f boxCenter, float boxX, float boxY,
+ float boxZ, BoundingBox rVal) {
+
+ TempVars vars = TempVars.get();
+
+ vars.vect1.x = center.x - xExtent;
+ if (vars.vect1.x > boxCenter.x - boxX) {
+ vars.vect1.x = boxCenter.x - boxX;
+ }
+ vars.vect1.y = center.y - yExtent;
+ if (vars.vect1.y > boxCenter.y - boxY) {
+ vars.vect1.y = boxCenter.y - boxY;
+ }
+ vars.vect1.z = center.z - zExtent;
+ if (vars.vect1.z > boxCenter.z - boxZ) {
+ vars.vect1.z = boxCenter.z - boxZ;
+ }
+
+ vars.vect2.x = center.x + xExtent;
+ if (vars.vect2.x < boxCenter.x + boxX) {
+ vars.vect2.x = boxCenter.x + boxX;
+ }
+ vars.vect2.y = center.y + yExtent;
+ if (vars.vect2.y < boxCenter.y + boxY) {
+ vars.vect2.y = boxCenter.y + boxY;
+ }
+ vars.vect2.z = center.z + zExtent;
+ if (vars.vect2.z < boxCenter.z + boxZ) {
+ vars.vect2.z = boxCenter.z + boxZ;
+ }
+
+ center.set(vars.vect2).addLocal(vars.vect1).multLocal(0.5f);
+
+ xExtent = vars.vect2.x - center.x;
+ yExtent = vars.vect2.y - center.y;
+ zExtent = vars.vect2.z - center.z;
+
+ vars.release();
+
+ return rVal;
+ }
+
+ /**
+ * <code>clone</code> creates a new BoundingBox 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 BoundingBox
+ */
+ public BoundingVolume clone(BoundingVolume store) {
+ if (store != null && store.getType() == Type.AABB) {
+ BoundingBox rVal = (BoundingBox) store;
+ rVal.center.set(center);
+ rVal.xExtent = xExtent;
+ rVal.yExtent = yExtent;
+ rVal.zExtent = zExtent;
+ rVal.checkPlane = checkPlane;
+ return rVal;
+ }
+
+ BoundingBox rVal = new BoundingBox(center.clone(),
+ xExtent, yExtent, zExtent);
+ return rVal;
+ }
+
+ /**
+ * <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() + " [Center: " + center + " xExtent: "
+ + xExtent + " yExtent: " + yExtent + " zExtent: " + zExtent
+ + "]";
+ }
+
+ /**
+ * intersects determines if this Bounding Box intersects with another given
+ * bounding volume. If so, true is returned, otherwise, false is returned.
+ *
+ * @see BoundingVolume#intersects(com.jme3.bounding.BoundingVolume)
+ */
+ public boolean intersects(BoundingVolume bv) {
+ return bv.intersectsBoundingBox(this);
+ }
+
+ /**
+ * determines if this bounding box intersects a given bounding sphere.
+ *
+ * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
+ */
+ public boolean intersectsSphere(BoundingSphere bs) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center);
+
+ if (FastMath.abs(center.x - bs.center.x) < bs.getRadius()
+ + xExtent
+ && FastMath.abs(center.y - bs.center.y) < bs.getRadius()
+ + yExtent
+ && FastMath.abs(center.z - bs.center.z) < bs.getRadius()
+ + zExtent) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * determines if this bounding box intersects a given bounding box. If the
+ * two boxes intersect in any way, true is returned. Otherwise, false is
+ * returned.
+ *
+ * @see BoundingVolume#intersectsBoundingBox(com.jme3.bounding.BoundingBox)
+ */
+ public boolean intersectsBoundingBox(BoundingBox bb) {
+ assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center);
+
+ if (center.x + xExtent < bb.center.x - bb.xExtent
+ || center.x - xExtent > bb.center.x + bb.xExtent) {
+ return false;
+ } else if (center.y + yExtent < bb.center.y - bb.yExtent
+ || center.y - yExtent > bb.center.y + bb.yExtent) {
+ return false;
+ } else if (center.z + zExtent < bb.center.z - bb.zExtent
+ || center.z - zExtent > bb.center.z + bb.zExtent) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * determines if this bounding box intersects with a given oriented bounding
+ * box.
+ *
+ * @see com.jme.bounding.BoundingVolume#intersectsOrientedBoundingBox(com.jme.bounding.OrientedBoundingBox)
+ */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// return obb.intersectsBoundingBox(this);
+// }
+ /**
+ * determines if this bounding box intersects with a given ray object. If an
+ * intersection has occurred, true is returned, otherwise false is returned.
+ *
+ * @see BoundingVolume#intersects(com.jme3.math.Ray)
+ */
+ public boolean intersects(Ray ray) {
+ assert Vector3f.isValidVector(center);
+
+ float rhs;
+
+ TempVars vars = TempVars.get();
+
+ Vector3f diff = ray.origin.subtract(getCenter(vars.vect2), vars.vect1);
+
+ final float[] fWdU = vars.fWdU;
+ final float[] fAWdU = vars.fAWdU;
+ final float[] fDdU = vars.fDdU;
+ final float[] fADdU = vars.fADdU;
+ final float[] fAWxDdU = vars.fAWxDdU;
+
+ fWdU[0] = ray.getDirection().dot(Vector3f.UNIT_X);
+ fAWdU[0] = FastMath.abs(fWdU[0]);
+ fDdU[0] = diff.dot(Vector3f.UNIT_X);
+ fADdU[0] = FastMath.abs(fDdU[0]);
+ if (fADdU[0] > xExtent && fDdU[0] * fWdU[0] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ fWdU[1] = ray.getDirection().dot(Vector3f.UNIT_Y);
+ fAWdU[1] = FastMath.abs(fWdU[1]);
+ fDdU[1] = diff.dot(Vector3f.UNIT_Y);
+ fADdU[1] = FastMath.abs(fDdU[1]);
+ if (fADdU[1] > yExtent && fDdU[1] * fWdU[1] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ fWdU[2] = ray.getDirection().dot(Vector3f.UNIT_Z);
+ fAWdU[2] = FastMath.abs(fWdU[2]);
+ fDdU[2] = diff.dot(Vector3f.UNIT_Z);
+ fADdU[2] = FastMath.abs(fDdU[2]);
+ if (fADdU[2] > zExtent && fDdU[2] * fWdU[2] >= 0.0) {
+ vars.release();
+ return false;
+ }
+
+ Vector3f wCrossD = ray.getDirection().cross(diff, vars.vect2);
+
+ fAWxDdU[0] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_X));
+ rhs = yExtent * fAWdU[2] + zExtent * fAWdU[1];
+ if (fAWxDdU[0] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ fAWxDdU[1] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Y));
+ rhs = xExtent * fAWdU[2] + zExtent * fAWdU[0];
+ if (fAWxDdU[1] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ fAWxDdU[2] = FastMath.abs(wCrossD.dot(Vector3f.UNIT_Z));
+ rhs = xExtent * fAWdU[1] + yExtent * fAWdU[0];
+ if (fAWxDdU[2] > rhs) {
+ vars.release();
+ return false;
+ }
+
+ vars.release();
+ return true;
+ }
+
+ /**
+ * @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.origin).subtractLocal(center);
+ Vector3f direction = vars.vect2.set(ray.direction);
+
+ float[] t = {0f, Float.POSITIVE_INFINITY};
+
+ float saveT0 = t[0], saveT1 = t[1];
+ boolean notEntirelyClipped = clip(+direction.x, -diff.x - xExtent, t)
+ && clip(-direction.x, +diff.x - xExtent, t)
+ && clip(+direction.y, -diff.y - yExtent, t)
+ && clip(-direction.y, +diff.y - yExtent, t)
+ && clip(+direction.z, -diff.z - zExtent, t)
+ && clip(-direction.z, +diff.z - zExtent, t);
+ vars.release();
+
+ if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) {
+ if (t[1] > t[0]) {
+ float[] distances = t;
+ Vector3f[] points = new Vector3f[]{
+ new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+ new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin)
+ };
+
+ CollisionResult result = new CollisionResult(points[0], distances[0]);
+ results.addCollision(result);
+ result = new CollisionResult(points[1], distances[1]);
+ results.addCollision(result);
+ return 2;
+ }
+
+ Vector3f point = new Vector3f(ray.direction).multLocal(t[0]).addLocal(ray.origin);
+ CollisionResult result = new CollisionResult(point, t[0]);
+ results.addCollision(result);
+ return 1;
+ }
+ return 0;
+ }
+
+ 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;
+ if (intersects(t.get1(), t.get2(), t.get3())) {
+ CollisionResult r = new CollisionResult();
+ results.addCollision(r);
+ return 1;
+ }
+ return 0;
+ } else {
+ throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
+ }
+ }
+
+ /**
+ * C code ported from <a href="http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt">
+ * http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt</a>
+ *
+ * @param v1 The first point in the triangle
+ * @param v2 The second point in the triangle
+ * @param v3 The third point in the triangle
+ * @return True if the bounding box intersects the triangle, false
+ * otherwise.
+ */
+ public boolean intersects(Vector3f v1, Vector3f v2, Vector3f v3) {
+ return Intersection.intersect(this, v1, v2, v3);
+ }
+
+ @Override
+ public boolean contains(Vector3f point) {
+ return FastMath.abs(center.x - point.x) < xExtent
+ && FastMath.abs(center.y - point.y) < yExtent
+ && FastMath.abs(center.z - point.z) < zExtent;
+ }
+
+ @Override
+ public boolean intersects(Vector3f point) {
+ return FastMath.abs(center.x - point.x) <= xExtent
+ && FastMath.abs(center.y - point.y) <= yExtent
+ && FastMath.abs(center.z - point.z) <= zExtent;
+ }
+
+ public float distanceToEdge(Vector3f point) {
+ // compute coordinates of point in box coordinate system
+ TempVars vars= TempVars.get();
+ Vector3f closest = vars.vect1;
+
+ point.subtract(center,closest);
+
+ // project test point onto box
+ float sqrDistance = 0.0f;
+ float delta;
+
+ if (closest.x < -xExtent) {
+ delta = closest.x + xExtent;
+ sqrDistance += delta * delta;
+ closest.x = -xExtent;
+ } else if (closest.x > xExtent) {
+ delta = closest.x - xExtent;
+ sqrDistance += delta * delta;
+ closest.x = xExtent;
+ }
+
+ if (closest.y < -yExtent) {
+ delta = closest.y + yExtent;
+ sqrDistance += delta * delta;
+ closest.y = -yExtent;
+ } else if (closest.y > yExtent) {
+ delta = closest.y - yExtent;
+ sqrDistance += delta * delta;
+ closest.y = yExtent;
+ }
+
+ if (closest.z < -zExtent) {
+ delta = closest.z + zExtent;
+ sqrDistance += delta * delta;
+ closest.z = -zExtent;
+ } else if (closest.z > zExtent) {
+ delta = closest.z - zExtent;
+ sqrDistance += delta * delta;
+ closest.z = zExtent;
+ }
+
+ vars.release();
+ return FastMath.sqrt(sqrDistance);
+ }
+
+ /**
+ * <code>clip</code> determines if a line segment intersects the current
+ * test plane.
+ *
+ * @param denom
+ * the denominator of the line segment.
+ * @param numer
+ * the numerator of the line segment.
+ * @param t
+ * test values of the plane.
+ * @return true if the line segment intersects the plane, false otherwise.
+ */
+ private boolean clip(float denom, float numer, float[] t) {
+ // Return value is 'true' if line segment intersects the current test
+ // plane. Otherwise 'false' is returned in which case the line segment
+ // is entirely clipped.
+ if (denom > 0.0f) {
+ if (numer > denom * t[1]) {
+ return false;
+ }
+ if (numer > denom * t[0]) {
+ t[0] = numer / denom;
+ }
+ return true;
+ } else if (denom < 0.0f) {
+ if (numer > denom * t[0]) {
+ return false;
+ }
+ if (numer > denom * t[1]) {
+ t[1] = numer / denom;
+ }
+ return true;
+ } else {
+ return numer <= 0.0;
+ }
+ }
+
+ /**
+ * Query extent.
+ *
+ * @param store
+ * where extent gets stored - null to return a new vector
+ * @return store / new vector
+ */
+ public Vector3f getExtent(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public float getXExtent() {
+ return xExtent;
+ }
+
+ public float getYExtent() {
+ return yExtent;
+ }
+
+ public float getZExtent() {
+ return zExtent;
+ }
+
+ public void setXExtent(float xExtent) {
+ if (xExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.xExtent = xExtent;
+ }
+
+ public void setYExtent(float yExtent) {
+ if (yExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.yExtent = yExtent;
+ }
+
+ public void setZExtent(float zExtent) {
+ if (zExtent < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ this.zExtent = zExtent;
+ }
+
+ public Vector3f getMin(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(center).subtractLocal(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public Vector3f getMax(Vector3f store) {
+ if (store == null) {
+ store = new Vector3f();
+ }
+ store.set(center).addLocal(xExtent, yExtent, zExtent);
+ return store;
+ }
+
+ public void setMinMax(Vector3f min, Vector3f max) {
+ this.center.set(max).addLocal(min).multLocal(0.5f);
+ xExtent = FastMath.abs(max.x - center.x);
+ yExtent = FastMath.abs(max.y - center.y);
+ zExtent = FastMath.abs(max.z - center.z);
+ }
+
+ @Override
+ public void write(JmeExporter e) throws IOException {
+ super.write(e);
+ OutputCapsule capsule = e.getCapsule(this);
+ capsule.write(xExtent, "xExtent", 0);
+ capsule.write(yExtent, "yExtent", 0);
+ capsule.write(zExtent, "zExtent", 0);
+ }
+
+ @Override
+ public void read(JmeImporter e) throws IOException {
+ super.read(e);
+ InputCapsule capsule = e.getCapsule(this);
+ xExtent = capsule.readFloat("xExtent", 0);
+ yExtent = capsule.readFloat("yExtent", 0);
+ zExtent = capsule.readFloat("zExtent", 0);
+ }
+
+ @Override
+ public float getVolume() {
+ return (8 * xExtent * yExtent * zExtent);
+ }
+}
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
diff --git a/engine/src/core/com/jme3/bounding/BoundingVolume.java b/engine/src/core/com/jme3/bounding/BoundingVolume.java
new file mode 100644
index 0000000..8db2e57
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/BoundingVolume.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2009-2012 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.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.Savable;
+import com.jme3.math.*;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ * <code>BoundingVolume</code> defines an interface for dealing with
+ * containment of a collection of points.
+ *
+ * @author Mark Powell
+ * @version $Id: BoundingVolume.java,v 1.24 2007/09/21 15:45:32 nca Exp $
+ */
+public abstract class BoundingVolume implements Savable, Cloneable, Collidable {
+
+ /**
+ * The type of bounding volume being used.
+ */
+ public enum Type {
+ /**
+ * {@link BoundingSphere}
+ */
+ Sphere,
+
+ /**
+ * {@link BoundingBox}.
+ */
+ AABB,
+
+ /**
+ * {@link com.jme3.bounding.OrientedBoundingBox}
+ */
+ OBB,
+
+ /**
+ * Currently unsupported by jME3.
+ */
+ Capsule;
+ }
+
+ protected int checkPlane = 0;
+ protected Vector3f center = new Vector3f();
+
+ public BoundingVolume() {
+ }
+
+ public BoundingVolume(Vector3f center) {
+ this.center.set(center);
+ }
+
+ /**
+ * Grabs the checkplane we should check first.
+ *
+ */
+ public int getCheckPlane() {
+ return checkPlane;
+ }
+
+ /**
+ * Sets the index of the plane that should be first checked during rendering.
+ *
+ * @param value
+ */
+ public final void setCheckPlane(int value) {
+ checkPlane = value;
+ }
+
+ /**
+ * getType returns the type of bounding volume this is.
+ */
+ public abstract Type getType();
+
+ /**
+ *
+ * <code>transform</code> alters the location of the bounding volume by a
+ * rotation, translation and a scalar.
+ *
+ * @param trans
+ * the transform to affect the bound.
+ * @return the new bounding volume.
+ */
+ public final BoundingVolume transform(Transform trans) {
+ return transform(trans, null);
+ }
+
+ /**
+ *
+ * <code>transform</code> alters the location of the bounding volume by a
+ * rotation, translation and a scalar.
+ *
+ * @param trans
+ * the transform to affect the bound.
+ * @param store
+ * sphere to store result in
+ * @return the new bounding volume.
+ */
+ public abstract BoundingVolume transform(Transform trans, BoundingVolume store);
+
+ public abstract BoundingVolume transform(Matrix4f trans, BoundingVolume store);
+
+ /**
+ *
+ * <code>whichSide</code> returns the side on which the bounding volume
+ * lies on a plane. Possible values are POSITIVE_SIDE, NEGATIVE_SIDE, and
+ * NO_SIDE.
+ *
+ * @param plane
+ * the plane to check against this bounding volume.
+ * @return the side on which this bounding volume lies.
+ */
+ public abstract Plane.Side whichSide(Plane plane);
+
+ /**
+ *
+ * <code>computeFromPoints</code> generates a bounding volume that
+ * encompasses a collection of points.
+ *
+ * @param points
+ * the points to contain.
+ */
+ public abstract void computeFromPoints(FloatBuffer points);
+
+ /**
+ * <code>merge</code> combines two bounding volumes into a single bounding
+ * volume that contains both this bounding volume and the parameter volume.
+ *
+ * @param volume
+ * the volume to combine.
+ * @return the new merged bounding volume.
+ */
+ public abstract BoundingVolume merge(BoundingVolume volume);
+
+ /**
+ * <code>mergeLocal</code> combines two bounding volumes into a single
+ * bounding volume that contains both this bounding volume and the parameter
+ * volume. The result is stored locally.
+ *
+ * @param volume
+ * the volume to combine.
+ * @return this
+ */
+ public abstract BoundingVolume mergeLocal(BoundingVolume volume);
+
+ /**
+ * <code>clone</code> creates a new BoundingVolume 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 BoundingVolume
+ */
+ public abstract BoundingVolume clone(BoundingVolume store);
+
+ public final Vector3f getCenter() {
+ return center;
+ }
+
+ public final Vector3f getCenter(Vector3f store) {
+ store.set(center);
+ return store;
+ }
+
+ public final void setCenter(Vector3f newCenter) {
+ center.set(newCenter);
+ }
+
+ /**
+ * Find the distance from the center of this Bounding Volume to the given
+ * point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public final float distanceTo(Vector3f point) {
+ return center.distance(point);
+ }
+
+ /**
+ * Find the squared distance from the center of this Bounding Volume to the
+ * given point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public final float distanceSquaredTo(Vector3f point) {
+ return center.distanceSquared(point);
+ }
+
+ /**
+ * Find the distance from the nearest edge of this Bounding Volume to the given
+ * point.
+ *
+ * @param point
+ * The point to get the distance to
+ * @return distance
+ */
+ public abstract float distanceToEdge(Vector3f point);
+
+ /**
+ * determines if this bounding volume and a second given volume are
+ * intersecting. Intersecting being: one volume contains another, one volume
+ * overlaps another or one volume touches another.
+ *
+ * @param bv
+ * the second volume to test against.
+ * @return true if this volume intersects the given volume.
+ */
+ public abstract boolean intersects(BoundingVolume bv);
+
+ /**
+ * determines if a ray intersects this bounding volume.
+ *
+ * @param ray
+ * the ray to test.
+ * @return true if this volume is intersected by a given ray.
+ */
+ public abstract boolean intersects(Ray ray);
+
+
+ /**
+ * determines if this bounding volume and a given bounding sphere are
+ * intersecting.
+ *
+ * @param bs
+ * the bounding sphere to test against.
+ * @return true if this volume intersects the given bounding sphere.
+ */
+ public abstract boolean intersectsSphere(BoundingSphere bs);
+
+ /**
+ * determines if this bounding volume and a given bounding box are
+ * intersecting.
+ *
+ * @param bb
+ * the bounding box to test against.
+ * @return true if this volume intersects the given bounding box.
+ */
+ public abstract boolean intersectsBoundingBox(BoundingBox bb);
+
+ /**
+ * determines if this bounding volume and a given bounding box are
+ * intersecting.
+ *
+ * @param bb
+ * the bounding box to test against.
+ * @return true if this volume intersects the given bounding box.
+ */
+// public abstract boolean intersectsOrientedBoundingBox(OrientedBoundingBox bb);
+ /**
+ *
+ * determines if a given point is contained within this bounding volume.
+ * If the point is on the edge of the bounding volume, this method will
+ * return false. Use intersects(Vector3f) to check for edge intersection.
+ *
+ * @param point
+ * the point to check
+ * @return true if the point lies within this bounding volume.
+ */
+ public abstract boolean contains(Vector3f point);
+
+ /**
+ * Determines if a given point intersects (touches or is inside) this bounding volume.
+ * @param point the point to check
+ * @return true if the point lies within this bounding volume.
+ */
+ public abstract boolean intersects(Vector3f point);
+
+ public abstract float getVolume();
+
+ @Override
+ public BoundingVolume clone() {
+ try{
+ BoundingVolume clone = (BoundingVolume) super.clone();
+ clone.center = center.clone();
+ return clone;
+ }catch (CloneNotSupportedException ex){
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter e) throws IOException {
+ e.getCapsule(this).write(center, "center", Vector3f.ZERO);
+ }
+
+ public void read(JmeImporter e) throws IOException {
+ center = (Vector3f) e.getCapsule(this).readSavable("center", Vector3f.ZERO.clone());
+ }
+
+}
+
diff --git a/engine/src/core/com/jme3/bounding/Intersection.java b/engine/src/core/com/jme3/bounding/Intersection.java
new file mode 100644
index 0000000..c53b792
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/Intersection.java
@@ -0,0 +1,284 @@
+/*
+ * 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.math.FastMath;
+import com.jme3.math.Plane;
+import com.jme3.math.Vector3f;
+import com.jme3.util.TempVars;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+/**
+ * This class includes some utility methods for computing intersection
+ * between bounding volumes and triangles.
+ * @author Kirill
+ */
+public class Intersection {
+
+ private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) {
+ minMax.set(x0, x0, 0);
+ if (x1 < minMax.x) {
+ minMax.setX(x1);
+ }
+ if (x1 > minMax.y) {
+ minMax.setY(x1);
+ }
+ if (x2 < minMax.x) {
+ minMax.setX(x2);
+ }
+ if (x2 > minMax.y) {
+ minMax.setY(x2);
+ }
+ }
+
+// private boolean axisTest(float a, float b, float fa, float fb, Vector3f v0, Vector3f v1, )
+// private boolean axisTestX01(float a, float b, float fa, float fb,
+// Vector3f center, Vector3f ext,
+// Vector3f v1, Vector3f v2, Vector3f v3){
+// float p0 = a * v0.y - b * v0.z;
+// float p2 = a * v2.y - b * v2.z;
+// if(p0 < p2){
+// min = p0;
+// max = p2;
+// } else {
+// min = p2;
+// max = p0;
+// }
+// float rad = fa * boxhalfsize.y + fb * boxhalfsize.z;
+// if(min > rad || max < -rad)
+// return false;
+// }
+ public static boolean intersect(BoundingBox bbox, Vector3f v1, Vector3f v2, Vector3f v3) {
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ TempVars vars = TempVars.get();
+
+
+ Vector3f tmp0 = vars.vect1,
+ tmp1 = vars.vect2,
+ tmp2 = vars.vect3;
+
+ Vector3f e0 = vars.vect4,
+ e1 = vars.vect5,
+ e2 = vars.vect6;
+
+ Vector3f center = bbox.getCenter();
+ Vector3f extent = bbox.getExtent(null);
+
+// float min,max,p0,p1,p2,rad,fex,fey,fez;
+// float normal[3]
+
+ // This is the fastest branch on Sun
+ // move everything so that the boxcenter is in (0,0,0)
+ v1.subtract(center, tmp0);
+ v2.subtract(center, tmp1);
+ v3.subtract(center, tmp2);
+
+ // compute triangle edges
+ tmp1.subtract(tmp0, e0); // tri edge 0
+ tmp2.subtract(tmp1, e1); // tri edge 1
+ tmp0.subtract(tmp2, e2); // tri edge 2
+
+ // Bullet 3:
+ // test the 9 tests first (this was faster)
+ float min, max;
+ float p0, p1, p2, rad;
+ float fex = FastMath.abs(e0.x);
+ float fey = FastMath.abs(e0.y);
+ float fez = FastMath.abs(e0.z);
+
+
+
+ //AXISTEST_X01(e0[Z], e0[Y], fez, fey);
+ p0 = e0.z * tmp0.y - e0.y * tmp0.z;
+ p2 = e0.z * tmp2.y - e0.y * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y02(e0[Z], e0[X], fez, fex);
+ p0 = -e0.z * tmp0.x + e0.x * tmp0.z;
+ p2 = -e0.z * tmp2.x + e0.x * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.x + fex * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Z12(e0[Y], e0[X], fey, fex);
+ p1 = e0.y * tmp1.x - e0.x * tmp1.y;
+ p2 = e0.y * tmp2.x - e0.x * tmp2.y;
+ min = min(p1, p2);
+ max = max(p1, p2);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ fex = FastMath.abs(e1.x);
+ fey = FastMath.abs(e1.y);
+ fez = FastMath.abs(e1.z);
+
+// AXISTEST_X01(e1[Z], e1[Y], fez, fey);
+ p0 = e1.z * tmp0.y - e1.y * tmp0.z;
+ p2 = e1.z * tmp2.y - e1.y * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y02(e1[Z], e1[X], fez, fex);
+ p0 = -e1.z * tmp0.x + e1.x * tmp0.z;
+ p2 = -e1.z * tmp2.x + e1.x * tmp2.z;
+ min = min(p0, p2);
+ max = max(p0, p2);
+ rad = fez * extent.x + fex * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Z0(e1[Y], e1[X], fey, fex);
+ p0 = e1.y * tmp0.x - e1.x * tmp0.y;
+ p1 = e1.y * tmp1.x - e1.x * tmp1.y;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+//
+ fex = FastMath.abs(e2.x);
+ fey = FastMath.abs(e2.y);
+ fez = FastMath.abs(e2.z);
+
+ // AXISTEST_X2(e2[Z], e2[Y], fez, fey);
+ p0 = e2.z * tmp0.y - e2.y * tmp0.z;
+ p1 = e2.z * tmp1.y - e2.y * tmp1.z;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fez * extent.y + fey * extent.z;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // AXISTEST_Y1(e2[Z], e2[X], fez, fex);
+ p0 = -e2.z * tmp0.x + e2.x * tmp0.z;
+ p1 = -e2.z * tmp1.x + e2.x * tmp1.z;
+ min = min(p0, p1);
+ max = max(p0, p1);
+ rad = fez * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+// AXISTEST_Z12(e2[Y], e2[X], fey, fex);
+ p1 = e2.y * tmp1.x - e2.x * tmp1.y;
+ p2 = e2.y * tmp2.x - e2.x * tmp2.y;
+ min = min(p1, p2);
+ max = max(p1, p2);
+ rad = fey * extent.x + fex * extent.y;
+ if (min > rad || max < -rad) {
+ vars.release();
+ return false;
+ }
+
+ // Bullet 1:
+ // first test overlap in the {x,y,z}-directions
+ // find min, max of the triangle each direction, and test for overlap in
+ // that direction -- this is equivalent to testing a minimal AABB around
+ // the triangle against the AABB
+
+
+ Vector3f minMax = vars.vect7;
+
+ // test in X-direction
+ findMinMax(tmp0.x, tmp1.x, tmp2.x, minMax);
+ if (minMax.x > extent.x || minMax.y < -extent.x) {
+ vars.release();
+ return false;
+ }
+
+ // test in Y-direction
+ findMinMax(tmp0.y, tmp1.y, tmp2.y, minMax);
+ if (minMax.x > extent.y || minMax.y < -extent.y) {
+ vars.release();
+ return false;
+ }
+
+ // test in Z-direction
+ findMinMax(tmp0.z, tmp1.z, tmp2.z, minMax);
+ if (minMax.x > extent.z || minMax.y < -extent.z) {
+ vars.release();
+ return false;
+ }
+
+// // Bullet 2:
+// // test if the box intersects the plane of the triangle
+// // compute plane equation of triangle: normal * x + d = 0
+// Vector3f normal = new Vector3f();
+// e0.cross(e1, normal);
+ Plane p = vars.plane;
+
+ p.setPlanePoints(v1, v2, v3);
+ if (bbox.whichSide(p) == Plane.Side.Negative) {
+ vars.release();
+ return false;
+ }
+//
+// if(!planeBoxOverlap(normal,v0,boxhalfsize)) return false;
+
+ vars.release();
+
+ return true; /* box and triangle overlaps */
+ }
+}
diff --git a/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java b/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java
new file mode 100644
index 0000000..f383a94
--- /dev/null
+++ b/engine/src/core/com/jme3/bounding/OrientedBoundingBox.java
@@ -0,0 +1,1522 @@
+/*
+ * 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;
+
+/**
+ * NOTE: This class has been commented out as it has too many dependencies.
+ */
+
+
+//
+//import java.io.IOException;
+//import java.nio.FloatBuffer;
+//
+////import com.jme.scene.TriMesh;
+//
+///**
+// * Started Date: Sep 5, 2004 <br>
+// * <br>
+// *
+// * @author Jack Lindamood
+// * @author Joshua Slack (alterations for .9)
+// * @version $Id: OrientedBoundingBox.java,v 1.35 2007/09/21 15:45:31 nca Exp $
+// */
+//public class OrientedBoundingBox extends BoundingVolume {
+//
+// private static final long serialVersionUID = 1L;
+//
+// static private final Vector3f _compVect3 = new Vector3f();
+//
+// static private final Vector3f _compVect4 = new Vector3f();
+//
+// static private final Vector3f _compVect5 = new Vector3f();
+//
+// static private final Vector3f _compVect6 = new Vector3f();
+//
+// static private final Vector3f _compVect7 = new Vector3f();
+//
+// static private final Vector3f _compVect8 = new Vector3f();
+//
+// static private final Vector3f _compVect9 = new Vector3f();
+//
+// static private final Vector3f _compVect10 = new Vector3f();
+//
+// static private final Vector3f tempVe = new Vector3f();
+//
+// static private final Matrix3f tempMa = new Matrix3f();
+//
+// static private final Quaternion tempQa = new Quaternion();
+//
+// static private final Quaternion tempQb = new Quaternion();
+//
+// private static final float[] fWdU = new float[3];
+//
+// private static final float[] fAWdU = new float[3];
+//
+// private static final float[] fDdU = new float[3];
+//
+// private static final float[] fADdU = new float[3];
+//
+// private static final float[] fAWxDdU = new float[3];
+//
+// private static final float[] tempFa = new float[3];
+//
+// private static final float[] tempFb = new float[3];
+//
+// /** X axis of the Oriented Box. */
+// public final Vector3f xAxis = new Vector3f(1, 0, 0);
+//
+// /** Y axis of the Oriented Box. */
+// public final Vector3f yAxis = new Vector3f(0, 1, 0);
+//
+// /** Z axis of the Oriented Box. */
+// public final Vector3f zAxis = new Vector3f(0, 0, 1);
+//
+// /** Extents of the box along the x,y,z axis. */
+// public final Vector3f extent = new Vector3f(0, 0, 0);
+//
+// /** Vector array used to store the array of 8 corners the box has. */
+// public final Vector3f[] vectorStore = new Vector3f[8];
+//
+// private final Vector3f tempVk = new Vector3f();
+// private final Vector3f tempForword = new Vector3f(0, 0, 1);
+// private final Vector3f tempLeft = new Vector3f(1, 0, 0);
+// private final Vector3f tempUp = new Vector3f(0, 1, 0);
+//
+// static private final FloatBuffer _mergeBuf = BufferUtils
+// .createVector3Buffer(16);
+//
+// /**
+// * If true, the box's vectorStore array correctly represents the box's
+// * corners.
+// */
+// public boolean correctCorners = false;
+//
+// public OrientedBoundingBox() {
+// for (int x = 0; x < 8; x++)
+// vectorStore[x] = new Vector3f();
+// }
+//
+// public Type getType() {
+// return Type.OBB;
+// }
+//
+// public BoundingVolume transform(Quaternion rotate, Vector3f translate,
+// Vector3f scale, BoundingVolume store) {
+// rotate.toRotationMatrix(tempMa);
+// return transform(tempMa, translate, scale, store);
+// }
+//
+// public BoundingVolume transform(Matrix3f rotate, Vector3f translate,
+// Vector3f scale, BoundingVolume store) {
+// if (store == null || store.getType() != Type.OBB) {
+// store = new OrientedBoundingBox();
+// }
+// OrientedBoundingBox toReturn = (OrientedBoundingBox) store;
+// toReturn.extent.set(FastMath.abs(extent.x * scale.x),
+// FastMath.abs(extent.y * scale.y),
+// FastMath.abs(extent.z * scale.z));
+// rotate.mult(xAxis, toReturn.xAxis);
+// rotate.mult(yAxis, toReturn.yAxis);
+// rotate.mult(zAxis, toReturn.zAxis);
+// center.mult(scale, toReturn.center);
+// rotate.mult(toReturn.center, toReturn.center);
+// toReturn.center.addLocal(translate);
+// toReturn.correctCorners = false;
+// return toReturn;
+// }
+//
+// public int whichSide(Plane plane) {
+// float fRadius = FastMath.abs(extent.x * (plane.getNormal().dot(xAxis)))
+// + FastMath.abs(extent.y * (plane.getNormal().dot(yAxis)))
+// + FastMath.abs(extent.z * (plane.getNormal().dot(zAxis)));
+// float fDistance = plane.pseudoDistance(center);
+// if (fDistance <= -fRadius)
+// return Plane.NEGATIVE_SIDE;
+// else if (fDistance >= fRadius)
+// return Plane.POSITIVE_SIDE;
+// else
+// return Plane.NO_SIDE;
+// }
+//
+// public void computeFromPoints(FloatBuffer points) {
+// containAABB(points);
+// }
+//
+// /**
+// * Calculates an AABB of the given point values for this OBB.
+// *
+// * @param points
+// * The points this OBB should contain.
+// */
+// private void containAABB(FloatBuffer points) {
+// if (points == null || points.limit() <= 2) { // we need at least a 3
+// // float vector
+// return;
+// }
+//
+// BufferUtils.populateFromBuffer(_compVect1, points, 0);
+// float minX = _compVect1.x, minY = _compVect1.y, minZ = _compVect1.z;
+// float maxX = _compVect1.x, maxY = _compVect1.y, maxZ = _compVect1.z;
+//
+// for (int i = 1, len = points.limit() / 3; i < len; i++) {
+// BufferUtils.populateFromBuffer(_compVect1, points, i);
+//
+// if (_compVect1.x < minX)
+// minX = _compVect1.x;
+// else if (_compVect1.x > maxX)
+// maxX = _compVect1.x;
+//
+// if (_compVect1.y < minY)
+// minY = _compVect1.y;
+// else if (_compVect1.y > maxY)
+// maxY = _compVect1.y;
+//
+// if (_compVect1.z < minZ)
+// minZ = _compVect1.z;
+// else if (_compVect1.z > maxZ)
+// maxZ = _compVect1.z;
+// }
+//
+// center.set(minX + maxX, minY + maxY, minZ + maxZ);
+// center.multLocal(0.5f);
+//
+// extent.set(maxX - center.x, maxY - center.y, maxZ - center.z);
+//
+// xAxis.set(1, 0, 0);
+// yAxis.set(0, 1, 0);
+// zAxis.set(0, 0, 1);
+//
+// correctCorners = false;
+// }
+//
+// public BoundingVolume merge(BoundingVolume volume) {
+// // clone ourselves into a new bounding volume, then merge.
+// return clone(new OrientedBoundingBox()).mergeLocal(volume);
+// }
+//
+// public BoundingVolume mergeLocal(BoundingVolume volume) {
+// if (volume == null)
+// return this;
+//
+// switch (volume.getType()) {
+//
+// case OBB: {
+// return mergeOBB((OrientedBoundingBox) volume);
+// }
+//
+// case AABB: {
+// return mergeAABB((BoundingBox) volume);
+// }
+//
+// case Sphere: {
+// return mergeSphere((BoundingSphere) volume);
+// }
+//
+// default:
+// return null;
+//
+// }
+// }
+//
+// private BoundingVolume mergeSphere(BoundingSphere volume) {
+// BoundingSphere mergeSphere = volume;
+// if (!correctCorners)
+// this.computeCorners();
+//
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(vectorStore[i].x);
+// _mergeBuf.put(vectorStore[i].y);
+// _mergeBuf.put(vectorStore[i].z);
+// }
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z + mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y + mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x + mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// _mergeBuf.put(mergeSphere.center.x - mergeSphere.radius).put(
+// mergeSphere.center.y - mergeSphere.radius).put(
+// mergeSphere.center.z - mergeSphere.radius);
+// containAABB(_mergeBuf);
+// correctCorners = false;
+// return this;
+// }
+//
+// private BoundingVolume mergeAABB(BoundingBox volume) {
+// BoundingBox mergeBox = volume;
+// if (!correctCorners)
+// this.computeCorners();
+//
+// _mergeBuf.rewind();
+// for (int i = 0; i < 8; i++) {
+// _mergeBuf.put(vectorStore[i].x);
+// _mergeBuf.put(vectorStore[i].y);
+// _mergeBuf.put(vectorStore[i].z);
+// }
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z + mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y + mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x + mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// _mergeBuf.put(mergeBox.center.x - mergeBox.xExtent).put(
+// mergeBox.center.y - mergeBox.yExtent).put(
+// mergeBox.center.z - mergeBox.zExtent);
+// containAABB(_mergeBuf);
+// correctCorners = false;
+// return this;
+// }
+//
+// private BoundingVolume mergeOBB(OrientedBoundingBox volume) {
+// // OrientedBoundingBox mergeBox=(OrientedBoundingBox) volume;
+// // if (!correctCorners) this.computeCorners();
+// // if (!mergeBox.correctCorners) mergeBox.computeCorners();
+// // Vector3f[] mergeArray=new Vector3f[16];
+// // for (int i=0;i<vectorStore.length;i++){
+// // mergeArray[i*2+0]=this .vectorStore[i];
+// // mergeArray[i*2+1]=mergeBox.vectorStore[i];
+// // }
+// // containAABB(mergeArray);
+// // correctCorners=false;
+// // return this;
+// // construct a box that contains the input boxes
+// // Box3<Real> kBox;
+// OrientedBoundingBox rkBox0 = this;
+// OrientedBoundingBox rkBox1 = volume;
+//
+// // The first guess at the box center. This value will be updated later
+// // after the input box vertices are projected onto axes determined by an
+// // average of box axes.
+// Vector3f kBoxCenter = (rkBox0.center.add(rkBox1.center, _compVect7))
+// .multLocal(.5f);
+//
+// // A box's axes, when viewed as the columns of a matrix, form a rotation
+// // matrix. The input box axes are converted to quaternions. The average
+// // quaternion is computed, then normalized to unit length. The result is
+// // the slerp of the two input quaternions with t-value of 1/2. The
+// // result is converted back to a rotation matrix and its columns are
+// // selected as the merged box axes.
+// Quaternion kQ0 = tempQa, kQ1 = tempQb;
+// kQ0.fromAxes(rkBox0.xAxis, rkBox0.yAxis, rkBox0.zAxis);
+// kQ1.fromAxes(rkBox1.xAxis, rkBox1.yAxis, rkBox1.zAxis);
+//
+// if (kQ0.dot(kQ1) < 0.0f)
+// kQ1.negate();
+//
+// Quaternion kQ = kQ0.addLocal(kQ1);
+// kQ.normalize();
+//
+// Matrix3f kBoxaxis = kQ.toRotationMatrix(tempMa);
+// Vector3f newXaxis = kBoxaxis.getColumn(0, _compVect8);
+// Vector3f newYaxis = kBoxaxis.getColumn(1, _compVect9);
+// Vector3f newZaxis = kBoxaxis.getColumn(2, _compVect10);
+//
+// // Project the input box vertices onto the merged-box axes. Each axis
+// // D[i] containing the current center C has a minimum projected value
+// // pmin[i] and a maximum projected value pmax[i]. The corresponding end
+// // points on the axes are C+pmin[i]*D[i] and C+pmax[i]*D[i]. The point C
+// // is not necessarily the midpoint for any of the intervals. The actual
+// // box center will be adjusted from C to a point C' that is the midpoint
+// // of each interval,
+// // C' = C + sum_{i=0}^1 0.5*(pmin[i]+pmax[i])*D[i]
+// // The box extents are
+// // e[i] = 0.5*(pmax[i]-pmin[i])
+//
+// int i;
+// float fDot;
+// Vector3f kDiff = _compVect4;
+// Vector3f kMin = _compVect5;
+// Vector3f kMax = _compVect6;
+// kMin.zero();
+// kMax.zero();
+//
+// if (!rkBox0.correctCorners)
+// rkBox0.computeCorners();
+// for (i = 0; i < 8; i++) {
+// rkBox0.vectorStore[i].subtract(kBoxCenter, kDiff);
+//
+// fDot = kDiff.dot(newXaxis);
+// if (fDot > kMax.x)
+// kMax.x = fDot;
+// else if (fDot < kMin.x)
+// kMin.x = fDot;
+//
+// fDot = kDiff.dot(newYaxis);
+// if (fDot > kMax.y)
+// kMax.y = fDot;
+// else if (fDot < kMin.y)
+// kMin.y = fDot;
+//
+// fDot = kDiff.dot(newZaxis);
+// if (fDot > kMax.z)
+// kMax.z = fDot;
+// else if (fDot < kMin.z)
+// kMin.z = fDot;
+//
+// }
+//
+// if (!rkBox1.correctCorners)
+// rkBox1.computeCorners();
+// for (i = 0; i < 8; i++) {
+// rkBox1.vectorStore[i].subtract(kBoxCenter, kDiff);
+//
+// fDot = kDiff.dot(newXaxis);
+// if (fDot > kMax.x)
+// kMax.x = fDot;
+// else if (fDot < kMin.x)
+// kMin.x = fDot;
+//
+// fDot = kDiff.dot(newYaxis);
+// if (fDot > kMax.y)
+// kMax.y = fDot;
+// else if (fDot < kMin.y)
+// kMin.y = fDot;
+//
+// fDot = kDiff.dot(newZaxis);
+// if (fDot > kMax.z)
+// kMax.z = fDot;
+// else if (fDot < kMin.z)
+// kMin.z = fDot;
+// }
+//
+// this.xAxis.set(newXaxis);
+// this.yAxis.set(newYaxis);
+// this.zAxis.set(newZaxis);
+//
+// this.extent.x = .5f * (kMax.x - kMin.x);
+// kBoxCenter.addLocal(this.xAxis.mult(.5f * (kMax.x + kMin.x), tempVe));
+//
+// this.extent.y = .5f * (kMax.y - kMin.y);
+// kBoxCenter.addLocal(this.yAxis.mult(.5f * (kMax.y + kMin.y), tempVe));
+//
+// this.extent.z = .5f * (kMax.z - kMin.z);
+// kBoxCenter.addLocal(this.zAxis.mult(.5f * (kMax.z + kMin.z), tempVe));
+//
+// this.center.set(kBoxCenter);
+//
+// this.correctCorners = false;
+// return this;
+// }
+//
+// public BoundingVolume clone(BoundingVolume store) {
+// OrientedBoundingBox toReturn;
+// if (store instanceof OrientedBoundingBox) {
+// toReturn = (OrientedBoundingBox) store;
+// } else {
+// toReturn = new OrientedBoundingBox();
+// }
+// toReturn.extent.set(extent);
+// toReturn.xAxis.set(xAxis);
+// toReturn.yAxis.set(yAxis);
+// toReturn.zAxis.set(zAxis);
+// toReturn.center.set(center);
+// toReturn.checkPlane = checkPlane;
+// for (int x = vectorStore.length; --x >= 0; )
+// toReturn.vectorStore[x].set(vectorStore[x]);
+// toReturn.correctCorners = this.correctCorners;
+// return toReturn;
+// }
+//
+// /**
+// * Sets the vectorStore information to the 8 corners of the box.
+// */
+// public void computeCorners() {
+// Vector3f akEAxis0 = xAxis.mult(extent.x, _compVect1);
+// Vector3f akEAxis1 = yAxis.mult(extent.y, _compVect2);
+// Vector3f akEAxis2 = zAxis.mult(extent.z, _compVect3);
+//
+// vectorStore[0].set(center).subtractLocal(akEAxis0).subtractLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[1].set(center).addLocal(akEAxis0).subtractLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[2].set(center).addLocal(akEAxis0).addLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[3].set(center).subtractLocal(akEAxis0).addLocal(akEAxis1).subtractLocal(akEAxis2);
+// vectorStore[4].set(center).subtractLocal(akEAxis0).subtractLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[5].set(center).addLocal(akEAxis0).subtractLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[6].set(center).addLocal(akEAxis0).addLocal(akEAxis1).addLocal(akEAxis2);
+// vectorStore[7].set(center).subtractLocal(akEAxis0).addLocal(akEAxis1).addLocal(akEAxis2);
+// correctCorners = true;
+// }
+//
+//// public void computeFromTris(int[] indices, TriMesh mesh, int start, int end) {
+//// if (end - start <= 0) {
+//// return;
+//// }
+//// Vector3f[] verts = new Vector3f[3];
+//// Vector3f min = _compVect1.set(new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+//// Vector3f max = _compVect2.set(new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+//// Vector3f point;
+//// for (int i = start; i < end; i++) {
+//// mesh.getTriangle(indices[i], verts);
+//// point = verts[0];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+////
+//// point = verts[1];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+////
+//// point = verts[2];
+//// if (point.x < min.x)
+//// min.x = point.x;
+//// else if (point.x > max.x)
+//// max.x = point.x;
+////
+//// if (point.y < min.y)
+//// min.y = point.y;
+//// else if (point.y > max.y)
+//// max.y = point.y;
+////
+//// if (point.z < min.z)
+//// min.z = point.z;
+//// else if (point.z > max.z)
+//// max.z = point.z;
+//// }
+////
+//// center.set(min.addLocal(max));
+//// center.multLocal(0.5f);
+////
+//// extent.set(max.x - center.x, max.y - center.y, max.z - center.z);
+////
+//// xAxis.set(1, 0, 0);
+//// yAxis.set(0, 1, 0);
+//// zAxis.set(0, 0, 1);
+////
+//// correctCorners = false;
+//// }
+//
+// public void computeFromTris(Triangle[] tris, int start, int end) {
+// if (end - start <= 0) {
+// return;
+// }
+//
+// Vector3f min = _compVect1.set(tris[start].get(0));
+// Vector3f max = _compVect2.set(min);
+// Vector3f point;
+// for (int i = start; i < end; i++) {
+//
+// point = tris[i].get(0);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+//
+// point = tris[i].get(1);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+//
+// point = tris[i].get(2);
+// if (point.x < min.x)
+// min.x = point.x;
+// else if (point.x > max.x)
+// max.x = point.x;
+//
+// if (point.y < min.y)
+// min.y = point.y;
+// else if (point.y > max.y)
+// max.y = point.y;
+//
+// if (point.z < min.z)
+// min.z = point.z;
+// else if (point.z > max.z)
+// max.z = point.z;
+// }
+//
+// center.set(min.addLocal(max));
+// center.multLocal(0.5f);
+//
+// extent.set(max.x - center.x, max.y - center.y, max.z - center.z);
+//
+// xAxis.set(1, 0, 0);
+// yAxis.set(0, 1, 0);
+// zAxis.set(0, 0, 1);
+//
+// correctCorners = false;
+// }
+//
+// public boolean intersection(OrientedBoundingBox box1) {
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// OrientedBoundingBox box0 = this;
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { box0.xAxis, box0.yAxis, box0.zAxis };
+// Vector3f[] akB = new Vector3f[] { box1.xAxis, box1.yAxis, box1.zAxis };
+// Vector3f afEA = box0.extent;
+// Vector3f afEB = box1.extent;
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = box1.center.subtract(box0.center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersects(com.jme.bounding.BoundingVolume)
+// */
+// public boolean intersects(BoundingVolume bv) {
+// if (bv == null)
+// return false;
+//
+// return bv.intersectsOrientedBoundingBox(this);
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
+// */
+// public boolean intersectsSphere(BoundingSphere bs) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(bs.center)) return false;
+//
+// _compVect1.set(bs.getCenter()).subtractLocal(center);
+// tempMa.fromAxes(xAxis, yAxis, zAxis);
+//
+// tempMa.mult(_compVect1, _compVect2);
+//
+// if (FastMath.abs(_compVect2.x) < bs.getRadius() + extent.x
+// && FastMath.abs(_compVect2.y) < bs.getRadius() + extent.y
+// && FastMath.abs(_compVect2.z) < bs.getRadius() + extent.z)
+// return true;
+//
+// return false;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
+// */
+// public boolean intersectsBoundingBox(BoundingBox bb) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(bb.center)) return false;
+//
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { xAxis, yAxis, zAxis };
+// Vector3f[] akB = new Vector3f[] { tempForword, tempLeft, tempUp };
+// Vector3f afEA = extent;
+// Vector3f afEB = tempVk.set(bb.xExtent, bb.yExtent, bb.zExtent);
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = bb.getCenter().subtract(center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersectsOBB2(com.jme.bounding.OBB2)
+// */
+// public boolean intersectsOrientedBoundingBox(OrientedBoundingBox obb) {
+// if (!Vector3f.isValidVector(center) || !Vector3f.isValidVector(obb.center)) return false;
+//
+// // Cutoff for cosine of angles between box axes. This is used to catch
+// // the cases when at least one pair of axes are parallel. If this
+// // happens,
+// // there is no need to test for separation along the Cross(A[i],B[j])
+// // directions.
+// float cutoff = 0.999999f;
+// boolean parallelPairExists = false;
+// int i;
+//
+// // convenience variables
+// Vector3f akA[] = new Vector3f[] { xAxis, yAxis, zAxis };
+// Vector3f[] akB = new Vector3f[] { obb.xAxis, obb.yAxis, obb.zAxis };
+// Vector3f afEA = extent;
+// Vector3f afEB = obb.extent;
+//
+// // compute difference of box centers, D = C1-C0
+// Vector3f kD = obb.center.subtract(center, _compVect1);
+//
+// float[][] aafC = { fWdU, fAWdU, fDdU };
+//
+// float[][] aafAbsC = { fADdU, fAWxDdU, tempFa };
+//
+// float[] afAD = tempFb;
+// float fR0, fR1, fR; // interval radii and distance between centers
+// float fR01; // = R0 + R1
+//
+// // axis C0+t*A0
+// for (i = 0; i < 3; i++) {
+// aafC[0][i] = akA[0].dot(akB[i]);
+// aafAbsC[0][i] = FastMath.abs(aafC[0][i]);
+// if (aafAbsC[0][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[0] = akA[0].dot(kD);
+// fR = FastMath.abs(afAD[0]);
+// fR1 = afEB.x * aafAbsC[0][0] + afEB.y * aafAbsC[0][1] + afEB.z
+// * aafAbsC[0][2];
+// fR01 = afEA.x + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1
+// for (i = 0; i < 3; i++) {
+// aafC[1][i] = akA[1].dot(akB[i]);
+// aafAbsC[1][i] = FastMath.abs(aafC[1][i]);
+// if (aafAbsC[1][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[1] = akA[1].dot(kD);
+// fR = FastMath.abs(afAD[1]);
+// fR1 = afEB.x * aafAbsC[1][0] + afEB.y * aafAbsC[1][1] + afEB.z
+// * aafAbsC[1][2];
+// fR01 = afEA.y + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2
+// for (i = 0; i < 3; i++) {
+// aafC[2][i] = akA[2].dot(akB[i]);
+// aafAbsC[2][i] = FastMath.abs(aafC[2][i]);
+// if (aafAbsC[2][i] > cutoff) {
+// parallelPairExists = true;
+// }
+// }
+// afAD[2] = akA[2].dot(kD);
+// fR = FastMath.abs(afAD[2]);
+// fR1 = afEB.x * aafAbsC[2][0] + afEB.y * aafAbsC[2][1] + afEB.z
+// * aafAbsC[2][2];
+// fR01 = afEA.z + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B0
+// fR = FastMath.abs(akB[0].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][0] + afEA.y * aafAbsC[1][0] + afEA.z
+// * aafAbsC[2][0];
+// fR01 = fR0 + afEB.x;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B1
+// fR = FastMath.abs(akB[1].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][1] + afEA.y * aafAbsC[1][1] + afEA.z
+// * aafAbsC[2][1];
+// fR01 = fR0 + afEB.y;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*B2
+// fR = FastMath.abs(akB[2].dot(kD));
+// fR0 = afEA.x * aafAbsC[0][2] + afEA.y * aafAbsC[1][2] + afEA.z
+// * aafAbsC[2][2];
+// fR01 = fR0 + afEB.z;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // At least one pair of box axes was parallel, so the separation is
+// // effectively in 2D where checking the "edge" normals is sufficient for
+// // the separation of the boxes.
+// if (parallelPairExists) {
+// return true;
+// }
+//
+// // axis C0+t*A0xB0
+// fR = FastMath.abs(afAD[2] * aafC[1][0] - afAD[1] * aafC[2][0]);
+// fR0 = afEA.y * aafAbsC[2][0] + afEA.z * aafAbsC[1][0];
+// fR1 = afEB.y * aafAbsC[0][2] + afEB.z * aafAbsC[0][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB1
+// fR = FastMath.abs(afAD[2] * aafC[1][1] - afAD[1] * aafC[2][1]);
+// fR0 = afEA.y * aafAbsC[2][1] + afEA.z * aafAbsC[1][1];
+// fR1 = afEB.x * aafAbsC[0][2] + afEB.z * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A0xB2
+// fR = FastMath.abs(afAD[2] * aafC[1][2] - afAD[1] * aafC[2][2]);
+// fR0 = afEA.y * aafAbsC[2][2] + afEA.z * aafAbsC[1][2];
+// fR1 = afEB.x * aafAbsC[0][1] + afEB.y * aafAbsC[0][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB0
+// fR = FastMath.abs(afAD[0] * aafC[2][0] - afAD[2] * aafC[0][0]);
+// fR0 = afEA.x * aafAbsC[2][0] + afEA.z * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[1][2] + afEB.z * aafAbsC[1][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB1
+// fR = FastMath.abs(afAD[0] * aafC[2][1] - afAD[2] * aafC[0][1]);
+// fR0 = afEA.x * aafAbsC[2][1] + afEA.z * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[1][2] + afEB.z * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A1xB2
+// fR = FastMath.abs(afAD[0] * aafC[2][2] - afAD[2] * aafC[0][2]);
+// fR0 = afEA.x * aafAbsC[2][2] + afEA.z * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[1][1] + afEB.y * aafAbsC[1][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB0
+// fR = FastMath.abs(afAD[1] * aafC[0][0] - afAD[0] * aafC[1][0]);
+// fR0 = afEA.x * aafAbsC[1][0] + afEA.y * aafAbsC[0][0];
+// fR1 = afEB.y * aafAbsC[2][2] + afEB.z * aafAbsC[2][1];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB1
+// fR = FastMath.abs(afAD[1] * aafC[0][1] - afAD[0] * aafC[1][1]);
+// fR0 = afEA.x * aafAbsC[1][1] + afEA.y * aafAbsC[0][1];
+// fR1 = afEB.x * aafAbsC[2][2] + afEB.z * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// // axis C0+t*A2xB2
+// fR = FastMath.abs(afAD[1] * aafC[0][2] - afAD[0] * aafC[1][2]);
+// fR0 = afEA.x * aafAbsC[1][2] + afEA.y * aafAbsC[0][2];
+// fR1 = afEB.x * aafAbsC[2][1] + afEB.y * aafAbsC[2][0];
+// fR01 = fR0 + fR1;
+// if (fR > fR01) {
+// return false;
+// }
+//
+// return true;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
+// */
+// public boolean intersects(Ray ray) {
+// if (!Vector3f.isValidVector(center)) return false;
+//
+// float rhs;
+// Vector3f diff = ray.origin.subtract(getCenter(_compVect2), _compVect1);
+//
+// fWdU[0] = ray.getDirection().dot(xAxis);
+// fAWdU[0] = FastMath.abs(fWdU[0]);
+// fDdU[0] = diff.dot(xAxis);
+// fADdU[0] = FastMath.abs(fDdU[0]);
+// if (fADdU[0] > extent.x && fDdU[0] * fWdU[0] >= 0.0) {
+// return false;
+// }
+//
+// fWdU[1] = ray.getDirection().dot(yAxis);
+// fAWdU[1] = FastMath.abs(fWdU[1]);
+// fDdU[1] = diff.dot(yAxis);
+// fADdU[1] = FastMath.abs(fDdU[1]);
+// if (fADdU[1] > extent.y && fDdU[1] * fWdU[1] >= 0.0) {
+// return false;
+// }
+//
+// fWdU[2] = ray.getDirection().dot(zAxis);
+// fAWdU[2] = FastMath.abs(fWdU[2]);
+// fDdU[2] = diff.dot(zAxis);
+// fADdU[2] = FastMath.abs(fDdU[2]);
+// if (fADdU[2] > extent.z && fDdU[2] * fWdU[2] >= 0.0) {
+// return false;
+// }
+//
+// Vector3f wCrossD = ray.getDirection().cross(diff, _compVect2);
+//
+// fAWxDdU[0] = FastMath.abs(wCrossD.dot(xAxis));
+// rhs = extent.y * fAWdU[2] + extent.z * fAWdU[1];
+// if (fAWxDdU[0] > rhs) {
+// return false;
+// }
+//
+// fAWxDdU[1] = FastMath.abs(wCrossD.dot(yAxis));
+// rhs = extent.x * fAWdU[2] + extent.z * fAWdU[0];
+// if (fAWxDdU[1] > rhs) {
+// return false;
+// }
+//
+// fAWxDdU[2] = FastMath.abs(wCrossD.dot(zAxis));
+// rhs = extent.x * fAWdU[1] + extent.y * fAWdU[0];
+// if (fAWxDdU[2] > rhs) {
+// return false;
+//
+// }
+//
+// return true;
+// }
+//
+// /**
+// * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
+// */
+// public IntersectionRecord intersectsWhere(Ray ray) {
+// Vector3f diff = _compVect1.set(ray.origin).subtractLocal(center);
+// // convert ray to box coordinates
+// Vector3f direction = _compVect2.set(ray.direction.x, ray.direction.y,
+// ray.direction.z);
+// float[] t = { 0f, Float.POSITIVE_INFINITY };
+//
+// float saveT0 = t[0], saveT1 = t[1];
+// boolean notEntirelyClipped = clip(+direction.x, -diff.x - extent.x, t)
+// && clip(-direction.x, +diff.x - extent.x, t)
+// && clip(+direction.y, -diff.y - extent.y, t)
+// && clip(-direction.y, +diff.y - extent.y, t)
+// && clip(+direction.z, -diff.z - extent.z, t)
+// && clip(-direction.z, +diff.z - extent.z, t);
+//
+// if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) {
+// if (t[1] > t[0]) {
+// float[] distances = t;
+// Vector3f[] points = new Vector3f[] {
+// new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+// new Vector3f(ray.direction).multLocal(distances[1]).addLocal(ray.origin)
+// };
+// IntersectionRecord record = new IntersectionRecord(distances, points);
+// return record;
+// }
+//
+// float[] distances = new float[] { t[0] };
+// Vector3f[] points = new Vector3f[] {
+// new Vector3f(ray.direction).multLocal(distances[0]).addLocal(ray.origin),
+// };
+// IntersectionRecord record = new IntersectionRecord(distances, points);
+// return record;
+// }
+//
+// return new IntersectionRecord();
+//
+// }
+//
+// /**
+// * <code>clip</code> determines if a line segment intersects the current
+// * test plane.
+// *
+// * @param denom
+// * the denominator of the line segment.
+// * @param numer
+// * the numerator of the line segment.
+// * @param t
+// * test values of the plane.
+// * @return true if the line segment intersects the plane, false otherwise.
+// */
+// private boolean clip(float denom, float numer, float[] t) {
+// // Return value is 'true' if line segment intersects the current test
+// // plane. Otherwise 'false' is returned in which case the line segment
+// // is entirely clipped.
+// if (denom > 0.0f) {
+// if (numer > denom * t[1])
+// return false;
+// if (numer > denom * t[0])
+// t[0] = numer / denom;
+// return true;
+// } else if (denom < 0.0f) {
+// if (numer > denom * t[0])
+// return false;
+// if (numer > denom * t[1])
+// t[1] = numer / denom;
+// return true;
+// } else {
+// return numer <= 0.0;
+// }
+// }
+//
+// public void setXAxis(Vector3f axis) {
+// xAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setYAxis(Vector3f axis) {
+// yAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setZAxis(Vector3f axis) {
+// zAxis.set(axis);
+// correctCorners = false;
+// }
+//
+// public void setExtent(Vector3f ext) {
+// extent.set(ext);
+// correctCorners = false;
+// }
+//
+// public Vector3f getXAxis() {
+// return xAxis;
+// }
+//
+// public Vector3f getYAxis() {
+// return yAxis;
+// }
+//
+// public Vector3f getZAxis() {
+// return zAxis;
+// }
+//
+// public Vector3f getExtent() {
+// return extent;
+// }
+//
+// @Override
+// public boolean contains(Vector3f point) {
+// _compVect1.set(point).subtractLocal(center);
+// float coeff = _compVect1.dot(xAxis);
+// if (FastMath.abs(coeff) > extent.x) return false;
+//
+// coeff = _compVect1.dot(yAxis);
+// if (FastMath.abs(coeff) > extent.y) return false;
+//
+// coeff = _compVect1.dot(zAxis);
+// if (FastMath.abs(coeff) > extent.z) return false;
+//
+// return true;
+// }
+//
+// @Override
+// public float distanceToEdge(Vector3f point) {
+// // compute coordinates of point in box coordinate system
+// Vector3f diff = point.subtract(center);
+// Vector3f closest = new Vector3f(diff.dot(xAxis), diff.dot(yAxis), diff
+// .dot(zAxis));
+//
+// // project test point onto box
+// float sqrDistance = 0.0f;
+// float delta;
+//
+// if (closest.x < -extent.x) {
+// delta = closest.x + extent.x;
+// sqrDistance += delta * delta;
+// closest.x = -extent.x;
+// } else if (closest.x > extent.x) {
+// delta = closest.x - extent.x;
+// sqrDistance += delta * delta;
+// closest.x = extent.x;
+// }
+//
+// if (closest.y < -extent.y) {
+// delta = closest.y + extent.y;
+// sqrDistance += delta * delta;
+// closest.y = -extent.y;
+// } else if (closest.y > extent.y) {
+// delta = closest.y - extent.y;
+// sqrDistance += delta * delta;
+// closest.y = extent.y;
+// }
+//
+// if (closest.z < -extent.z) {
+// delta = closest.z + extent.z;
+// sqrDistance += delta * delta;
+// closest.z = -extent.z;
+// } else if (closest.z > extent.z) {
+// delta = closest.z - extent.z;
+// sqrDistance += delta * delta;
+// closest.z = extent.z;
+// }
+//
+// return FastMath.sqrt(sqrDistance);
+// }
+//
+// public void write(JMEExporter e) throws IOException {
+// super.write(e);
+// OutputCapsule capsule = e.getCapsule(this);
+// capsule.write(xAxis, "xAxis", Vector3f.UNIT_X);
+// capsule.write(yAxis, "yAxis", Vector3f.UNIT_Y);
+// capsule.write(zAxis, "zAxis", Vector3f.UNIT_Z);
+// capsule.write(extent, "extent", Vector3f.ZERO);
+// }
+//
+// public void read(JMEImporter e) throws IOException {
+// super.read(e);
+// InputCapsule capsule = e.getCapsule(this);
+// xAxis.set((Vector3f) capsule.readSavable("xAxis", Vector3f.UNIT_X.clone()));
+// yAxis.set((Vector3f) capsule.readSavable("yAxis", Vector3f.UNIT_Y.clone()));
+// zAxis.set((Vector3f) capsule.readSavable("zAxis", Vector3f.UNIT_Z.clone()));
+// extent.set((Vector3f) capsule.readSavable("extent", Vector3f.ZERO.clone()));
+// correctCorners = false;
+// }
+//
+// @Override
+// public float getVolume() {
+// return (8*extent.x*extent.y*extent.z);
+// }
+//} \ No newline at end of file