Physics - Bullet HelloWorld example

From Wikiid
Jump to: navigation, search

This is a set of comments by 'Mirv' from the bullet forums - they are culled from his 'hello_world' example program.

Introduction

Ok, welcome to the first tutorial for the Bullet Physics Library!

First of all, what does a physics library do?

Well, the answer is fairly simple: physics! Collision detection, solving what happens when things collide, that sort of thing.

A physics library will not draw things for you. It will not make sounds. It has no concept of things like an actual bullet, damage to a person, casting shadows, or anything like that. These tasks are left to the programmer (you!). A physics library will simply say what happens in terms of push and pull forces on objects you define.

Now, bullet is designed with relative realism in mind. Inputting "normal" values for calculations works just fine, but you'll see what I mean if you use a gravity value of [0, -100000, 0]. Things go strange. This is more to do with floating point inaccuracies and the like rather than anything wrong with Bullet. I recommend playing around with various numbers in this tutorial in order to get a feel for what they do.

Sample Code

But enough dribble, you all want to get down coding awesomely interactive scenes. No so fast! First you need to know how to set up Bullet. Deal with one or two objects first, and move on from there. So without further ado, hello world!

 // Include necessary Bullet headers for this tutorial.
 #include "CcdPhysicsEnvironment.h"
 #include "CcdPhysicsController.h"
 #include "BroadphaseCollision/SimpleBroadphase.h"
 #include "CollisionDispatch/CollisionDispatcher.h"
 #include "CollisionShapes/BoxShape.h"
 #include "Dynamics/RigidBody.h"
 #include "PHY_IMotionState.h"
 void updateInput();
 void drawGL();
 void drawBox(float, float, float);
 unsigned int bProgramRunning = 1;  // Set this to 0 to stop (and therefore exit) the program.

A physics environment is what you would expect. Physics behave differently if you're under water, in the air, in space or on land. Different amounts of gravity may apply, resistance to inertia, etc.

 CcdPhysicsEnvironment *physics_environment;

A BroadPhaseInterface is an interface into an algorithm that will determine if objects in an overall environment may collide. In this case, it will test axis-aligned bounding boxes within the environment. Various algorithms exist for this - one of the least effective will be used in this tutorial, for simplicity.

 BroadphaseInterface *broadphase;

A collision dispatcher deals with what to do when objects collide. Essentially, the BroadPhaseInterface will send possible collisions to the dispatcher, which will send the pairs off to see what happens to them. From the docs: CollisionDispatcher supports algorithms that handle ConvexConvex and ConvexConcave collision pairs. Time of Impact, Closest Points and Penetration Depth.

 CollisionDispatcher *collision_dispatcher;

A Collision Shape is some shape that physics will affect. Common types are boxes and spheres, but any triangle mesh can be used as well.

 CollisionShape *box_a;
 CollisionShape *box_b;

A CcdPhysicsController is a physics object that supports continuous collision detection and time of impact based physics resolution. This is the object through which actual physics calculations are applied based upon how an object is constructed, its shape, etc; it controls the object.

 CcdPhysicsController *box_a_controller;
 CcdPhysicsController *box_b_controller;

