Physics V2 overview

Babylon.js
5 min readApr 4, 2023

Babylon.js 6.0 is set to be released soon. Among its many groundbreaking features, the new Physics plugin is expected to be a standout addition. Today, I would like to share with you the key pillars that helped shape the architecture of this exciting new feature.

Easy transition

One of the fundamental principles of Babylon.js is to maintain backward compatibility. However, there are times when it is necessary to address errors and accumulated technical debt, which may require breaking backward compatibility. In such cases, it is important to ensure that legacy mechanisms are still available or that a viable migration path is provided. With the design of the new plugin architecture, we took great care to balance the needs of both casual and advanced users, with a focus on ease of transition while providing in-depth customization options for more advanced users. This approach enables a smooth transition while empowering users to take ownership of more technical features quickly.

Here are the equivalent:

PhysicsImpostor → PhysicsAggregate
PhysicsJoint -> PhysicsConstraint

As simple as that! A quick example:
In legacy

 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);

Becomes

var sphereAggregate = new BABYLON.PhysicsAggregate(sphere, BABYLON.ShapeType.SPHERE, { mass: 1, restitution: 0.9 }, scene);
var groundAggregate = new BABYLON.PhysicsAggregate(ground, BABYLON.ShapeType.BOX, { mass: 0, restitution: 0.9 }, scene);

Then, from the aggregate, it’s possible to get and tweak the individual Body, Shape, Material. Same for constraints aKa joints. Core principles kept unchanged.

Core concepts like shapes, body and constraints are the same between all different Physics Engines.

Performance improvements

Instances

In computer graphics and simulation, optimal performance can be achieved by processing large batches of data. This approach facilitates multithreading and enhances memory efficiency by limiting cache misses. It is essential to ensure that the data layout is suitable for this purpose.

Similarly, in the context of the new plugin architecture, instances are utilized to achieve optimal performance. Rather than creating a thousand individual bodies, a batch of 1000 bodies can be created. This strategy speeds up rendering and the transfer of matrix data between the plugin and scene. Additionally, multiple bodies can use a single shape instance, further enhancing the efficiency and speed of the system.

High performance with a lot of instances.

Collision observers

Collision handling can cause significant CPU bottlenecks, whether for gameplay or cosmetic purposes. In the legacy version of the plugin, collision was managed on a per-impostor basis, requiring registration of callbacks for each of the thousands of impostors in the scene. While this approach remains viable with the new V2 architecture, it is only suitable when handling collisions for a few distinct bodies.

However, when dealing with a more substantial number of bodies, a different approach is necessary. With the new plugin architecture, collision observers can now be added directly to the plugin, providing a more efficient solution. These observers receive collision events for bodies with enabled collision flags, and the user can filter and trigger custom actions based on the colliders’ properties and states.

Memory footprint

As previously demonstrated, replacing a PhysicsImpostor with a PhysicsAggregate is a simple process, creating a body for computing dynamics and a shape for collision detection.

However, for more complex physics simulations, additional optimization may be necessary. Typically, a scene will have a limited number of shapes but many more bodies. The new plugin architecture allows bodies and shapes to be created independently and then associated with one another.

Although this approach requires more verbose code, it provides greater control over the simulation and reduces the memory footprint of the scene.

Access to wasm data

The first plugin to be released will use intensive WASM data to babylon. Using a function to do the conversion can be cpu intensive and we saw a 2x to 3x improvement when using a data ‘shortcut’. Basically these functions are short but called many many times a second. So, any data conversion will be seen in the profiler.
Something that looked like this in legacy:

impostor.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform);
impostor.object.position.set(this._tmpAmmoTransform.getOrigin().x(), this._tmpAmmoTransform.getOrigin().y(),this._tmpAmmoTransform.getOrigin().z());
if (!impostor.object.rotationQuaternion) {
if (impostor.object.rotation) {
this._tmpQuaternion.set(
this._tmpAmmoTransform.getRotation().x(),
this._tmpAmmoTransform.getRotation().y(),
this._tmpAmmoTransform.getRotation().z(),
this._tmpAmmoTransform.getRotation().w());
this._tmpQuaternion.toEulerAnglesToRef(impostor.object.rotation);
}
} else {
impostor.object.rotationQuaternion.set(
this._tmpAmmoTransform.getRotation().x(),
this._tmpAmmoTransform.getRotation().y(),
this._tmpAmmoTransform.getRotation().z(),
this._tmpAmmoTransform.getRotation().w());
}

Is now reduced to

const transformBuffer = new Float32Array(this._plugin.HEAPU8.buffer, this._bodyBuffer + bufOffset, 16);
const index = i * 16;
for (let mi = 0; mi < 15; mi++) {
matrixData[index + mi] = transformBuffer[mi];
}

It’s highly encouraged to use the same, where ever it’s possible for future physics plugins.

Debugging

The use of debug rendering information in the new physics system has addressed the issue of misinterpretation that arose in the legacy system.
In the past, rendering was done based on expected outcomes, which often led to issues and inaccuracies in the interpretation of results.
Offset computed by the physics engine could be missing for example. Convexhull may be different as well.
With the new system, debug rendering information is coming from the plugin. Offset, matrix and geometry is coming from the plugin.
This is more work for the plugin creator but a lot less to care for everyone else.

New Physics Viewer will make it easier to see how the Physics plugin handles the shapes and dynamics.

Conclusion

After taking these factors into consideration and conducting several iterations on the plugin and architecture, we have achieved a remarkable improvement in performance compared to the older version using Ammojs. In fact, the new version is around 10 to 20 times faster. In the documentation, we have included some Playgrounds (which are being written as we are drafting this blog post) that demonstrate the difference between the legacy and new version. We recommend referring to these when you are porting your project.

Moving to the new version should be straightforward, and you can expect a boost in performance. With a little extra effort, you can achieve even better performance. Coupled with instances and reworked collision event detection, it’s a whole new world for physics on the web that is available.

--

--

Babylon.js

Babylon.js: Powerful, Beautiful, Simple, Open — Web-Based 3D At Its Best. https://www.babylonjs.com/