Bowl of Eggs Physics Demo

Hmmmm I tried the setPosition function

def rollTask(self):
    #Read the mouse position and tilt the maze accordingly
       if base.mouseWatcherNode.hasMouse():
          mpos = base.mouseWatcherNode.getMouse() #get the mouse position
          #upos = base.mouseWatcherNode.getZ
          self.bowl.setP(mpos.getY() * -30)
          self.bowl.setR(mpos.getX() * 30)
          self.bowl.setZ(2)
          self.SIM_bowl.body.setRotation(self.geomRotMat(self.bowl.getHpr()))
          self.SIM_bowl.body.setPosition(self.geomPosMat(self.bowl.getZ()))

Used as a basis from

def geomPostMat(self,pos):
        dum=NodePath('')
        dum.setPos(pos)
        mat=dum.getMat()
        mat4=[]
        for col in range(3):
            mat3.append(mat.getCol3(col)[0])
            mat3.append(mat.getCol3(col)[1])
            mat3.append(mat.getCol3(col)[2])
            mat3.append(mat.getCol3(col)[3])
        dum.removeNode()
        return mat4

However the bowl does not levitate or use Z as the starting point?

Note that there is another way to define the rotation directly, using setQuaternion :

self.SIM_bowl.body.setQuaternion(self.bowl.getQuat())

But, setting the rotation directly is not the ideal way, especially when there is collision with the object. The ideal way is to apply force to it, so that it will rotate naturally.
If you’d like the bowl stays in the mid air, you should connect it to another body with joint type you need. Then you should nail that body to ode.environment using fixedJoint.
Insert this before creating the bowl :

        # create a static position body
        levitation=1
        self.hangingBody = ode.Body(self.ode_WORLD)
        self.hangingBody.setPosition((0,0,levitation))
        fJ=ode.FixedJoint(self.ode_WORLD)
        fJ.attach(self.hangingBody, ode.environment)
        fJ.setFixed()
        # make sure to "save" the joint
        self.simJoints.append(fJ)

insert this after creating the bowl :

        self.SIM_bowl.body.setPosition(self.hangingBody.getPosition())
        bJ=ode.BallJoint(self.ode_WORLD)
        bJ.attach(self.SIM_bowl.body, self.hangingBody)
        bJ.setAnchor(self.hangingBody.getPosition())
        # make sure to "save" the joint
        self.simJoints.append(bJ)

the new tiltTask :

    def tiltTask(self):
    #Read the mouse position and tilt the maze accordingly
       if base.mouseWatcherNode.hasMouse():
          mpos = base.mouseWatcherNode.getMouse() #get the mouse position
          self.bowl.setP(mpos.getY() * -10)
          self.bowl.setR(mpos.getX() * 10)
          self.SIM_bowl.body.setAngularVel((0,0,0))
          self.SIM_bowl.body.addRelForceAtRelPos((0,0,-5),(mpos.getX() * 10,mpos.getY() * 10,0))

and make sure to add levitation to the eggs’ randomized Z.

Gotcha,
Thats a great idea to hold the bowl in midair…I suppose the joints act like a pedastal to hold it up. Ive been reading more on the ODE API so I am beginning to understand more of the concepts you give.

Adding a velocity to the tilt makes it look much more realistic

Thanks, much appreciated.

Sorry, you don’t need to set it’s pitch & roll.

