*beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Show what you made with Bullet Physics SDK: Games, Demos, Integrations with a graphics engine, modeler or any other application
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

*beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

hello,

i've noticed some other people are having trouble "reading" Bullet demos too... demos are actually pretty good once you have some idea how to use the library, once you know what to look for...

anyway, i had to boil down few demos and collect just the very basics of Bullet in a single file so i can have a good look at it and hopefully start understanding whats going on... here is my first cooked demo:
'Simplified BulletDinoDemo and how to compile it on Linux'
http://www.bulletphysics.com/Bullet/php ... f=9&t=2274


...and this listing here is pretty much the same thing only translated to C++ API, lets call it "main.cpp" and put it in "HelloWorld" demo folder, then compile with:

g++ -I../../src main.cpp -lglut -L../../out/linuxx86/optimize/libs -lbulletdynamics -lbulletmath -lbulletcollision


//---------------------------------------------------------------------------------------------------
#include <GL/glut.h>
#include "btBulletDynamicsCommon.h"

static float time = 0.0;

static btScalar matrix[16];
static btTransform trans;

static btDiscreteDynamicsWorld *dynamicsWorld;
static btRigidBody *box1, *box2;


static void draw(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//*** draw box1
glColor3f(0.0, 0.0, 1.0);
glPushMatrix();
box1->getMotionState()->getWorldTransform(trans);
trans.getOpenGLMatrix(matrix);
glMultMatrixf(matrix);
glutSolidCube(40);
glPopMatrix();

//*** draw box2
glColor3f(1.0, 1.0, 0.0);
glPushMatrix();
box2->getMotionState()->getWorldTransform(trans);
trans.getOpenGLMatrix(matrix);
glMultMatrixf(matrix);
glutSolidCube(10);
glPopMatrix();

glutSwapBuffers();
}


static void timer(void)
{
float dtime = time;
time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
dtime = time - dtime;

if(dynamicsWorld)
dynamicsWorld->stepSimulation(dtime, 10);

glutPostRedisplay();
}


int main(int argc, char** argv)
{
//*** init Bullet Physics
btQuaternion qtn;

btCollisionShape *shape;
btDefaultMotionState *motionState;

btDefaultCollisionConfiguration *collisionCfg
= new btDefaultCollisionConfiguration();

btAxisSweep3 *axisSweep
= new btAxisSweep3(btVector3(-100,-100,-100), btVector3(100,100,100), 128);

dynamicsWorld = new btDiscreteDynamicsWorld(new btCollisionDispatcher(collisionCfg),
axisSweep, new btSequentialImpulseConstraintSolver, collisionCfg);

dynamicsWorld->setGravity(btVector3(0, -10, 0));


//*** box1 - STATIC / mass=btScalar(0.0)
shape = new btBoxShape(btVector3(20,20,20));

trans.setIdentity();
qtn.setEuler(0, 0.25, -0.05);
trans.setRotation(qtn);
trans.setOrigin(btVector3(0, -20, 0));
motionState = new btDefaultMotionState(trans);

box1 = new btRigidBody(btScalar(0.0), motionState, shape, btVector3(0,0,0));
dynamicsWorld->addRigidBody(box1);

//*** box2 - DYNAMIC / mass=btScalar(1.0)
shape = new btBoxShape(btVector3(5,5,5));

trans.setIdentity();
qtn.setEuler(0.8, 0.7, 0.4);
trans.setRotation(qtn);
trans.setOrigin(btVector3(-10, 50, 0));
motionState = new btDefaultMotionState(trans);

box2 = new btRigidBody(btScalar(1.0), motionState, shape, btVector3(1,1,1));
dynamicsWorld->addRigidBody(box2);


//*** init GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("Jumpin' little BoX");

glutDisplayFunc(draw);
glutIdleFunc(timer);


//*** init OpenGL
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);

glMatrixMode(GL_PROJECTION);
gluPerspective( 50.0, 1.0, 20.0, 100.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(0.0, 5.0, 90.0, 0.0, 8.0, 0.0, 0.0, 1.0, 0.0);


glutMainLoop();


//*** EXIT
delete shape;
delete motionState;
delete collisionCfg;
delete axisSweep;
}
//---------------------------------------------------------------------------------------------------




