Three.JS Physics 2 Ammo.js

Using Physics in Three.JS with Ammo.js : Demolishing a Wall

Introduction – Before you start

In this example of using physics with the coupling of Three.JS and Ammo.js, we start from the final state of the code put in place in the previous chapter :

In this chapter, our objective will be to demolish a wall thanks to a physical collision. If you’re ready, let’s begin!

Ammo.JS Physics wall Three.JS
The Destruction of a wall !

Light code modifications

Let’s return to the code from the previous chapter. We will begin our modifications with a very light recasting of the createCube function. We will create a color parameter, used to define the color of the cube.

We also modify the scale parameter. Initially, this parameter was a simple Integer; we will transform it into a Vector3 in order to define a different measurement on each XYZ axis.

Here is our slightly modified createCube function :

function createCube(scale , position, mass, color, rot_quaternion)
{
    let quaternion = undefined;

    if(rot_quaternion == null)
    {
        quaternion = {x: 0, y: 0, z: 0, w:  1};
    }
    else
    {
      quaternion = rot_quaternion;
    }

    // ------ Graphics Universe - Three.JS ------
    let newcube = new THREE.Mesh(new THREE.BoxBufferGeometry(scale.x, scale.y, scale.z), new THREE.MeshPhongMaterial({color: color}));
    newcube.position.set(position.x, position.y, position.z);
    scene.add(newcube);

    // ------ Physics Universe - Ammo.js ------
    let transform = new Ammo.btTransform();
    transform.setIdentity();
    transform.setOrigin( new Ammo.btVector3( position.x, position.y, position.z ) );
    transform.setRotation( new Ammo.btQuaternion( quaternion.x, quaternion.y, quaternion.z, quaternion.w ) );
    let defaultMotionState = new Ammo.btDefaultMotionState( transform );

    let structColShape = new Ammo.btBoxShape( new Ammo.btVector3( scale.x*0.5, scale.y*0.5, scale.z*0.5 ) );
    structColShape.setMargin( 0.05 );

    let localInertia = new Ammo.btVector3( 0, 0, 0 );
    structColShape.calculateLocalInertia( mass, localInertia );

    let RBody_Info = new Ammo.btRigidBodyConstructionInfo( mass, defaultMotionState, structColShape, localInertia );
    let RBody = new Ammo.btRigidBody( RBody_Info );

    physicsUniverse.addRigidBody( RBody );
    newcube.userData.physicsBody = RBody;
    rigidBody_List.push(newcube);
}

Setting up the Scene

Once these modifications are done, let’s return to the AmmoStart function. Next, use createCube to put in place the actors of our scene.

Let’s begin with the support, the ground of our dynamic scene :

createCube(new THREE.Vector3(50, 2, 90) , new THREE.Vector3(15, -5, 30) , 0 , 0x2c3e50, null);

Here are the details of the parameters used :

  • scaleHeight on the XYZ axes : (50, 2, 90).
  • positionPosition on the XYZ axes : (15, -5, 30).
  • massMass of our object : 0 (Infinite mass, static object).
  • colorColor of our object in hexadecimal form : 0x2c3e50.
  • rot_quaternion – Quaternion, initial rotation of the object : null so {x: 0, y: 0, z: 0, w: 1} by default.

In the same manner, let’s create a block oriented at a slant :

createCube(new THREE.Vector3(8, 1, 15) , new THREE.Vector3(15, 0, 0) , 0 , 0xffffff, {x: 0.383, y: 0, z: 0, w:  0.924} );

Thus, we use the rot_quaternion parameter to define an initial rotation, turn the object and orient it on a slant.

Here are our two static blocks :

Ammo.JS Physics Three.JS ground and slant
Our static objects – The ground and the slope

Next, remaining with createCube, let’s create all our dynamic bricks with the help of loops :

for(var z = 30 ; z > 15 ; z -= 5)
{
    for(var j = 0 ; j < 10 ; j += 2.2)
    {
        for(var i = 0 ; i < 30 ; i += 2.1)
        {
          createCube(new THREE.Vector3(2, 2, 1.5) , new THREE.Vector3(i, j, z) , 1 , 0xffffff, null);
        }
    }
}

Thus, we create stacked bricks, with the mass 1. Here is the current state of our scene :

Ammo.JS Physics wall Three.JS bricks
Our dynamic bricks

To finish, let’s create a cube in freefall above the slope. The latter will fall on the top of the slope and will be propelled towards the walls of bricks :

setTimeout( function()
{
    createCube(new THREE.Vector3(6, 6, 6) , new THREE.Vector3(15, 500, -1) , 10000 , 0xc0392b, {x: 0.383, y: 0, z: 0.383, w:  0.924} );
}, 3000);

We choose to make a cube appear with a mass of 10000 at an altitude of 500, after an elapsed time of 3 seconds (3000 ms).

Final code and result

Here is the final code of our AmmoStart function :

function AmmoStart()
{
    tmpTransformation = new Ammo.btTransform();

    initPhysicsUniverse();
    initGraphicsUniverse();

    createCube(new THREE.Vector3(50, 2, 90) , new THREE.Vector3(15, -5, 30) , 0 , 0x2c3e50, null);


    for(var z = 30 ; z > 15 ; z -= 5)
    {
        for(var j = 0 ; j < 10 ; j += 2.2)
        {
            for(var i = 0 ; i < 30 ; i += 2.1)
            {
              createCube(new THREE.Vector3(2, 2, 1.5) , new THREE.Vector3(i, j, z) , 1 , 0xffffff, null);
            }
        }
    }

    createCube(new THREE.Vector3(8, 1, 15) , new THREE.Vector3(15, 0, 0) , 0 , 0xffffff, {x: 0.383, y: 0, z: 0, w:  0.924} );

    setTimeout( function()
    {
        createCube(new THREE.Vector3(6, 6, 6) , new THREE.Vector3(15, 500, -1) , 10000 , 0xc0392b, {x: 0.383, y: 0, z: 0.383, w:  0.924} );
    }, 3000);

    render();
}

And to finish this chapter, here is an overview of our creation :

The destruction of the wall