Mesh Collider Broken

So I came across this issue today where the mesh collider wasn’t working and no amount of changes to my settings would fix it.

Luckily, simply deleting the mesh asset and reimporting it fixed it and it is working stellar once again!

But it makes me to wonder… if this mesh collider is broken, are there any others that might be broken as well? Is this a known issue at PlayCanvas?

Hi @CaseyDeCoder,

That’s interesting, I can’t reproduce this. If it’s the same asset being used it wouldn’t normally matter if you reimport it or not. The mesh collider is being generated on runtime, not on import time.

Are you able to reproduce this on a sample project? It would be good to know if there is an issue to log on the engine repo.

‘Unfortunately’ I’m not able to reproduce this now, but it has happened to me once before, hence why I was compelled to make this post.
If it happens to me again, I’ll be sure to make efforts to isolate the problem.

3 Likes

Finally found the issue!
Mesh Collider Not Working

When using differently scaled mesh colliders of the same mesh, only 1 instance of the collider will be used.

When I originally stated that reimporting the mesh fixes the issue, I had only added the mesh back to my stairs but had forgotten they were used on some decoration as well. After reapplying it, the issue came back and with some experimentation I found this to be the issue.

For us personally I don’t think it will be such a big deal, but I’d still like to make an issue for this. Should I do that on the PlayCanvas or the Ammo github?

3 Likes

PlayCanvas repo please :slight_smile:

Something for @will I believe to potentially look at

Placing a mesh into the physics world is a computationally expensive operation - the engine generates an Ammo compound shape from a number of bvh triangle meshes, one for each mesh instance. Once generated the trimeshes then get cached for future re-use. As a result, when you use the same mesh for another collider, the trimeshes are taken from the cache, instead of generating new ones. Please, add an issue to the engine repo:

For the moment you could patch the following engine method to avoid grabbing a mesh from the cache (or implement your own logic for when to do that):

// do this before any of your level entities are initialized
this.app.systems.collision.implementations.mesh.createPhysicalShape = function (entity, data) {
   // patched method
}
3 Likes

Seems like I’m facing the same issue, if I use the same model asset as a mesh on different scale entities same thing happens, also getting some weird jumping/bouncing issues, any way to patch it app wise/globally?

Yes, that’s a known issue, right now the mesh collider will cache the shape and reuse it even if other entities have a different scale.

Workarounds:

  1. Patch the engine to disable the cache or add your own rules.
  2. Import multiple variations of the same for each different scale.

huh,seems like that’s gonna be a tough one, ideally you would allow it to be reused if the scale is same :thinking: is there any working patch in your memory?

Check this issue, it links also a forum discussion with some insight:

Seems like it changed since then

This method of patching wont work or am I wrong

// do this before any of your level entities are initialized
this.app.systems.collision.implementations.mesh.createPhysicalShape = function (entity, data) {
   // patched method
}

its now createAmmoMesh

    createAmmoMesh(mesh, node, shape) {
        let triMesh;

        if (this.system._triMeshCache[mesh.id]) {
            triMesh = this.system._triMeshCache[mesh.id];
        } else {
            const vb = mesh.vertexBuffer;

            const format = vb.getFormat();
            let stride;
            let positions;
            for (let i = 0; i < format.elements.length; i++) {
                const element = format.elements[i];
                if (element.name === SEMANTIC_POSITION) {
                    positions = new Float32Array(vb.lock(), element.offset);
                    stride = element.stride / 4;
                    break;
                }
            }

            const indices = [];
            mesh.getIndices(indices);
            const numTriangles = mesh.primitive[0].count / 3;

            const v1 = new Ammo.btVector3();
            const v2 = new Ammo.btVector3();
            const v3 = new Ammo.btVector3();
            let i1, i2, i3;

            const base = mesh.primitive[0].base;
            triMesh = new Ammo.btTriangleMesh();
            this.system._triMeshCache[mesh.id] = triMesh;

            for (let i = 0; i < numTriangles; i++) {
                i1 = indices[base + i * 3] * stride;
                i2 = indices[base + i * 3 + 1] * stride;
                i3 = indices[base + i * 3 + 2] * stride;
                v1.setValue(positions[i1], positions[i1 + 1], positions[i1 + 2]);
                v2.setValue(positions[i2], positions[i2 + 1], positions[i2 + 2]);
                v3.setValue(positions[i3], positions[i3 + 1], positions[i3 + 2]);
                triMesh.addTriangle(v1, v2, v3, true);
            }

            Ammo.destroy(v1);
            Ammo.destroy(v2);
            Ammo.destroy(v3);
        }

        const useQuantizedAabbCompression = true;
        const triMeshShape = new Ammo.btBvhTriangleMeshShape(triMesh, useQuantizedAabbCompression);

        const scaling = this.system._getNodeScaling(node);
        triMeshShape.setLocalScaling(scaling);
        Ammo.destroy(scaling);

        const transform = this.system._getNodeTransform(node);
        shape.addChildShape(transform, triMeshShape);
        Ammo.destroy(transform);
    }

