From 85387062540d0692b3ee38594b567e38efabc8ab Mon Sep 17 00:00:00 2001 From: "sha..RD" Date: Thu, 10 Oct 2013 00:29:34 +0000 Subject: [PATCH] * Add capability to serialize BVH data structure for MeshCollisionShape when native bullet is used (see http://hub.jmonkeyengine.org/forum/topic/meshcollisionshape-serialize/) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10825 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- ...et_collision_shapes_MeshCollisionShape.cpp | 49 ++++- .../collision/shapes/MeshCollisionShape.java | 202 ++++++++++++------ .../collision/shapes/MeshCollisionShape.java | 18 +- 3 files changed, 196 insertions(+), 73 deletions(-) diff --git a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp index 475b110ef..a5eabcb3c 100644 --- a/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp +++ b/engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp @@ -31,11 +31,14 @@ */ /** - * Author: Normen Hansen - */ +* Author: Normen Hansen +*/ #include "com_jme3_bullet_collision_shapes_MeshCollisionShape.h" #include "jmeBulletUtil.h" #include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "btBulletDynamicsCommon.h" +#include "BulletCollision/Gimpact/btGImpactShape.h" + #ifdef __cplusplus extern "C" { @@ -47,24 +50,58 @@ extern "C" { * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_createShape - (JNIEnv * env, jobject object, jlong arrayId) { + (JNIEnv* env, jobject object,jboolean isMemoryEfficient,jboolean buildBVH, jlong arrayId) { jmeClasses::initJavaClasses(env); btTriangleIndexVertexArray* array = reinterpret_cast(arrayId); - btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(array, true, true); + btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(array, isMemoryEfficient, buildBVH); return reinterpret_cast(shape); } - + + JNIEXPORT jbyteArray JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_saveBVH(JNIEnv* env, jobject, jlong meshobj){ + btBvhTriangleMeshShape* mesh = reinterpret_cast(meshobj); + btOptimizedBvh* bvh = mesh->getOptimizedBvh(); + unsigned int ssize = bvh->calculateSerializeBufferSize(); + char* buffer = (char*)btAlignedAlloc(ssize, 16); + bool success = bvh->serialize(buffer, ssize, true); + if(!success){ + jclass newExc = env->FindClass("java/lang/RuntimeException"); + env->ThrowNew(newExc, "Unableto Serialize, native error reported"); + } + + jbyteArray byteArray = env->NewByteArray(ssize); + env->SetByteArrayRegion(byteArray, 0, ssize , (jbyte*) buffer); + btAlignedFree(buffer); + return byteArray; + }; + + JNIEXPORT jlong JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_setBVH(JNIEnv* env, jobject,jbyteArray bytearray,jlong meshobj){ + int len = env->GetArrayLength (bytearray); + void* buffer = btAlignedAlloc(len, 16); + env->GetByteArrayRegion (bytearray, 0, len, reinterpret_cast(buffer)); + + btOptimizedBvh* bhv = btOptimizedBvh::deSerializeInPlace(buffer, len, true); + btBvhTriangleMeshShape* mesh = reinterpret_cast(meshobj); + mesh->setOptimizedBvh(bhv); + return reinterpret_cast(buffer); + }; + /* * Class: com_jme3_bullet_collision_shapes_MeshCollisionShape * Method: finalizeNative * Signature: (J)V */ JNIEXPORT void JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_finalizeNative - (JNIEnv * env, jobject object, jlong arrayId){ + (JNIEnv* env, jobject object, jlong arrayId,jlong nativeBVHBuffer){ btTriangleIndexVertexArray* array = reinterpret_cast(arrayId); delete(array); + if (nativeBVHBuffer > 0) { + void* buffer = reinterpret_cast(nativeBVHBuffer); + btAlignedFree(buffer); + } } + #ifdef __cplusplus } #endif + diff --git a/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java index 4d68e029a..9f36d28a8 100644 --- a/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java +++ b/engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java @@ -31,6 +31,13 @@ */ package com.jme3.bullet.collision.shapes; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; + import com.jme3.bullet.util.NativeMeshUtil; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; @@ -40,41 +47,91 @@ import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Basic mesh collision shape + * * @author normenhansen */ public class MeshCollisionShape extends CollisionShape { + private static final String VERTEX_BASE = "vertexBase"; + private static final String TRIANGLE_INDEX_BASE = "triangleIndexBase"; + private static final String TRIANGLE_INDEX_STRIDE = "triangleIndexStride"; + private static final String VERTEX_STRIDE = "vertexStride"; + private static final String NUM_TRIANGLES = "numTriangles"; + private static final String NUM_VERTICES = "numVertices"; + private static final String NATIVE_BVH = "nativeBvh"; protected int numVertices, numTriangles, vertexStride, triangleIndexStride; protected ByteBuffer triangleIndexBase, vertexBase; protected long meshId = 0; + protected long nativeBVHBuffer = 0; + private boolean memoryOptimized; public MeshCollisionShape() { } /** - * creates a collision shape from the given TriMesh - * @param mesh the TriMesh to use + * Creates a collision shape from the given Mesh. + * Default behavior, more optimized for memory usage. + * + * @param mesh */ public MeshCollisionShape(Mesh mesh) { - createCollisionMesh(mesh); + this(mesh, true); } + /** + * Creates a collision shape from the given Mesh. + * memoryOptimized determines if optimized instead of + * quantized BVH will be used. + * Internally, memoryOptimized BVH is slower to calculate (~4x) + * but also smaller (~0.5x). + * It is preferable to use the memory optimized version and then serialize + * the resulting MeshCollisionshape as this will also save the + * generated BVH. + * An exception can be procedurally / generated collision shapes, where + * the generation time is more of a concern + * + * @param mesh the Mesh to use + * @param memoryOptimized True to generate a memory optimized BVH, + * false to generate quantized BVH. + */ + public MeshCollisionShape(final Mesh mesh, final boolean memoryOptimized) { + this.memoryOptimized = memoryOptimized; + this.createCollisionMesh(mesh); + } + + /** + * Advanced constructor, usually you don’t want to use this, but the Mesh + * based one. Passing false values can lead to a crash, use at own risk + * + * This constructor bypasses all copy logic normally used, this allows for + * faster bullet shape generation when using procedurally generated Meshes. + * + * + * @param indices the raw index buffer + * @param vertices the raw vertex buffer + * @param memoryOptimized use quantisize BVH, uses less memory, but slower + */ + public MeshCollisionShape(ByteBuffer indices, ByteBuffer vertices, boolean memoryOptimized) { + this.triangleIndexBase = indices; + this.vertexBase = vertices; + this.numVertices = vertices.limit() / 4 / 3; + this.numTriangles = this.triangleIndexBase.limit() / 4 / 3; + this.vertexStride = 12; + this.triangleIndexStride = 12; + this.memoryOptimized = memoryOptimized; + this.createShape(true); + } + private void createCollisionMesh(Mesh mesh) { - triangleIndexBase = BufferUtils.createByteBuffer(mesh.getTriangleCount() * 3 * 4); - vertexBase = BufferUtils.createByteBuffer(mesh.getVertexCount() * 3 * 4); - numVertices = mesh.getVertexCount(); - vertexStride = 12; //3 verts * 4 bytes per. - numTriangles = mesh.getTriangleCount(); - triangleIndexStride = 12; //3 index entries * 4 bytes each. + this.triangleIndexBase = BufferUtils.createByteBuffer(mesh.getTriangleCount() * 3 * 4); + this.vertexBase = BufferUtils.createByteBuffer(mesh.getVertexCount() * 3 * 4); + this.numVertices = mesh.getVertexCount(); + this.vertexStride = 12; // 3 verts * 4 bytes per. + this.numTriangles = mesh.getTriangleCount(); + this.triangleIndexStride = 12; // 3 index entries * 4 bytes each. IndexBuffer indices = mesh.getIndicesAsList(); FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); @@ -93,69 +150,86 @@ public class MeshCollisionShape extends CollisionShape { vertices.rewind(); vertices.clear(); - createShape(); + this.createShape(true); } - /** - * creates a jme mesh from the collision shape, only needed for debugging - */ -// public Mesh createJmeMesh(){ -// return Converter.convert(bulletMesh); -// } - public void write(JmeExporter ex) throws IOException { + @Override + public void write(final JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); - capsule.write(numVertices, "numVertices", 0); - capsule.write(numTriangles, "numTriangles", 0); - capsule.write(vertexStride, "vertexStride", 0); - capsule.write(triangleIndexStride, "triangleIndexStride", 0); - - capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]); - capsule.write(vertexBase.array(), "vertexBase", new byte[0]); + capsule.write(numVertices, MeshCollisionShape.NUM_VERTICES, 0); + capsule.write(numTriangles, MeshCollisionShape.NUM_TRIANGLES, 0); + capsule.write(vertexStride, MeshCollisionShape.VERTEX_STRIDE, 0); + capsule.write(triangleIndexStride, MeshCollisionShape.TRIANGLE_INDEX_STRIDE, 0); + + triangleIndexBase.position(0); + byte[] triangleIndexBasearray = new byte[triangleIndexBase.limit()]; + triangleIndexBase.get(triangleIndexBasearray); + capsule.write(triangleIndexBasearray, MeshCollisionShape.TRIANGLE_INDEX_BASE, null); + + vertexBase.position(0); + byte[] vertexBaseArray = new byte[vertexBase.limit()]; + vertexBase.get(vertexBaseArray); + capsule.write(vertexBaseArray, MeshCollisionShape.VERTEX_BASE, null); + + if (memoryOptimized) { + byte[] data = saveBVH(objectId); + capsule.write(data, MeshCollisionShape.NATIVE_BVH, null); + } } - public void read(JmeImporter im) throws IOException { + @Override + public void read(final JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); - numVertices = capsule.readInt("numVertices", 0); - numTriangles = capsule.readInt("numTriangles", 0); - vertexStride = capsule.readInt("vertexStride", 0); - triangleIndexStride = capsule.readInt("triangleIndexStride", 0); - - triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0])); - vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0])).order(ByteOrder.nativeOrder()); - createShape(); + this.numVertices = capsule.readInt(MeshCollisionShape.NUM_VERTICES, 0); + this.numTriangles = capsule.readInt(MeshCollisionShape.NUM_TRIANGLES, 0); + this.vertexStride = capsule.readInt(MeshCollisionShape.VERTEX_STRIDE, 0); + this.triangleIndexStride = capsule.readInt(MeshCollisionShape.TRIANGLE_INDEX_STRIDE, 0); + + this.triangleIndexBase = BufferUtils.createByteBuffer(capsule.readByteArray(MeshCollisionShape.TRIANGLE_INDEX_BASE, null)); + this.vertexBase = BufferUtils.createByteBuffer(capsule.readByteArray(MeshCollisionShape.VERTEX_BASE, null)); + + byte[] nativeBvh = capsule.readByteArray(MeshCollisionShape.NATIVE_BVH, null); + if (nativeBvh == null) { + // Either using non memory optimized BVH or old J3O file + memoryOptimized = false; + createShape(true); + } else { + // Using memory optimized BVH, load from J3O, then assign it. + memoryOptimized = true; + createShape(false); + nativeBVHBuffer = setBVH(nativeBvh, this.objectId); + } } - protected void createShape() { -// bulletMesh = new IndexedMesh(); -// bulletMesh.numVertices = numVertices; -// bulletMesh.numTriangles = numTriangles; -// bulletMesh.vertexStride = vertexStride; -// bulletMesh.triangleIndexStride = triangleIndexStride; -// bulletMesh.triangleIndexBase = triangleIndexBase; -// bulletMesh.vertexBase = vertexBase; -// bulletMesh.triangleIndexBase = triangleIndexBase; -// TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride); -// objectId = new BvhTriangleMeshShape(tiv, true); -// objectId.setLocalScaling(Converter.convert(getScale())); -// objectId.setMargin(margin); - meshId = NativeMeshUtil.createTriangleIndexVertexArray(triangleIndexBase, vertexBase, numTriangles, numVertices, vertexStride, triangleIndexStride); - Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Mesh {0}", Long.toHexString(meshId)); - objectId = createShape(meshId); - Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); - setScale(scale); - setMargin(margin); + private void createShape(boolean buildBvt) { + this.meshId = NativeMeshUtil.createTriangleIndexVertexArray(this.triangleIndexBase, this.vertexBase, this.numTriangles, this.numVertices, this.vertexStride, this.triangleIndexStride); + Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Mesh {0}", Long.toHexString(this.meshId)); + this.objectId = createShape(memoryOptimized, buildBvt, this.meshId); + Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(this.objectId)); + this.setScale(this.scale); + this.setMargin(this.margin); } - private native long createShape(long meshId); + /** + * returns the pointer to the native buffer used by the in place + * de-serialized shape, must be freed when not used anymore! + */ + private native long setBVH(byte[] buffer, long objectid); + + private native byte[] saveBVH(long objectId); + + private native long createShape(boolean memoryOptimized, boolean buildBvt, long meshId); @Override - protected void finalize() throws Throwable { + public void finalize() throws Throwable { super.finalize(); - Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Finalizing Mesh {0}", Long.toHexString(meshId)); - finalizeNative(meshId); + Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Finalizing Mesh {0}", Long.toHexString(this.meshId)); + if (this.meshId > 0) { + this.finalizeNative(this.meshId, this.nativeBVHBuffer); + } } - private native void finalizeNative(long objectId); -} + private native void finalizeNative(long objectId, long nativeBVHBuffer); +} \ No newline at end of file diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java index e701bf645..0feb4753e 100644 --- a/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java @@ -57,14 +57,26 @@ public class MeshCollisionShape extends CollisionShape { public MeshCollisionShape() { } + /** + * Creates a collision shape from the given TriMesh + * + * @param mesh + * the TriMesh to use + */ + public MeshCollisionShape(Mesh mesh) { + this(mesh, false); + } + /** - * creates a collision shape from the given TriMesh + * API compatibility with native bullet. + * * @param mesh the TriMesh to use + * @param dummy Unused */ - public MeshCollisionShape(Mesh mesh) { + public MeshCollisionShape(Mesh mesh, boolean dummy) { createCollisionMesh(mesh, new Vector3f(1, 1, 1)); } - + private void createCollisionMesh(Mesh mesh, Vector3f worldScale) { this.scale = worldScale; bulletMesh = Converter.convert(mesh);