Jump to content

Ode Solver Updated On Sourceforge

Recommended Posts

Hello All,

I've updated the source code in CVS and released a new package to download with the latest changes/additions. I've done a bit of work on it in my spare time with the hope that it would spur some interest and contributions from other people in the Houdini comunity.

The project page is:


You'll also need to download ODE to compile this (you can get pre-compiled version of ODE which makes this easy)


I've included a sample .hip file in the archive that outlines the features I've added. There's still a lot that can be done to improve/extend the solver and any feedback is appreciated.

Mark Tucker of SESI sent me some very detailed feedback on how to improve what I have so far.. I'll post his feedback in this forum as well. I plan on posting feedback, todo's, etc on the project page so the ideas don't get lost. I'm ramping up in my current production and Sony and it's going to me more difficult for me to contribute regularly


Link to comment
Share on other sites

Mark Tucker of SESI sent me some very detailed feedback on how to improve what I have so far.. I'll post his feedback in this forum as well. 


Here it is:

Now on to the code:

First, I notice that you have a SIM_OdeData data type, but it isn't used

anywhere. I assume it was meant to do the job that you ended up using a

SIM_EmptyData for in the odeBody OTL. Both approaches are fine, but

creating your own data type like SIM_OdeData lets you use those

GETSET_DATA_FUNCS() macros and create PRM_Templates which make for a

cleaner interface (both in the code and for the user). Not a big deal

though since you have the OTL wrapper.

Similarly mySIM_Geometry has one function that SIM_Geometry doesn't

have, but that function is never caledin the code. So I would just get

rid of those classes. In the specific case of mySIM_Geometry, if you

want to be able to modify the gdp of a SIM_Geometry directly, you should