cheers,
http://www.geocities.com/ze_aks

p.s.
QUIZ - "Simplified BulletDinoDemo" uses C-API and this listing for "Boiled Down HelloWorld" uses C++ Bullet API, but other than that they are supposed to be ~similar - why is the output so much different?

[EDIT:]
ANSWER - *localInertia*, the last argument was set with wrong values:
box2 = new btRigidBody(btScalar(1.0), motionState, shape, btVector3(1,1,1));

C API apparently calculates inertia internally, while C++ API prefers to be served... but, we got source code so we can shape all these function calls as we see fit... anyhow, here's how to fix it so that two demos have the same output:

btScalar mass(1.0);
btVector3 inertia(0,0,0);
shape->calculateLocalInertia(mass, inertia);
box2 = new btRigidBody(mass, motionState, shape, inertia);
Last edited by abaraba on Fri Jun 27, 2008 4:27 am, edited 17 times in total.
gunnar
Posts: 18
Joined: Fri Jun 20, 2008 2:01 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by gunnar »

Sweet! Thanks for sharing the nice and simple demo code! This should be enough to get anyone started with bullet.
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

thanks,
i'm just starting with Bullet, so feel free to correct me or add anything to the above implementation.. or help me understand how else Bullet can be used, i think there is more than one way, some of which may be complete misuse and id like to know about those common errors too

also, before just recently i haven't been coding for a while, so some simple everyday things i've lost sight of, for example what are the pros and cons of having global static pointers like:

static btDiscreteDynamicsWorld *dynamicsWorld;
static btRigidBody *box1, *box2;

on exit, i do not destroy these, but they seem to get deleted anyway, whats the best practice? id very much appreciate if someone can correct me in such mistakes and show better or more efficient way to hold or initialize these variables?


cheers
grasuper
Posts: 2
Joined: Fri Jun 27, 2008 7:38 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by grasuper »

Thank you for the nice example!

But I have a problem with this C++ code.

When I tried to run your C code, I wrote down the whole path of the "Bullet-C-Api.h" like this:
#include "/....../bullet-2.69/src/Bullet-C-Api.h".
And,
>> gcc main.c -lglut -L/....../bullet-2.69/....../libs -lbulletdynamics -lbulletcollision -lbulletmath

I could see the beautiful result.

In case of the C++ code, I also wrote down the whole path of the "btBulletDynamicsCommon.h" like this:
#include "/....../bullet-2.69/src/btBulletDynamicsCommon.h".
And,
>> g++ main.cpp -lglut -L/....../bullet-2.69/....../libs -lbulletdynamics -lbulletcollision -lbulletmath
(the same command except two things: gcc -> g++. main.c -> main.cpp)

In this case, I could see a lot of errors related to the header files in the /....../src folder such as,
"not declared in this scope".

Is there any mistake in my process? Didn't you see such things?

Thanks.
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

In case of the C++ code, I also wrote down the whole path of the "btBulletDynamicsCommon.h" like this: #include "/....../bullet-2.69/src/btBulletDynamicsCommon.h".
1. //---------------------------------------------------------------------------------------------------
2. #include <GL/glut.h>
3. #include "btBulletDynamicsCommon.h"
.
but, why would you want to change that 3rd line?
just leave it as it is and copy/paste the whole listing, then it will compile with:

g++ -I../../src main.cpp -lglut -L../../out/linuxx86/optimize/libs -lbulletdynamics -lbulletmath -lbulletcollision

#include "/....../bullet-2.69/src/
-L/....../bullet-2.69/....../libs
"..errors related to the header files in the /....../src folder"
what the... where did you see anything like that?

"/....../" <--WRONG,
it can only be something like "../../../../ and so on", eg. TWO dots BEFORE slash where each "../" means 'one directory up'

cheers
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

