* 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
experimental
sha..RD 11 years ago
parent c9c4bdeb54
commit 8538706254
  1. 49
      engine/src/bullet-native/com_jme3_bullet_collision_shapes_MeshCollisionShape.cpp
  2. 202
      engine/src/bullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java
  3. 18
      engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java

@ -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<btTriangleIndexVertexArray*>(arrayId);
btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(array, true, true);
btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(array, isMemoryEfficient, buildBVH);
return reinterpret_cast<jlong>(shape);
}
JNIEXPORT jbyteArray JNICALL Java_com_jme3_bullet_collision_shapes_MeshCollisionShape_saveBVH(JNIEnv* env, jobject, jlong meshobj){
btBvhTriangleMeshShape* mesh = reinterpret_cast<btBvhTriangleMeshShape*>(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<jbyte*>(buffer));
btOptimizedBvh* bhv = btOptimizedBvh::deSerializeInPlace(buffer, len, true);
btBvhTriangleMeshShape* mesh = reinterpret_cast<btBvhTriangleMeshShape*>(meshobj);
mesh->setOptimizedBvh(bhv);
return reinterpret_cast<jlong>(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<btTriangleIndexVertexArray*>(arrayId);
delete(array);
if (nativeBVHBuffer > 0) {
void* buffer = reinterpret_cast<void*>(nativeBVHBuffer);
btAlignedFree(buffer);
}
}
#ifdef __cplusplus
}
#endif

@ -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.
* <code>memoryOptimized</code> determines if optimized instead of
* quantized BVH will be used.
* Internally, <code>memoryOptimized</code> 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 dont 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);
}

@ -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);

Loading…
Cancel
Save