Short intro
After making the changes to the physics engine architecture in Babylon.js, I added a new impostor type, influenced by cannon's wonderful impostors implementations - the Particle Impostor. The Particle is a very simple impostor. It can be compared to a point in space so it reduces the need for heavy calculations when checking for collisions - it is a point-to-body kind of collision, which is much simpler to calculate.
Cannon's cloth demo (http://schteppe.github.io/cannon.js/examples/threejs_cloth.html) is showing the power of particles. My implementation of them (and the integration in Babylon's architecture itself) was influenced heavily by this implementation. With a big thanks to Schteppe, the creator of the wonderful Cannon.js - the physics engine I will mainly use in this tutorial.
In this tutorial / blog post / infomercial I will recreate cannon's cloth demo using Babylon.
Let's first understand what's going on!
Now, why is it that complex?
Physics engines (at least the once integrated in Babylon.js) relay on a very important characteristic of an object - rigidity. That means, in general, that the physics body is not elastic. it doesn't change doing the entire scene's life. A sphere will always have the same radius. A box will always have the same dimensions. If you do change the object's parameters (mainly size, but also position vertices in case of a mesh impostor), you will have to create a new (Rigid!) body for this object. So, if we create a ground object (a flat plane with predefined dimensions) and register its impostor (thus creating its physics body), the physics engine will always treat it as a very narrow box that cannot be changed. The bottom line is that you cannot use a single impostor to create soft bodies, like a cloth.
Making a soft body from a single mesh
Imagine a plane with four corners. Each corner has a sphere in its position. Wait, why imagine? Here is an illustration!
Those four spheres in the corners will simulate our soft body. The theory is this: If we keep on updating the square's position vertices using the position of those 4 corners, we can create "stretching" effect if we move the spheres individually.
You can see what I mean in this playground: http://www.babylonjs-playground.com/#1J3WEP. Go, watch, come back for explanations.
Back? Great. What Do I do there?
I create the spheres using the positions vertex-data of the ground:
//get the position vertices data
var positions = ground.getVerticesData(BABYLON.VertexBuffer.PositionKind);
//where will they be stored?
var spheres = [];
for (var i = 0; i < positions.length; i = i + 3) {
//get the position of the i's sphere
var v = BABYLON.Vector3.FromArray(positions, i);
//create a sphere, position it in "v"
var s = BABYLON.MeshBuilder.CreateSphere("s" + i, { diameter: 0.5 }, scene);
s.position.copyFrom(v);
//push it to the sphere's array for future reference
spheres.push(s);
}
Notice I am taking the raw positions and converting them to vector3 objects. If you transform the ground, those vectors must be transformed as well (using BABYLON.Vector3.TransformCoordinates(...)
). I won't cover it here, but I will be more than happy to explain how it works.
And then I update the ground's positions (of the internal geometry!) using the following function:
ground.registerBeforeRender(function () {
var positions = [];
//get each sphere's position, add to a temporary array
spheres.forEach(function (s) {
positions.push(s.position.x, s.position.y, s.position.z);
});
//set this array as the new positions vector
ground.updateVerticesData(BABYLON.VertexBuffer.PositionKind, positions);
});
To simulate a change, I change the last sphere's position in the x axis in every frame, and this what creates the motion in the playground demo.
Wonderful! Now we can "stretch" objects. And now we understand the basics of soft bodies in a rigid world. You probably got this by now - the same concept can be applied to a ground object with many subdivisions:
Now changing each sphere's position will stretch the ground in many ways. Here is a playground: http://www.babylonjs-playground.com/#1J3WEP#2. Go, come back, we'll continue.
Integrating the physics engine
Ok, now we know how to "soften" a hard mesh. We just change its positions vertex-data. But how do we use it with our physics engine?
Particles!
Each sphere we saw before will act as an individual impostor. A particle impostor. This (rigid) impostors, will help us get physics-enabled soft body, like a cloth.
Let's take the last playground as our basis. We have the spheres, we have the ground, now we need to add physics.
Let's take one step at a time.
First, create the impostors
Each sphere will get a new PhysicsImpostor, with the Particle type. The first impostors will have "0" mass. This will simulate a bar holding the cloth. Like a curtain:
//create the impostors
spheres.forEach(function(point, idx) {
//what is the impostor's mass?
var mass = idx < subdivisions ? 0 : 1;
point.physicsImpostor = new BABYLON.PhysicsImpostor(point, BABYLON.PhysicsImpostor.ParticleImpostor, {
mass: mass
}, scene);
});
Impostors created!
Second, connect the impostors
Great. Now we have our impostors, but if we run this scene, all impostors (except for the ones with 0 mass) will fall into oblivion. We need to connect all of the impostors. That's the purpose of physics joints.
To do that I will use the DistanceJoint, which is a joint connecting two physics objects with a predefined distance between them. The physics engine will keep the distance between those objects.
function createJoint(imp1, imp2) {
var joint = new BABYLON.DistanceJoint({
maxDistance: distanceBetweenPoints
})
imp1.addJoint(imp2, joint);
}
//create the impostors
spheres.forEach(function(point, idx) {
if (idx >= subdivisions) {
createJoint(point.physicsImpostor, spheres[idx - subdivisions].physicsImpostor);
if (idx % subdivisions) {
createJoint(point.physicsImpostor, spheres[idx - 1].physicsImpostor);
}
}
});
What am I doing here?
First I connect vertically - I start from the second row and connect it with the row above it. Then I connect horizontally - except for the first impostor in each row, I connect each with the impostor before it.
Now we have a net of impostors connected together. Here is how it looks like:
The playground's link - http://www.babylonjs-playground.com/#1J3WEP#3
Demos
Now we have a cloth. We can do so much with it!
Cloth blocked by a sphere
First, let's put a sphere in the cloth's path and see how it reacts:
And the playground - http://www.babylonjs-playground.com/#1J3WEP#5
The particle impostors collide against the sphere, making the curtain stop when hitting it. Notice that it is important to have a relatively large amount of subdivisions. If it is too low, the impostors won't collide and the cloth will simply go "through" the sphere. It is not the mesh that collides against the sphere, it is the particles!
Cloth thrown on a sphere
Now, let's "throw" the cloth on top of a sphere and see what's happening:
Playground - http://www.babylonjs-playground.com/#1J3WEP#6
Pretty neat, huh?
Catch a rolling ball
A nice idea for a game would be to throw nets at a running ball and see how many attempts are needed to actually catch it. Here is a simulation of such a game:
The possibilities are endless.
Is there an easier way to use this?
Yes! Babylon.js will soon have a new physics extension called "physics bodies" that will hold implementations of common physics bodies. The cloth will be there. So will some car implementations and other cool things.
When will it be available? Now, that's a good question :)
But wait, what about Oimo.js?
Babylon's physics architecture's main goal was to unify all physics engines to one single API. This means that in a perfect world changing the physics engine to Oimo instead of Cannon should work out of the box.
Go ahead, try it!
Here is the sphere demo using Oimo - http://www.babylonjs-playground.com/#1J3WEP#133
Just one important thing - Oimo doesn't have a particle impostor. This is emulated using a very small sphere. Which has its down sides.
What's next?
Now it's your turn. The possibilities are endless!
Play around with this implementation, try out. Ask me any question you have. Once the Babylon physics body library will be finished, I will publish the full implementation, including transformation adjustments and other types of meshes. But that shouldn't stop you from experimenting.
Physics engines are a wonderful thing! Use them!