3rd Boiled demo,
it builds up on this simple example, with addition of "world_map", ie. instead of big blue cube there is a *stunt race track* as a static background, so new lesson is how to initialize "btBvhTriangleMeshShape", keys - w,s,a,d

http://www.geocities.com/ze_aks/Boiled3.tar.bz2

files should be in some folder inside /bullet/Demo, eg "../bullet/Demos/Boiled3/main.cpp"


this is a new bit,
it makes a NEW SHAPE out of mesh vertices data, quite similar to creating a DISPLAY LIST in OpenGL, actually if you look at the "world.h" you may notice that there is the ~same loop as this one:
//---------------------------------------------------------------------------------------------------
//*** init WORLD MESH - STATIC
int i,j;
btVector3 btV3[3];
btTriangleMesh *mTriMesh = new btTriangleMesh();
for(i=0;i<sizeof(face_indicies)/sizeof(face_indicies[0]);i++)
{
for(j=0;j<3;j++)
{
int vi=face_indicies[j];
btV3[j] = btVector3(vertices[vi][0],vertices[vi][1],vertices[vi][2]);
}
mTriMesh->addTriangle(btV3[0], btV3[1], btV3[2]);
}

shape = new btBvhTriangleMeshShape(mTriMesh, true);

trans.setIdentity();
motionState = new btDefaultMotionState(trans);
world_mesh = new btRigidBody(btScalar(0.0), motionState, shape);

dynamicsWorld->addRigidBody(world_mesh);
//---------------------------------------------------------------------------------------------------



cheers
www.geocities.com/ze_aks
meclory
Posts: 12
Joined: Wed Jun 18, 2008 4:29 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by meclory »

Hi,

Can you elaborate how did you make the trimesh data in your header file? Is there any recommended tool or method?

Thank you.
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

good question,
i was asking myself the same thing few days ago and luckily i remembered..


model is from Google Sketchup 3d warehouse:
http://sketchup.google.com/3dwarehouse/

then you need to convert/export to something usable, try this for help:
http://forums.devshed.com/game-developm ... 19710.html


then,
the sweet part is actually 3d modeling program called "Deep Exploration 3D" which has best 'save as' options i've seen in any program so far, among many ways to modify geometry on export and many formats there is also option to save mesh as "opengl display list" ...thats it! so, short answer is: *DEEP EXPLORATION* (made by Right Hemisphere)


btw, Google 3D Warehouse models seem to be complete rubbish once they leave Sketchup format, even before, there seem to be something strange how those meshes are composed... i get inverted triangles everywhere and lots of overlapping triangles that i dont know how to fix, compact or tidy up... thats why i had to turn on double-sided polygons on and lighting off, as normals are not correct everywhere (if anywhere)


BUT, my new toy for "express 3d modeling resource" is 3D Ripper DX:
http://www.deep-shadows.com/hax/3DRipperDX.htm

its fantastic, i now have new mesh for a demo -Prince of Persia: Sands of Time- level map, taken with 3dripper saved as .obj, imported with Deep Exploration and exported to OpenGL display list..



cheers

..what id really like tho is to be able to pass over emulator/plugin overlay to grab some meshes from ps2 or psx games like katamari damacy and metal gear which i would use for my next game - "Metal Gear Katamari" where you roll up stuff around shadow moses until is big enough to roll up metal gear rex... so far i can only get flat pancakes meshes from emulators with either 3dripper or 'glinterceptor and OGLE' ..naaa nanana nana... katamari damacy..

..apparently they can rip from gamecube emulator but not any psx or ps2 emulator yet
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by z8000 »

Note that this example leaks memory. You are reusing the same symbols, 'shape' and 'motionState', to refer to two different dynamically allocated objects. You need to use 2 shapes, and 2 motion states. Then at cleanup time, delete all 4. I think the rule with Bullet with respect to memory is "if you allocate it, you own it, and therefore you delete it". It's a good rule.
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

thanks for that,
i hear what you saying, but still dont see a problem..
only use 'shape' and 'motionState' at the beginning as a *temporary_structures* to pass on some values to constructors when initialising btRigidBody... they don't seem to complain? and i dont see how or where later in the code/main loop that matters? ..i mean, it works, eh?