Motion states. These are the "interface to explicitly synchronise the world transformation." It basically allows the user to retrieve position, rotation, etc, information from an object, or (in the case of kinematic and static objects) to set those properties. For an example of its use, see the drawing code later.

 DefaultMotionState a_motion_state;
 DefaultMotionState b_motion_state;
 int main(int argc, char **argv)
 {
   SimdVector3 localInertia;
   initScreen();
   initGL();

A simple broadphase does a brute force check of O(n^2) on all aabb's.

   collision_dispatcher = new CollisionDispatcher();
   broadphase = new SimpleBroadphase();

Create a new environment to put our objects into.

   physics_environment = new CcdPhysicsEnvironment(collision_dispatcher, broadphase);

Deactivation time in an environment sets the amount of time (in seconds) after objects with a velocity less than a certain threshold will deactivate. Deactivation means that it has become stationary, and physics updates for it are not required until it is activated, or moved, or something collides with it.

   physics_environment->setDeactivationTime(2.0f);
   physics_environment->setGravity(0.0f, -10.0f, 0.0f);

CcdConstructionInfo defines various properties of the construction of an object as it pertains to collision detection and physics calculations. We can use the same construction information to produce many of the same type of object. Take bricks for example. Each brick is constructed the same, so you can use the same construction information for each.

   CcdConstructionInfo object_cons_a;
   CcdConstructionInfo object_cons_b;

Create a new box collision shape. This will be the "static" box.

   box_a = new BoxShape(SimdVector3(30.0f, 2.0f, 30.0f));
   a_motion_state.setWorldPosition(0.0f, -10.0f, 0.0f);

The object is static and doesn't need most of this stuff. You can include it for consistency, but otherwise it doesn't matter:

   object_cons_a.m_friction = 0.5f;
   object_cons_a.m_linearDamping = 0.2f;
   object_cons_a.m_angularDamping = 0.0f;
   object_cons_a.m_gravity = SimdVector3(0.0f, 0.0f, 0.0f);
   object_cons_a.m_mass = 0.0f;  // Object a is our static object, so mass really doesn't affect things.
   box_a->CalculateLocalInertia(0.0f, localInertia);
   object_cons_a.m_localInertiaTensor = localInertia;

This stuff is needed though:

   object_cons_a.m_collisionFlags = CollisionObject::isStatic;  // A static object. It won't move with physics.
   object_cons_a.m_collisionShape = box_a;                      // Set the shape to do collision tests and physics against.
   object_cons_a.m_MotionState = &a_motion_state;
   box_a_controller = new CcdPhysicsController(object_cons_a);  // Create a new controller for the object.

Ok, near as I can tell, motion threshold is about efficiency. If the object didn't move much, then don't treat it as a continuously moving object, just test against it's current position. For example, if an airplane moves 1cm, you can fairly safely perform physics calculations of a person hitting it without doing exact calculations of movement over the time period for that 1cm. You can do the calculations based just upon the plane's new position and rotation.

   box_a_controller->GetRigidBody()->m_ccdSquareMotionTreshold = 1.0f;  // The object is static anyway in this case.
   physics_environment->addCcdPhysicsController(box_a_controller);  // Add the object to the environment.

Now do box b. Box b will be a dynamic motion object.

   box_b = new BoxShape(SimdVector3(1.0f, 1.0f, 1.0f));  // Create a new box collision shape. This will be the "dynamic" box.
   // box_b->SetMargin(0.1f);  // Feel free to play around with this value.

Set up orientation and location of the object.

   SimdQuaternion quat;
   SimdVector3 axis(0,0,1);
   SimdScalar angle=45.0f;
   quat.setRotation(axis,angle);
   b_motion_state.setWorldOrientation(quat.getX(),quat.getY(),quat.getZ(),quat[3]);
   b_motion_state.setWorldPosition(0.0f, 10.0f, 0.0f);

Object 'b' has its motion affected in a dynamic manner, and so set various physical properties.

   object_cons_b.m_friction = 0.5f;
   object_cons_b.m_linearDamping  = 0.2f;  // Think of this as resistance to translational motion.
   object_cons_b.m_angularDamping = 0.1f;  // Think of this as resistance to rotation.
   object_cons_b.m_gravity = SimdVector3(0.0f, 0.0f, 0.0f);
   object_cons_b.m_mass = 1.0f;  // Object a is our static object, so mass really doesn't affect things.
   box_b->CalculateLocalInertia(1.0f, localInertia);
   object_cons_b.m_localInertiaTensor = localInertia;
   object_cons_b.m_collisionFlags = 0;      // Nothing special - treat it as a normal dynamic object.
   object_cons_b.m_collisionShape = box_b;  // Set the shape to do collision tests against.
   object_cons_b.m_MotionState = &b_motion_state;
   box_b_controller = new CcdPhysicsController(object_cons_b);          // Create a new controller for the object.
   box_b_controller->GetRigidBody()->m_ccdSquareMotionTreshold = 1.0f;  // As before.
   box_b_controller->SetAngularVelocity(0.0f, 0.0f, 5.0f, false);       // This will apply some rotation force to the object.

Add the physics object to our environment. This will allow the environmnent to affect it.

   physics_environment->addCcdPhysicsController(box_b_controller);

Note that in this tutorial, there is no timered callback or determination of time between frames. So the faster your computer can run this program, the faster the physics will appear to go! */

   while(bProgramRunning)
   {
     updateInput();
     physics_environment->proceedDeltaTime(0.0f, 0.00167f);  // Update the environment.
     drawGL();                                               // Draw the boxes on-screen.
     glFinish();                                             // Wait for OpenGL calls to finish.
     SDL_GL_SwapBuffers();                                   // Update the screen.
   }
   SDL_Quit();
   return 0;
 }

This function updates user input, and how it will affect the scene.

 void updateInput()
 {
   SDL_Event event;

Go over generic events here - the Escape key is one such example.

   while(SDL_PollEvent(&event))  // Pump the event list.
   {
     switch(event.type)
     {
       case SDL_QUIT: bProgramRunning = 0; break;  // Quit the program.
       case SDL_KEYDOWN:
         switch(event.key.keysym.sym)
         {
           case SDLK_ESCAPE: bProgramRunning = 0; break;  // For the moment, escape quits the program.
           case SDLK_SPACE:  // Resets the scene.
             // Reset the dynamic box with some values.
             box_b_controller->setPosition(0.0f, 10.0f, 0.0f);  // Comment out this line to see some interesting occurrences.
             box_b_controller->setOrientation(0.0f, 0.0f, 1.0f, 0.2f);
             box_b_controller->SetLinearVelocity(0.0f, 0.0f, 0.0f, false);  // Watch what happens when you don't set the position, and leave this all 0. Set the second value to -1.0f and try again.
             box_b_controller->SetAngularVelocity(0.0f, 0.0f, 0.0f, false);
             // a_motion_state.setWorldPosition(0.0f, -20.0f, 0.0f);  // This will work because "a" is static.
             break;
           case SDLK_q: bProgramRunning = 0; break;  // Pressing q will quit too.
           default: break;
         }
         break;
       default: break;
     }
   }
 }

To get the position of box A:

 a_motion_state.getWorldPosition(x, y, z);
 glTranslatef(x, y, z);
 glColor3f(0.5f, 0.5f, 0.5f);
 drawBox(30.0f, 2.0f, 30.0f);  // Draws box a. Box a should not be moving.

...or...

 b_motion_state.m_worldTransform.getOpenGLMatrix(m);  // Get the OpenGL transformation matrix directly.
 glMultMatrixf(m);



Wikiid Pages relating to the Bullet physics engine (edit)
Main articles: Physics - Bullet
On the Bullet website: Forum, Doxygen docs, FAQ, A Module diagram, Draft user manual, CCD & Physics Paper
Annotated example programs: HelloWorld, Tutorial2
Other: Collected advice snippets
See also : Intersection tests, GameTools Sabot