I tried the simJoints function but it returned a message that it couldnt find the Joints. Did I place them in the right section?:

    def startScene(self):
        if self.ode_SPACE.getNumGeoms()>1:
           for o in self.simObjects:
               o.destroy()
           self.simObjects = []
           for j in self.simJoints:
               j = None

        self.table=loader.loadModelCopy('misc/rgbCube')
        self.table.reparentTo(render)
        self.table.setScale(200,200,.5)
        self.table.setZ(.5*self.table.getSz())
        self.table.setColor(.2,.5,.7,1)
        self.table.setLightOff()
        self.simObjects.append( ODEbox( world=self.ode_WORLD, space=self.ode_SPACE,
                                        realObj=self.table,
                                        density=0, friction=0 ) )


 #############new## create a static position body
        levitation=1 
        self.hangingBody = ode.Body(self.ode_WORLD) 
        self.hangingBody.setPosition((0,0,levitation)) 
        fJ=ode.FixedJoint(self.ode_WORLD) 
        fJ.attach(self.hangingBody, ode.environment)
        fJ.setFixed() 
        # make sure to "save" the joint 
        self.simJoints.append(fJ)

        # load the bowl
        self.bowl=loader.loadModelCopy('bowl')
        self.bowl.reparentTo(render)
        self.bowl.setZ(.5)
        self.SIM_bowl = ODEtrimesh( world=self.ode_WORLD, space=self.ode_SPACE,
                                     realObj=self.bowl, collObj=self.bowl,
                                     density=10, friction=1 )
        self.simObjects.append(self.SIM_bowl)

 #############new##################       
        self.SIM_bowl.body.setPosition(self.hangingBody.getPosition())
        bJ=ode.BallJoint(self.ode_WORLD)
        bJ.attach(self.SIM_bowl.body, self.hangingBody) 
        bJ.setAnchor(self.hangingBody.getPosition()) 
        # make sure to "save" the joint 
        self.simJoints.append(bJ)



        # list to keep ODE joints (to connect the collision spheres of the egg),
        # to keep their effect, or else Python considers the joints as garbage !!
        self.simJoints = []

I assume this is where the bowl is created and the levitation code sandwiches it there

first, simJoints is not a function, but a list to hold the active joints, so that they won’t be removed by Python. Of course, you have to create it before using it. Move “self.simJoints = []” to the place before the 1st joint created.

Thanks ynjh_jo, I apologize if I seem to be a pest, but I really do appreciate your help very much as I am eager to learn.

I thought I would give myself a few days to try my best to figure out some of this ODE physics work. I can say after 3 days of solid work I have found out alot of what I can’t do!

In regards to the code snippets and file above above some strange things are happening.

In the function,

def startScene(self):

where I add the code to set my Fixed Joint to the scene

levitation=2
        self.hangingBody = ode.Body(self.ode_WORLD)
        self.hangingBody.setPosition((1,0,levitation))
        self.simJoints = []
        fJ=ode.FixedJoint(self.ode_WORLD)
        fJ.attach(self.hangingBody, ode.environment)
        fJ.setFixed()
        self.simJoints.append(fJ)

The joint is set appropriately,
next the bowl is loaded…
then I added code to attach the bowl to the fixed object with a Balljoint:

        self.SIM_bowl.body.setPosition(self.hangingBody.getPosition())
        self.simJoints = []
        bJ=ode.BallJoint(self.ode_WORLD)
        bJ.attach(self.SIM_bowl.body, self.hangingBody)
        bJ.setAnchor(self.hangingBody.getPosition())
        self.simJoints.append(bJ)

I run the program but the bowl does not remain fixed to the hangingBody…it seems to recognize the hangingBody’s location (as it falls from the “levitation’s” height and 1 unit to the X value given) but falls instead of levitates.

The only way I was able to get it to levitate was placing this code in the tiltTask function:

  def tiltTask(self):
       if base.mouseWatcherNode.hasMouse():
          mpos = base.mouseWatcherNode.getMouse() #get the mouse position
          self.SIM_bowl.body.setAngularVel((0,0,0))
          self.SIM_bowl.body.addRelForceAtRelPos((0,0,-5),(mpos.getX() * 10,mpos.getY() * 10,0))
          levitation=1
          self.hangingBody = ode.Body(self.ode_WORLD)
          self.hangingBody.setPosition((0,0,levitation))
          self.simJoints = []
          fJ=ode.FixedJoint(self.ode_WORLD)
          fJ.attach(self.hangingBody, ode.environment)
          fJ.setFixed()
        # make sure to "save" the joint
          self.simJoints.append(fJ)
          self.SIM_bowl.body.setPosition(self.hangingBody.getPosition())
          self.simJoints = []
          bJ=ode.BallJoint(self.ode_WORLD)
          bJ.attach(self.SIM_bowl.body, self.hangingBody)
          bJ.setAnchor(self.hangingBody.getPosition())
        # make sure to "save" the joint
          self.simJoints.append(bJ)
        # make sure to "save" the joint

