Simple Box

From Physics Simulation Wiki

Jump to: navigation, search

Contents

Introduction

All tutorials that we cover in this series would use the example browser for rendering and interactivity. You need to ensure that you are able to create header and cpp sources as detailed in Understanding the Bullet ExampleBrowser framework#Using CommonExampleInterface to define custom physics demos section.

In this tutorial, we will create a simple box rigid body that falls under gravity. If you have read the previous tutorials, you should have the understanding of the Example Framework of the bullet sdk. If not, we would suggest you read Understanding the Bullet ExampleBrowser framework tutorial first to get yourself familiar with the framework.


Code Organization

We create a header file (SimpleBox.h) and a source file (SimpleBox.cpp).

SimpleBox.h

For this simple demo, we declare our create function as follows:

class CommonExampleInterface* ET_SimpleBoxCreateFunc(struct CommonExampleOptions& options);

SimpleBox.cpp

The source file first first adds the required headers which are as follows:

#include "SimpleBox.h"

#include "btBulletDynamicsCommon.h"
#include "LinearMath/btVector3.h"
#include "LinearMath/btAlignedObjectArray.h" 
#include "../CommonInterfaces/CommonRigidBodyBase.h"

The first header is for our own class's create function. The second header is to include Bullet dynamics related classes. The next two headers are for btVector3 and btAlignedObjectArray types. Finally, we include the CommonRigidBodyBase header which provides the skeleton for our demo.

Next, we begin definition of our class SimpleBoxExample as follows:

struct SimpleBoxExample : public CommonRigidBodyBase {
   SimpleBoxExample(struct GUIHelperInterface* helper):CommonRigidBodyBase(helper) {}
   virtual ~SimpleBoxExample(){}
   virtual void initPhysics();
   virtual void renderScene();
   void resetCamera() {
      float dist = 41;
      float pitch = 52;
      float yaw = 35;
      float targetPos[3]={0,0.46,0};
      m_guiHelper->resetCamera(dist,pitch,yaw,targetPos[0],targetPos[1],targetPos[2]);
   }
};

We inherit our SimpleBoxExample class from CommonRigidBodyBase class so that our demo can simulate rigid bodies. As we saw in the Getting Started tutorial, this class provides a lot of skeleton for us so that we only need to provide our rigid bodies and constraints. We override three functions, resetCamera, renderScene and initPhysics.

The resetCamera function resets our camera to create a reasonable viewpoint. The renderScene function is defined as follows

void SimpleBoxExample::renderScene()
{
   CommonRigidBodyBase::renderScene();	
}

It basically renders our physics scene by simply calling parent class's renderScene function. Finally, the initPhysics function is defined which allocates our rigid bodies and sets up our rendering. Lets go through the initPhysics function bit by bit.

void SimpleBoxExample::initPhysics()
{
   m_guiHelper->setUpAxis(1);
   createEmptyDynamicsWorld();

We set the Y axis as the world up axis by calling the m_guiHelper->setUpAxis(1) function. Next, we call parent class's createEmptyDynamicsWorld function. The CommonRigidBodyBase class defines createEmptyDynamicsWorld as follows:

virtual void createEmptyDynamicsWorld() {
   m_collisionConfiguration = new btDefaultCollisionConfiguration();
   m_dispatcher = new	btCollisionDispatcher(m_collisionConfiguration);
   m_broadphase = new btDbvtBroadphase();
   btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
   m_solver = sol;
   m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);
   m_dynamicsWorld->setGravity(btVector3(0, -10, 0));
}

This function helps in creating all basic Bullet physics objects like btDefaultCollisionConfguration, btCollisionDispatcher, btDbvtBroadPhase, btSequentialImpulseConstraintSolver and btDiscreteDynamicsWorld. Finally, it sets our physics simulation's gravity force.

Continuing with our initPhysics function definition, we next define our physics debug drawer for our dynamics world. The debug flags are set to ensure rendering of physics object in wireframe and drawing of contact points.

  m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld);
  if (m_dynamicsWorld->getDebugDrawer())
     m_dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawContactPoints);

We then define our ground shape which will act as our floor. The floor is basically a box shape which is given a dimension of 50 in each axis. Next, our new created shape is added to the list of collision shapes.

  btBoxShape* groundShape = createBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.)));
  m_collisionShapes.push_back(groundShape);

We then provide the default orientation and position of our ground.

  btTransform groundTransform;
  groundTransform.setIdentity();
  groundTransform.setOrigin(btVector3(0,-50,0)); 

Since our floor will be a static object, we set its mass to 0. We then call the createRigidBody function that is defined in the parent CommonRigidBodyBase class.

   {
	btScalar mass(0.);
	createRigidBody(mass,groundTransform,groundShape, btVector4(0,0,1,1));
   }

The createRigidBody function is defined as follows in the CommonRigidBodyBase class.

btRigidBody* createRigidBody(float mass, const btTransform& startTransform, btCollisionShape* shape,const btVector4& color = btVector4(1, 0, 0, 1))
{
   btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));
   bool isDynamic = (mass != 0.f);

   btVector3 localInertia(0, 0, 0);
   if (isDynamic)
      shape->calculateLocalInertia(mass, localInertia);
   
   btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
   btRigidBody::btRigidBodyConstructionInfo cInfo(mass, myMotionState, shape, localInertia);
   btRigidBody* body = new btRigidBody(cInfo); 

   body->setUserIndex(-1);
   m_dynamicsWorld->addRigidBody(body);
   return body;
}

It basically checks the mass to see if the rigid body is a static rigid body or a dynamic rigid body. For a dynamic rigid body, we calculate its local inertia by calling calculateLocalInertia function. We then create a motion state which helps in providing attribute interpolation. Next, we fill in the btRigidBodyConstructionInfo structure passing it the required mass, motion state, shape and local intertia. Finally the btRigidBodyConstructionInfo is passed to the btRigidBody constructor to create our rigid body. The newly created rigid body is then added to our physics dynamics world.

We then create our box shape. It is given a dimension of 1 in all three axis. Next it is added to the list of collision shapes.

   { 
      btBoxShape* colShape = createBoxShape(btVector3(1,1,1));
      m_collisionShapes.push_back(colShape);

We then define our simple box's mass and position by creating a simple transform. We then call createRigidBody function again to create our rigid body.

 
      btTransform startTransform;
      startTransform.setIdentity();
      btScalar	mass(1.f); 
      startTransform.setOrigin(btVector3(btScalar(0),btScalar(20),btScalar(0)));
      createRigidBody(mass,startTransform,colShape);		 
   }

Finally, we call autogenerateGraphicsObjects function which generates graphical objects from our physics rigid bodies.

   m_guiHelper->autogenerateGraphicsObjects(m_dynamicsWorld);
}

In the end of SimpleBox.cpp, we define our create function which simply calls our class's constructor.

CommonExampleInterface* ET_SimpleBoxCreateFunc(CommonExampleOptions& options)
{
   return new SimpleBoxExample(options.m_guiHelper);
}

For the rest of our demos, only the initPhysics function would change, all other functions would virtually remain unchanged.

Adding our Example in the ExampleBrowser GUI

In the App_BulletExampleBrowser project, there is a source file ExampleEnteries.cpp. This file creates one entry for each example demo. Scrolling down slightly in the file, we see a global static ExampleEntry array called gDefaultExamples. This array is filled with all demo enteries that are shown in the ExampleBrowser combobox. It is best to append your enteries at the end of the array.

We add two enteries, the first entry is the parent category where we want to add our demo. This usually has an id of 0. For example, if we want to have our demo shown under the ExtendedTutorials group, we write it like this

ExampleEntry(0,"Extended Tutorials"),

For all enteries that we want to add to this group, we add additional enteries but this time we give an id of 1 as shown below

ExampleEntry(1,"Simple Box", "Simplest possible demo creating a single box rigid body that falls under gravity", ET_SimpleBoxCreateFunc),

The second argument is the text which will be shown in the demo selection combobox. The third argument is the description of our demo which is shown in the description box. The final argument is the create function we declared and defined for our class.

Premake and CMake build scripts

In order to ensure that the required source files are included in the App_BulletExampleBrowser project, we have to edit the premake build file (premake4.lua) and cmake build files (cmakelists.txt). We will now elaborate details about editing the build scripts for each build system separately.

Where are these build scipts located

For the example browser application, the build scripts are present in the {BULLET_ROOT}/examples/ExampleBrowser sub-folder.

Editing Premake4.lua for premake

Open the premake4.lua script file. Ensure that the first line (i.e. the project name) is App_BulletExampleBrowser. Next, scroll down until you see another project named BulletExampleBrowserLib. Scroll further down until you see a files section. You must enter the relative path of your sources. Supposing we store our sources (SimpleBox.h/cpp) in the examples/ExtendedTutorials sub-folder, we would then add the following entry in the files section.

files {
   --list of existing source files 
   "../ExtendedTutorials/SimpleBox.cpp",
   "../ExtendedTutorials/SimpleBox.h",
   --additional source files 

Note that you may omit adding headers as they will be included with the source files. Advanced user could also use wild card to include all source files in a given folder as follows.

files {
   --list of existing source files 
   "../ExtendedTutorials/*", 
   --additional source files  

An important note. After you have edited premake4.lua file, ensure that the root folder premake batch script is rerun to ensure that the new changes take effect and our sources are included.

Editing CMakeLists.txt for CMake

Open the CMakeLists.txt script file. Next, scroll down until you see the following variable definition

SET(BulletExampleBrowser_SRCS

You must enter the relative path of your sources. Supposing we store our sources (SimpleBox.h/cpp) in the examples/ExtendedTutorials sub-folder, we would then add the following enteries.

SET(BulletExampleBrowser_SRCS
   #list of existing source files 
   "../ExtendedTutorials/SimpleBox.cpp",
   "../ExtendedTutorials/SimpleBox.h",
   #additional source files 

Note that you may omit adding headers as they will be included with the source files. Note that the wild card enteries do not work in case of CMake and the best approach is to add all sources individually.

An important note. After you have edited CMakeLists.txt file, rerun CMake to ensure that the new changes take effect and our sources are included in the BulletExampleBrowser project.

Snapshot

If you have followed along you should see the SimpleBox demo shown in the example browser. Double clicking the demo in the demo selection box should display our box fall under gravity. We can interact with our box rigid body by left-clicking and dragging by mouse. File:SimpleBox.png

Congratulations. You have finished your first tutorial of setting up a single rigid body box falling under gravity. The next tutorial will show us how to handle multiple boxes.

Personal tools