[Resolved] ODE Movement/Forces

I know a recent thread was made with a question about forces, but since my question is somewhat different, I wanted to avoid “thread jacking” and just play it safe by posting a new thread. After searching through the forum for anything related to Panda3D’s implementation of ODE, examining the few examples I could find, and experimenting with the mostly undocumented methods in the reference, I finally succumbed to my failure and came here to ask for assistance.

So far, thanks to pro-rsoft’s ODE sample code, I am at the point where I have a modeled terrain in an ODE world with gravity, and a ball object that successfully falls onto and collides smoothly with said terrain. That’s great. What I want to do now is control its movement with the keyboard. Specifically, my first goal has been to just focus on upward motion to figure out how to work this system. After that, I will need to get other motion working.

The first thing that came to mind was applying a force on positive Z to counter the effect of the gravitational negative Z force. I have tried the following with absolutely no effect–at least none that I could see:

self.ball.body.addForce(0.0, 0.0, 10.0)
self.ball.body.setForce(0.0, 0.0, 10.0)

The second thing I tried was to directly set the linear velocity. I got it to move upward on keypress, but it seemed too unnatural, which makes sense since the velocity should theoretically be changing as forces are applied to it. I tried to factor even this in, but it still seemed totally incorrect.

self.ball.body.setLinearVel(self.ball.body.getLinearVel().getX(), self.ball.body.getLinearVel().getY(), 15.0*self.liftValue)

(note to the above code line: self.liftValue was sort of my flag for when the key was being held down; it would be 1 on pressing the key and 0 on lifting the key)

I blame my lack of understanding of the system. I humbly request an explanation as to what I am doing incorrectly.

Try adding the force every step.

I thought of that, too. Darn, I should have added that information. Anyway, I put it in a task and had it constantly adding the force. Nothing happened.

Xidram, I have a base level Character Controller in one of my work in progress projects, I could paste the relevant code if it helped. It has xy movement and the ability to jump. It doesn’t work too well at steep angles or anything, bit it might be helpful. It’s based on the panda3d eggs in a bowl demo’s pyODE implementation, and the (in)famous raycar demo - http://www.enchantedage.com/raycar < ooh, looks like he’s updated his page (the link used to be broken).

Let my know if my code would help.

I have a basic fps demo I’ve mostly got working - having some problems getting realistic shooting responses and I don’t yet have crouching or weapon fx, but I’m hoping to upload it shortly, code and all. Might be of some use to you, though I want to keep it under wraps until then:-)

Anyway, my basic approach is to have a desired velocity and to then work out the force required to obtain that velocity in the current time step… and then clamp that force by what I refer to as the player impulse (Adjusted by time), i.e. how strong they are. (And that gets adjusted depending on if the player is touching the ground or not, to have limited air control.) It does help when doing this stuff to know your basic mechanics equations though - if you don’t know 'em I’ld look them up. f=ma is all you need to do the suggested, though momentum equations are occasional useful to work out the desired velocity in the first place.

Aurilliance and Lethe, thank you both for the replies and offers.

I suppose I should describe my goal in this before going further. I am trying to accomplish a sort of ‘free flight’ movement primarily. While moving around on the surface of the terrain and jumping would be great for terrestrial movement, (which is another part of my goal,) my question is–can the same mechanics be used for free flight? By “free flight,” think a combination of aeroplane and VTOL; hovering, lifting and falling, forward and reverse, strafing, rotation–you get the idea. This is why I was thinking in terms of applying forces.

Lethe, perhaps I am misinterpreting; are you suggesting I simply bruteforce the changing velocity by applying physics equations? If that is the case, then what would be the point of having actual “forces” that can be applied to the physical objects? It seems rather like doing it the old way without ODE implementation, which at best would also include manual acceleration and such. I just figured that ODE’s forces would make the job that much easier; though, as I said before, I lack experience with the system. Would manually handling the velocities work better for the goal I laid out above?

By the way, to add to my response to Pro-rsoft, I removed the key-press condition and just put self.ball.body.addForce(0.0,0.0,10.0) into a task that started right away. Still, nothing happened. I figure–maybe I should post some code as I may have skipped a critical step.

The following is the ball class that would become the character class later:

