Search code examples
bulletammo.js

Ammo.js custom mesh collision with sphere


I'm trying to generate collisions for every object/mesh. They are all static and should collide with a ball/sphere, which is dynamic.

My code looks like this:

const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(-1.1726552248001099, 2.6692488193511963, 0));
transform.setRotation(new Ammo.btQuaternion(0.5, -0.5, 0.5, 0.4999999701976776));
const motionState = new Ammo.btDefaultMotionState(transform);

// Vertices and indices are parsed by GLTF parser by loaders.gl
const vertices = Entity.vertices;
const indices = Entity.indices;
const scale = [0.15933185815811157, 1.1706310510635376, 0.15933185815811157];

// btConvexHullShape or btBvhTriangleMeshShape, see below.

const localInertia = new Ammo.btVector3(0, 0, 0);

const rbInfo = new Ammo.btRigidBodyConstructionInfo(0, motionState, shape, localInertia);
const object = new Ammo.btRigidBody(rbInfo);

this._physicsWorld.addRigidBody(object);

I tried 2 methods: btConvexHullShape and btBvhTriangleMeshShape and both did not work.

  • btConvexHullShape
const shape = new Ammo.btConvexHullShape();
for (let i = 0; i < vertices.length / 3; i++) {
    shape.addPoint(new Ammo.btVector3(vertices[i * 3] * scale[0], vertices[i * 3 + 1] * scale[1], vertices[i * 3 + 2] * scale[2]));
}

Path of the sphere using btConvexHullShape is something like this (it doesn't enter the hole): Path of the sphere using btConvexHullShape

I'm guessing Ammo.js connects top-most points? How can I control which points are connected based on indices? It also seems that this approach is very slow (30k vertices takes some time), so I tried:

  • btBvhTriangleMeshShape
const mesh = new Ammo.btTriangleMesh(true, true);
for (let i = 0; i * 3 < indices.length; i++) {
    mesh.addTriangle(
        new Ammo.btVector3(vertices[indices[i * 3]] * scale[0], vertices[indices[i * 3] + 1] * scale[1], vertices[indices[i * 3] + 2] * scale[2]),
        new Ammo.btVector3(vertices[indices[i * 3 + 1]] * scale[0], vertices[indices[i * 3 + 1] + 1] * scale[1], vertices[indices[i * 3 + 1] + 2] * scale[2]),
        new Ammo.btVector3(vertices[indices[i * 3 + 2]] * scale[0], vertices[indices[i * 3 + 2] + 1] * scale[1], vertices[indices[i * 3 + 2] + 2] * scale[2]),
        false
    );
}
const shape = new Ammo.btBvhTriangleMeshShape(mesh, true, true);

This method is a bit better, but the sphere just goes through the object when it bounces once (bounces off the circle and goes through the object (red circle)): Path of the sphere using btBvhTriangleMeshShape


Solution

  • The reason why btConvexHullShape wasn't working (although the collision around it actually works) is because it isn't for concave shapes.

    As for the btBvhTriangleMeshShape, I have been inputting the vertices the wrong way. I had to multiply the indices by 3 (because each vertex has 3 components).

    Here's the full working code:

    const transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin(new Ammo.btVector3(-1.1726552248001099, 2.6692488193511963, 0));
    transform.setRotation(new Ammo.btQuaternion(0.5, -0.5, 0.5, 0.4999999701976776));
    const motionState = new Ammo.btDefaultMotionState(transform);
    
    // Vertices and indices are parsed by GLTF parser by loaders.gl
    const vertices = Entity.vertices;
    const indices = Entity.indices;
    const scale = [0.15933185815811157, 1.1706310510635376, 0.15933185815811157];
    
    const mesh = new Ammo.btTriangleMesh(true, true);
    mesh.setScaling(new Ammo.btVector3(scale[0], scale[1], scale[2]));
    for (let i = 0; i * 3 < indices.length; i++) {
        mesh.addTriangle(
            new Ammo.btVector3(vertices[indices[i * 3] * 3], vertices[indices[i * 3] * 3 + 1], vertices[indices[i * 3] * 3 + 2]),
            new Ammo.btVector3(vertices[indices[i * 3 + 1] * 3], vertices[indices[i * 3 + 1] * 3 + 1], vertices[indices[i * 3 + 1] * 3 + 2]),
            new Ammo.btVector3(vertices[indices[i * 3 + 2] * 3], vertices[indices[i * 3 + 2] * 3 + 1], vertices[indices[i * 3 + 2] * 3 + 2]),
            false
        );
    }
    const shape = new Ammo.btBvhTriangleMeshShape(mesh, true, true);
    
    const localInertia = new Ammo.btVector3(0, 0, 0);
    
    const rbInfo = new Ammo.btRigidBodyConstructionInfo(0, motionState, shape, localInertia);
    const object = new Ammo.btRigidBody(rbInfo);
    
    this._physicsWorld.addRigidBody(object);