Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

The future of AI-driven development. Join the discussion around insights on low code's and AI's roles in building mission-critical apps.

Has GenAI transformed how you work? Or has the hype cycle played out? Tell us your thoughts on its impact inn your organization.

DevEx Roundtable: Join the discussion about the evolving needs of the developer from streamlined workflows to infrastructures investments.

WebGL Physics-Based Car Using Babylon.js and Oimo.js

In this tutorial, I will explain the implementation of the Oimo.js ''All-Terrain'' car using Babylon.js' new physics engine architecture.

By  · Tutorial
Comment (0)
Save
8.5K Views

One of the most common first demos each physics engine presents consists of a working car driving on a predefined terrain. It is a wonderful demo, showing off impostors, joints, motors, and integration with other frameworks. Let's see how to do that using Oimo.js and Babylon.js.

In this tutorial, I will explain the implementation of the Oimo.js "All-Terrain" car using Babylon.js' new physics engine architecture. The demo, for the impatient readers, is here: http://www.babylonjs-playground.com/#SFELK#2 . As you can see, the car has working wheels, it can turn right and left (surprise!), and it has (kind-of) suspension.

A quick note before I begin. Every time I present a new physics demo, I hear the same things—Why is everything based on simple geometric shapes? Why is the car so "simple"?
A car is a complex object, it is not a simple box with 4 spheres for wheels. And, you are right! But not in the physics-engine world. I am showing just the impostors. Take the impostors, cover them with a nice mesh of a beautiful Ferrari (or a Lada, if that's your thing), and everything will look better. Deep inside though, they will still be boxes and spheres.

Let's first cover the steps we need to take in order to reach our goal:

  1. Construct the basic scene - car mesh(es), ground.
  2. Enable the physics engine
  3. Set up the impostors
  4. Connect the impostors using joints:
    1. Joints for the wheels
    2. Joints for the suspensions
  5. Enable user input
  6. Add a bit of terrain objects
  7. Improvements, like a first-person driving simulator
  8. ???
  9. Profit!

Constructing the Basic Scene

For the basic scene, we will require a simple flat ground, a few obstacles for our car, four wheels, and one body:

Great, we now have the meshes we need, and it all looks like this: http://www.babylonjs-playground.com/#1PVBTF

Enabling the Physics Engine

As I want to show how to build a car using Oimo.js, I will be using the OimoJSPlugin:

scene.enablePhysics(undefined, new BABYLON.OimoJSPlugin())

If you ask why undefined, it is due to pure laziness (and the fact that I want to show you that you can be lazy too). Undefined (or null) will simply set the default gravity to be -9.81 in the Y axis, the standard gravity in our beautiful, beautiful world.

That's all it takes to enable physics, now we need to start defining impostors

Set Up the Impostors

I have a car, which is built using a single box (the car's body) and 4 wheels/spheres. As Oimo has only 3 impostor types - Box, Sphere, and Cylinder, I think it is obvious which one we will take.

I define the ground and car's impostors like this:

We have now all of the impostors we need to continue, and our scene looks like this: http://www.babylonjs-playground.com/#1PVBTF#1

You will notice that all meshes are simply free-falling until they touch the ground. This is because we haven't connected the car using joints... which is actually our next step!

Joints (The Physics-Engine Type)

Let's first see what movement we require and on what axes. This way we will understand better what joints we will require.

  1. Angular movement between the car and the wheel on the y axis - to enable steering
  2. Linear movement between the car and the suspension - to allow a "4x4" style spring suspension
  3. Angular movement between wheel and suspension on the z axis (in local coordinates) - so that the car can move forwards!

Very poorly illustrated here:

The expected movements

What joints correlate to these movement types? We have a few options:

*Note: Great thanks to the ODE physics engine documentation. Without them, no one would have joint illustrations*

  1. A joint/constraint that has angular movement only on one axis is the hinge joint:
    Hinge joint
  2. A joint that has linear freedom in one defined axis is the prismatic joint. Think slider joint that only moves backwards and forwards.
    slider joint
  3. A joint that has two hinge joints combined in one is the wheel joint. 
    wheel joint
  4. A joint that has angular AND linear freedom of movement on the same axes is the slider joint, which is the prismatic joint with an extra angular movement. Think prismatic joint that can also turn.
    slider joint

What I chose:

  1. Slider joints for the connection between the suspensions and the car body
  2. A hinge joint for the connection between the wheel and the suspensions.

Speaking of suspension, you might have noticed I haven't created them yet. Let's add them to the party, right after the last code we added. I will also make them invisible, so they would only serve as the connection between the wheel and the car:

The Slider Joints

I decided to use a slider joint to connect the car and the suspension object. I could have chosen the wheel joint, and then connect the wheel to the suspension with the wheel joint, and then connect the body and the suspension with a prismatic joint. The main question is - where do we want our angular steering constraint - at the connection to the car (turning the suspensions AND the wheel) or just down at the wheel. I chose the first one, as it made more sense.

An oimo.js slider joint has another wonderful feature - a motor. A motor will enable angular movement. I will use this motor to enable steering. I will also limit the motorical angular movement, so that the steering direction will be limited as well. We don't want the steering to turn without stopping.

Let's see the definition of the first slider joint:


The Main Pivot

The main pivot is the point on the main physics impostor to which the 2nd body will be connected. Connecting two objects without defining the pivot will connect both at the center of their mass. This might be the desired functionality in various cases, but we want the suspension to start from the four edges of the car, and not from the middle of it. Here is a quick example to help explain things:

Without pivot - http://www.babylonjs-playground.com/#BGUY , the sphere will be connected to the center of the object. This means that they are both connected in their 0,0,0 position (in local coordinates). But what if we want to connect the sphere to a different point of the log? Here it is with pivot defined: http://www.babylonjs-playground.com/#BGUY#1

If it is not understandable now, please leave a comment!

So the main pivot is the edge of the car's body.

The Axis Chosen

The slider joint has one axis defined. As I require the movement on the y-axis, the axis I chose was (0, -1, 0). If you wonder why -1 and not 1, it is due to the way I am connecting the two obejct - which object is the main one. The movement should happen on the Y axis, that is true, but towards the floor, and not towards the sky. And as I am connecting the body to the suspension (the car's body is the main impostor) the axis is actually -1 on the y-axis.

Native Parameters

The native parameters are engine-specific parameters that the other engines don't have. These are parameters that only Oimo.js will understand and are therefore separated from the rest. If you use native parameters, you won't be able to simply change the engine from Oimo to Cannon. This will require a bit of work.

The native parameters, in this case, correlate to the constraints in hand—min and max are the definition of the length of linear movement—how long will the suspension be. The limit is a definition for the motor—what are its movement limits.

There are three other slider joints, all with the same definition (but with a different pivot, of course). Then we are done connecting the car to the suspension.

The Hinge Joints

To connect the wheel and the suspension I will use a hinge joint.

A hinge joint is also a motor enabled joint, which means that it can move in a specific direction if provided with the right parameters.

Let's see how I defined the joint:

When adding all 4, you will notice something very interesting:
http://www.babylonjs-playground.com/#1PVBTF#2 

The joints struggle to stay in place.

Oimo's default number of iterations (10) doesn't allow it to process the joint constraints correctly. If you notice that your joints are "losing" their axis, try increasing the number of iterations the engine is running per frame. This might (might!) hurt performance, but if you need a car, there is no way out of this. (Note - Oimo themselves solve it differently - they run a "correction" function to keep the wheels in place. I find this method to be a hack. Hacks are great, but not the best solutions in the world.) 

To increase the number of iterations, set the first variable in OimoJSPlugin constructor:
scene.enablePhysics(undefined, new BABYLON.OimoJSPlugin(200)). Notice how the suspension is now intact and stay in the y-axis: http://www.babylonjs-playground.com/#1PVBTF#3

You might notice that the car goes crazy after a while. We will take care of that in a second when we enable user input.

Enable User Input

I want my car to drive! So, it's time to enable the user input. I always prefer the arrow keys to be located on WASD, but it's really up to you if you want to change it.

The main concept: with keyDown the engine will start moving forward (or steering will be triggered). With keyUp the movement will be reset.

I will comment in the code with explanations:

This is what it looks like afterwards : http://www.babylonjs-playground.com/#1PVBTF#5

Now I can move the car forward and backward. If the ground is too slippery for you, add some friction! You could also add mass to the wheels to keep them firm on the ground.

You can also steer, with an angle changing over time. Try it out for yourself!

Terrain Objects

The car I built is a 4x4 suspended car. Now we need to test it!

Let's add a few objects for us to maneuver around:

And now we have a few obstacles in the way: http://www.babylonjs-playground.com/#1PVBTF#6

Car with obstacles

Improvements

Camera Movement

There are a few improvements I can implement rather easily... 

First, you might notice that when pressing forward the camera moves as well.

To cancel that, simply cancel the camera movement keys. The simplest way is... to reset the keys arrays:

Different Types of Cameras

We can add a camera that will serve as a first-person camera, giving players a view from the driver seat.

I have created two cameras: a regular free camera and a "virtual reality" camera that can be moved with a cell phone:

Here it is: http://www.babylonjs-playground.com/#1PVBTF#7

Notice that I set the car's body AND the holders to be invisible.

And That's It!

Now, we have a very simple car implemented in Oimo.js and Babylon.js. This is a very simple demo and a quick & easy tutorial just to get you started. Of course, there is much more we could do.

I hope I managed to teach a bit about the constraints/joints and how to use them. If you have any questions, please write them in the comments and I will answer as soon as I can!

Published at DZone with permission of Raanan Weber, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Comments