Cloth Physics Simulation for Babylon.js
Weave got an interesting read here: How to create soft cloth-like bodies using Babylon's new physics architecture.
Join the DZone community and get the full member experience.
Join For FreeShort 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 ones 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 during 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 a "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, and come back for explanations.
Back, yet? Great. What did I do there?
I created 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 that 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 check it out and come back, then 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. These (rigid) impostors, will help us get a 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 this 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:
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. There will also be 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 is to unify all physics engines to one single API. In a perfect world, this means that changing the physics engine to Oimo instead of Cannon should work out of the box.
Go ahead, try it!
And, we just found out it isn't a perfect world. Oimo's distance joint works a bit differently than Cannon's, and the connections look a bit "stretched". Oimo is also much faster (being much simpler than Cannon), so the fabric will "vibrate" very fast.
But, fear not. There is a solution to everything...
First, the distance between the two joints should be kept to a minimum. Setting the distance to 0.1 will prevent the stretching.
The solution to the "vibrations" is to reduce the time step. Which means "slowing down" the physics engine.
Here is the sphere demo using Oimo - http://www.babylonjs-playground.com/#1J3WEP#8
Just one important thing, Oimo doesn't have a particle impostor. This is emulated using a very small sphere, which has its downsides.
What's Next?
Now it's your turn. The possibilities are endless!
Play around with this implementation, try it out. Ask me any questions you have. Once the Babylon physics body library isfinished, 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!
Connect with me on Twitter or LinkedIn to continue the discussion.
Published at DZone with permission of Raanan Weber, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Future of Software Development: Generative AI Augmenting Roles and Unlocking Co-Innovation
-
Decoding ChatGPT: The Concerns We All Should Be Aware Of
-
The Role of Automation in Streamlining DevOps Processes
-
Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
Comments