Free MPEG Video Example
Click here to go to the MPEG Video example

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



Above Content Copyright © 1998 - 2005 Alan Baylis, All Rights Reserved