Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.bullet.util;
     33 
     34 import com.bulletphysics.collision.shapes.ConcaveShape;
     35 import com.bulletphysics.collision.shapes.ConvexShape;
     36 import com.bulletphysics.collision.shapes.ShapeHull;
     37 import com.bulletphysics.collision.shapes.TriangleCallback;
     38 import com.bulletphysics.util.IntArrayList;
     39 import com.jme3.bullet.collision.shapes.CollisionShape;
     40 import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
     41 import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
     42 import com.jme3.math.Matrix3f;
     43 import com.jme3.scene.Geometry;
     44 import com.jme3.scene.Mesh;
     45 import com.jme3.scene.Node;
     46 import com.jme3.scene.Spatial;
     47 import com.jme3.scene.VertexBuffer.Type;
     48 import com.jme3.util.BufferUtils;
     49 import com.jme3.util.TempVars;
     50 import java.nio.FloatBuffer;
     51 import java.util.ArrayList;
     52 import java.util.Iterator;
     53 import java.util.List;
     54 import javax.vecmath.Vector3f;
     55 
     56 /**
     57  *
     58  * @author CJ Hare, normenhansen
     59  */
     60 public class DebugShapeFactory {
     61 
     62     /** The maximum corner for the aabb used for triangles to include in ConcaveShape processing.*/
     63     private static final Vector3f aabbMax = new Vector3f(1e30f, 1e30f, 1e30f);
     64     /** The minimum corner for the aabb used for triangles to include in ConcaveShape processing.*/
     65     private static final Vector3f aabbMin = new Vector3f(-1e30f, -1e30f, -1e30f);
     66 
     67     /**
     68      * Creates a debug shape from the given collision shape. This is mostly used internally.<br>
     69      * To attach a debug shape to a physics object, call <code>attachDebugShape(AssetManager manager);</code> on it.
     70      * @param collisionShape
     71      * @return
     72      */
     73     public static Spatial getDebugShape(CollisionShape collisionShape) {
     74         if (collisionShape == null) {
     75             return null;
     76         }
     77         Spatial debugShape;
     78         if (collisionShape instanceof CompoundCollisionShape) {
     79             CompoundCollisionShape shape = (CompoundCollisionShape) collisionShape;
     80             List<ChildCollisionShape> children = shape.getChildren();
     81             Node node = new Node("DebugShapeNode");
     82             for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) {
     83                 ChildCollisionShape childCollisionShape = it.next();
     84                 CollisionShape ccollisionShape = childCollisionShape.shape;
     85                 Geometry geometry = createDebugShape(ccollisionShape);
     86 
     87                 // apply translation
     88                 geometry.setLocalTranslation(childCollisionShape.location);
     89 
     90                 // apply rotation
     91                 TempVars vars = TempVars.get();
     92 
     93                 Matrix3f tempRot = vars.tempMat3;
     94 
     95                 tempRot.set(geometry.getLocalRotation());
     96                 childCollisionShape.rotation.mult(tempRot, tempRot);
     97                 geometry.setLocalRotation(tempRot);
     98 
     99                 vars.release();
    100 
    101                 node.attachChild(geometry);
    102             }
    103             debugShape = node;
    104         } else {
    105             debugShape = createDebugShape(collisionShape);
    106         }
    107         if (debugShape == null) {
    108             return null;
    109         }
    110         debugShape.updateGeometricState();
    111         return debugShape;
    112     }
    113 
    114     private static Geometry createDebugShape(CollisionShape shape) {
    115         Geometry geom = new Geometry();
    116         geom.setMesh(DebugShapeFactory.getDebugMesh(shape));
    117 //        geom.setLocalScale(shape.getScale());
    118         geom.updateModelBound();
    119         return geom;
    120     }
    121 
    122     public static Mesh getDebugMesh(CollisionShape shape) {
    123         Mesh mesh = null;
    124         if (shape.getCShape() instanceof ConvexShape) {
    125             mesh = new Mesh();
    126             mesh.setBuffer(Type.Position, 3, getVertices((ConvexShape) shape.getCShape()));
    127             mesh.getFloatBuffer(Type.Position).clear();
    128         } else if (shape.getCShape() instanceof ConcaveShape) {
    129             mesh = new Mesh();
    130             mesh.setBuffer(Type.Position, 3, getVertices((ConcaveShape) shape.getCShape()));
    131             mesh.getFloatBuffer(Type.Position).clear();
    132         }
    133         return mesh;
    134     }
    135 
    136     /**
    137      *  Constructs the buffer for the vertices of the concave shape.
    138      *
    139      * @param concaveShape the shape to get the vertices for / from.
    140      * @return the shape as stored by the given broadphase rigid body.
    141      */
    142     private static FloatBuffer getVertices(ConcaveShape concaveShape) {
    143         // Create the call back that'll create the vertex buffer
    144         BufferedTriangleCallback triangleProcessor = new BufferedTriangleCallback();
    145         concaveShape.processAllTriangles(triangleProcessor, aabbMin, aabbMax);
    146 
    147         // Retrieve the vextex and index buffers
    148         return triangleProcessor.getVertices();
    149     }
    150 
    151     /**
    152      *  Processes the given convex shape to retrieve a correctly ordered FloatBuffer to
    153      *  construct the shape from with a TriMesh.
    154      *
    155      * @param convexShape the shape to retreieve the vertices from.
    156      * @return the vertices as a FloatBuffer, ordered as Triangles.
    157      */
    158     private static FloatBuffer getVertices(ConvexShape convexShape) {
    159         // Check there is a hull shape to render
    160         if (convexShape.getUserPointer() == null) {
    161             // create a hull approximation
    162             ShapeHull hull = new ShapeHull(convexShape);
    163             float margin = convexShape.getMargin();
    164             hull.buildHull(margin);
    165             convexShape.setUserPointer(hull);
    166         }
    167 
    168         // Assert state - should have a pointer to a hull (shape) that'll be drawn
    169         assert convexShape.getUserPointer() != null : "Should have a shape for the userPointer, instead got null";
    170         ShapeHull hull = (ShapeHull) convexShape.getUserPointer();
    171 
    172         // Assert we actually have a shape to render
    173         assert hull.numTriangles() > 0 : "Expecting the Hull shape to have triangles";
    174         int numberOfTriangles = hull.numTriangles();
    175 
    176         // The number of bytes needed is: (floats in a vertex) * (vertices in a triangle) * (# of triangles) * (size of float in bytes)
    177         final int numberOfFloats = 3 * 3 * numberOfTriangles;
    178         FloatBuffer vertices = BufferUtils.createFloatBuffer(numberOfFloats);
    179 
    180         // Force the limit, set the cap - most number of floats we will use the buffer for
    181         vertices.limit(numberOfFloats);
    182 
    183         // Loop variables
    184         final IntArrayList hullIndicies = hull.getIndexPointer();
    185         final List<Vector3f> hullVertices = hull.getVertexPointer();
    186         Vector3f vertexA, vertexB, vertexC;
    187         int index = 0;
    188 
    189         for (int i = 0; i < numberOfTriangles; i++) {
    190             // Grab the data for this triangle from the hull
    191             vertexA = hullVertices.get(hullIndicies.get(index++));
    192             vertexB = hullVertices.get(hullIndicies.get(index++));
    193             vertexC = hullVertices.get(hullIndicies.get(index++));
    194 
    195             // Put the verticies into the vertex buffer
    196             vertices.put(vertexA.x).put(vertexA.y).put(vertexA.z);
    197             vertices.put(vertexB.x).put(vertexB.y).put(vertexB.z);
    198             vertices.put(vertexC.x).put(vertexC.y).put(vertexC.z);
    199         }
    200 
    201         vertices.clear();
    202         return vertices;
    203     }
    204 }
    205 
    206 /**
    207  *  A callback is used to process the triangles of the shape as there is no direct access to a concave shapes, shape.
    208  *  <p/>
    209  *  The triangles are simply put into a list (which in extreme condition will cause memory problems) then put into a direct buffer.
    210  *
    211  * @author CJ Hare
    212  */
    213 class BufferedTriangleCallback extends TriangleCallback {
    214 
    215     private ArrayList<Vector3f> vertices;
    216 
    217     public BufferedTriangleCallback() {
    218         vertices = new ArrayList<Vector3f>();
    219     }
    220 
    221     @Override
    222     public void processTriangle(Vector3f[] triangle, int partId, int triangleIndex) {
    223         // Three sets of individual lines
    224         // The new Vector is needed as the given triangle reference is from a pool
    225         vertices.add(new Vector3f(triangle[0]));
    226         vertices.add(new Vector3f(triangle[1]));
    227         vertices.add(new Vector3f(triangle[2]));
    228     }
    229 
    230     /**
    231      *  Retrieves the vertices from the Triangle buffer.
    232      */
    233     public FloatBuffer getVertices() {
    234         // There are 3 floats needed for each vertex (x,y,z)
    235         final int numberOfFloats = vertices.size() * 3;
    236         FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(numberOfFloats);
    237 
    238         // Force the limit, set the cap - most number of floats we will use the buffer for
    239         verticesBuffer.limit(numberOfFloats);
    240 
    241         // Copy the values from the list to the direct float buffer
    242         for (Vector3f v : vertices) {
    243             verticesBuffer.put(v.x).put(v.y).put(v.z);
    244         }
    245 
    246         vertices.clear();
    247         return verticesBuffer;
    248     }
    249 }
    250