After setting up the Bullet library in previous post, now we will move onto the first program of Bullet. In this program I will create a plane and will add some rigid bodies to the scene. Rigid bodies are such objects which do not get deformed when they collide with something else. Most of the trees, building, stones, etc will be rigit bodies in a 3D scene. Bullet provides a class btRigidBody for defining rigid bodies. For our program we are going to use spheres as our rigid bodies, the reason for choosing spheres is, they are the easiest objects when it comes to calculating collision detections. You need only two pieces of information when you want to detect the collision detection, the radius and the position. What I have done is created a class called bullet inside my tools library, this will take care of all the physics that I will deal with throughout the project.
class bullet { public: bullet(); ~bullet(); btStaticPlaneShape* plane; //infinite plane for tests std::vector<btRigidBody*> bodies; //all the rigid bodies btClock m_clock; btDynamicsWorld* world; //every physical object go to the world btDispatcher* dispatcher; //what collision algorithm to use? btCollisionConfiguration* collisionConfig; //what collision algorithm to use? btBroadphaseInterface* broadphase; //should Bullet examine every object, or just what close to each other btConstraintSolver* solver; //solve collisions, apply forces, impulses void createInfinitePlaneAtOrigin(); void bullet_stepSimulation(); btRigidBody* addSphere(float ,float ,float ,float ,float ); };
The main program can be divided into four different parts for simplicity. Here are the parts of our first Bullet physics program- 1- initializations : all the bullet initializations will be done here. 2- creating a plane : create a plane shape. (i will explain what it actually means below in detail) 3- adding spheres. 4- rendering spheres & plane. Explaining adding spheres & planes: When you run your graphics engine without any physics all you’re concerned about is the 3D world that you’re rendering your things in. You’re not bothered about the collisions of the objects in your scene. All you care about is the shading of your objects i.e. how your object looks. The moment you add some physics library in your scene, you create another world. Think of it as a parallal universe to the 3D world that you’re building. This parallal universe will be responsible for all the calculations of the positions of your objects based on the physics. This world, lets call it PHYSICS world, is not bothered about HOW your objects are shaded. In this world we are strictly concerned with the position of the object, mass, and inertia of the object. In our application we will maintain a vector (btRigidBody) of all the shapes that we have in the 3D. We will use this vector for all the collision detections in the scene. So when you add a plane or spheres, you’re actually adding that to the vector. Bullet provides us with different shapes that we can add to our scene. In our program we will use two shapes provided by bullet, plane and sphere. When we add any shape to the vector i mentioned above, we also specify its position, mass, inertia into btMotionState object which is then passed to btRigidBody and then its finally added to the vector. Here is a sample code to add a rigid body for your physics.
btRigidBody* bullet::addSphere(float rad, float x,float y,float z,float mass) { btTransform t; //position and rotation t.setIdentity(); t.setOrigin(btVector3(x,y,z)); //put it to x,y,z coordinates btSphereShape* sphere=new btSphereShape(rad); //it's a sphere, so use sphereshape btVector3 inertia(0,0,0); //inertia is 0,0,0 for static object, else if(mass!=0.0) sphere->calculateLocalInertia(mass,inertia); //it can be determined by this function (for all kind of shapes) btMotionState* motion=new btDefaultMotionState(t); //set the position (and motion) btRigidBody::btRigidBodyConstructionInfo info(mass,motion,sphere,inertia); //create the constructioninfo, you can create multiple bodies with the same info btRigidBody* body=new btRigidBody(info); //let's create the body itself world->addRigidBody(body); //and let the world know about it bodies.push_back(body); //to be easier to clean, I store them a vector return body; } // code: https://www.youtube.com/user/thecplusplusguy
Once you add a shape, you can finally go onto rendering your object. Rendering a shape is not at all different from earlier, but with bullet, you need to ask the physics engine about the position of the shape. Because it is the bullet lib which is going to calculate new positions of your object based on the physics interactions. Speaking in terms of matrices, bullet library will give you the translation matrix for your object.
float mat[16]={0}; btTransform t; body->getMotionState()->getWorldTransform(t); t.getOpenGLMatrix(mat);
You get the btTransform object of the shape (body) that you want to query using getWorldTransform(). The btTransform has a method to get a matrix of the object in OpenGL format. You send an empty matrix to this function, the function will fill up the matrix and then you can use it in your program. If you’re working on fixed functionality pipeline you can simply use the matrix returned like this->
glPushMatrix(); glMultMatrixf(mat); //translation,rotation // render the shape glPopMatrix();
But if you’re working in programmable pipeline like myself, you will need to get transpose of the matrix, you then multiply the new matrix with view and projection and then send it to shaders. (MVPs are usually send to shaders using uniforms) After this you can render your object like you were doing before and there will be proper collosion detection between different objects. You can call addSphere method on a keypress, this will give the effect of firing a bullet. In my scene I am adding a new shape where my camera is placed, I am also giving some initial velocity to the object.
PS: This program is based on a bullet physics tutorial by The CplusPlus guy. All the documentation for the bullet classes and functions is provided on documentation section on bullet webpage.
I would also like to add a couple of mistakes which I did, which ‘rendered’ my code useless-
1- I was adding spheres inside the display loop which caused program to slow down.
2- I was not resetting the model matrix when I was calculating positions of a new sphere.