Oimo - How to use a new Physics plugin

Oimo - How to use a new Physics plugin


To understand this tutorial, you should have some experience with Babylon.js or completed my first tutorial.

Objective

In this tutorial, we will try to use a new physics plugin : Oimo.js.
As you may know, Babylon.js use a plugin system to work with physics. This means if a new physics engine comes out, it can easily be added into Babylon.
I created this plugin because I was not totally convinced by the current one (cannon.js) in terms of performance, and I wanted to try the new plugin system. Anyway, this time it will not be a game prototype, but a technical demo of Oimo.js with Babylon.
In this page, I will assume you know how to use basic components of Babylon.js (but if you completed my two first tutorials, you will be fine) :)
Let's start !

What you will create

Click on the image to run the demo. It's a simple platform with some falling cubes and spheres. A gradient shader is used for the skydome and for the material on the cubes. I will of course explain all of this.
Click on the image below or here to run the physics demo.

The environment

Our environment is composed of two things :

  • a skydome
  • a platform
The platform is only a box with a texture on it, nothing too fancy.
The skydome is only a big sphere with backface culling activated (this means the material will be applied on the sphere and INSIDE of it. The particularity of this skydome is its material : it will be a gradient shader.

A shader ?

A shader is a common component in 3D games. It's a computer program used to compute the position and the appropriate level of color for a given model. They can be used to create specials effects with a great performance.
I could make an whole article on shaders, how they works, what they can do, but for now we will try to use them within Babylon.
What you should keep in mind is:

  • A shader is computed by the GPU (graphic card), so it's very very fast
  • A shader is actually files: a vertex shader and a fragment shader
  • The vertex shader computes the position of each vertex of a given mesh
  • The fragment shader computes the color applied on each vertex
  • the variable gl_Position is computed in the vertex shader, and gl_FragColor is computed in the fragment shader
You can see some shader in action here, by using the tool Create Your Own Shader (CYOS).
How can you use it in Babylon ? It's simple: BABYLON.ShaderMaterial!

A ShaderMaterial is just a normal material linked to the vertex shader and to the fragment shader. By working with CYOS, I created a gradient shader : this shader apply two colors (top Color and bottomColor) on the mesh. Nothing is done in the vertex shader, because I don't want to update the position of my vertex.


precision mediump float;

// Attributes
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;

// Uniforms
uniform mat4 worldViewProjection;

// Varying
varying vec4 vPosition;
varying vec3 vNormal;

void main() {

    vec4 p = vec4( position, 1. );
    vPosition = p;
    vNormal = normal;
    gl_Position = worldViewProjection * p;

}
    

However, the magic is done in the fragment shader. The two colors are retrieved from 'uniforms' parameters (uniforms are data given as input to the shader, you will see it later), mixed up and applied to the mesh automatically.


precision mediump float;

uniform mat4 worldView;
varying vec4 vPosition;
varying vec3 vNormal;

// PARAMETER GIVEN IN THE JS CODE //
// Offset position
uniform float offset;
// Exponent
uniform float exponent;
// Colors
uniform vec3 topColor;
uniform vec3 bottomColor;

void main(void) {
    float h = normalize(vPosition + offset).y;
    gl_FragColor = vec4( mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0 );
}
    

These two shaders should be saved in two files (gradient.vertex.fx for the vertex shader, and gradient.fragment.fx for our fragment shader) and put in a specific folder of our demo, let's call it 'shaders' (very simple, isn't it ?).
Click here if you want to try the gradient shader.

The skydome

We should now apply our shader on our skydome, with two colors.


/** SKYBOX **/
BABYLON.Engine.ShadersRepository = "shaders/";

var skybox = BABYLON.Mesh.CreateSphere("skyBox", 10, 2500, scene);

var shader = new BABYLON.ShaderMaterial("gradient", scene, "gradient", {});
shader.setFloat("offset", 0);
shader.setFloat("exponent", 0.6);
shader.setColor3("topColor", BABYLON.Color3.FromInts(0,119,255));
shader.setColor3("bottomColor", BABYLON.Color3.FromInts(240,240, 255));
shader.backFaceCulling = false;
skybox.material = shader;

The first line is here to indicate to Babylon the folder containing our shaders, and the second one creates our skydome (remember ? A biiig sphere). But let's give a closer look at the shader creation.


var shader = new BABYLON.ShaderMaterial("gradient", scene, "gradient", {});
    

Here, 4 parameters :

  • The shader name
  • The game scene
  • The shader file name - We called our files gradient.vertex.fx, remember ?
  • A set of options linked to our shader.

In the next 4 lines, I give 4 parameters to my shader (used in the fragment shader):


shader.setFloat("offset", 0);
shader.setFloat("exponent", 0.6);
shader.setColor3("topColor", BABYLON.Color3.FromInts(0,119,255));
shader.setColor3("bottomColor", BABYLON.Color3.FromInts(240,240, 255));
    

