package net.cis.client.game.common.engine;
import java.nio.FloatBuffer;
import org.apache.commons.logging.LogFactory;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
public class SimpleBoundingVolumeFactory
{
/**
* Creates all known BoundingVolumes and chooses the one with the least enclosed volume.
* The created bounding volume is then set as the spatial bounding volume.
*
* @param s Spatial to calculate the bounding volume for
* @return the Bounding volume that was set. NULL if an error occurred
*/
public static BoundingVolume createBestBoundingVolume(Spatial s)
{
try
{
BoundingVolume vol = s.getWorldBound();
BoundingVolume bs = getBoundingSphere(s);
BoundingVolume bb = getAABoundingBox(s);
// System.out.println("For: " + s);
// System.out.println("Old: " + vol + " = " + vol.getVolume());
// System.out.println("Sphere: " + bs + " = " + bs.getVolume());
// System.out.println("Box: " + bb + " = " + bb.getVolume());
//TODO: List and sort for minimum getVolume();
if(vol.getVolume() <= bs.getVolume() && vol.getVolume() <= bb.getVolume() && vol.getVolume() > 0)
return vol;
if(bs.getVolume() < bb.getVolume() && bs.getVolume() > 0)
{
s.setModelBound(bs);
return bs;
}
else if(bb.getVolume() > 0)
{
s.setModelBound(bb);
return bb;
}
}
catch(Exception e)
{
LogFactory.getLog(SimpleBoundingVolumeFactory.class).error("Failed to calculate best bounding volume for " + s, e);
}
return null;
}
/**
* Calculates the BoundingSphere for the Spatial. In case of a Node the BoundingSphere will encompass all child nodes
*/
private static BoundingVolume getBoundingSphere(Spatial s)
{
if(s instanceof Geometry)
return getBoundingSphere((Geometry)s);
if(s instanceof Node)
{
Node n = (Node)s;
BoundingVolume result = new BoundingSphere();
int validChildCount = 0;
for(Spatial cs : n.getChildren())
{
BoundingVolume bs = getBoundingSphere(cs);
if(bs != null)
{
result = result.merge(bs);
++validChildCount;
}
}
if(validChildCount > 0)
return result;
}
return null;
}
/**
* Calculates the Axis Aligned Bounding Box for the Spatial. In case of a Node the BoundingSphere will encompass all child nodes
*/
private static BoundingVolume getAABoundingBox(Spatial s)
{
if(s instanceof Geometry)
return getAABoundingBox((Geometry)s);
if(s instanceof Node)
{
Node n = (Node)s;
BoundingVolume result = null;
int validChildCount = 0;
for(Spatial cs : n.getChildren())
{
BoundingVolume bs = getAABoundingBox(cs);
if(bs != null)
{
if(result == null)
result = bs;
else
result.merge(bs);
++validChildCount;
}
}
if(validChildCount > 0)
return result;
}
return null;
}
/**
* Calculates the BoundingSphere for a Geometry
*/
public static BoundingSphere getBoundingSphere(Geometry g)
{
VertexBuffer posBuffer = g.getMesh().getBuffer(Type.Position);
BoundingSphere bs = new BoundingSphere();
bs.computeFromPoints((FloatBuffer)posBuffer.getData());
return bs;
}
/**
* Calculates the Axis Aligned Bounding Box for a Geometry
* @param g
* @return
*/
public static BoundingBox getAABoundingBox(Geometry g)
{
VertexBuffer posBuffer = g.getMesh().getBuffer(Type.Position);
BoundingBox bb = new BoundingBox();
bb.computeFromPoints((FloatBuffer)posBuffer.getData());
return bb;
}
}