Implementing the Bullet physics library.

Bullet is a physics engine that simulates collision detection and soft and rigid body dynamics. It has been used in many released video games as well as for visual effects in movies. The Bullet physics library is free and open-source software subject to the terms of the zlib License.

Some of the features Bullet has to offer include:

A rich set of rigid body and soft body constraints with constraint limits and motors

You can find the source code link and more information at: http://bulletphysics.org.

Let's take a look at how you can incorporate Bullet into your own game project. I am not going to spend the time going over how to link the libraries to our demo project since we have covered this a few times now. If you do need a refresher, flip back a few chapters to see how. What we are going to do is incorporate the Bullet engine into our demo engine and then use the Bullet engine's calculations to position our game objects in real time. In this example, we are going to create a simple ground plane and then a ball (sphere) to fall and collide with the ground. We will be using Bullet's built-in types to support this, including gravity to give it a realistic effect.

Beginning with the Ground GameObject, we set up variables to hold some of our needed physics values. The first is of type btCollisionShape. This is a Bullet type that allows the definition of simple shapes to use when creating bounding objects for the physics tests. The next is of type btDefaultMotionState, which again is a Bullet datatype that describes the way the object should behave when in motion. The last variable we need is of type btRigidBody, which is a Bullet datatype that will hold all the physical properties of the object that our physics engine would be concerned with:

class GroundObject : BookEngine::GameObject 
{ 
   ... 
 
   btCollisionShape* groundShape = nullptr; 
   btDefaultMotionState* groundMotionState = nullptr; 
   btRigidBody* groundRigidBody = nullptr; 

Once we have these variables defined, we can then construct the physics representation of the ground object in its Init() function:

void GroundObject::Init(const glm::vec3& pos, const glm::vec3& scale) 
{ 
   ... 
   groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), 1); 
   groundMotionState = 
      new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(m_position.x, m_position.y, m_position.z))); 

We start by setting our groundShape variable to be a btStaticPlanShape. This is a Bullet object that specifies a simple plane object, which is perfect for our needs and a simple ground object. Next, we set the groundMotionState. We do this by using the btDefaultMotionState Bullet object. The btDefaultMotionState is the type used for specifying the way an object will behave in motion. When creating a new btDefaultMotionState, we need to pass in some information about the object's transform, that is the rotation and position of the object. To do this we pass a btTransform object with its own parameters of a rotation in quaternion format (btQuaternion(0, 0, 0, 1)) and a position in vector 3 format (btVector3(m_position.x, m_position.y, m_position.z)):

btRigidBody::btRigidBodyConstructionInfo 
 groundRigidBodyCI(0, groundMotionState, groundShape, btVector3(0, 0,  0)); 
 groundRigidBody = new btRigidBody(groundRigidBodyCI); 

Now, with the groundShape and groundMotionState set, we can move on to creating and setting the rigid body info. First we define a holder btRigidBodyConstuctionInfo variable for the construction information called groundRigidBodyCI. This object takes in a few parameter values, a scaler value to specify mass, the motion state of the object, the collision shape, and a vector 3 to specify the local Inertia value. Inertia is the resistance of any physical object to any change in its state of motion. It is basically the tendency of objects to keep moving in a straight line at constant velocity.

Since our ground object is static and does not require any changes based on the physics input, we can forego an  Update() function and move on to the Ball object that we will be using to test out the system.

Moving into the BallObject.h file, we define some variables that we need, much like we did for our ground object. We create a motion state, a scalar (an integer) value for mass, collision shape, and, finally, a rigid body:

btDefaultMotionState* fallMotionState;
btScalar mass = 1;
btCollisionShape* fallShape;
btRigidBody* fallRigidBody;
...  

Now, moving into the BallObject.cpp file, we assign some values to the variables we have just defined:

void BallObject::Init(const glm::vec3& pos, const glm::vec3& scale)
 {
    ...
    
    fallShape = new btSphereShape(10);
    btVector3 fallInertia(0.0f, 0.0f, 0.0f);  

First, we set our collision shape. In this case, we are going to use the type btSphereShape. This is the default shape for spheres and takes in a parameter to set the radius for the sphere. Next, we create a vector 3 holder for the sphere's inertia. We are setting this to be all zeros since we want this ball to fall freely with no resistance based on the mass of the object and a gravity value we will set shortly:

fallMotionState =
       new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1),     
btVector3(m_position.x, m_position.y, m_position.z)));

Next, we set the ball's motion state, just like we did for the ground object. We set the rotation to be 0 and the position to the current position of the ball object:

 fallShape->calculateLocalInertia(mass, fallInertia);
    btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,  fallMotionState, fallShape, fallInertia);
    fallRigidBody = new btRigidBody(fallRigidBodyCI);
     }
  

We then calculate the local inertia value using the handy calculateLocalInertia() function, passing in the mass and fallInertia values. This will set the falling vector for our ball object to be used in the first tick of the physics engine. Finally, we end with setting up the rigid body object, exactly like we did previously with the ground object.

