예제 #1
0
/**
 * 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();
    }
  }
}