The bowl now levitates but does not collide with the egg (the egg falls through) nor responds to the arrow keys (the arrow keys are another issue I guess)
I really struggled to find a solution to post to the community on this but I never found a way to get this to work. Sorry to bother again…

Your grand problem is you initialized simJoints twice. Basically, simJoints only serves as a container for any created joint in your scene, so you only have to create it once in a (simulation) lifetime. It will be destroyed too when restarting the scene, and then created again.
You can directly nail your bowl to ode.environment:

        self.simJoints = []
        levitation=2

        # load the bowl
        self.bowl=loader.loadModelCopy('bowl')
        self.bowl.reparentTo(render)
        self.bowl.setPos(1,0,levitation)
        self.SIM_bowl = ODEtrimesh( world=self.ode_WORLD, space=self.ode_SPACE,
                                     realObj=self.bowl, collObj=self.bowl,
                                     density=10, friction=1 )
        self.simObjects.append(self.SIM_bowl)

        bJ=ode.BallJoint(self.ode_WORLD)
        bJ.attach(self.SIM_bowl.body, ode.environment)
        bJ.setAnchor(self.SIM_bowl.body.getPosition())
        # make sure to "save" the joint
        self.simJoints.append(bJ)

And of course you can’t move the bowl anywhere again, since it’s nailed to the environment. No matter how you apply force to it, it will remain there.
You may want to see –[THIS]– too, to see the other joints in action.

ynjh_jo, the link you gave for your PyODE eggs example seems not work now. Would you please update it again? Thank you very much!

panda3dprojects.com seems to be down. Try –[HERE]–

It’s up again :wink:

Why we don’t need to set the P&R? Can the following:

self.SIM_bowl.body.addRelForceAtRelPos((0,0,-5),(mpos.getX() * 10,mpos.getY() * 10,0))

set the models in right position automatically?

How do you determine the position(mpos.getX() * 10,mpos.getY() * 10,0), where to apply the force? mpos.getX() * 10,mpos.getY() * 10 is the degree the bowl needs to pitch and roll, right?

This is really a great place to learn Panda and ODE. I read your code through, but there’re many details I can’t understand, like what’s ‘trimesh’ :confused: . I try to concentrate on the geom, joints and force, which I think is the most important things I need to know now.
Then I’ll move to your “carPhysical” code. Since my final year project is developing a game like NFS, I must take pains in your example. Thank you very much!

@Liquid7800 Thanks for introducing me to such a good post, or I’ll miss it! :smiley:

First things you should know :

  1. physics simulation is performed on ode.body, while ode.geom is for collision detection
  2. ODE does the physics simulation
  3. then we synchronize the visible geometry to it’s body. This is done in simulate method :
x, y, z = body.getPosition()
R = body.getRotation()
mat =  Mat4(R[0], R[3], R[6], 0.,
            R[1], R[4], R[7], 0.,
            R[2], R[5], R[8], 0.,
            x, y, z, 1.0)
o.realObj.setMat(mat)
o.realObj.setScale(o.scale)

That’s the part to update the visible geometry’s transformation, directly on it’s transform state level, using transform matrix. But by setting the transform matrix directly, only for setting the position and rotation, unfortunately it also overrides the scale too, since rotation and scale is melted in the matrix. Shear is ignored here.
That update part is the one used in the 3rd ODE tutorial (using pygame), and the 1st poster in CodeSnippet forum here used it 1:1. And I just followed that path and been BLIND for so long time, without even realized that there is “body.getQuaternion”. Just realized it lately, arrrgh … damn stupid me !
So it can be done this way too, which is a lot simpler :

o.realObj.setPos(*body.getPosition())
o.realObj.setQuat(Quat(*body.getQuaternion()))