as for memory leak,
could you explain a bit more how does that causes memory leak?
how did you detect memory leak, did you use any tool?

can you point out some more extreme or more obvious case where this 'bad practice' would cause more noticeable problems?


cheers
chunky
Posts: 145
Joined: Tue Oct 30, 2007 9:23 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by chunky »

could you explain a bit more how does that causes memory leak?
Because it's impossible to know when to delete the motionstate, because it's shared between objects. To find out when no more objects are using it, you'd need to be using reference-counting, which in C++ is a pain in the ass.

Therefore, you just sit on the single motionstate and just forget about it. You leak a total of maybe 50 bytes, once during execution
how did you detect memory leak, did you use any tool?
It's just a logical thing.

In my game, all my spaceships share the same collision object by using a static pointer in the class:

Code: Select all

class SpaceShip {
public:
  SpaceShip() {
    if(NULL == mShipShape) {
      mShipShape = new btSphereShape(btScalar(5.0));
    }
  }
protected:
  static btCollisionShape *mShipShape;
};

btCollisionShape *TWSpaceShip::mShipShape = NULL;
This code "leaks" in that a resource is allocated that is never cleared, but really I don't care since it's just a single collisionshape that for practical reasons is persistent for the life of the entire applications.

Regarding tools to detect leaks, valgrind is your friend. valgrind, valgrind, valgrind. Learn it. Use it. Port your code to Linux if it doesn't already run on linux, just so you can use it. It's like various other applications - it's the category inventor and the category killer all in one.
can you point out some more extreme or more obvious case where this 'bad practice' would cause more noticeable problems?
If you're deleting everything and starting again. For example, if you delete the whole world and everything in it when you end the level, and create more for the next level. *that* is going to leak.

Gary (-;
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

thanks for explanation,

yes, im working on Linux, but never in my life i used any sort of profiler or any such tools, i write my programs in Midnight Commander text editor and i never even used a debugger.. ever, so my questions my seem stupid and i appreciate your time to make it more clear
>>"how did you detect memory leak, did you use any tool?"
It's just a logical thing.
ok, i too know in theory about new/delete allocators and stuff, but what im saying is - are you sure? can compiler surprise us and actually compile that source so it doesnt leak?


i was running "top" and i started and killed application over 20 times, it would always allocate about the same time of memory, would not allocate any more during execution and would free it all on exit


maybe i dont understand what memory leak is,
but i just dont see any memory leaking anywhere?

basically i believe memory leak can be one of these cases:

1.) when executed, program will allocate more memory than needed, but memory allocation system of the OS would free that memory after program is exited, so no big deal

2.) when executed, program will allocate more memory than needed, BUT on exit Operating System would not be able to free that memory, so running program repeatedly would sooner or later consume all system memory

3.) when executed, bad program will keep trying to allocate memory, therefore using all of it until system crashes (this is my picture of memory leak and thats why i thought i would catch any memory leak by simply monitoring memory usage and not need any other tools)

so, it seems to me that my memory leak is in 1st category, just like your spaceships, in which case i too dont care, just as i dont care declaring variable INT when smaller CHAR would do.. does that make sense or am i still overlooking something important?



btw,
i think i read that author of this library said somewhere on this forum that collision shapes could/should be reused, which i thought is exactly what i was doing


cheers,

gameBoX Linux
http://www.geocities.com/ze_aks/myos.html
Last edited by abaraba on Fri Aug 22, 2008 1:49 am, edited 1 time in total.
chunky
Posts: 145
Joined: Tue Oct 30, 2007 9:23 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by chunky »

can compiler surprise us and actually compile that source so it doesnt leak?
No.
i was running "top" and i started and killed application over 20 times, it would always allocate about the same time of memory, would not allocate any more during execution and would free it all on exit
Ah. See, you need to understand that the operating system is responsible for a lot of things rather than your application. The application launches and allocates memory, grabs other resources [eg, opens file handles]. The operating system keeps track of every resource that the application grabs. When the application quits, the operating system releases all resources that that application grabbed. Technically there are a few more complicated rules, but that's the essence of it.