class Ball():
    def __init__(self,  world):
        # Load the model
        self.model = loader.loadModel('smiley')
        self.model.flattenLight()
        self.model.setTextureOff()
        # Create the nodepath
        self.np = self.model.copyTo(render)
        self.np.setPos(0.0, 0.0, 20.0)
        self.np.setHpr(0.0, 0.0, 0.0)
        # Create the body and set the mass
        self.body = OdeBody(world.world)
        M = OdeMass()
        M.setSphere(50, 1)
        self.body.setMass(M)
        self.body.setPosition(self.np.getPos(render))
        self.body.setQuaternion(self.np.getQuat(render))
        # Create the geom
        self.geom = OdeSphereGeom(world.space, 1)
        self.geom.setCollideBits(BitMask32(0x00000001))
        self.geom.setCategoryBits(BitMask32(0x00000001))
        self.geom.setBody(self.body)

The following is the main class that starts the whole program:

class Main(DirectObject):
    def simulationTask(self,  task):
        self.world.space.autoCollide() # Setup the contact joints
        # Step the simulation and set the new positions
        self.world.world.quickStep(globalClock.getDt())
        if not self.ball.np.isEmpty():
            self.ball.np.setPosQuat(render, self.ball.geom.getBody().getPosition(), Quat(self.ball.geom.getBody().getQuaternion()))
        self.world.contactgroup.empty() # Clear the contact joints
        return task.cont

    def lift(self, flag):
        self.liftValue = flag
        
    def move(self,  task):
        if self.liftValue:
            self.ball.body.addForce(0.0, 0.0, 10.0)
        return task.cont

    def __init__(self):
        self.liftValue = 0

        self.accept('escape',  sys.exit)
        self.accept('q', self.lift, [1])
        self.accept('q-up', self.lift, [0])
        
        self.world = World()
        self.ball = Ball(self.world)
        
        taskMgr.doMethodLater(0.5, self.simulationTask, 'Physics Simulation Task')
        taskMgr.add(self.move,  'Character Movement Task')
        
        run()

And then of course I start it up by creating a Main() object. Oh, and I added the key-press condition back into it since I know that as ugly as it looks now, (I will change it to the traditional keymap dictionary,) it does function as it’s supposed to. I know this because, as I wrote in my original post, replacing the addForce() with setLinearVel() showed a visible change while the key was being held down.

I doubt the World() class code would help much since obviously whatever ODE-related stuff in there works properly as can be observed with the ball falling and colliding with the terrain. It really only contains just that; loading the terrain and setting it up with the ODE world and space.

In order to lift an object, you must apply a force larger than the gravity force.

As lethe said, the equation of force is f=m.a
So, to lift it, you must apply a force larger than ( yourObjectMass x 10 )
10 or 9.81 is the gravity acceleration you pass to ODE via ODEworld.setGravity().

I thought of that, too, and tried setting it first to a solid 150.0, and since the mass is 1, I would expect that to do something. I then even tried 150.0*self.ball.body.getMass().check(), and still nothing happened. I have worked with some physics before in certain other applications, (most notably Second Life,) so I do have a general understanding of mass and forces. My problem here is that the forces that are supposedly being added are showing absolutely no change to the ball’s motion, which is baffling me.

M.setSphere(50, 1)

The first parameter is density, isn’t it ?

150.0*self.ball.body.getMass().check(), what is check() method ? I usually use getMagnitude()

May be you can try:
M.setSphere(1, 1)
And use a force bigger than 10, say 100, 1000 … you will see the effect.

And try use other demos, or my demos :sunglasses::
discourse.panda3d.org/viewtopic.php?t=5915

Nooo, the mass is NOT 1.
Density means mass density per volume unit. You use a sphere of radius 1 with density 50.
Sphere volume is (4/3).phi.r.r.r = 4.1888
So, the mass is (volume x mass density) = 4.1888 x 50 = 209.4395 = getMagnitude() as clcheung said.

Oh, thank you both so much. That did it. I should have looked at setSphere() in the documentation. I assumed 50 was the radius. And why I said Mass = 1–I tried printing getMass().check() and it returned 1. This is mostly undocumented stuff, so the returning of an integer brought me to the assumption that getMass().check() would return the value of the mass.

Thank you all again for spending the time helping me.