Uff, this.app.systems.collision.implementations.mesh doesn’t exist in todays version, where should I start exploring? :grinning:

No, that’s still valid:

Capture
:thinking:
I’ve thought this would return undefined if there were no models with mesh/asset as a collision, added few from editor, loaded few via code, still undefined
1.64.4

Okay did further tests, if there is at least 1 entity with mesh as collision + rigidbody (editor based), implementations.mesh is not undefined, but if there are no assets from inside editor and rather loaded using app.assets.loadFromUrlAndFilename, it’s always undefined :face_with_raised_eyebrow:

Update: Okay, type: ‘mesh’, was missing
But now the real question is, how would you patch using

this.app.systems.collision.implementations.mesh.createPhysicalShape = function (entity, data) {
   // patched method
}

before entities, if its not accesible yet

Got it, 1.64.4

const entity = new pc.Entity('Entity'); 
      entity.addComponent('collision', {
      type: 'mesh',
      asset: new pc.Mesh()
});

this.app.systems.collision.implementations.mesh.createAmmoMesh = function (mesh, node, shape) {
 let triMesh;

            const vb = mesh.vertexBuffer;

            const format = vb.getFormat();
            let stride;
            let positions;
            for (let i = 0; i < format.elements.length; i++) {
                const element = format.elements[i];
                if (element.name === pc.SEMANTIC_POSITION) {
                    positions = new Float32Array(vb.lock(), element.offset);
                    stride = element.stride / 4;
                    break;
                }
            }

            const indices = [];
            mesh.getIndices(indices);
            const numTriangles = mesh.primitive[0].count / 3;

            const v1 = new Ammo.btVector3();
            const v2 = new Ammo.btVector3();
            const v3 = new Ammo.btVector3();
            let i1, i2, i3;

            const base = mesh.primitive[0].base;
            triMesh = new Ammo.btTriangleMesh();

            for (let i = 0; i < numTriangles; i++) {
                i1 = indices[base + i * 3] * stride;
                i2 = indices[base + i * 3 + 1] * stride;
                i3 = indices[base + i * 3 + 2] * stride;
                v1.setValue(positions[i1], positions[i1 + 1], positions[i1 + 2]);
                v2.setValue(positions[i2], positions[i2 + 1], positions[i2 + 2]);
                v3.setValue(positions[i3], positions[i3 + 1], positions[i3 + 2]);
                triMesh.addTriangle(v1, v2, v3, true);
            }

            Ammo.destroy(v1);
            Ammo.destroy(v2);
            Ammo.destroy(v3);


        const useQuantizedAabbCompression = true;
        const triMeshShape = new Ammo.btBvhTriangleMeshShape(triMesh, useQuantizedAabbCompression);

        const scaling = this.system._getNodeScaling(node);
        triMeshShape.setLocalScaling(scaling);
        Ammo.destroy(scaling);

        const transform = this.system._getNodeTransform(node);
        shape.addChildShape(transform, triMeshShape);
        Ammo.destroy(transform);
}

Thanks Leonidas

1 Like