top's memory usage meter is also terrible, do not rely on it for anything, ever, other than a vague "this application is probably using more memory than this other application" heuristic. The reasons for this are about three steps further on from the concept of leaks :-)
1.) when executed, program will allocate more memory than needed, but memory allocation system of the OS would free that memory after program is exited, so no big deal
The second half of that is correct, the OS will clear the resource when the program exits. A program "allocating more memory than is needed" is only half the story. The issue is that a program may allocate a resource, then not free it. That's all that a leak is, simply put.

In my example there, the resource is created [via c++ new], but is never freed [with an appropriate c++ delete], so it's technically a leak. In the case there, it only leaks once ever, and only leaks a tiny bit, so I tolerate it.
2.) when executed, program will allocate more memory than needed, BUT on exit Operating System would not be able to free that memory, so running program repeatedly would sooner or later consume all system memory
Not possible, that would be a bug in your operating system.
3.) when executed, bad program will keep trying to allocate memory, therefore using all of it until system crashes (this is my picture of memory leak and thats why i thought i would catch any memory leak by simply monitoring memory usage and not need any other tools)
Well, you've captured the essence of why leaks are bad. Grabbing resources then not releasing them is bad. But again, you only have half the story.

Leaks are more of an issue when, instead of being a one-off thing like in my example, they're constant things. For example, imagine if you were working on a game where powerups appear and disappear regularly. If your powerups were leaking memory [for example, they allocate space for a string that is that powerup's name] but then don't clear it, then every time a powerup is created and deleted, you leak the memory that took up the space of that string. After your game has been running long enough, and enough powerups have been spawned and despawned, the memory usage of the game gets too high.

It's memory that you don't have a pointer to anymore, it's just sitting there. The operating system or runtime cannot free it because your operatingsystem or runtime need explicit support for things like keeping track of resources - C++ doesn't currently have such a thing.

Things like java or scripting languages don't have this problem because they do what's called "garbage collection", where the runtime keeps track of what memory is in-use, and when memory is no longer accessible, it gets freed automatically.

Wikipedia is your perpetual friend

You should also install valgrind. I know, I sound like a broken record, but get it anyway.

Gary (-;
abaraba
Posts: 56
Joined: Thu Jun 19, 2008 7:54 am

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by abaraba »

thanks, i appreciate that,
you make it perfectly clear... and of course i agree..


but,
in this particular example, if i was to make it properly i would need to add 2 more pointers as z8000 said and allocate them all the same, so in practice you would not know which binary is which, they would have the same memory footprint, eh?


or... how about,
instead adding one more of each "shape" and "motionState"... why not get rid of them completely, like this:

Code: Select all

box1 = new btRigidBody( btScalar(0.0), (new btDefaultMotionState(trans)), 
     (new btBoxShape(btVector3(20,20,20))), btVector3(0,0,0));

  dynamicsWorld->addRigidBody(box1);

box2 = new btRigidBody( btScalar(1.0), (new btDefaultMotionState(trans)), 
     (new btBoxShape(btVector3(5,5,5))), btVector3(1,1,1));

  dynamicsWorld->addRigidBody(box2);

program still compiles and works as ever,
i wonder what would Valgrind say about that?

but seriously,
whats happening in above example, who calls whom constructor there?
and would they still exist in memory as before (memory leak) or these objects are truly temporary now, since the very next line the handle to that memory will be lost and practically free to use for anything else, right?


cheers
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: *beginners* - Boiled Down 'HelloWorld' demo with OpenGL

Post by z8000 »

When you do not retain a pointer to a dynamically allocated object, and pass that object off to some other object or function that does not claim ownership of the dynamically allocated object, you have a memory leak.

Yes, this was a trivial example and yes, the OS will reclaim the memory on exit. However, what if your application needs to run for several hours? A day? A month? Forevermore? You'll run out of memory eventually and start swapping -- the kiss of death.

This is a core software engineering principle: any resource you create, you should also dispose of, or at least ensure proper ownership semantics.