use a SIM_GeometryCopy data type (you didn't need to create your own class).

As a final note on mySIM_Geometry, on line 351 you cast a "const

SIM_Geometry *" to a "mySIM_Geometry *", which you should never ever do

with the SIM library. Even though in this case the cast to

mySIM_Geometry is safe (since mySIM_Geometry has no virtual functions or

member data), the casting away of the const is a mortal sin in DOPs.

Doing so lets you modify data that is shared by mutliple objects, or

used inthe cache (so you are modifying data back in time). If you do

find that you need the funtionality provided by mySIM_Geometry::setGdp,

I would suggest you use code like this:

SIM_GeometryCopy        *modgeo;




if( modgeo )


      GU_Detail *modgdp = modgeo->lockGeometry();

      // modify modgdp any way you want



The other big problem I see is your use of gobal data for m_odeGlobals

and m_odeBodies. This means you can't have more than one ODE solver

simulation in any hip file. I woudl suggest creating a SIM_Data subclass

to hold this information instead. Somthing like this:

class SIM_OdeWorldData : public SIM_Data



      odeGlobals              &getOrCreateOdeGlobals()

                        { return m_odeGlobals; }

      std::map<int, odeBody>          &getOdeBodies()

                        { return m_odeBodies; }


      explicit SIM_OdeWorldData(const SIM_DataFactory *factory)

                      : BaseClass(factory),




                      { }

      virtual ~SIM_OdeWorldData()

                      { clear(); }

      // This ensures that this data is always kept in RAM, even when

      // the cache runs out of space and writes out the simulation

      // step to disk. This is necessary because the ODE data can't

      // be written to disk.

      virtual bool getCanBeSavedToDiskSubclass() const

                      { return false; }

      // Start fro scratch. We create a brand new world.

      virtual void initiailizeSubclass()



                              m_odeBodies = new std::map<int,


                              m_odeGlobals = new odeGlobals();

                              m_shareCount = new int(1);


      // To make one world equal to another, copythe data exactly.

      // The share count lets several of these data share the same

      // ODE world and bodies without worrying that the ODE data

      // will get deleted as long as any SIM_OdeWorldData is holding

      // onto it.

      virtual void makeEqualSubclass(const SIM_Data *src)


                      SIM_OdeWorldData *world;

                      world = SIM_DATA_CASTCONST(src,


                      if( world )



                              m_shareCount = world->m_shareCount;

                              m_odeBodies = world->m_odeBodies;

                              m_odeGlobals = world->m_odeGlobals;





      void            clear()


                              if( m_shareCount )



                                      if(*m_shareCount == 0)



                                              delete m_odeGlobals;

                                              delete m_shareCount;



                              m_shareCount = 0;

                              m_odeGlobals = 0;

                              m_odeBodies = 0;


      std::map<int, odeBody>  *m_odeBodies;

      odeGlobals              *m_odeGlobals;

      int                    *m_shareCount;


I also notice that m_odeBodies.clear() probably doesn't free the ODE

bodies, and deleting m_odeGlobals doesn't delete the ODE World. That

should probably be fixed too in the clear() function...

So now that you have this SIM_Data for storing the global data, in the

solver, instead of doing:

if(currTime == 0 || !m_odeGlobals )



SIM_OdeWorldData *worlddata;

worlddata = SIM_DATA_CREATE(*this, "ODEWorld", SIM_OdeWorldData,


This will create a new SIM_OdeWorldData if one isn't there already,

otherwise it will return a copy of the one that is already there (it

returns a _copy_ of the one that's already there because you are asking

for a non-const pointer). When it makes that copy, it will use

makeEqualSubclass to make the copy equal to the old one, so essentially

you'll be getting back the same ODE world data.

A few other points. You can probably simplify the code for dealing with

constraints a lot by using the SIM_ConstraintIterator class. It provides

an interface to loop through all constraints of a particular type. It

should be easier than looping through the constraints yourself. You

should also never have to go through the "getQueryObject" functionin the

solver code. You have the following code:

goalAnchor->getQueryObject().getFieldString ("Options", 0, "objectname",


Which works fine, but is much slower than:

goalobj = goalAnchor->getReferencedObject(time);

I would definitely split processSubData into two functions,

processConstraints and procesForces. processConstraints should be

something like:

// Initialize all constraints. This gives constraints a chance to

// prepare themselves for use, such as by looking up their

// goal objects.

SIM_ConstraintIterator::initConstraints(*object, time);

SIM_DataFilterByType anchorFilter("SIM_ConAnchor");

SIM_ConstratinIterator conit(*object, 0, &anchorFilter,

                            &anchorFilter, time);

for (conit.rewind(); !conit.atEnd(); conit.advance())


      SIM_ConRel                      *conrel;

      const SIM_ConAnchorSpatial      *spanchor, *goalspanchor;

      const SIM_ConAnchorRotational  *rotanchor, *goalrotanchor;

      conrel = conit.getRelationship();

      spanchor = SIM_DATA_CASTCONST(it.getCurrentAnchor(),


      goalspanchor = SIM_DATA_CASTCONST(it.getGoalAnchor(),


      if( spanchor && goalspanchor )


              // Handle spatial constraints.


      rotanchor = SIM_DATA_CASTCONST(it.getCurrentAnchor(),


      goalrotanchor = SIM_DATA_CASTCONST(it.getGoalAnchor(),


      if( rotanchor && goalrotanchor )


              // Handle Rotational constraints.



As for forces, you don't need to handle each force type individually.

You should use the functions on the SIM_Force base class. Also, there

are functions for making a calback for all data that matches a

particular data type. So processForces would look something like:


/// This is a helper class to iterate over all the forces on an object

/// and sum them up.


class ODE_EachForceCallback : public SIM_EachDataCallback



                    ODE_EachForceCallback(const UT_Vector3 &pos,

                                          const UT_Vector3 &vel,

                                          const UT_Vector3 &angvel,

                                          const fpreal mass,

                                          UT_Vector3 &force,

                                          UT_Vector3 &torque)

                                    : myPosition(pos),






                                { }

    virtual                    ~ODE_EachForceCallback()

                                { }

    virtual void                callbackConst(const SIM_Data *data,

                                              const char *name);


    UT_Vector3                  myPosition;

    UT_Vector3                  myVelocity;

    UT_Vector3                  myAngVel;

    fpreal                      myMass;

    UT_Vector3                  &myForce;

    UT_Vector3                  &myTorque;



ODE_EachForceCallback::callbackConst(const SIM_Data *data, const char *)


    const SIM_Force    *forcedata = SIM_DATA_CASTCONST(data,


    UT_Vector3          force, torque;

    forcedata->getForce(myPosition, myVelocity, myAngVel,

                        myMass, force, torque);

    myForce += force;

    myTorque += torque;


processForces(SIM_Object *object)


      // Grab the velocity, angular velocity, mass, and center of

      // mass fromthe object (from the Position data).

      UT_Vector3      sumforces(0.0, 0.0, 0.0);

      UT_Vector3      sumtorque(0.0, 0.0, 0.0);

      ODE_EachForceCallback            callback(com,










      // Apply sumforces and sumtorques to object...


I also noticed you calling getNthConstSubData to grab the force and

constraint data. You should avoid this function whenever possible. If

you call this function for each subdata, you've created an O(n^2)

algorithm on the number of subdata.

As a final point, I notice you don't create SIM_Impacts data on the

objects. This is fine, and I don't even know if the ODE Solver is able

to provide equivalent information. But if it is possible, outputting

impacts into SIM_Impacts data would allow the ODE Solver to work with

the SOP Solver to do denting and other such effects. You also need to be

able to generate and respond to SIM_Impacts data to work with other

solvers (like the cloth solver). But given the way ODE handles

constraints, I don't know how much luck you'll have generating the

impact information you need when one of the objects in the collision is

cloth (a deforming triangle mesh). But this is all definitely more

advanced funcitonality which is not required for just a solid RBD Solver.

I'm going to cut myself off there. I hope you find this all helpful (but

not overwhelming). If you want to post these comments more publicly (to

odforce when you announce your changes, or to sourceforge) so that other

people can do some of the work please feel free. And if you have more

questions, please ask. All the "code" above is just off the top of my

head so is no doubt full of syntactical and logical errors).

Thanks for all the effort you've put into this. It's really great to see

a serious open source DOPs solver...


Link to comment
Share on other sites

Thanks Jason! We'll probalby want to put pre-compiled plugins in the sourceforge site.

For those of you who are just trying this out, you'd be wise to download the latest package on the sourceforge site. The solver really depends on a couple of OTL's to define the ode bodies. The sourceforge package includes that along with a sample hip file with various examples


Link to comment
Share on other sites

so, this beast really works, huh?  :ph34r:


Yup, it seems to! Daniel did a marvellous job of getting a great first version out there it seems. I've played with all of his demo scenes and they work just brilliantly; and it seems like they'll get faster if we manage to get some of Mark's suggested enhancements in there.

I've been trying to get the Windows version to compile for me but I'm really bad at it:) The linux one was smoothe to build but not on my Windows machine.

Link to comment
Share on other sites

Yup, it seems to!  Daniel did a marvellous job of getting a great first version out there it seems. I've played with all of his demo scenes and they work just brilliantly; and it seems like they'll get faster if we manage to get some of Mark's suggested enhancements in there.

I've been trying to get the Windows version to compile for me but I'm really bad at it:) The linux one was smoothe to build but not on my Windows machine.


I should be able to get a windows ver going (hell, I worked on it under windows!). I need to install 8.0.474 here at home first, then I'll post it up. They way I had it the wondow's ver required the ode.dll in your path. I'm assuming I can build it staticly so it's all baked in? In any event, it's not a big deal to have the ode.dll as well. Probably won't get to this until tomorrow night


Link to comment
Share on other sites

Woo! Thanks Dan :).

Spare time eh? Enjoy it while it lasts ;)


Heh, yea.. that time will die out I'm afraid. We start lighting our first sequence next month. I did most of this work a few months ago, just waited on the offical "ok" from Sony before posting anything. Work is much more demanding these days


Link to comment
Share on other sites

Great stuff Daniel (and Mark for the great insights)!

Thank you for sharing all you hard work.

I would like to officially place my vote for extending days by at least 6 hrs. so that we can all have time to explore things like this... *sigh*

... oh wait, I guess that would mean shortening work days by 6 hrs... all in favor say aye! :P

Link to comment
Share on other sites

Agh, I meant sourceforge, not odforce.  B)

Can anyone drop a Windows compile for 8.0.474 here too?


Attached is a Windows Compile of the solver ( 8.0.474 ):


Again, you'll still want to download the latest sourceforge distribution to get a sample hip file and a couple of support Digital Assests. The solver is farily useless without the Digital Assets to attach some solver specific attributes


Link to comment
Share on other sites

A couple of things for people trying to follow along at home:

If you are compiling on linux, the compile.sh file that does the compile, refers to


but there is no such file, there is only SIM_SolverODE.cpp. So you need to rename the .cpp file to .C

The config.sh script says you "probably" have to comment out the typedef of int8 in ode-0.5/include/common.h. In fact, you definitely have to do this, and the file is actually


After that, it's all gravy!

Link to comment
Share on other sites

How would you (i.e., a coder better than me: anyone) suggest that we fix this type of thing? Are there namespaces or such that'd be able to scope typedefs like this to certain source files? Is it a SESI thing, an ODE thing or can the plugin developers just do it themselves somehow without having to modify the ODE or HDK headers (with potentially disasterous consequences)?


PS. Thanks for the Windows compile, Daniel.

Link to comment
Share on other sites

  • 1 month later...

Woah, guys!

I've just installed the ODE solver and it works all right. Marvelous job! It may sound silly to just say "thanks" and that's it, but... thank you thank you thank you =)

PS. oh, yeah. I've just forgot to thank you once more.

Link to comment
Share on other sites

Woah, guys!

I've just installed the ODE solver and it works all right.  Marvelous job!  It may sound silly to just say "thanks" and that's it, but...  thank you thank you thank you =)

PS. oh, yeah. I've just forgot to thank you once more.


You're Welcome, You're Welcome, You're Welcome

I hope as some of the coders free up they can continue to build on it.. me, I'm starting to get my hands full with work and life


Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...