With the ball object, we do expect the physics engine output to affect the ball object. It's because of this that we need to make some adjustments in the Update() function of the ball object:

void BallObject::Update(float deltaTime)
 {
    btTransform trans;
    fallRigidBody->getMotionState()->getWorldTransform(trans);
    m_position.x = trans.getOrigin().getX();
    m_position.y = trans.getOrigin().getY();
    m_position.z = trans.getOrigin().getZ();
  }

The first step in the update loop for the ball object is to get the physics object's transform from the rigid body. Once we have this transform object, we can then set the ball object's mesh (the visible object) to make the position of the physics transform object. That's it for the object itself. The ball and ground objects now house all the needed physics information. We can now implement the physics engine loop into our game loop and get the ball rolling, no pun intended!

For the implementation of the physics engine into our existing game engine's loop, we need to set up a few values first. Jumping into our Gameplayscreen.h, we define the variables to hold these values:

btBroadphaseInterface* broadphase = new btDbvtBroadphase();  

First is a definition of a btBroadphaseInterface class object, which provides a Bullet interface to detect AABB-overlapping object pairs. In this case, we are setting it to a btDbvtBroadphase, which implements a btBroadphase using two dynamic AABB bounding volume hierarchies/trees. This tends to be the best broadphase when working with many moving objects; its insertion/addition and removal of objects is generally faster than the sweep and prune broad phases found in the btAxisSweep3 and bt32BitAxisSweep3:

btDefaultCollisionConfiguration* collisionConfiguration = new     
btDefaultCollisionConfiguration(); btCollisionDispatcher* dispatcher = new
btCollisionDispatcher(collisionConfiguration); btSequentialImpulseConstraintSolver* solver = new
btSequentialImpulseConstraintSolver;

Next, we have defined for the collision configuration, collision dispatcher, and sequential impulse constraint solver. We won't go too deep on each of these, but the main points are that the collision configuration sets up some Bullet internal values, such as collision detection stack allocators and pool memory allocators. The collision dispatcher is the definition of how the collisions will be handled. It supports algorithms that handle ConvexConvex and ConvexConcave collision pairs, time of impact, closest points, and penetration depth. Finally, the sequential impulse constraint solver, which defines what can be thought of as the algorithm, will determine how to solve the collisions between objects. For those wishing to know, it is a Single Instruction, Multiple Data (SIMD) implementation of the Projected Gauss-Seidel (iterative LCP) method:

btDiscreteDynamicsWorld* dynamicsWorld = new       
btDiscreteDynamicsWorld(dispatcher, broadphase, solver,
collisionConfiguration);

The last variable we need to define is for our dynamics world object. A btDiscreteDynamicsWorld provides a discrete rigid body simulation. This can be thought of as the environment or world in which the physics simulation occurs. Once we have this defined, we have all the pieces in place to start our physics simulation.

Let's jump into the GameplayScreen.cpp file and look at the OnEntry() function that we will use to initialize the physics simulation:

void GameplayScreen::OnEntry() 
{ 
   ... 
 
   dynamicsWorld->setGravity(btVector3(0, -1, 0)); 
   dynamicsWorld->addRigidBody(m_ground.groundRigidBody); 
   dynamicsWorld->addRigidBody(m_ball.fallRigidBody); 
... 
} 

The first thing we set is our gravity vector. In our simple example, we are setting this to be -1 on the y axis. Next, we add the two created rigid bodies to the simulation environment, one for the ground and one for the ball. That handles the initialization of our physics engine; now we need to handle updating it on each engine tick:

void GameplayScreen::Update(float deltaTime) 
{ 
   CheckInput(deltaTime); 
   dynamicsWorld->stepSimulation(1 / 60.f, 10); 
   m_ball.Update(deltaTime); 

Inside of the GameplayScreen::Update() function, we first check the input, then call the update on the physics engine, before finally calling the update on the game objects themselves. It is important to note this order. We want to accept the user's input first, but we want to make sure we have updated the physics engine before the objects. The reason is that the physics calculations should have some effect on the objects and we don't want to cause a case where our drawing loop is ahead of the physics loop, as this would definitely cause some unwanted effects. You will also notice the physics update function, stepSimulation, takes in two parameters. The first is the amount of time to step the simulation by. This is typically the time since you last called it. In this case, we are setting this to 1/60 of a second, or 60 FPS. The second parameter is the maximum number of steps that Bullet is allowed to take each time you call it. If you pass a very large value as the first parameter, say, something like five times the size of the fixed internal time step or game clock, then you must increase the number of  maxSubSteps to compensate for this; otherwise, your simulation is losing time, which will again result in some unwanted physics calculation output.

That's it! We now have a physics engine running its simulation and effecting the objects in our world that we are drawing on the screen. You can see this in action by running the PhysicsDemo example project in the Chapter05 GitHub repository. The output will look something like the following: