Friday, May 27, 2011

PhysX3: A simple bouncing box

In this tutorial, I will show you how to create a simple box at a specific position and let it bounce under influence of gravity. We will be adding to the code base from the previous tutorials. We first create the PhysX3 sdk object and store it into a global pointer as given in the previous tutorial. For this tutorial, the following headers and libs are needed.

#include <iostream>
#include <GL/freeglut.h>
#include < PxPhysicsAPI.h>
#include < PxExtensionsAPI.h>
#include < PxDefaultErrorCallback.h>
#include < PxDefaultAllocator.h>
#include < PxDefaultSimulationFilterShader.h>
#include < PxDefaultCpuDispatcher.h>
#include < PxShapeExt.h>
#include < PxMat33Legacy.h>
#include < PxSimpleFactory.h>

using namespace std;

#pragma comment(lib, "PhysX3_x86.lib")
#pragma comment(lib, "PxTask.lib")
#pragma comment(lib, "Foundation.lib")
#pragma comment(lib, "PhysX3Extensions.lib")
#pragma comment(lib, "GeomUtils.lib")

As you can see, now we have to add a lot of headers this is because a lot of parameters which were given by default in the previous PhysX versions have to be given explicitly.


const int WINDOW_WIDTH=1024,
WINDOW_HEIGHT=768;

static PxPhysics* gPhysicsSDK = NULL;
static PxDefaultErrorCallback gDefaultErrorCallback;
static PxDefaultAllocator gDefaultAllocatorCallback;
static PxSimulationFilterShader gDefaultFilterShader=PxDefaultSimulationFilterShader;

PxScene* gScene = NULL;
PxReal myTimestep = 1.0f/60.0f;
PxRigidActor *box;

In the above lines, we store the variables for screen size, the global physx sdk pointer, scene pointer and the box rigid body pointer. In PhysX3, we no more need to provide the shape desc for objects since now only the geometry has to be specified using the available classes as we will see in a minute.


gPhysicsSDK = PxCreatePhysics(PX_PHYSICS_VERSION, gDefaultAllocatorCallback, gDefaultErrorCallback, PxTolerancesScale() );
if(gPhysicsSDK == NULL) {
cerr<<"Error creating PhysX3 device."<<endl;
cerr<<"Exiting..."<<endl;
exit(1);
}

if(!PxInitExtensions(*gPhysicsSDK))
cerr<< "PxInitExtensions failed!" <<endl;



In the above lines, we call the CreatePhysX function.Now in PhysX3, we need to provide the allocator and the error callback. We pass in the default handlers provided by the PhysX3 sdk. Once we have a valid PhysX object, we initialize the extensions. This is required so that the other extensions are setup properly.


//Create the scene
PxSceneDesc sceneDesc(gPhysicsSDK->getTolerancesScale());
sceneDesc.gravity=PxVec3(0.0f, -9.8f, 0.0f);
if(!sceneDesc.cpuDispatcher) {
PxDefaultCpuDispatcher* mCpuDispatcher = PxDefaultCpuDispatcherCreate(1);
if(!mCpuDispatcher)
cerr<<"PxDefaultCpuDispatcherCreate failed!"<<endl;
sceneDesc.cpuDispatcher = mCpuDispatcher;
}
if(!sceneDesc.filterShader)
sceneDesc.filterShader = gDefaultFilterShader;

gScene = gPhysicsSDK->createScene(sceneDesc);
if (!gScene)
cerr<<"createScene failed!"<<endl;
gScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, 1.0);
gScene->setVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES, 1.0f);

In the above lines, the scene object is created and the parameters for the simulation are passed in as was in the previous physX versions. In this case, we ask PhysX to set up the global scene gravity to 9.8 m/s^2 The -ve sign is to make it point downwards.


PxMaterial* mMaterial = gPhysicsSDK->createMaterial(0.5,0.5,0.5);



In the above lines, we set the default material's physical properties like static and dynamic friction and the coefficient of restitution. In PhysX3, the default materials are not accessed by index now we need to give an a new object.
Creating actors

//Create actors
//1) Create ground plane
PxReal d = 0.0f;
PxTransform pose = PxTransform(PxVec3(0.0f, 0, 0.0f),PxQuat(PxHalfPi, PxVec3(0.0f, 0.0f, 1.0f)));
PxRigidStatic* plane = gPhysicsSDK->createRigidStatic(pose);
if (!plane)
cerr<<"create plane failed!"<<endl;
PxShape* shape = plane->createShape(PxPlaneGeometry(), *mMaterial);
if (!shape)
cerr<<"create shape failed!"<<endl;
gScene->addActor(*plane);

//2) Create cube
PxReal density = 1.0f;
PxTransform transform(PxVec3(0.0f, 10.0f, 0.0f), PxQuat::createIdentity());
PxVec3 dimensions(0.5,0.5,0.5);
PxBoxGeometry geometry(dimensions);

PxRigidDynamic *actor = PxCreateDynamic(*gPhysicsSDK, transform, geometry, *mMaterial, density);
actor->setAngularDamping(0.75);
actor->setLinearVelocity(PxVec3(0,0,0));
if (!actor)
cerr<<"create actor failed!"<<endl;
gScene->addActor(*actor);

box = actor;
}

In the above lines, we create two actors for the ground plane and the box. For each of these actors, we first set their transform. In PhysX3 the transforms are now given as quaternions. We then create the PxRigid[Static/Dynamic] object by calling appropriate function to create a static/dynamic object. Next, the shape of the object is specified and then the actor is added to the scene. For box, the box geometry is specified here. We use this geometry object by storing the actor reference into a global variable.

Handling PhysX stepping loop
Once the actor stuff is done, the next thing we need to handle is the PhysX loop. This will allow the simulation to step ahead in time. We will implement an asynchronous loop which can be done using the following lines of code.

void StepPhysX()
{
gScene->simulate(myTimestep);

//...perform useful work here using previous frame's state data
while(!gScene->fetchResults() )
{
// do something useful
}
}

These lines were copied directly from the documentation so rather than commenting anything on these, I would ask you to go and read the asynchronous loop section in the PhysX documentation. We will call this function once from our display function.

Rendering of Actors
The next step is to render the actors. To make easier for use to work with PhysX, I have modularized the code into functions. First, the RenderActors() function is implemented as follows,

void RenderActors()
{
// Render all the actors in the scene
DrawActor(box);
}

Nothing fancy here, we just call the DrawActor function passing it the box actor reference. The DrawActor function is implemented as follows,

 
void DrawActor(PxRigidActor* actor)
{
PxU32 nShapes = actor->getNbShapes();
PxShape** shapes=new PxShape*[nShapes];

actor->getShapes(shapes, nShapes);
while (nShapes--)
{
DrawShape(shapes[nShapes]);
}
delete [] shapes;
}

It looks a lot like the previous PhysX version DrawActor function. It basically ask the actor for the number of shapes it has and then calls the DrawShapes function with the shape parameter. The DrawShape function is implemented as follows,


void DrawShape(PxShape* shape)
{
PxGeometryType::Enum type = shape->getGeometryType();
switch(type)
{
case PxGeometryType::eBOX:
DrawBox(shape);
break;
}
}

This function first identifies the type of the shape and then uses a switch case to call the appropriate Draw function. For our case, we check for the Box object and then call the DrawBox function passing it the shape parameter. The DrawBox function is implemented as follows,


void DrawBox(PxShape* pShape) {
PxTransform pT = PxShapeExt::getGlobalPose(*pShape);
PxBoxGeometry bg;
pShape->getBoxGeometry(bg);
PxMat33 m = PxMat33Legacy(pT.q );
float mat[16];
getColumnMajor(m,pT.p, mat);
glPushMatrix();
glMultMatrixf(mat);
glutSolidCube(bg.halfExtents.x*2);
glPopMatrix();
}

The DrawBox function gets the current Global pose (basically the shapes tranform matrix) and then converts it into a column major form (so that we can pass this matrix to OpenGL). Next, the currnet transformation matrix is stored (glPushMatrix) and then the box's matrix is multiplied to the current matrix. Then, we draw the box's geometry (using the glutSolidCube function) and finally revert the old transformation back by calling glPopMatrix.

The Display Function
Now after these function definitions, the display function becomes this,

void Display() {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//setup the view transformation using
//gluLookAt(...);

//Update PhysX
if (gScene)
{
StepPhysX();
}

RenderActors();

glutSwapBuffers();
}


That's it. Attached is the complete source code for this tutorial. Here is the snapshot of the application.
PhysX3.1 Simple Box

Source code for this tutorial

12 comments:

antonio said...

hi!!
I'm trying to modify programs, to understand how it works Physx.
I'd like to be able to insert objects, providing keyboard position.
ie: create the object through a window and then enter the location where I want to object is created.

Then, I create a second window where you can view the current position of various objects.
What do you advise me to use?
thanks!!

Mobeen said...

HI Antonio,
You can create a vector as i did for multiple obejcts and then assign each box an id starting from 0 to totalObjecs-1 so the id of the object is its index. Then depending on which object is currently selected, u can modify its global pose to assign it the new position. It should be a simple extension of my multiple objects project.

Try it out i think it should not take u more time.

Nikhil said...

Much thanks, your PhysX tutorials are very useful.

Mobeen said...

My pleasure Nikhil,
I m glad that my tutorials are useful for u.

antonio said...

hi!!
how are you?
I am proceeding with Physic ... I modified your program, I created the other.
the most important, in my opinion, is where I separate the graphics and dynamic part .... if you want I can send you ....
well, but now I will have a problem ... I have to create a cylinder ...
Open GL has no problems, but is not shaped Physic.
reading forums, I saw that you can create, with a cap and a box ..
there is an easier way?
thanks
Antonio

Mobeen said...

Hi,
Well I think using triangle mesh would be simpler to do but I have not yet used it for this purpose. You can try it out and let me know how it goes.

Lubiluk said...

Hi,
great tutorial, thanks!
I have ported your source file for compilation under linux. You can check it here: http://about.something.pl/art/getting-started-with-physx-sdk-3-0-on-linux.html
I hope you give free license for you code. If not, let me know.
Regards!

Mobeen said...

Thanks Lubiluk for the Linux port. Yeah I donot impose any license on this as long as the original NVIDIA PhysX license is not violated ofcourse.

Pozarnik said...

Thanks so much for porting this example to PhysX 3.x!

I was wondering why you used PxCreateDynamic for the box. I tried changing the code to make use of

PxPhysics::createRigidDynamic(..)
and
PxShape::createShape(...)

Which seemed more intuitive for me, but even if I try using the same material the behaviour of the box is not the same. I assume it's either a problem with the default restitution, or linear damping.

Does anyone know what the difference might be? THANKS!

Mobeen said...

Hi Poznarik,
Honestly speaking, I simply converted the code from the existing tutorial so it was simply a find replace job. I did not dig deep into using the other two functions you have pointed out may be if I have time, I will go into this and revert if I have something to share. Thanks for spotting this though.

人外人 said...

I want to learn physx3,so I download the code and I compile it in visual studio 2010. But I meet some problem. In the function"DrawBox","PxMat33 m = PxMat33Legacy(pT.q );" is a error.

Mobeen said...

Hi,
Check these tutorials (http://mmmovania.blogspot.com/2011/10/converting-existing-physx30-tutorials.html) which have already sorted this problem. I am repeating the solution for you here. The problem is that PxMat33Legacy is deprecated in PhysX3.0 & above so you should replace the DrawBox function with this one.
==================================
void DrawBox(PxShape* pShape) {
PxTransform pT = PxShapeExt::getGlobalPose(*pShape);
PxBoxGeometry bg;
pShape->getBoxGeometry(bg);
PxMat33 m = PxMat33(pT.q );
float mat[16];
getColumnMajor(m,pT.p, mat);
glPushMatrix();
glMultMatrixf(mat);
glutSolidCube(bg.halfExtents.x*2);
glPopMatrix();
}
==============================

see if this helps.
Mobeen

Popular Posts

Copyright (C) 2011 - Movania Muhammad Mobeen. Awesome Inc. template. Powered by Blogger.