mpos.getX() & mpos.getY() result is in window space :
= mpos.getX() result is -1 (left) ~ 1 (right)
= mpos.getY() result is -1 (bottom) ~ 1 (top)
which is exactly similar to bowl’s and the camera’s default XY orientation (facing to Y+), assuming it’s not moved around.

addRelForceAtRelPos adds force RELATIVE to the body’s rotation and position (it’s own coordinate space & origin).
So addRelForceAtRelPos((0,0,-5),(mpos.getX() * 10,mpos.getY() * 10,0)) adds (0,0,-5) linear force (down) at (x10, y10). Then, 10 multiplier is used to get a farther distance from the bowl’s origin, which allow the force to give a greater effect :
M = F * d (remember it in highschool physics ?)

TriMesh is collision geom type which is built of triangles. Here we use it for the bowl only. It’s constructed by using the actual triangles of the bowl, by packing it’s vertices list and indices list :

meshdata=ode.TriMeshData()
# create TriMesh data
meshdata.build(collVertices,collFaces)
# and pass it to ODE
self.geom = ode.GeomTriMesh(meshdata,space)

in ODEtrimesh method.

ynjh_jo, Thank you so much!
It’s so clear now. ode.body receives the force you give, then we need to apply all the changes on ode.body to the real geometry.

But there’s one thing that I’m a little confusing:

So the force do not always have to be set exactly on the body(inside the bound of the model). It is actually applied on the body’s coordinate, right?

Forces can be anywhere in the world. It can be absolute or relative. There are addForce, addForceAtPos, addForceAtRelPos, addRelForce, addRelForceAtPos, addRelForceAtRelPos. Anyone with Rel means relative to the body, otherwise it’s at world coordinate.

To extend the discussion of positional forces, the force applied is the same magnitude regardless of where it is applied relative to an object. However, positional forces applied to a point other than the objects center of mass also apply a torque on the object, inducing change in angular velocity. Torque is equal to

(distance from center) * (portion of force perpendicular to axis)

Think of an invisible lever extending from the object’s center of mass to the point of force application. A large distance should produce more torque, but only if the force vector does not point directly at the object center.

Glad you could find this useful Aq_la…PyODE integration was difficult once exposed to it but it starts to make sense really fast…

Well, I know I had been asking about how to levitate the bowl. I tried the bowl>BallJoint>hangingbody to fixedJoint>ode.environment, and while that worked (the position was locked though) I still wanted to have the option to move the bowl around with forces …but have the bowl levitated.

Well I read a whole LOT about ODE and found this cool tutorial :
opende.sourceforge.net/wiki/inde … ects_to_2d

This introduced me to the Plane2DJoint. I tried to implement this in the bowlNegg example, trying this arrangement:
bowl>BallJoint>hangingBody>Plane2DJoint(z-axis 2)>ode.world

This almost worked…I got the bowl to have forces and it moved only on XY…but I couldnt get my Z coordinate to be fixed or set wherever I wanted…it kept defaulting to the ODE.World orgin (0,0,0).

So I came up with this hack by creating a pedestal to hold the bowl up with a hingeJoint to keep the bowl tilt going.

Check out the code here:
yousendit.com/download/QlVqS … M2swTVE9PQ
The new addition begins in the

def startScene(self):

function
I left my pedestal visible to show what is actually holding the bowl up.

Ideally I would have wanted to set the plane2dJoint at whatever z I wanted with a hingeJoint on the bowl to keep the tilt…but I tried that ODE tutorial and I couldnt figure out how to set a Z at 1 or 2, so I came up with this idea to hold the bowl up…
but I guess its not really levitating! :confused:

As a note in my posted example, if my hingeJoint was set at only one axis the pedestal would be spinning or falling cause the tilt is caused by adding a force…I suppose using the get.quaternation technique mentioned above, may prevent this because no force is being applied?

Just wondering, this was the most efficient way I could think of for levitating the bowl and keeping movement by forces.

Does anyone have any suggestions or improvements to the above mentioned code (technique) that may work better or more efficiently for adjusting the bowl’s Z (levitating)?