📄 boundingsphere.java
字号:
/*
* 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 com.jme.bounding;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme.intersection.IntersectionRecord;
import com.jme.math.FastMath;
import com.jme.math.Plane;
import com.jme.math.Quaternion;
import com.jme.math.Ray;
import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.math.Plane.Side;
import com.jme.scene.TriMesh;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.geom.BufferUtils;
/**
* <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());
public float radius;
private static final long serialVersionUID = 2L;
static final private float radiusEpsilon = 1f + 0.00001f;
static final private FloatBuffer _mergeBuf = BufferUtils.createVector3Buffer(8);
static private final Vector3f[] verts = new Vector3f[3];
/**
* 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, TriMesh 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 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 Vector3f();
FloatBuffer buf = BufferUtils.createFloatBuffer(points.limit());
points.rewind();
buf.put(points);
buf.flip();
recurseMini(buf, buf.limit() / 3, 0, 0);
}
private static Vector3f tempA = new Vector3f(), tempB = new Vector3f(), tempC = new Vector3f(), tempD = new Vector3f();
/**
* 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.set(0, 0, 0);
break;
case 1:
this.radius = 1f - radiusEpsilon;
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);
return;
}
for (int i = 0; i < p; i++) {
BufferUtils.populateFromBuffer(tempA, points, i+ap);
if (tempA.distanceSquared(center) - (radius * radius) > radiusEpsilon - 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);
}
}
}
/**
* 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() * radiusEpsilon;
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() * radiusEpsilon;
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) + radiusEpsilon - 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) + radiusEpsilon - 1f;
}
/**
* <code>transform</code> modifies the center of the sphere to reflect the
* change made via a rotation, translation and scale.
*
* @param rotate
* the rotation change.
* @param translate
* the translation change.
* @param scale
* the size change.
* @param store
* sphere to store result in
* @return BoundingVolume
* @return ref
*/
public BoundingVolume transform(Quaternion rotate, Vector3f translate,
Vector3f scale, 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(scale, sphere.center);
rotate.mult(sphere.center, sphere.center);
sphere.center.addLocal(translate);
sphere.radius = FastMath.abs(getMaxAxis(scale) * radius) + radiusEpsilon - 1f;
return sphere;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -