/** * ManifoldResult is a helper class to manage contact results. * * @author jezek2 */ public class ManifoldResult implements DiscreteCollisionDetectorInterface.Result { protected final BulletStack stack = BulletStack.get(); protected final ObjectPool<ManifoldPoint> pointsPool = BulletPool.get(ManifoldPoint.class); private PersistentManifold manifoldPtr; // we need this for compounds private final Transform rootTransA = new Transform(); private final Transform rootTransB = new Transform(); private CollisionObject body0; private CollisionObject body1; private int partId0; private int partId1; private int index0; private int index1; public ManifoldResult() {} public ManifoldResult(CollisionObject body0, CollisionObject body1) { init(body0, body1); } public void init(CollisionObject body0, CollisionObject body1) { this.body0 = body0; this.body1 = body1; this.rootTransA.set(body0.getWorldTransform()); this.rootTransB.set(body1.getWorldTransform()); } public PersistentManifold getPersistentManifold() { return manifoldPtr; } public void setPersistentManifold(PersistentManifold manifoldPtr) { this.manifoldPtr = manifoldPtr; } public void setShapeIdentifiers(int partId0, int index0, int partId1, int index1) { this.partId0 = partId0; this.partId1 = partId1; this.index0 = index0; this.index1 = index1; } public void addContactPoint(Vector3f normalOnBInWorld, Vector3f pointInWorld, float depth) { assert (manifoldPtr != null); // order in manifold needs to match if (depth > manifoldPtr.getContactBreakingThreshold()) { return; } stack.vectors.push(); try { boolean isSwapped = manifoldPtr.getBody0() != body0; Vector3f pointA = stack.vectors.get(); pointA.scaleAdd(depth, normalOnBInWorld, pointInWorld); Vector3f localA = stack.vectors.get(); Vector3f localB = stack.vectors.get(); if (isSwapped) { rootTransB.invXform(pointA, localA); rootTransA.invXform(pointInWorld, localB); } else { rootTransA.invXform(pointA, localA); rootTransB.invXform(pointInWorld, localB); } ManifoldPoint newPt = pointsPool.get(); newPt.init(localA, localB, normalOnBInWorld, depth); newPt.positionWorldOnA.set(pointA); newPt.positionWorldOnB.set(pointInWorld); int insertIndex = manifoldPtr.getCacheEntry(newPt); newPt.combinedFriction = calculateCombinedFriction(body0, body1); newPt.combinedRestitution = calculateCombinedRestitution(body0, body1); /// todo, check this for any side effects if (insertIndex >= 0) { // const btManifoldPoint& oldPoint = m_manifoldPtr->getContactPoint(insertIndex); manifoldPtr.replaceContactPoint(newPt, insertIndex); } else { manifoldPtr.addManifoldPoint(newPt); } // User can override friction and/or restitution if (BulletGlobals.gContactAddedCallback != null && // and if either of the two bodies requires custom material ((body0.getCollisionFlags() & CollisionFlags.CUSTOM_MATERIAL_CALLBACK) != 0 || (body1.getCollisionFlags() & CollisionFlags.CUSTOM_MATERIAL_CALLBACK) != 0)) { // experimental feature info, for per-triangle material etc. CollisionObject obj0 = isSwapped ? body1 : body0; CollisionObject obj1 = isSwapped ? body0 : body1; BulletGlobals.gContactAddedCallback.invoke( newPt, obj0, partId0, index0, obj1, partId1, index1); } pointsPool.release(newPt); } finally { stack.vectors.pop(); } } /// User can override this material combiner by implementing gContactAddedCallback and setting // body0->m_collisionFlags |= btCollisionObject::customMaterialCallback; private static float calculateCombinedFriction(CollisionObject body0, CollisionObject body1) { float friction = body0.getFriction() * body1.getFriction(); float MAX_FRICTION = 10f; if (friction < -MAX_FRICTION) { friction = -MAX_FRICTION; } if (friction > MAX_FRICTION) { friction = MAX_FRICTION; } return friction; } private static float calculateCombinedRestitution(CollisionObject body0, CollisionObject body1) { return body0.getRestitution() * body1.getRestitution(); } public void refreshContactPoints() { assert (manifoldPtr != null); if (manifoldPtr.getNumContacts() == 0) { return; } boolean isSwapped = manifoldPtr.getBody0() != body0; if (isSwapped) { manifoldPtr.refreshContactPoints(rootTransB, rootTransA); } else { manifoldPtr.refreshContactPoints(rootTransA, rootTransB); } } }
/** * Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy * optimization. Uses an interface to access the triangles to allow for sharing graphics/physics * triangles. * * @author jezek2 */ public class BvhTriangleMeshShape extends TriangleMeshShape { private OptimizedBvh bvh; private boolean useQuantizedAabbCompression; private boolean ownsBvh; private ObjectPool<MyNodeOverlapCallback> myNodeCallbacks = BulletPool.get(MyNodeOverlapCallback.class); public BvhTriangleMeshShape() { super(null); this.bvh = null; this.ownsBvh = false; } public BvhTriangleMeshShape( StridingMeshInterface meshInterface, boolean useQuantizedAabbCompression) { this(meshInterface, useQuantizedAabbCompression, true); } public BvhTriangleMeshShape( StridingMeshInterface meshInterface, boolean useQuantizedAabbCompression, boolean buildBvh) { super(meshInterface); this.bvh = null; this.useQuantizedAabbCompression = useQuantizedAabbCompression; this.ownsBvh = false; // construct bvh from meshInterface // #ifndef DISABLE_BVH Vector3f bvhAabbMin = new Vector3f(), bvhAabbMax = new Vector3f(); meshInterface.calculateAabbBruteForce(bvhAabbMin, bvhAabbMax); if (buildBvh) { bvh = new OptimizedBvh(); bvh.build(meshInterface, useQuantizedAabbCompression, bvhAabbMin, bvhAabbMax); ownsBvh = true; } // JAVA NOTE: moved from TriangleMeshShape recalcLocalAabb(); // #endif //DISABLE_BVH } /** * Optionally pass in a larger bvh aabb, used for quantization. This allows for deformations * within this aabb. */ public BvhTriangleMeshShape( StridingMeshInterface meshInterface, boolean useQuantizedAabbCompression, Vector3f bvhAabbMin, Vector3f bvhAabbMax) { this(meshInterface, useQuantizedAabbCompression, bvhAabbMin, bvhAabbMax, true); } /** * Optionally pass in a larger bvh aabb, used for quantization. This allows for deformations * within this aabb. */ public BvhTriangleMeshShape( StridingMeshInterface meshInterface, boolean useQuantizedAabbCompression, Vector3f bvhAabbMin, Vector3f bvhAabbMax, boolean buildBvh) { super(meshInterface); this.bvh = null; this.useQuantizedAabbCompression = useQuantizedAabbCompression; this.ownsBvh = false; // construct bvh from meshInterface // #ifndef DISABLE_BVH if (buildBvh) { bvh = new OptimizedBvh(); bvh.build(meshInterface, useQuantizedAabbCompression, bvhAabbMin, bvhAabbMax); ownsBvh = true; } // JAVA NOTE: moved from TriangleMeshShape recalcLocalAabb(); // #endif //DISABLE_BVH } @Override public BroadphaseNativeType getShapeType() { return BroadphaseNativeType.TRIANGLE_MESH_SHAPE_PROXYTYPE; } public void performRaycast( TriangleRaycastCallback callback, Vector3f raySource, Vector3f rayTarget) { MyNodeOverlapCallback myNodeCallback = myNodeCallbacks.get(); myNodeCallback.init(callback, meshInterface); bvh.reportRayOverlappingNodex(myNodeCallback, raySource, rayTarget); myNodeCallbacks.release(myNodeCallback); } public void performConvexcast( TriangleConvexcastCallback callback, Vector3f raySource, Vector3f rayTarget, Vector3f aabbMin, Vector3f aabbMax) { MyNodeOverlapCallback myNodeCallback = myNodeCallbacks.get(); myNodeCallback.init(callback, meshInterface); bvh.reportBoxCastOverlappingNodex(myNodeCallback, raySource, rayTarget, aabbMin, aabbMax); myNodeCallbacks.release(myNodeCallback); } /** Perform bvh tree traversal and report overlapping triangles to 'callback'. */ @Override public void processAllTriangles(TriangleCallback callback, Vector3f aabbMin, Vector3f aabbMax) { // #ifdef DISABLE_BVH // // brute force traverse all triangles // btTriangleMeshShape::processAllTriangles(callback,aabbMin,aabbMax); // #else // first get all the nodes MyNodeOverlapCallback myNodeCallback = myNodeCallbacks.get(); myNodeCallback.init(callback, meshInterface); bvh.reportAabbOverlappingNodex(myNodeCallback, aabbMin, aabbMax); myNodeCallbacks.release(myNodeCallback); // #endif//DISABLE_BVH } public void refitTree() { bvh.refit(meshInterface); recalcLocalAabb(); } /** * For a fast incremental refit of parts of the tree. Note: the entire AABB of the tree will * become more conservative, it never shrinks. */ public void partialRefitTree(Vector3f aabbMin, Vector3f aabbMax) { bvh.refitPartial(meshInterface, aabbMin, aabbMax); VectorUtil.setMin(localAabbMin, aabbMin); VectorUtil.setMax(localAabbMax, aabbMax); } @Override public String getName() { return "BVHTRIANGLEMESH"; } @Override public void setLocalScaling(Vector3f scaling) { stack.vectors.push(); try { Vector3f tmp = stack.vectors.get(); tmp.sub(getLocalScaling(), scaling); if (tmp.lengthSquared() > BulletGlobals.SIMD_EPSILON) { super.setLocalScaling(scaling); /* if (ownsBvh) { m_bvh->~btOptimizedBvh(); btAlignedFree(m_bvh); } */ /// m_localAabbMin/m_localAabbMax is already re-calculated in btTriangleMeshShape. We could // just scale aabb, but this needs some more work bvh = new OptimizedBvh(); // rebuild the bvh... bvh.build(meshInterface, useQuantizedAabbCompression, localAabbMin, localAabbMax); } } finally { stack.vectors.pop(); } } public OptimizedBvh getOptimizedBvh() { return bvh; } public void setOptimizedBvh(OptimizedBvh bvh) { assert (this.bvh == null); assert (!ownsBvh); this.bvh = bvh; ownsBvh = false; } public boolean usesQuantizedAabbCompression() { return useQuantizedAabbCompression; } //////////////////////////////////////////////////////////////////////////// protected static class MyNodeOverlapCallback implements NodeOverlapCallback { public StridingMeshInterface meshInterface; public TriangleCallback callback; private Vector3f[] triangle /*[3]*/ = new Vector3f[] {new Vector3f(), new Vector3f(), new Vector3f()}; private VertexData data = new VertexData(); public MyNodeOverlapCallback() {} public void init(TriangleCallback callback, StridingMeshInterface meshInterface) { this.meshInterface = meshInterface; this.callback = callback; } public void processNode(int nodeSubPart, int nodeTriangleIndex) { meshInterface.getLockedReadOnlyVertexIndexBase(data, nodeSubPart); // int* gfxbase = (int*)(indexbase+nodeTriangleIndex*indexstride); ByteBuffer gfxbase_ptr = data.indexbase; int gfxbase_index = (nodeTriangleIndex * data.indexstride); assert (data.indicestype == ScalarType.PHY_INTEGER || data.indicestype == ScalarType.PHY_SHORT); Vector3f meshScaling = meshInterface.getScaling(); for (int j = 2; j >= 0; j--) { int graphicsindex; if (data.indicestype == ScalarType.PHY_SHORT) { graphicsindex = gfxbase_ptr.getShort(gfxbase_index + j * 2) & 0xFFFF; } else { graphicsindex = gfxbase_ptr.getInt(gfxbase_index + j * 4); } // float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); ByteBuffer graphicsbase_ptr = data.vertexbase; int graphicsbase_index = graphicsindex * data.stride; triangle[j].set( graphicsbase_ptr.getFloat(graphicsbase_index + 4 * 0) * meshScaling.x, graphicsbase_ptr.getFloat(graphicsbase_index + 4 * 1) * meshScaling.y, graphicsbase_ptr.getFloat(graphicsbase_index + 4 * 2) * meshScaling.z); } /* Perform ray vs. triangle collision here */ callback.processTriangle(triangle, nodeSubPart, nodeTriangleIndex); meshInterface.unLockReadOnlyVertexBase(nodeSubPart); data.unref(); } } }