Babylon.js has a plugin system for physics engines that enables the user to add physics interactions to the scene's objects. Unlike the internal collision system, a physics engine calculates objects' body dynamics and emulates "real-life" interactions between them. So if two objects collide, they will "bounce" off one another, just like you would expect from a real-life object.
Babylon.js' plugin system allowed us to use well established physics engines and to integrate them into Babylon.js' render loop. Apart from very advanced usage, there is no need to interact directly with the physics engine. Babylon.js does the work for you.
This tutorial will show the basic usage of the physics system.
There are plugins for 3 physics engines:
Each engine has its own features and its own way of calculating the body dynamics. We at Babylon.js tried collecting the common usage of all engines and provide an easy-to-use interface to them.
To enable the physics engine, call the scene's enablePhysics
function:
var scene = new BABYLON.Scene(engine);
var gravityVector = new BABYLON.Vector3(0,-9.81, 0);
var physicsPlugin = new BABYLON.CannonJSPlugin();
scene.enablePhysics(gravityVector, physicsPlugin);
Both parameters are optional. The default parameters are shown in the example. This is the same as calling:
scene.enablePhysics();
To use OimoJS simply change the 2nd parameter to new BABYLON.OimoJSPlugin()
:
scene.enablePhysics(new BABYLON.Vector3(0,-9.81, 0), new BABYLON.OimoJSPlugin());
Calling this function will create a new BABYLON.PhysicsEngine object that will be in charge of handling the physics interactions.
The physics engine is now enabled and is running during the render loop.
To allow interaction between objects, the physics engines use an impostor, which is a simpler representation of a complex object. An impostor, as a rule, is a rigid body - meaning it cannot be changed during interaction. A sphere will always have the same radius, a box will always have the same length. If you want to change the object, a new impostor will be created.
Each physics engine has different types of Impostors. The following table shows what each engine supports, and what it uses to simulate the missing impostors
Impostor Type | Cannon.js | Oimo.js | Energy.js | Notes |
---|---|---|---|---|
Box | Box | Box | Box | |
Sphere | Sphere | Sphere | Sphere | |
Particle | Particle | Sphere | Unknown | |
Plane | Plane | Box | Plane | Simulates an unlimited surface. Like a floor that never ends. Consider using Box |
Cylinder | Cylinder | Cylinder | Cylinder | |
Mesh | Mesh | Box | Mesh | Use only when necessary - will lower performance. Cannon's mesh impostor only collides against spheres and planes |
Heightmap | Heightmap | Box | Mesh |
Using simple impostors for complex objects will increase performance but decrease the reality of the scene's physics. Consider when complex impostors (like the mesh or the heightmap) is needed, and when the simpler geometries can be used.
To enable physics on an object(*) you need to assign it a physics impostor. The signature of the impostor's constructor is (provided with TypeScript type definition):
new BABYLON.PhysicsImpostor(object: IPhysicsEnabledObject, type: number, options: PhysicsImpostorParameters, scene:BABYLON.Scene);
You will notice that I keep on writing object and not mesh, and that the first parameter is not a mesh but an interface (IPhysicsEnabledObject). It is possible to assign an impostor to any Babylon object that has at least two parameters:
position: BABYLON.Vector3;
rotationQuaternion: BABYLON.Quaternion
An AbstractMesh will be the first choice, of course. But a Solid Particle also applies, and so does a light or certain cameras. I will show how to use an impostor on different object types in the advanced tutorial.
Type can be one of the following:
BABYLON.PhysicsImpostor.SphereImpostor;
BABYLON.PhysicsImpostor.BoxImpostor;
BABYLON.PhysicsImpostor.PlaneImpostor;
BABYLON.PhysicsImpostor.MeshImpostor;
BABYLON.PhysicsImpostor.CylinderImpostor;
BABYLON.PhysicsImpostor.ParticleImpostor;
BABYLON.PhysicsImpostor.HeightmapImpostor;
Options is a JSON. The interface is as follows:
export interface PhysicsImpostorParameters {
mass: number;
friction?: number;
restitution?: number;
nativeOptions?: any;
}
0
as a value will create a static impostor - good for floors.I hope no explanation is required.
I will extend the playground's basic scene to have physics interactions between the sphere and the ground.
I will first have to enable physics:
scene.enablePhysics();
Afterwards, I can create the impostors.
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(sphere, BABYLON.PhysicsImpostor.SphereImpostor, { mass: 1, restitution: 0.9 }, scene);
ground.physicsImpostor = new BABYLON.PhysicsImpostor(ground, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 0, restitution: 0.9 }, scene);
Playground example: https://www.babylonjs-playground.com/#BEFOO -
In the example above, you noticed I kept a reference of the physics impostor attached to the sphere and the ground. This is not mandatory, but it is recommended to keep a reference of this object in order to interact with the physics body.
The physics impostor holds a set of functions that can be executed on the physics engine's body:
The physics impostor synchronizes the physics engine's body and the connected object with each frame. That means that changing the object's position or rotation in Babylon code will also move the impostor. The impostor is also the one updating the object's position after the physics engine is finished calculating the next step.
Playground example (sphere rotation and position) - https://www.babylonjs-playground.com/#B5BDU -
Playground example (box rotation and position) - https://www.babylonjs-playground.com/#2ADVLV -
Simply put, the linear velocity is in charge of updating the object's position. A velocity in any axis will cause a movement in its direction. To get the object's liner velocity (a BABYLON.Vector3):
impostor.getLinearVelocity();
To set the object's linear velocity use:
impostor.setLinearVelocity(new BABYLON.Vector3(0,1,0));
Playground example - https://www.babylonjs-playground.com/#BXII -
The physics engine is in charge of calculating the body's velocity. Changing it will not make it fixed, but give it a "push". The physics engine will take the velocity into account and will modify it using gravity and collision interactions.
If the linear velocity was changing the position, the angular velocity is changing the rotation.
To get the object's angular velocity (a BABYLON.Quaternion):
impostor.getAngularVelocity();
To set the object's linear velocity use:
impostor.setAngularVelocity(new BABYLON.Quaternion(0,1,0,0));
Playground example - https://www.babylonjs-playground.com/#IGM3H -
Same as the linear velocity - setting this value will only cause the physics engine to recalculate the body dynamics. The value will not stay fixed.
Applying a force/impulse on a body will change its velocities (linear and angular) according to the body's properties (mass is taken into account, for example).
Cannon supports both force and impulse (different aspects of the same concept. Read about the difference here - http://www.differencebetween.com/difference-between-impulse-and-vs-force/) Oimo only supports impulses. Applying a force will fallback to impulse.
To apply an impulse, use the applyImpulse function of the impostor:
impostor.applyImpulse(new BABYLON.Vector3(10, 10, 0), sphere.getAbsolutePosition());
The first variable is the direction and amount of impulse to apply. The second is where on the body itself the force will be applied. Using this in a game of pool - you can hit the ball at various contact point locations and the interaction will vary (sometimes called "using English"). This is the way to simulate that.
Playground example - https://www.babylonjs-playground.com/#26LQEZ -
You can add a callback function that will be called when an impostor collides with another impostor. This is how to change the color of an object if it collides against the ground.
sphereImpostor.registerOnPhysicsCollide(groundImpostor, function(main, collided) {
main.object.material.diffuseColor = new BABYLON.Color3(Math.random(), Math.random(), Math.random());
});
Note that in this case, I assumed the impostor's body is a mesh with a material.
Playground example - https://www.babylonjs-playground.com/#1NASOD -
Notice that the callback will be executed each and every time both impostors collide, but will stop when they are touching (when the sphere no longer bounces).
To connect two impostors together, you can now use joints. Think of the joint as a limitation (or constraint) of either rotation or position (or both) between two impostors. Each engine supports different types of joints (which usually have different names as well):
Joint Type | Cannon.js | Oimo.js | Energy.js | Notes |
---|---|---|---|---|
Distance | Distance | Distance | --- | A fixed distance between two impostors |
Hinge | Hinge | Hinge | Hinge | A joint allowing rotation on a single axis (much like your knee) |
Hinge2 | ---- | Wheel | Hinge2 | A joint allowing rotation on a single axis in two different points |
Ball And Socket | Point To Point | Ball | Ball And Socket | A joint allowing one of the objects to rotate around a specific socket (like your hip) |
Slider | ---- | Slider | Slider | A joint allowing changing the position along a single axis |
Cannon also has a special Spring joint that will simulate a spring connected between two impostors.
A further explanation of the joints (including illustrations) is soon to be written.
To add a new joint the impostor has two help classes:
impostor.addJoint(otherImpostor, joint);
//or
impostor.createJoint(otherImpostor, jointType, jointData);
Joint types can be selected from the following enum:
BABYLON.PhysicsJoint.DistanceJoint;
BABYLON.PhysicsJoint.HingeJoint;
BABYLON.PhysicsJoint.BallAndSocketJoint;
BABYLON.PhysicsJoint.WheelJoint;
BABYLON.PhysicsJoint.SliderJoint;
BABYLON.PhysicsJoint.Hinge2Joint = BABYLON.PhysicsJoint.WheelJoint;
BABYLON.PhysicsJoint.PointToPointJoint = BABYLON.PhysicsJoint.BallAndSocketJoint;
BABYLON.PhysicsJoint.SpringJoint;
Babylon has 3 help-classes to add joints:
BABYLON.DistanceJoint
, BABYLON.HingeJoint
, BABYLON.Hinge2Joint
.
DistanceJoint playground - https://www.babylonjs-playground.com/#26QVLZ -
Using scene.getPhysicsEngine()
, you can get access to functions that will influence the engine directly.
The physics engine assumes a certain frame-rate to be taken into account when calculating the interactions. The time between each step can be changed to "accelerate" or "slow down" the physics interaction. Here is the same scene with different time steps - accelerating and slowing down:
Default time step - https://www.babylonjs-playground.com/#2B84TV -
You can change the scene's gravity using the physics engine's setGravity(vector3)
function.
This can be done in real time, even after setting the gravity:
Playground demo (click to toggle positive/negative gravity) - https://www.babylonjs-playground.com/#A2WGF -