blob: e9a00802a64fd8a44a3f5f1f7ee8249af7028b92 [file] [log] [blame]
/*
* 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 jme3tools.optimize;
import com.jme3.bounding.BoundingBox;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.math.Ray;
import com.jme3.math.Triangle;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.debug.WireBox;
import java.util.*;
public class Octnode {
static final Vector3f[] extentMult = new Vector3f[]
{
new Vector3f( 1, 1, 1), // right top forw
new Vector3f(-1, 1, 1), // left top forw
new Vector3f( 1,-1, 1), // right bot forw
new Vector3f(-1,-1, 1), // left bot forw
new Vector3f( 1, 1,-1), // right top back
new Vector3f(-1, 1,-1), // left top back
new Vector3f( 1,-1,-1), // right bot back
new Vector3f(-1,-1,-1) // left bot back
};
final BoundingBox bbox;
final ArrayList<OCTTriangle> tris;
Geometry[] geoms;
final Octnode[] children = new Octnode[8];
boolean leaf = false;
FastOctnode fastNode;
public Octnode(BoundingBox bbox, ArrayList<OCTTriangle> tris){
this.bbox = bbox;
this.tris = tris;
}
private BoundingBox getChildBound(int side){
float extent = bbox.getXExtent() * 0.5f;
Vector3f center = new Vector3f(bbox.getCenter().x + extent * extentMult[side].x,
bbox.getCenter().y + extent * extentMult[side].y,
bbox.getCenter().z + extent * extentMult[side].z);
return new BoundingBox(center, extent, extent, extent);
}
private float getAdditionCost(BoundingBox bbox, OCTTriangle t){
if (bbox.intersects(t.get1(), t.get2(), t.get3())){
float d1 = bbox.distanceToEdge(t.get1());
float d2 = bbox.distanceToEdge(t.get2());
float d3 = bbox.distanceToEdge(t.get3());
return d1 + d2 + d3;
}
return Float.POSITIVE_INFINITY;
}
private void expandBoxToContainTri(BoundingBox bbox, OCTTriangle t){
Vector3f min = bbox.getMin(null);
Vector3f max = bbox.getMax(null);
BoundingBox.checkMinMax(min, max, t.get1());
BoundingBox.checkMinMax(min, max, t.get2());
BoundingBox.checkMinMax(min, max, t.get3());
bbox.setMinMax(min, max);
}
private boolean contains(BoundingBox bbox, OCTTriangle t){
if (bbox.contains(t.get1()) &&
bbox.contains(t.get2()) &&
bbox.contains(t.get3())){
return true;
}
return false;
}
public void subdivide(int depth, int minTrisPerNode){
if (tris == null || depth > 50 || bbox.getVolume() < 0.01f || tris.size() < minTrisPerNode){
// no need to subdivide anymore
leaf = true;
return;
}
ArrayList<OCTTriangle> keepTris = new ArrayList<OCTTriangle>();
ArrayList[] trisForChild = new ArrayList[8];
BoundingBox[] boxForChild = new BoundingBox[8];
// create boxes for children
for (int i = 0; i < 8; i++){
boxForChild[i] = getChildBound(i);
trisForChild[i] = new ArrayList<Triangle>();
}
for (OCTTriangle t : tris){
float lowestCost = Float.POSITIVE_INFINITY;
int lowestIndex = -1;
int numIntersecting = 0;
for (int i = 0; i < 8; i++){
BoundingBox childBox = boxForChild[i];
float cost = getAdditionCost(childBox, t);
if (cost < lowestCost){
lowestCost = cost;
lowestIndex = i;
numIntersecting++;
}
}
if (numIntersecting < 8 && lowestIndex > -1){
trisForChild[lowestIndex].add(t);
expandBoxToContainTri(boxForChild[lowestIndex], t);
}else{
keepTris.add(t);
}
// boolean wasAdded = false;
// for (int i = 0; i < 8; i++){
// BoundingBox childBox = boxForChild[i];
// if (contains(childBox, t)){
// trisForChild[i].add(t);
// wasAdded = true;
// break;
// }
// }
// if (!wasAdded){
// keepTris.add(t);
// }
}
tris.retainAll(keepTris);
for (int i = 0; i < 8; i++){
if (trisForChild[i].size() > 0){
children[i] = new Octnode(boxForChild[i], trisForChild[i]);
children[i].subdivide(depth + 1, minTrisPerNode);
}
}
}
public void subdivide(int minTrisPerNode){
subdivide(0, minTrisPerNode);
}
public void createFastOctnode(List<Geometry> globalGeomList){
fastNode = new FastOctnode();
if (geoms != null){
Collection<Geometry> geomsColl = Arrays.asList(geoms);
List<Geometry> myOptimizedList = GeometryBatchFactory.makeBatches(geomsColl);
int startIndex = globalGeomList.size();
globalGeomList.addAll(myOptimizedList);
fastNode.setOffset(startIndex);
fastNode.length = myOptimizedList.size();
}else{
fastNode.setOffset(0);
fastNode.length = 0;
}
for (int i = 0; i < 8; i++){
if (children[i] != null){
children[i].createFastOctnode(globalGeomList);
}
}
}
public void generateFastOctnodeLinks(Octnode parent, Octnode nextSibling, int side){
fastNode.setSide(side);
fastNode.next = nextSibling != null ? nextSibling.fastNode : null;
// We set the next sibling property by going in reverse order
Octnode prev = null;
for (int i = 7; i >= 0; i--){
if (children[i] != null){
children[i].generateFastOctnodeLinks(this, prev, i);
prev = children[i];
}
}
fastNode.child = prev != null ? prev.fastNode : null;
}
private void generateRenderSetNoCheck(Set<Geometry> renderSet, Camera cam){
if (geoms != null){
renderSet.addAll(Arrays.asList(geoms));
}
for (int i = 0; i < 8; i++){
if (children[i] != null){
children[i].generateRenderSetNoCheck(renderSet, cam);
}
}
}
public void generateRenderSet(Set<Geometry> renderSet, Camera cam){
// generateRenderSetNoCheck(renderSet, cam);
bbox.setCheckPlane(0);
cam.setPlaneState(0);
Camera.FrustumIntersect result = cam.contains(bbox);
if (result != Camera.FrustumIntersect.Outside){
if (geoms != null){
renderSet.addAll(Arrays.asList(geoms));
}
for (int i = 0; i < 8; i++){
if (children[i] != null){
if (result == Camera.FrustumIntersect.Inside){
children[i].generateRenderSetNoCheck(renderSet, cam);
}else{
children[i].generateRenderSet(renderSet, cam);
}
}
}
}
}
public void collectTriangles(Geometry[] inGeoms){
if (tris.size() > 0){
List<Geometry> geomsList = TriangleCollector.gatherTris(inGeoms, tris);
geoms = new Geometry[geomsList.size()];
geomsList.toArray(geoms);
}else{
geoms = null;
}
for (int i = 0; i < 8; i++){
if (children[i] != null){
children[i].collectTriangles(inGeoms);
}
}
}
public void renderBounds(RenderQueue rq, Matrix4f transform, WireBox box, Material mat){
int numChilds = 0;
for (int i = 0; i < 8; i++){
if (children[i] != null){
numChilds ++;
break;
}
}
if (geoms != null && numChilds == 0){
BoundingBox bbox2 = new BoundingBox(bbox);
bbox.transform(transform, bbox2);
// WireBox box = new WireBox(bbox2.getXExtent(), bbox2.getYExtent(),
// bbox2.getZExtent());
// WireBox box = new WireBox(1,1,1);
Geometry geom = new Geometry("bound", box);
geom.setLocalTranslation(bbox2.getCenter());
geom.setLocalScale(bbox2.getXExtent(), bbox2.getYExtent(),
bbox2.getZExtent());
geom.updateGeometricState();
geom.setMaterial(mat);
rq.addToQueue(geom, Bucket.Opaque);
box = null;
geom = null;
}
for (int i = 0; i < 8; i++){
if (children[i] != null){
children[i].renderBounds(rq, transform, box, mat);
}
}
}
public final void intersectWhere(Ray r, Geometry[] geoms, float sceneMin, float sceneMax,
CollisionResults results){
for (OCTTriangle t : tris){
float d = r.intersects(t.get1(), t.get2(), t.get3());
if (Float.isInfinite(d))
continue;
Vector3f contactPoint = new Vector3f(r.getDirection()).multLocal(d).addLocal(r.getOrigin());
CollisionResult result = new CollisionResult(geoms[t.getGeometryIndex()],
contactPoint,
d,
t.getTriangleIndex());
results.addCollision(result);
}
for (int i = 0; i < 8; i++){
Octnode child = children[i];
if (child == null)
continue;
if (child.bbox.intersects(r)){
child.intersectWhere(r, geoms, sceneMin, sceneMax, results);
}
}
}
}