Common ODE Joints
By Alan Baylis 04/03/2006
From the first tutorial
here we looked at how to create a single object. Now we can move on to creating multiple objects and joining them
together with joints. The types of joints we are covering today are the three most oft used types, namely the Ball and Socket joint, the Hinge joint and a Slider joint.
* Contains a separate example program for each type of joint
Starting out with the basic code from the first tutorial we now expand on the
base code. To show the joints in action we will need
to increase the number of objects by one, and then create a new joint group and
a joint ID. So our global variables for ODE, which are the same for all three
types of joints, now look like this:
MATRIX GeomMatrix;
dWorldID World;
dSpaceID Space;
MyObject Object[2]; // two geom objects
dJointGroupID contactgroup;
dJointGroupID jointgroup; // contact group for the new joint
dJointID Joint; // the joint ID
We now move on to the initialization routine for ODE which I called InitODE.
I will list the whole function again, but I'll just comment on the new
code required to make the joints.
void InitODE()
{
World = dWorldCreate();
Space = dHashSpaceCreate(0);
contactgroup = dJointGroupCreate(0);
// As well as the contact group for collisions we need to create a new joint group and
// assign its ID to jointgroup
jointgroup = dJointGroupCreate(0);
dCreatePlane(Space, 0, 1, 0, 0);
dWorldSetGravity(World, 0, -1.0, 0);
dWorldSetCFM(World, 1e-5);
dWorldSetERP(World, 0.2);
dWorldSetContactMaxCorrectingVel(World, 0.9);
dWorldSetContactSurfaceLayer(World, 0);
dWorldSetAutoDisableFlag(World, 1);
dReal sides[3];
dMass m;
dMatrix3 R;
VECTOR tempVect(0.0, 0.0, 0.0);
sides[0] = 2.0;
sides[1] = 2.0;
sides[2] = 2.0;
// Here we instantiate the two bodies we will be using
Object[0].Body = dBodyCreate(World);
Object[1].Body = dBodyCreate(World);
// Set up for body 1. Nothing much has changed here, we are now dealing with an array
// instead of a single object and I have changed its initial position and rotated the
// object 45 degrees around the Y axis using dRFromEulerAngles.
dBodySetPosition(Object[0].Body, -1.4142, 1.0, -5.0);
dBodySetLinearVel(Object[0].Body, tempVect.x, tempVect.y, tempVect.z);
dBodySetData(Object[0].Body, (void*)i);
dRFromEulerAngles(R, 0.0, pi / 4, 0.0);
dBodySetRotation(Object[0].Body, R);
dMassSetBox(&m, DENSITY, sides[0], sides[1], sides[2]);
Object[0].Geom[0] = dCreateBox(Space, sides[0], sides[1], sides[2]);
dGeomSetBody(Object[0].Geom[0], Object[0].Body);
dBodySetMass(Object[0].Body, &m);
// Set up for body 2. As above, except for its initial position.
dBodySetPosition(Object[1].Body, 1.4142, 3.0, -5.0);
dBodySetLinearVel(Object[1].Body, tempVect.x, tempVect.y, tempVect.z);
dBodySetData(Object[1].Body, (void*)i);
dRFromEulerAngles(R, 0.0, pi / 4, 0.0);
dBodySetRotation(Object[1].Body, R);
dMassSetBox(&m, DENSITY, sides[0], sides[1], sides[2]);
Object[1].Geom[0] = dCreateBox(Space, sides[0], sides[1], sides[2]);
dGeomSetBody(Object[1].Geom[0], Object[1].Body);
dBodySetMass(Object[1].Body, &m);
// To join the two objects together we first need to create a new joint with a call to
// dJointCreateBall and retain the ID returned in our Joint variable.
Joint = dJointCreateBall(World, jointgroup);
// We now instruct ODE that this new joint will be attached to our two object bodies.
dJointAttach(Joint, Object[0].Body, Object[1].Body);
// And lastly we tell ODE where exactly the two objects are joined in world coordinates.
dJointSetBallAnchor(Joint, 0.0, 2.0, -5.0);
}
Now there is only one more change needed to get this ball and socket example
working, and that is to change the call to DrawGeom in our original SimLoop
routine to the following.
DrawGeom(Object[0].Geom[0], 0, 0, 0);
DrawGeom(Object[1].Geom[0], 0, 0, 0);
Pretty simple huh? The most significant change to the InitODE routine was the
addition of the last three lines of code which create and initialize the ball
and socket joint. To create a hinge joint you would use the following lines of
code instead.
Joint = dJointCreateHinge(World, jointgroup);
dJointAttach(Joint, Object[0].Body, Object[1].Body);
dJointSetHingeAnchor(Joint, 0.0, 2.0, -5.0);
dJointSetHingeAxis(Joint, 0, 0, 1);

And to create a slider joint you would use these lines of code.
Joint = dJointCreateSlider(World, jointgroup);
dJointAttach(Joint, Object[0].Body, Object[1].Body);
dJointSetSliderAxis(Joint, 0, 1, 0);
dBodyAddForce(Object[1].Body, 0, 200, 0); // Added a force to show the slider joint in action

Also, don't forget to clean up the extra joint group by adding the
following line of code to the CloseODE routine. I'm notorious for not cleaning
up properly but I know you will do the right thing.
dJointGroupDestroy(jointgroup);
In the next tutorial we will look at constraining the range of movement of a
joint (using stops) and also mess around with applying forces to the objects.
Alan Baylis