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