Tutorial on JavaScript Physics Using AmmoJS and ThreeJS

Hello All

Just wanted to share a tutorial I wrote on medium about JavaScript 3D physics. Its more like an introductory tutorial using Ammo.js and threejs.

I hope someone finds it helpful :smiley:

Intro to JavaScript 3D Physics using Ammo.js and Three.js

Update
Still in the same spirit, a follow up article has been published :grin:

Moving Objects In JavaScript 3D Physics using Ammo.js and Three.js

Another Update
Third article; based on collision detection :wink:

Collision Detection In Javascript 3D Physics using Ammo.js and Three.js

18 Likes

Nice tutorial :grin:

BTW the grammarly extension will help you catch basic grammar mistakes in your article.

4 Likes

Thanks :smile:

1 Like

Updated to include link to follow up article

is ammo.js best physics engine ?

I can’t really say. But I do know that Bullet Physics, it’s parent, is widely used.

@extremety1989

Think of it like this. Ammo.js to, say, Phys.js is what Fighter Jet is to a paper airplane. If you need a fully featured physics engine - there’s just no competition on the market to Ammo at the moment. If you only need a subset of what a physics engine typically is - you can get excellent results with the few other decent engines on the market.

4 Likes

Added a new article on collision detection.

1 Like

Thanks for this tutorial, I appreciate your efforts.

1 Like

This is the best tutorial about Ammo.js for beginners in Ammo.js. I study the Collision Detection lesson. I use TypeScript as a programming language and pure WebGL 1.0 to draw objects:

move-cube

I want to print names of colliding objects as in the lesson. I try to use setUserPointer/getUserPointer.

I created an object to keep a user data:

const userData = { name: name };

I keep this object in a body using setUserPointer:

this.body.setUserPointer(userData);

I try to get these names using getUserPointer and print them. But I get “undefined” instead of names:

function detectCollison(): void
{
    const dispatcher = physicsWorld.getDispatcher();
    const numManifolds = dispatcher.getNumManifolds();

    for (let i = 0; i < numManifolds; i++)
    {
        const contactManifold = dispatcher.getManifoldByIndexInternal(i);
        const body0 = contactManifold.getBody0();
        const body1 = contactManifold.getBody1();

        const p0 = body0.getUserPointer();
        const p1 = body1.getUserPointer();

        console.log("first object: " + (p0 as any).name);
        console.log("second object: " + (p1 as any).name);
    }
}

This is a solution for my problem above:

const userData = { name: name };
(this.body as any).userData = userData;
function detectCollison(): void
{
    const dispatcher = physicsWorld.getDispatcher();
    const numManifolds = dispatcher.getNumManifolds();

    for (let i = 0; i < numManifolds; i++)
    {
        const contactManifold = dispatcher.getManifoldByIndexInternal(i);
        const body0 = contactManifold.getBody0();
        const body1 = contactManifold.getBody1();

        const rb0 = (Ammo as any).castObject( contactManifold.getBody0(), Ammo.btRigidBody );
        const rb1 = (Ammo as any).castObject( contactManifold.getBody1(), Ammo.btRigidBody );

        console.log("first object:", rb0.userData);
        console.log("second object:", rb1.userData);
    }
}
1 Like

Glad you found a solution. Sorry I wasn’t on time to reply.

1 Like

Hi, thanks for the tutorial links they are great!

I haven’t had the chance to test the code yet. BUT (:grin:) It looks to me as though all transforms are flat. As in they are all tied to the scene/world transform (or rather the .setIdentity() matrix) and not nested children of other Mesh/RigidBody objects. In your example it works fine as the ThreeJS Mesh objects are all children of the Scene.

In the initialisation functions, createBall and createBlock.

let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin( new Ammo.btVector3( pos.x, pos.y, pos.z ) );
transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
let motionState = new Ammo.btDefaultMotionState( transform );

It doesn’t look like parent transforms are factored in.

And neither in the updatePhysics function:

ms.getWorldTransform( tmpTrans );
let p = tmpTrans.getOrigin();
let q = tmpTrans.getRotation();
objThree.position.set( p.x(), p.y(), p.z() );
objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );

Here the .getWorldTransform is computed, but nested transforms weren’t setup in the createBlock and createBall functions.

Is it the case that for this tutorial we just don’t worry about nested transforms or will this still work?

Is bullet even designed to work this way with nested transforms (Possibly need to use some sort of weld joint instead)?

If so, do you know what the extra steps required would be to include a joint or parent/child relationship?

Thanks again!

1 Like

Hi @Deahgib I’m glad you found the tutorial links great.

The transforms were made flat on purpose to avoid complexities that might obscure the “learning process” .

While updating the transform of mesh objects that are parented you can either have a one to one mapping from physics world to the local space (as portrayed in the tutorials), or you perform further transformation to its parent’s (local) space or to global/world space.

As was noted earlier this was not included to keep the tutorials simple.