A BABYLON.ShaderMaterial contains several methods do give parameters to the shaders (the variables called 'uniforms' - not to be confused with unicorns). You should know that Babylon gives some variables by default to all shaders:

  • world - The world matrix
  • view - The scene view matrix
  • worldView - The scene view matrix in the world
  • projection - The scene projection matrix
  • worldViewProjection - The world view projection matrix
All others uniforms variables should be added with the corresponding method 'setXXXX' where XXXX is the type of the uniform.
Here, 4 uniforms are not in this list: offset (a float), exponent (a float), topColor (a color), bottomColor (a color). These variables are all given to the shader.

Finally, this line will apply the shader material on the inside of the sphere.
Add a camera and an hemispheric light, and you have your skydome !


    shader.backFaceCulling = false;

The platform

The platform is just a box scaled along the y-axis, with a ground texture. Here is the code :


/** GROUND **/

// Material
var mat = new BABYLON.StandardMaterial("ground", scene);
var t = new BABYLON.Texture("img/ground3.jpg", scene);
t.uScale = t.vScale = 10;
mat.diffuseTexture = t;
mat.specularColor = BABYLON.Color3.Black();

// Object
var g = BABYLON.Mesh.CreateBox("ground", 400, scene);
g.position.y = -20;
g.scaling.y = 0.01;

g.material = mat;
    

Cubes and spheres

A loop is used to create all objects: spheres and cubes. The function getPosition is a function creating a Vector3 with A random position in X and Z.


// SPHERES
var s = BABYLON.Mesh.CreateSphere("s", 30, randomNumber(20, 30), scene);
s.position = getPosition(y);
var matSphere = new BABYLON.StandardMaterial("boxmat", scene);
matSphere.diffuseColor = BABYLON.Color3.FromInts(75, 71, 89);
matSphere.specularColor = BABYLON.Color3.Black();
s.material = matSphere;
    

For the box, the same shader as the skydome is used, but with two differents colors.
All boxes are given a random rotation.


// BOXES
var d = BABYLON.Mesh.CreateBox("s", randomNumber(10, 20), scene);
d.position = getPosition(y);
var shaderBox = new BABYLON.ShaderMaterial("gradient", scene, "gradient", {});
shaderBox.setFloat("offset", 10);
shaderBox.setFloat("exponent", 1.0);
shaderBox.setColor3("topColor", BABYLON.Color3.FromInts(129,121,153));
shaderBox.setColor3("bottomColor", BABYLON.Color3.FromInts(161,152, 191));
d.material = shaderBox;

// Random rotation
d.rotation.x = randomNumber(-Math.PI/2, Math.PI/2);
d.rotation.y = randomNumber(-Math.PI/2, Math.PI/2);
d.rotation.z = randomNumber(-Math.PI/2, Math.PI/2);
    

And finally, all objects are saved in an array to be used later.


// SAVE OBJECT
objects.push(s, d);
// INCREMENT HEIGHT
y+=10;
    

Finally, a small condition is added in the render loop: if the mesh falls below -100 units, a new random position is computed above the ground.


scene.registerBeforeRender(function() {
    objects.forEach(function(obj) {
        // If object falls
        if (obj.position.y < -100) {
            obj.position = getPosition(200);
        }
    });
});

And now, the magic: the physics engine !

How to activate the physics engine

First, you have to activate the physics engine and tell Babylon.js which plugin you will use. Only one plugin can be used in one scene, so choose carefully :) Here, we use a OimoJsPlugin.


scene = new BABYLON.Scene(engine);
scene.enablePhysics(new BABYLON.Vector3(0,-10,0), new BABYLON.OimoJSPlugin());
        

And that's it ! The physics engine is activated !

Now, some physics properties should be added to our ground. To add a physics state, just add this line:


g.setPhysicsState({ impostor: BABYLON.PhysicsEngine.BoxImpostor, move:false});
    

The object given in parameter to setPhysicsState contains 2 attributes :

  • impostor - The physics impostor. Currently, only 3 are supported with Oimo.js : Sphere, Box and Plane
  • move - Is this object should move ? If yes, set it to true.
Several other attributes can be added in this object:
  • mass - The object mass
  • friction - The object friction attribute
  • restitution - The object restitution attribute

We want spheres to bounce a little bit more than cubes. We can set it with the attribute restitution:


s.setPhysicsState({impostor:BABYLON.PhysicsEngine.SphereImpostor, move:true, mass:1, friction:0.5, restitution:0.5});
d.setPhysicsState({impostor:BABYLON.PhysicsEngine.BoxImpostor, move:true, mass:1, friction:0.5, restitution:0.1});
    

What's next ?

Click on the image below to get the final code source if you want to take a look at it. I updated/add several things in it, but feel free to modify it as you wish.


If you have any questions about it, feel free to email me at temechon [at] pixelcodr [dot] com, or leave a comment below, I'll answer quickly.
You can also subscribe to the newsletter and you will receive an email when a new tutorial is out. No spam, and unsubscribe whenever you want.

Cheers !