02-26-2018, 02:42 PM
(This post was last modified: 03-16-2018, 07:10 PM by Brian Beuken.)
Code:
#include <bullet/btBulletDynamicsCommon.h>
class PhysicsObj
{
public:
PhysicsObj(btCollisionShape* Shape, // to create a new object it needs shape
float mass, // and mass data
const btVector3 &Position, // these have defaults(0,0,0), but best to be replaced
const btQuaternion &Rotation ); // here be dragons...but glm and bt, can handle it
~PhysicsObj();
// best to allow our Object to set and return use things in accessors
inline btCollisionShape* GetShape(){return m_Shape;}
inline void SetShape(btCollisionShape* S) {m_Shape = S; }
inline btRigidBody* GetRigidBody(){return m_Body; }
inline void SetRighidBody(btRigidBody* RB) { m_Body = RB; }
inline btMotionState* GetMotionState() {return m_Status;}
// couple of useful utility functions to simplify the rotate and position of our physics objects
// these act on the Rigid body and therefore will reposition/orient an object while it is
// in the physics world, useful for restarting or teleporting.
inline void SetOrientation(btQuaternion Rotation)
{
// could just m_body->setXXX but there's a slim chance you might add multiple rigid bodies to an object
// and you would make GetRigidBody return the active one, so this is a safer method to allow for later expansion
GetRigidBody()->setWorldTransform(btTransform(Rotation, m_Body->getWorldTransform().getOrigin()));
}
inline void SetPosition(btVector3 Position)
{
GetRigidBody()->setWorldTransform(btTransform(m_Body->getOrientation(), Position));
}
// This gets the Motionstate transform, which is useful variables made available after the step
// as these are generated by the step system, they should be used as read only, as writing to them
// has no effect and sending them back to the motionstate is not useful,
void GetTransform(btScalar* transform)
{
if (m_Status)
m_Status->GetWorldTransform(transform); //this is the important part
}
protected:
btRigidBody* m_Body;
btCollisionShape* m_Shape;
GameValues* m_Status; // the general position, orientation and scale of our object to return back
};
It is possible to build the source code for Bullet, but really thats just too much effort... best to install it on your target with
sudo apt-get install libbullet-dev
Bullet itself makes use of 4 main sub libraries
BulletCollision, BulletSoftBody, BulletDynamics, LinearMath
These need to be included in your VisualGDB/Studio library list and of course you need to include your library directory for the header files /usr/include/bullet
Once done its all pretty much self contained and then its up to you to make the physics world.
Bullet works by creating a physics based world, populated with objects which represent your objects and providing a set of base classes which you can attach to your own objects and keep track of their position, and orientation so that you can render them. And in doing so it provides physical property concepts of velocity, mass, orientation, shape and other real world properties you can expect a 300metre long spacecraft to have, as well as a 5’2” overweight but surprisingly athletic plumber. Once these representative physical objects are set up, Bullet then updates their motion and forces acting on or against them and keeps track of all the positional and orientation information in its own class members, allowing you to use them in your own position and rendering systems.
In the simplest terms, Bullet has all these objects in this unseen physics world, and then works out what happens to them when they all get thrown up into the air bouncing off each other and then landing and bouncing on the ground. Rotational effects, friction, material properties, momentum retention and absorption as well as many other things are taken care of for us.
In order for this magic to work, we must allow Bullet to create and maintain this world which will mainly contain mathematical concepts of our terrain and of any moving or static objects we deem important enough to be part that dynamic world. The objects are added to the world, given some physical properties, and a suitable “solver” function is then called which then does the work to move them around. With a few optimisations added to the mix for efficiency and speed, such as a choice of method for a broad phase collision to reduce unwanted tests.
We can have quite some variety of basic object shapes which are based on our previous ideas of Primitive collision types, and of course every object can have it’s own physical properties and different solvers can be used for different kinds of motion and interaction.
By using Bullets methods to implement motion, we effectively can let it take care of all the movement, collision, response and reactions we should expect our objects to have. That lets us focus more on logic and decision making. Bullet will create events we can detect and objects we can test, when something important happens, like a collision, allowing us to make the right kind of sound, or destruction of a relevant object, and then Bullet can be asked to continue to work on the motion of the interacting objects according to standard rules of how we think the universe should work.
We should bear in mind though it’s not exact, physics in the real universe works on a quantum level, everything checked and tested at all times. Computers work on a sequential, one at time, at a given point in time, level. But it does work quite effectively, at the cost of quite a lot of processing!
Bullet needs a few things set up before it can work...
btBroadphaseInterface* BroadPhase;
btDefaultCollisionConfiguration* CollisionConfiguration;
btCollisionDispatcher* Dispatcher;
btSequentialImpulseConstraintSolver* ConstraintSolver;
btDiscreteDynamicsWorld* DynamicPhysicsWorld;
each in turn need to be initialised
// create the main physics systems
BroadPhase = new btDbvtBroadphase();
CollisionConfiguration = new btDefaultCollisionConfiguration();
Dispatcher = new btCollisionDispatcher(CollisionConfiguration);
ConstraintSolver = new btSequentialImpulseConstraintSolver;
DynamicPhysicsWorld = new btDiscreteDynamicsWorld(Dispatcher, BroadPhase, ConstraintSolver, CollisionConfiguration);
// set a "normal" gravity level
DynamicPhysicsWorld->setGravity(btVector3(0, -9.81f, 0));
Once done, we will now have a physics world that simply needs to be updated in steps,
DynamicPhysicsWorld->stepSimulation(1 / 60.0f);
but we still have to populate the physics world, and that requires us to create our rigid bodies from a list of possible types
In my book I create a class called a PhysicsObj, which any render object can have an instance of, the header is in the code snippet above. The rest of the code can be downloaded from my site.
Once you have a PhysicObj created, you can focus on moving that object around, or allowing it to be moved around by the physics simulation. Then its a (fairly) simple step to acquire orientation and position data that will allow you to render your object and if you choose, its physics boundings.
You will find a good example of setting up Bullet in the V2MD2ModelDemo example code.
Brian Beuken
Lecturer in Game Programming at Breda University of Applied Sciences.
Author of The Fundamentals of C/C++ Game Programming: Using Target-based Development on SBC's
Lecturer in Game Programming at Breda University of Applied Sciences.
Author of The Fundamentals of C/C++ Game Programming: Using Target-based Development on SBC's