git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@6971 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
		
			
				
	
	
		
			322 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * 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.ArrayList;
 | |
| import java.util.Arrays;
 | |
| import java.util.Collection;
 | |
| import java.util.List;
 | |
| import java.util.Set;
 | |
| 
 | |
| 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);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 |