Posts
Search
Contact
Cookies
About
RSS

RaylibOdeMech simple quick start tutorial

Added 17 Feb 2026, 2:26 p.m. edited 17 Feb 2026, 3:37 p.m.

For the first tutorial we'll have an in depth look at terrain.c in the examples folder.

(I'm assuming you've been able to follow the setup instructions in the doxygen docs and have compiled the framework and that the examples work)

As with most other frameworks a little bit of work is needed before you can use the framework lets look at the code of this:

int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------

// Physics context, holds all physics state
PhysicsContext* physCtx = NULL;

GraphicsContext graphics = { 0 };
InitGraphics(&graphics, screenWidth, screenHeight, "Raylib and OpenDE");

initCamera(&graphics);

physCtx = InitPhysics();

Nothing too wild here, basically just setting up the "contexts" or global information we need to handle both the graphics and physics contexts, I may change InitGraphics in future to just return a pointer like InitPhysics (early days still lots to do!)

Now everything is set up we can create physics objects and see them behave in a simulation, but before we do its worth quickly going over some of the internals of the framework.

When you use the framework so say create a dynamic box, a body, geometry for collision and a visual for the geometry are bundled together ready to be used, not only that but it is registered on a global list for automagical freeing of resources when you shutdown the simulation.

Its important not to change the ODE userdata on a body or geom, as they have custom data assigned to them by the framework, these structures are available for use and also include their own userdata void pointers (so nothing lost but a plenty of functionality gained - as you'll see)

In this example as its relatively simple the set up for the world itself is equally simple:

   // set up for the items in the world
//--------------------------------------------------------------------------------------

// the graphical mesh for the ground must be unloaded by the user
Model ground = LoadModel("data/ground2.obj");

// framework looks after the physics stuff and rendering
CreateStaticTrimesh(physCtx, &graphics, ground, &graphics.groundTexture, 2.5f);


// Create random simple objects with random textures
for (int i = 0; i < NUM_OBJ; i++) {
addRandomPhys(physCtx, &graphics, (Vector3){rndf(-3, 3), rndf(6, 12), rndf(-3, 3)});
}

Here we're using Raylib to load in a model of some terrain, handily there is a convenience function to create a static collision mesh for this model, this will give our other objects something more interesting than a flat plane to fall on.

the addRandomPhys function adds, well you guessed it.... in common with the standard functions to add various shapes this function returns an entity pointer to the next shape, here we discard it as we will just be using the frameworks global entity list to manipulate the world as we'll see later

The examples all follow a pattern of updating the world, stepping the physics and then doing the rendering, here is the code to update and manipulate the world

    //--------------------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------------

// baked in controls (example only camera!)
updateCamera(&graphics);

bool spcdn = IsKeyDown(KEY_SPACE); // cache space key status (don't look up for each object iterration
cnode_t* node = physCtx->objList->head;

while (node != NULL) {
entity* ent = node->data;
dBodyID bdy = ent->body;
cnode_t* next = node->next; // get the next node now in case we delete this one
const dReal* pos = dBodyGetPosition(bdy);
if (spcdn) {
// apply force if the space key is held
const dReal* v = dBodyGetLinearVel(bdy);
if (v[1] < 10 && pos[1]<10) { // cap upwards velocity and don't let it get too high
dBodyEnable (bdy); // case its gone to sleep
dMass mass;
dBodyGetMass (bdy, &mass);
// give some object more force than others
float f = rndf(8,20) * mass.mass;
dBodyAddForce(bdy, rndf(-f,f), f*10, rndf(-f,f));
}
}


if(pos[1]<-10) {
// would be more efficient to just reuse the object and
// reposition it with zeroed velocities
// but this is used to aid testing
FreeBodyAndGeoms(bdy);
free(ent);
clistDeleteNode(physCtx->objList, &node);
addRandomPhys(physCtx, &graphics, (Vector3){rndf(-3, 3), rndf(6, 12), rndf(-3, 3)});
}

node = next;
}

The example code uses the built in camera but you are free to spin your own using Raylib, updating it here allows it to react to the movement keys and the mouse. (QWEASD and shift)

The fist section deals with pushing all the dynamic shapes up in the air when the space bar is pressed, let look at how we iterate through the global list of dynamic shapes (you can easily maintain your own list of a subset of the global list for your own purposes, but you are responsible for maintaining this list - although while on the global as well they will be automagically rendered and later freed when needed)

    cnode_t* node = physCtx->objList->head;

while (node != NULL) {
entity* ent = node->data;
dBodyID bdy = ent->body;
cnode_t* next = node->next; // get the next node now in case we delete this one
const dReal* pos = dBodyGetPosition(bdy);

A cnode_t is a node in a linked list, (another part of my code base I mashed into this project) we start off with the entity and the head of the list and go through the list next node at a time

The fist thing we do is find out which entity this entry in the list is referring to, for ease of reading (mainly) we get the ODE body id into a local variable, and in case we later in the loop decide we need to delete this entity we record which is the next node in the list to visit. Having got the ODE body we can the find its position, we'll use this late to see if we need to do something with the entity

    if (spcdn) {
// apply force if the space key is held
const dReal* v = dBodyGetLinearVel(bdy);
if (v[1] < 10 && pos[1]<10) { // cap upwards velocity and don't let it get too high
dBodyEnable (bdy); // case its gone to sleep
dMass mass;
dBodyGetMass (bdy, &mass);
// give some object more force than others
float f = rndf(8,20) * mass.mass;
dBodyAddForce(bdy, rndf(-f,f), f*10, rndf(-f,f));
}
}

Notice in the section where we react to the space bar, were are using ODE functionality without the framework getting in the way.

If the body isn't going up to fast and it hasn't reached a threshold height then we are giving it a random shove mainly upwards, based on the mass of the entity.

The next section of code (the final part of the main loop's update) deals with object that have fallen off the ground object and are falling into oblivion....

	if(pos[1]<-10) {
// would be more efficient to just reuse the object and
// reposition it with zeroed velocities
// but this is used to aid testing
FreeBodyAndGeoms(bdy);
free(ent);
clistDeleteNode(physCtx->objList, &node);
addRandomPhys(physCtx, &graphics, (Vector3){rndf(-3, 3), rndf(6, 12), rndf(-3, 3)});
}

node = next;
}

ODE returns positions in an array giving x=0, y=1 and z=2, so here we are checking the height of the entity and if its too far below the the object is destroyed and a new random one created

Currently there are three steps to destroying an entity an if future while keeping these functions exposed there will be a FreeEntity function that does these three tasks, but they are worth explaining so you understand what the framework is doing

First is FreeBodiesAndGeoms - this takes a body and frees all the geoms that are attached to it (you can have multiple geoms in a single body to make more complex shapes (see addDumbbell function for an example of this), critically when disposing of the ODE geoms, it also free's the custom geom data too.

Next the entity structure is freed, and finally its entry in the frameworks entity list is removed - this is why we found out what the next node in the list is!

Basic maintenance of structures like these and proper functions (ahem like freeEntity!) make memory management very much easier, and give is allsorts of benefits.

Stepping the physics is simple

	// Step the physics
//----------------------------------------------------------------------------------

physTime = GetTime();
int pSteps = stepPhysics(physCtx);
physTime = GetTime() - physTime;

physTime isn't essential and is just used as a display, stepping the physics, basically runs tiny slices to catch the physics up with the current real time.  The physics engine itself has sub steps but I discovered that syncing the physics like this to real time yields robust simulations (famous last words)

Rendering the world is also very simple

    BeginMode3D(graphics.camera);
drawBodies(&graphics, physCtx);
drawStatics(&graphics, physCtx);
EndMode3D();

Draw bodies does just that, going through each bodies geoms and optionally rendering them (as you may discover late with the other examples, there are a number of reasons to sometimes no render some geoms but have them collide, and sometimes not even that - a common (ahmen cough) cheat with cars is to hang a counter weight under them that doesn't collide - this can swing under the ground and keep a car from tipping over too much - but I digress we need to clean up after ourselves - yes I no the OS can do this but its very good practice to do this and is also invaluable when using the address sanitiser to find leaks.

    CleanupPhysics(physCtx);

// only bit of static trimesh that needs manual cleanup
UnloadModel(ground);

CleanupGraphics(&graphics);

CloseWindow(); // Close window and OpenGL context

Currently an additional model (above the ones that the framework uses) must be freed by the user, other than that if you run CleanupGraphics and CleanupPhysics you will basically free everything on the global entity list

There are some framework structs that need freeing but that is detailed in the examples covering them, like for example in the derby.c example where a bunch of car's are created, the structures for maintaining the cars have to be freed - but to be honest for things like that I just tend to use simple arrays

You next step should be to pick another example - which ever you fancy, look at the code, and work out how it does what it does!

I would welcome and constructive instructions or feedback (please initially contact via the website)

The frame work is available at https://github.com/chriscamacho/RayLibOdeMech

Enjoy