Script that allows mesh colliders to collide with each other

Playcanvas seems to currently only use btBvhTriangleMeshShape as mesh colliders, which can be concave but they are less performant and they can’t collide with each other.

I’ve written a script I’d like to share with you which instead uses btConvexHullShape for all mesh colliders, so if you have this script in your project, your rigidbodies with mesh colliders will collide with each other and performance and stability of the simulation should be better too.

HOWEVER: It replaces all your mesh colliders with their convex hulls, so make sure you understand what that means! If you want to use concave meshes with it, you should first create a convex decomposition and store the convex parts as different submeshes, then this should work too.

If you want to use both btBvhTriangleMeshShape and btConvexHullShape I guess you can relatively easily extend this script to only use convexHull shapes for meshcolliders that you specifically mark.

I also have a question though: This is my first week with javascript and I still try to get the hang on it. Would there be a more elegant way of doing this (especially in relation to the type of script !function(){}(); and replacing the createPhysicalShape function of the private system implementation through the proto object)?

The script was written against 1.4.0-dev, so might not work with newer versions:

!(function() {
    pc.CollisionComponentSystem.prototype.overrideMeshShapeCreation = function() {
        this.implementations['mesh'].__proto__.createPhysicalShape = function (entity, data) {
            if (typeof Ammo !== 'undefined' && data.model) {
                var model = data.model;
                var shape = new Ammo.btCompoundShape();

                var i, j, t;
                for (i = 0; i < model.meshInstances.length; i++) {
                    var meshInstance = model.meshInstances[i];
                    var mesh = meshInstance.mesh;
                    var ib = mesh.indexBuffer[pc.RENDERSTYLE_SOLID];
                    var vb = mesh.vertexBuffer;

                    var format = vb.getFormat();
                    var stride = format.size / 4;
                    var positions;
                    for (j = 0; j < format.elements.length; j++) {
                        var element = format.elements[j];
                        if ( === pc.SEMANTIC_POSITION) {
                            positions = new Float32Array(vb.lock(), element.offset);

                    var indices = new Uint16Array(ib.lock());
                    var numTriangles = mesh.primitive[0].count / 3;

                    var convexHullShape = new Ammo.btConvexHullShape();
                    var seen = {};
                    var p = new Ammo.btVector3();
                    var base = mesh.primitive[0].base;
                    for (j = 0; j < numTriangles; j++) {
                        for(t=0; t < 3; t++) {
                            var idx = indices[base + j * 3 + t] * stride;
                            if(!seen.hasOwnProperty[idx]) {
                                seen[idx] = true;
                                p.setValue(positions[idx], positions[idx + 1], positions[idx + 2]);
                                convexHullShape.addPoint(p, true);

                    var wtm = meshInstance.node.getWorldTransform();
                    var scl = wtm.getScale();
                    convexHullShape.setLocalScaling(new Ammo.btVector3(scl.x, scl.y, scl.z));

                    var pos = meshInstance.node.getPosition();
                    var rot = meshInstance.node.getRotation();

                    var transform = new Ammo.btTransform();
                    transform.getOrigin().setValue(pos.x, pos.y, pos.z);

                    var ammoQuat = new Ammo.btQuaternion();
                    ammoQuat.setValue(rot.x, rot.y, rot.z, rot.w);

                    shape.addChildShape(transform, convexHullShape);

                var entityTransform = entity.getWorldTransform();
                var scale = entityTransform.getScale();
                var vec = new Ammo.btVector3();
                vec.setValue(scale.x, scale.y, scale.z);

                return shape;
            return undefined;
    pc.CollisionComponentSystem.prototype.recreateAllShapes = function() {
        var components =;
        for (var id in components) {

I think normally you don’t need to wrap the prototype overrides/extension in a !function () {} () as you are not executing any code unless you want to make use of closures and not pollute the global space with temporary, global variables.

In this particular case, I believe you could get away with not using !function () {} ().

Thank you for the reply.

I am making two function calls at the end of the script so I guess I do need the !function(){}(); because of that? What I really wanna replace is the CollisionMeshSystemImpl.prototype.createPhysicalShape function, but CollisionMeshSystemImpl is a private variable of the CollisionComponentSystem so I can’t access it through the pc namespace and instead have to replace it on this.implementations[‘mesh’].proto, so I need to wrap it in a function in pc.CollisionComponentSystem.prototype and then call this function

Or is there a way how I can directly access CollisionMeshSystemImpl.prototype ?

AFAIK, you can make function calls in global space.

I don’t think you can as it’s wrapped in a anonymous function and not exposed globally.

1 Like