/*
* Copyright (c) 2003-2009 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 org.mt4j.components.bounds;
import java.nio.FloatBuffer;
import org.mt4j.components.MTComponent;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh;
import org.mt4j.components.visibleComponents.shapes.mesh.Triangle;
import org.mt4j.util.camera.IFrustum;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.Plane;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.ToolsBuffers;
import org.mt4j.util.math.Vector3D;
import processing.core.PGraphics;
/**
* <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, Christopher Ruff
*/
public class BoundingSphere implements IBoundingShape{
// private static final Logger logger = Logger.getLogger(BoundingSphere.class.getName());
public float radius;
private static final long serialVersionUID = 2L;
static final private float radiusEpsilon = 1f + 0.00001f;
static final private FloatBuffer _mergeBuf = ToolsBuffers.createVector3Buffer(8);
static private final Vector3D[] verts = new Vector3D[3];
protected Vector3D center = new Vector3D();
private MTComponent peerComponent;
/** The Constant _compVect1. */
protected static final transient Vector3D _compVect1 = new Vector3D();
// private Vector3D centerPointObjSpace;
private Vector3D[] worldVecs;
private boolean worldVecsDirty;
private Vector3D centerPointWorld;
private boolean centerWorldDirty;
private float radiusWorld;
private boolean radiusWorldDirty;
// /**
// * 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, Vector3D c) {
// this.center.setValues(c);
// this.radius = r;
// }
/**
* The Constructor.
*
* @param peerComponent the peer component
*/
public BoundingSphere(AbstractShape peerComponent){
this.peerComponent = peerComponent;
// this.computeFromVertices(peerComponent.getGeometryInfo().getVertices());
// this.computeFromPoints(peerComponent.getGeometryInfo().getVertBuff());
// this.computeFromPoints(Vector3D.getDeepVertexArrayCopy(peerComponent.getGeometryInfo().getVertices()));
if (peerComponent instanceof MTTriangleMesh){
MTTriangleMesh mesh = (MTTriangleMesh)peerComponent;
Triangle[] tris = mesh.getTriangles();
this.computeFromTris(tris, 0, tris.length);
}else{
this.computeFromPoints(Vector3D.getDeepVertexArrayCopy(peerComponent.getGeometryInfo().getVertices())); //FIXME can we avoid the copy??
// this.computeFromPoints(peerComponent.getGeometryInfo().getVertBuff()); //FIXME can we avoid the copy??
}
this.worldVecsDirty = true;
this.centerWorldDirty = true;
// this.worldVecs = this.getVectorsGlobal();
// this.centerPointWorld = this.getCenterPointGlobal();
this.radiusWorldDirty = true;
// this.radiusWorld = this.getRadiusWorld();
}
/**
* The Constructor.
*
* @param peerComponent the peer component
* @param vectors the vectors
*/
public BoundingSphere(MTComponent peerComponent, Vector3D[] vectors){
this.peerComponent = peerComponent;
this.computeFromPoints(vectors);
this.worldVecsDirty = true;
this.centerWorldDirty = true;
// this.worldVecs = this.getVectorsGlobal(); //Do it only if requested to save memory (no cached values)
// this.centerPointWorld = this.getCenterPointGlobal();
this.radiusWorldDirty = true;
// this.radiusWorld = this.getRadiusWorld();
}
/**
* The Constructor.
*
* @param peerComponent the peer component
* @param center the center
* @param radius the radius
*/
public BoundingSphere(MTComponent peerComponent, Vector3D center, float radius){
this.peerComponent = peerComponent;
this.radius = radius;
this.center = new Vector3D(center);
this.worldVecsDirty = true;
this.centerWorldDirty = true;
// this.worldVecs = this.getVectorsGlobal();
// this.centerPointWorld = this.getCenterPointGlobal();
this.radiusWorldDirty = true;
// this.radiusWorld = this.getRadiusWorld();
}
/* (non-Javadoc)
* @see org.mt4j.components.bounds.IBoundingShape#setGlobalBoundsChanged()
*/
public void setGlobalBoundsChanged(){
this.worldVecsDirty = true;
this.centerWorldDirty = true;
this.radiusWorldDirty = true;
}
/**
* Draw bounds.
*
* @param g the g
*/
public void drawBounds(PGraphics g){
g.pushMatrix();
g.pushStyle();
g.fill(150,180);
Vector3D l = this.getCenterPointLocal();
g.translate(l.x, l.y, l.z);
g.sphere(this.getRadius());
g.popStyle();
g.popMatrix();
}
/**
* <code>getRadius</code> returns the radius of the bounding sphere. (in object space)
*
* @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(Vector3D[] points) {
calcWelzl(points);
}
/**
* <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;
}
Vector3D[] vertList = new Vector3D[(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);
vertList[count++] = tris[i].v0;
vertList[count++] = tris[i].v1;
vertList[count++] = tris[i].v2;
}
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, MTTriangleMesh mesh, int start, int end) {
// if (end - start <= 0) {
// return;
// }
//
// Vector3D[] vertList = new Vector3D[(end - start) * 3];
//
// Triangle[] tris = mesh.getTriangles();
//
// int count = 0;
// for (int i = start; i < end; i++) {
// mesh.getTriangle(indices[i], verts);
// vertList[count++] = new Vector3D(verts[0]);
// vertList[count++] = new Vector3D(verts[1]);
// vertList[count++] = new Vector3D(verts[2]);
// }
//
// averagePoints(vertList);
// }
/**
* Calculates a minimum bounding sphere for the set of points. The algorithm
* was originally found at
* http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1
* in C++ and translated to java by Cep21
*
* @param points
* The points to calculate the minimum bounds from.
*/
public void calcWelzl(FloatBuffer points) {
if (center == null){
center = new Vector3D();
}
FloatBuffer buf = ToolsBuffers.createFloatBuffer(points.limit());
points.rewind();
float[] pointsArr = ToolsBuffers.getFloatArray(points);
buf.put(pointsArr);
buf.flip();
this.recurseMini(buf, buf.limit() / 3, 0, 0);
}
/**
* Calculates a minimum bounding sphere for the set of points. The algorithm
* was originally found at
* http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-SmallestEnclosingSpheres&forum=cotd&id=-1
* in C++ and translated to java by Cep21
*
* @param points
* The points to calculate the minimum bounds from.
*/
public void calcWelzl(Vector3D[] points) {
if (center == null){
center = new Vector3D();
}
this.recurseMini(points, points.length, 0, 0);
}
private static Vector3D tempA = new Vector3D(), tempB = new Vector3D(), tempC = new Vector3D(), tempD = new Vector3D();
/**
* 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) {
switch (b) {
case 0:
this.radius = 0;
this.center.setXYZ(0, 0, 0);
break;
case 1:
this.radius = 1f - radiusEpsilon;
ToolsBuffers.populateFromBuffer(center, points, ap-1);
break;
case 2:
ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
setSphere(tempA, tempB);
break;
case 3:
ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
setSphere(tempA, tempB, tempC);
break;
case 4:
ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
ToolsBuffers.populateFromBuffer(tempD, points, ap-4);
setSphere(tempA, tempB, tempC, tempD);
return;
}
for (int i = 0; i < p; i++) {
ToolsBuffers.populateFromBuffer(tempA, points, i+ap);
if (Vector3D.distanceSquared(tempA, center) - (radius * radius) > radiusEpsilon - 1f) {
for (int j = i; j > 0; j--) {
ToolsBuffers.populateFromBuffer(tempB, points, j + ap);
ToolsBuffers.populateFromBuffer(tempC, points, j - 1 + ap);
ToolsBuffers.setInBuffer(tempC, points, j + ap);
ToolsBuffers.setInBuffer(tempB, points, j - 1 + ap);
}
recurseMini(points, i, b + 1, ap + 1);
}
}
}
/**
* 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(Vector3D[] points, int p, int b, int ap) {
switch (b) {
case 0:
this.radius = 0;
this.center.setXYZ(0, 0, 0);
break;
case 1:
this.radius = 1f - radiusEpsilon;
// ToolsBuffers.populateFromBuffer(center, points, ap-1);
this.center.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
break;
case 2:
tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
// ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
// ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
setSphere(tempA, tempB);
break;
case 3:
// ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
// ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
// ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
tempC.setXYZ(points[ap-3].x, points[ap-3].y, points[ap-3].z);
setSphere(tempA, tempB, tempC);
break;
case 4:
// ToolsBuffers.populateFromBuffer(tempA, points, ap-1);
// ToolsBuffers.populateFromBuffer(tempB, points, ap-2);
// ToolsBuffers.populateFromBuffer(tempC, points, ap-3);
// ToolsBuffers.populateFromBuffer(tempD, points, ap-4);
tempA.setXYZ(points[ap-1].x, points[ap-1].y, points[ap-1].z);
tempB.setXYZ(points[ap-2].x, points[ap-2].y, points[ap-2].z);
tempC.setXYZ(points[ap-3].x, points[ap-3].y, points[ap-3].z);
tempD.setXYZ(points[ap-4].x, points[ap-4].y, points[ap-4].z);
setSphere(tempA, tempB, tempC, tempD);
return;
}
for (int i = 0; i < p; i++) {
// ToolsBuffers.populateFromBuffer(tempA, points, i+ap);
tempA.setXYZ(points[i+ap].x, points[i+ap].y, points[i+ap].z);
if (Vector3D.distanceSquared(tempA, center) - (radius * radius) > radiusEpsilon - 1f) {
for (int j = i; j > 0; j--) {
// ToolsBuffers.populateFromBuffer(tempB, points, j + ap);
// ToolsBuffers.populateFromBuffer(tempC, points, j - 1 + ap);
tempB.setXYZ(points[j + ap].x, points[j + ap].y, points[j + ap].z);
tempC.setXYZ(points[j - 1 + ap].x, points[j - 1 + ap].y, points[j - 1 + ap].z);
// ToolsBuffers.setInBuffer(tempC, points, j + ap);
// ToolsBuffers.setInBuffer(tempB, points, j - 1 + ap);
points[j+ap].setXYZ(tempC.x, tempC.y, tempC.z);
points[j - 1 + ap].setXYZ(tempB.x, tempB.y, tempB.z);
}
recurseMini(points, i, b + 1, ap + 1);
}
}
}
/**
* 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(Vector3D O, Vector3D A, Vector3D B, Vector3D C) {
Vector3D a = A.getSubtracted(O);
Vector3D b = B.getSubtracted(O);
Vector3D c = C.getSubtracted(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.setXYZ(0, 0, 0);
radius = 0;
} else {
// Vector3D o = a.cross(b).multLocal(c.lengthSquared()).addLocal(
// c.cross(a).multLocal(b.lengthSquared())).addLocal(
// b.cross(c).multLocal(a.lengthSquared())).divideLocal(
// Denominator);
Vector3D o = a.getCross(b).scaleLocal(c.lengthSquared()).addLocal(
c.getCross(a).scaleLocal(b.lengthSquared())).addLocal(
b.getCross(c).scaleLocal(a.lengthSquared())).scaleLocal(
1f/Denominator);
radius = o.length() * radiusEpsilon;
// O.add(o, center);
center.setValues(O.getAdded(o));
}
}
/**
* 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(Vector3D O, Vector3D A, Vector3D B) {
Vector3D a = A.getSubtracted(O);
Vector3D b = B.getSubtracted(O);
Vector3D acrossB = a.getCross(b);
float Denominator = 2.0f * acrossB.dot(acrossB);
if (Denominator == 0) {
center.setXYZ(0, 0, 0);
radius = 0;
} else {
// Vector3D o = acrossB.cross(a).multLocal(b.lengthSquared())
// .addLocal(b.cross(acrossB).multLocal(a.lengthSquared()))
// .divideLocal(Denominator);
Vector3D o = acrossB.getCross(a).scaleLocal(b.lengthSquared())
.addLocal(b.getCross(acrossB).scaleLocal(a.lengthSquared()))
.divideLocal(Denominator);
// .scaleLocal(1f/Denominator);
radius = o.length() * radiusEpsilon;
// O.add(o, center);
center.setValues(O.getAdded(o));
}
}
/**
* 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(Vector3D O, Vector3D A) {
radius = ToolsMath.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) + radiusEpsilon - 1f;
// center.interpolate(O, A, .5f);
center.setValues(this.interpolate(O, A, .5f));
}
/**
* Sets this vector to the interpolation by changeAmnt from beginVec to finalVec
* this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
* @param beginVec the beging vector (changeAmnt=0)
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginVec towards finalVec
*/
public Vector3D interpolate(Vector3D beginVec,Vector3D finalVec, float changeAmnt) {
float x =(1-changeAmnt)*beginVec.x + changeAmnt*finalVec.x;
float y =(1-changeAmnt)*beginVec.y + changeAmnt*finalVec.y;
float z =(1-changeAmnt)*beginVec.z + changeAmnt*finalVec.z;
return new Vector3D(x,y,z);
}
/**
* <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(Vector3D[] points) {
//logger.info("Bounding Sphere calculated using average points.");
center = points[0].getCopy();
for (int i = 1; i < points.length; i++) {
center.addLocal(points[i]);
}
float quantity = 1.0f / points.length;
// center.multLocal(quantity);
center.scaleLocal(quantity);
float maxRadiusSqr = 0;
for (int i = 0; i < points.length; i++) {
Vector3D diff = points[i].getSubtracted(center);
float radiusSqr = diff.lengthSquared();
if (radiusSqr > maxRadiusSqr) {
maxRadiusSqr = radiusSqr;
}
}
radius = (float) Math.sqrt(maxRadiusSqr) + radiusEpsilon - 1f;
}
private float getMaxAxis(Vector3D scale) {
float x = ToolsMath.abs(scale.x);
float y = ToolsMath.abs(scale.y);
float z = ToolsMath.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 Side whichSide(Plane plane) {
// float distance = plane.pseudoDistance(center);
// if (distance <= -radius) { return Side.NEGATIVE; }
// if (distance >= radius) { return Side.POSITIVE; }
// return Side.NONE;
// }
/**
* <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 int
*/
public int whichSide(Plane plane) {
// float distance = plane.pseudoDistance(center);
// if (distance <= -radius) { return Side.NEGATIVE; }
// if (distance >= radius) { return Side.POSITIVE; }
// return Side.NONE;
int side = plane.classifyPoint(center);
return side;
}
// /**
// * <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();
// Vector3D temp_center = sphere.getCenter();
// BoundingSphere rVal = new BoundingSphere();
// return merge(temp_radius, temp_center, rVal);
// }
//
// case Capsule: {
// BoundingCapsule capsule = (BoundingCapsule) volume;
// float temp_radius = capsule.getRadius()
// + capsule.getLineSegment().getExtent();
// Vector3D temp_center = capsule.getCenter();
// BoundingSphere rVal = new BoundingSphere();
// return merge(temp_radius, temp_center, rVal);
// }
//
// case AABB: {
// BoundingBox box = (BoundingBox) volume;
// Vector3D radVect = new Vector3D(box.xExtent, box.yExtent,
// box.zExtent);
// Vector3D 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;
//
// }
// }
private Vector3D tmpRadVect = new Vector3D();
// /**
// * <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();
// Vector3D temp_center = sphere.getCenter();
// return merge(temp_radius, temp_center, this);
// }
//
// case AABB: {
// BoundingBox box = (BoundingBox) volume;
// Vector3D radVect = tmpRadVect;
// radVect.set(box.xExtent, box.yExtent, box.zExtent);
// Vector3D temp_center = box.center;
// return merge(radVect.length(), temp_center, this);
// }
//
// case OBB: {
// return mergeOBB((OrientedBoundingBox) volume);
// }
//
// case Capsule: {
// BoundingCapsule capsule = (BoundingCapsule) volume;
// return merge(capsule.getRadius() + capsule.getLineSegment().getExtent(),
// capsule.getCenter(), this);
// }
//
// 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;
// Vector3D oldCenter = _compVect2.set( center );
//
// // compute new radius and center from obb points
// computeFromPoints(_mergeBuf);
// Vector3D 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, Vector3D temp_center,
// BoundingSphere rVal) {
// Vector3D diff = temp_center.subtract(center, _compVect1);
// float lengthSquared = diff.lengthSquared();
// float radiusDiff = temp_radius - radius;
//
// float fRDiffSqr = radiusDiff * radiusDiff;
//
// if (fRDiffSqr >= lengthSquared) {
// if (radiusDiff <= 0.0f) {
// return this;
// }
//
// Vector3D rCenter = rVal.getCenter();
// if ( rCenter == null ) {
// rVal.setCenter( rCenter = new Vector3D() );
// }
// rCenter.set(temp_center);
// rVal.setRadius(temp_radius);
// return rVal;
// }
//
// float length = (float) Math.sqrt(lengthSquared);
//
// Vector3D rCenter = rVal.getCenter();
// if ( rCenter == null ) {
// rVal.setCenter( rCenter = new Vector3D() );
// }
// if (length > radiusEpsilon) {
// 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));
// return rVal;
// }
public Vector3D getCenter() {
return center;
}
// /*
// * (non-Javadoc)
// *
// * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
// */
// public boolean intersectsSphere(BoundingSphere bs) {
// if (!Vector3D.isValidVector(center) || !Vector3D.isValidVector(bs.center)) return false;
//
// Vector3D diff = getCenter().subtract(bs.getCenter(), _compVect1);
// float rsum = getRadius() + bs.getRadius();
// return (diff.dot(diff) <= rsum * rsum);
// }
//
// /*
// * (non-Javadoc)
// *
// * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
// */
// public boolean intersectsBoundingBox(BoundingBox bb) {
// if (!Vector3D.isValidVector(center) || !Vector3D.isValidVector(bb.center)) return false;
//
// if (FastMath.abs(bb.center.x - getCenter().x) < getRadius()
// + bb.xExtent
// && FastMath.abs(bb.center.y - getCenter().y) < getRadius()
// + bb.yExtent
// && FastMath.abs(bb.center.z - getCenter().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);
// }
//
// public boolean intersectsCapsule(BoundingCapsule bc) {
// return bc.intersectsSphere(this);
// }
/**
* Check a vector... if it is null or its floats are NaN or infinite,
* return false. Else return true.
* @param vector the vector to check
* @return true or false as stated above.
*/
public static boolean isValidVector(Vector3D vector) {
if (vector == null) return false;
if (Float.isNaN(vector.x) ||
Float.isNaN(vector.y) ||
Float.isNaN(vector.z)) return false;
if (Float.isInfinite(vector.x) ||
Float.isInfinite(vector.y) ||
Float.isInfinite(vector.z)) return false;
return true;
}
/*
* (non-Javadoc)
*
* @see com.jme.bounding.BoundingVolume#intersects(com.jme.math.Ray)
*/
public boolean intersects(Ray ray) {
if (!isValidVector(center))
return false;
// Vector3D diff = _compVect1.set(ray.getOrigin()).subtractLocal(getCenter());
Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(getCenter());
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);
if (b >= 0.0) {
return false;
}
return b*b >= a;
}
public Vector3D getIntersectionLocal(Ray ray){
Vector3D rayDir = ray.getRayDirectionNormalized();
Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(this.getCenter());
float a = diff.dot(diff) - (getRadius()*getRadius());
float a1, discr, root;
if (a <= 0.0) {
// inside sphere
a1 = rayDir.dot(diff);
discr = (a1 * a1) - a;
root = ToolsMath.sqrt(discr);
float[] distances = new float[] { root - a1 };
Vector3D hitVect = new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()) ;
// Vector3D[] points = new Vector3D[] {
// hitVect
// };
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
return hitVect;
}
a1 = rayDir.dot(diff);
if (a1 >= 0.0) {
// return new IntersectionRecord();
return null;
}
discr = a1*a1 - a;
if (discr < 0.0){
// return new IntersectionRecord();
return null;
}else if (discr >= ToolsMath.ZERO_TOLERANCE) {
root = ToolsMath.sqrt(discr);
float[] distances = new float[] { -a1 - root, -a1 + root };
Vector3D[] points = new Vector3D[] {
new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()),
new Vector3D(rayDir).scaleLocal(distances[1]).addLocal(ray.getRayStartPoint())
};
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
//FIXME WELCHEN PUNKT NEHMEN?
return points[0];
} else {
float[] distances = new float[] { -a1 };
Vector3D[] points = new Vector3D[] {
new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())};
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
return points[0];
}
}
// /*
// * (non-Javadoc)
// *
// * @see com.jme.bounding.BoundingVolume#intersectsWhere(com.jme.math.Ray)
// */
// public IntersectionRecord intersectsWhere(Ray ray) {
// Vector3D rayDir = ray.getDirection();
//
// Vector3D diff = _compVect1.setValues(ray.getRayStartPoint()).subtractLocal(this.getCenter());
// float a = diff.dot(diff) - (getRadius()*getRadius());
// float a1, discr, root;
//
// if (a <= 0.0) {
// // inside sphere
// a1 = rayDir.dot(diff);
//
// discr = (a1 * a1) - a;
// root = FastMath.sqrt(discr);
// float[] distances = new float[] { root - a1 };
//
// Vector3D[] points = new Vector3D[] {
// new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())
// };
//
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
// }
//
//
// a1 = rayDir.dot(diff);
// if (a1 >= 0.0) {
// return new IntersectionRecord();
// }
//
//
// discr = a1*a1 - a;
// if (discr < 0.0){
// return new IntersectionRecord();
// }else if (discr >= FastMath.ZERO_TOLERANCE) {
// root = FastMath.sqrt(discr);
// float[] distances = new float[] { -a1 - root, -a1 + root };
// Vector3D[] points = new Vector3D[] {
// new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint()),
// new Vector3D(rayDir).scaleLocal(distances[1]).addLocal(ray.getRayStartPoint())
// };
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
// } else {
// float[] distances = new float[] { -a1 };
// Vector3D[] points = new Vector3D[] {
// new Vector3D(rayDir).scaleLocal(distances[0]).addLocal(ray.getRayStartPoint())};
// IntersectionRecord record = new IntersectionRecord(distances, points);
// return record;
// }
// }
//@Override
public boolean containsPointLocal(Vector3D point) {
return Vector3D.distanceSquared(getCenter(), point)
<
(getRadius() * getRadius());
// return getCenter().distanceSquared(point) < (getRadius() * getRadius());
}
public float distanceToEdge(Vector3D point) {
return Vector3D.distance(center, point) - radius;
}
public float getVolume() {
return 4 * ToolsMath.ONE_THIRD * ToolsMath.PI * radius * radius * radius;
}
//@Override
public Vector3D getCenterPointLocal() {
return center.getCopy();
}
//@Override
public Vector3D getCenterPointGlobal() {
if (centerWorldDirty){
Vector3D tmp = this.getCenterPointLocal();
tmp.transform(this.peerComponent.getGlobalMatrix());
this.centerPointWorld = tmp;
this.centerWorldDirty = false;
return this.centerPointWorld;
}else{
return this.centerPointWorld;
}
// Vector3D tmp = center.getCopy();
// tmp.transform(this.peerComponent.getAbsoluteLocalToWorldMatrix());
// return tmp;
}
//@Override
public float getHeightXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getHeightXYVectLocal().length();
case RELATIVE_TO_PARENT:{
Vector3D p = this.getHeightXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
}
case GLOBAL:{
Vector3D p = this.getHeightXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
}
default:
return -1;
}
}
//@Override
public Vector3D getHeightXYVectLocal() {
return new Vector3D(0, this.getRadius()*2,0);
}
//@Override
public Vector3D[] getVectorsLocal() {
return new Vector3D[]{this.center.getCopy()};
}
//@Override
public Vector3D[] getVectorsGlobal() {
if (this.worldVecsDirty){
Vector3D[] vecs = Vector3D.getDeepVertexArrayCopy(this.getVectorsLocal());
Vector3D.transFormArrayLocal(this.peerComponent.getGlobalMatrix(), vecs);
this.worldVecs = vecs;
this.worldVecsDirty = false;
return this.worldVecs;
}else{
return this.worldVecs;
}
// Vector3D tmp = center.getCopy();
// tmp.transform(this.peerComponent.getAbsoluteLocalToWorldMatrix());
// return new Vector3D[]{tmp};
}
//@Override
public float getWidthXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getWidthXYVectLocal().length();
case RELATIVE_TO_PARENT:{
Vector3D p = this.getWidthXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
}
case GLOBAL:{
Vector3D p = this.getWidthXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
}
default:
return -1;
}
}
//@Override
public Vector3D getWidthXYVectLocal() {
return new Vector3D(this.getRadius()*2, 0,0);
}
//@Override
public boolean isContainedInFrustum(IFrustum frustum) {
int test = frustum.isSphereInFrustum(this.getCenterPointGlobal(), this.getRadiusWorld());
if (test == IFrustum.OUTSIDE
){
return false;
}else{
return true;
}
}
//TODO cache centerPointWorld und radiusWorld!
//To avoid obj creation for each frustum test
private Vector3D tmpVec = new Vector3D();
private Matrix tmpMatrix = new Matrix();
private float getRadiusWorld(){
if (this.radiusWorldDirty){
tmpVec.setXYZ(this.getRadius(), 0,0);
tmpMatrix.set(this.peerComponent.getGlobalMatrix());
tmpMatrix.removeTranslationFromMatrix();
tmpVec.transform(tmpMatrix);
this.radiusWorld = tmpVec.length();
this.radiusWorldDirty = false;
return this.radiusWorld;
}else{
return this.radiusWorld;
}
// tmpVec.setXYZ(this.getRadius(), 0,0);
//// Matrix m = new Matrix(this.peerComponent.getAbsoluteLocalToWorldMatrix());
// tmpMatrix.set(this.peerComponent.getAbsoluteLocalToWorldMatrix());
// tmpMatrix.removeTranslationFromMatrix();
// tmpVec.transform(tmpMatrix);
// return tmpVec.length();
}
}