physics engine: impulse

Return to Code Snippets

physics engine: impulse

Postby BrianInSuwon » Wed Jul 02, 2008 12:34 pm

Here is some code to add impulse (acceleration) to an object . Note, the longer you hold down the key, the longer the force is applied to the object. Therefore, the acceleration will be greater. Its like turning rockets on. The object will not stop until acted on by an equal but opposite impulse. In other words, if you turn on the forward rockets for 10 seconds you will need to turn on the back rockets with one burst for 10 seconds or two bursts at 5 seconds / burst to cancel out the impulses.
    Impulse = Force X time = mass X change in velocity

As long as the rockets are on, the velocity with increase. Turn the rockets off and the velocity with remain the same, but not decrease. Note: To get the position, I had to use
Code: Select all
self.cubeBoy.getY(self.plnp)

which is the position of the cube relative to a point light I attached to the render node at 0,0,0.

Using
Code: Select all
self.cubeBoy.getPos()

always returned (0,0,0)


Code: Select all
from direct.directbase.DirectStart import *
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
from direct.gui.OnscreenText import OnscreenText
import sys, math, random

# Function to put title on the screen.
def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1,1,1,1),
            pos=(1.0,0.8), align=TextNode.ARight, scale = .08)

# Function to put instructions on the screen.
def addInstructions(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(-1.23, pos), align=TextNode.ALeft, scale = .07)
           
# Function to put output on the screen.
def addOutput(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(-1.1, pos), align=TextNode.ALeft, scale = .06)
           
# Function to put information to the screen.
def addInfo (msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(0, -0.8), align=TextNode.ACenter, scale = .06)

class World (DirectObject):
    def __init__(self):
        base.win.setClearColor(Vec4(0,0,0,1))
        base.setBackgroundColor(0,0,0,1)

        # Post the instructions

        self.title = addTitle("Impulse")
        self.inst1 = addInstructions(0.85, "[ESC]: Quit")
        self.inst2 = addInstructions(0.75, "[W]:   Applies +Y force")
        self.inst3 = addInstructions(0.65, "[E]:   Applies -Y force")
        self.inst4 = addInfo ("The force is applied as long as the key is held down, i.e., the rockets are on and the impulse is increasing")
        self.output1 = addOutput(0.30, "")
        self.output2 = addOutput(0.20, "")
        self.output3 = addOutput(0.10, "")
        self.output4 = addOutput(0, "")
       
       
       
        #ambient light
        self.alight = AmbientLight('alight')
        self.alight.setColor(VBase4(0.5,0.5, 0.5, 1))
        self.alnp = render.attachNewNode(self.alight)
        render.setLight(self.alnp)
       
        #light used as a reference point for 0,0,0
        self.plight = PointLight('plight')
        self.plight.setColor(VBase4(1, 1, 1, 1))
        self.plight.setAttenuation(Point3(0,0.2, 0))
        self.plnp = render.attachNewNode(self.plight)
        render.setLight(self.plnp)

        #object to apply a force to
        self.cubeBoy = loader.loadModel('1cube')
       
        #'camera'
        base.oobe()
        base.disableMouse()
        self.camDist =20
        self.camHeight = 10
        base.camera.setPos(0,-self.camDist,self.camHeight)
        base.camera.lookAt(self.cubeBoy)
        base.camera.reparentTo(self.cubeBoy)
           
        #initialize variables
        self.Yforce = 40
        self.thisPImpulse = 0
        self.totalPImpulse = 0
        self.thisNImpulse = 0
        self.totalNImpulse = 0
        self.totalImpulse = 0
        self.currentImpulse = 0
        self.timeForceApplied = 0
       
        #setup a force along Y axis
        self.setupForce()
       
        #keys to add positive and negative impulse
        #
        #as long as the key is held down, the force is applied
        #i.e., the rockets are on
        #impulse = force X time
        #if you increase the amount of time the force is applied
        #you increase the impulse, which is an increase in acceleration
       
        self.accept("w", self.PYimpulse, [1])
        self.accept("w-up", self.PYimpulse, [-1])
        self.accept("e", self.NYimpulse, [1])
        self.accept("e-up", self.NYimpulse, [-1])
        self.accept("escape", sys.exit)

        #task to update output
        taskMgr.add(self.updateT, "updateT")
       
       
    def updateT(self,task):
        self.output1 ['text'] = "Cube Position:           %0.2f" % self.cubeBoy.getY(self.plnp)
        self.output2 ['text'] = "Last applied impulse:    %0.2f" % self.currentImpulse
        self.output3 ['text'] = "Force of 40 applied for: %0.4f seconds " % self.timeForceApplied
        self.output4 ['text'] = "Total impulse:           %0.2f" % self.totalImpulse
        return Task.cont
   
    def PYimpulse(self, arg):
        if arg ==1:
            self.pull = LinearVectorForce(0, self.Yforce, 0)
            self.fn.addForce(self.pull)
            self.an.getPhysical(0).addLinearForce(self.pull)
           
            self.startTime = globalClock.getFrameTime()
        else:
            self.fn.removeForce(self.pull)
            self.an.getPhysical(0).removeLinearForce(self.pull)
           
            self.endTime = globalClock.getFrameTime()
            self.timeForceApplied = self.endTime - self.startTime
 
            self.thisPImpulse = self.timeForceApplied * self.Yforce
            self.currentImpulse = self.thisPImpulse
            self.totalPImpulse+=self.thisPImpulse #ever applied
            self.totalImpulse = self.totalPImpulse + self.totalNImpulse #current net impulse

           
           

       
    def NYimpulse(self, arg):
        if arg ==1:
            self.pull = LinearVectorForce(0, -self.Yforce, 0)
            self.fn.addForce(self.pull)
            self.an.getPhysical(0).addLinearForce(self.pull)
           
            self.startTime = globalClock.getFrameTime()
        else:
            self.fn.removeForce(self.pull)
            self.an.getPhysical(0).removeLinearForce(self.pull)
           
            self.endTime = globalClock.getFrameTime()
            self.timeForceApplied = self.endTime - self.startTime
            self.startTime =0
           
            self.thisNImpulse = self.timeForceApplied * -self.Yforce
            self.currentImpulse = self.thisNImpulse
            self.totalNImpulse+=self.thisNImpulse
            self.totalImpulse = self.totalPImpulse + self.totalNImpulse
       


    def setupForce(self):
        # The physical object, that is, the object which is acted upon by
        # the physics system, needs an ActorNode to define the coordinate
        # system in which it is moving, and to manifest the object's
        # actions. 
        self.an = ActorNode('actor node')
        self.anp = render.attachNewNode(self.an)

        # A model is parented to the ActorNode just so we can see what
        # it's doing.
       
        self.cubeBoy.reparentTo(self.anp)
   
        #light to highlight object
        self.plight1 = PointLight('plight1')
        self.plight1.setColor(VBase4(1, 1, 1, 1))
        self.plight1.setAttenuation(Point3(0,0.2, 0))
        self.plnp1 = self.cubeBoy.attachNewNode(self.plight1)
        self.plnp1.setPos(0,0,1)
        render.setLight(self.plnp1)

        # We need to attach it to a PhysicsManager to do the actual work
        # of moving it around. 
        base.physicsMgr.attachPhysicalNode(self.an)

        # This method must be called to enable the task that computes the
        # PhysicsManager every frame. 
        base.enableParticles()

        # Now apply a force to the ActorNode.  The Force needs a
        # ForceNode, to define the coordinate system in which it is
        # applied.
        self.fn = ForceNode('pull')
        self.fnp = render.attachNewNode(self.fn)
        self.pull = LinearVectorForce(0, 0, 0)
        self.fn.addForce(self.pull)

        # Here we make it a local force that affects only this one
        # ActorNode.  We could make it a global force instead by adding it
        # to base.physicsMgr.
        self.an.getPhysical(0).addLinearForce(self.pull)
   
w=World()
run()
BrianInSuwon
 
Posts: 98
Joined: Wed Jun 18, 2008 1:25 am

Postby koryrok » Tue May 19, 2009 11:38 pm

Very nice, thanks.
koryrok
 
Posts: 23
Joined: Sat May 16, 2009 7:35 am

Postby astelix » Wed May 20, 2009 2:04 am

nice snip thanks for share it - it would be perfect adding a counterforce attenuation (friction) so that would be applied on any kind of vehicle anyhow is a good starting point
My Rig:
P3D 1.7.0@WinXP & Kubuntu 10.04- Athlon 64 5200 X2 ~ Radeon 3200HD (integrated)
User avatar
astelix
 
Posts: 866
Joined: Mon Mar 27, 2006 4:36 pm
Location: Milano, ITA

Postby shazoom » Thu Feb 25, 2010 12:33 am

Thanks for the example!

I got a couple of errors, one was presumable because the models which come with panda have changed (box is clearly too small but it works) and the other due to late initialization of the particle system. Here is a diff (simple fix but maybe someone will find it useful :)):

***************
*** 56,66 ****
self.plight.setAttenuation(Point3(0,0.2, 0))
self.plnp = render.attachNewNode(self.plight)
render.setLight(self.plnp)

#object to apply a force to
! self.cubeBoy = loader.loadModel('1cube')

#'camera'
base.oobe()
base.disableMouse()
self.camDist =20
--- 56,66 ----
self.plight.setAttenuation(Point3(0,0.2, 0))
self.plnp = render.attachNewNode(self.plight)
render.setLight(self.plnp)

#object to apply a force to
! self.cubeBoy = loader.loadModel('box')

#'camera'
base.oobe()
base.disableMouse()
self.camDist =20
***************
*** 151,160 ****
--- 151,164 ----
self.totalImpulse = self.totalPImpulse + self.totalNImpulse



def setupForce(self):
+ # This method must be called to enable the task that computes the
+ # PhysicsManager every frame.
+ base.enableParticles()
+
# The physical object, that is, the object which is acted upon by
# the physics system, needs an ActorNode to define the coordinate
# system in which it is moving, and to manifest the object's
# actions.
self.an = ActorNode('actor node')
***************
*** 175,188 ****

# We need to attach it to a PhysicsManager to do the actual work
# of moving it around.
base.physicsMgr.attachPhysicalNode(self.an)

- # This method must be called to enable the task that computes the
- # PhysicsManager every frame.
- base.enableParticles()
-
# Now apply a force to the ActorNode. The Force needs a
# ForceNode, to define the coordinate system in which it is
# applied.
self.fn = ForceNode('pull')
self.fnp = render.attachNewNode(self.fn)
--- 179,188 ----
shazoom
 
Posts: 17
Joined: Mon Feb 08, 2010 8:29 pm

Postby radu » Thu Feb 25, 2010 5:49 am

good stuff, thanks for sharing. ran it directly with the above diffs and all works fine, except for the little camera that follows the cube, but i can manage that :)
Adopt a Panda (thread)
and get your funky Panda avatar here
User avatar
radu
 
Posts: 153
Joined: Fri Nov 06, 2009 11:09 am
Location: Bucharest

Postby ambyra » Fri Feb 26, 2010 3:26 pm

how do you apply the patch?
ambyra
 
Posts: 130
Joined: Sat Sep 20, 2008 5:47 pm

Postby radu » Fri Feb 26, 2010 6:09 pm

You could probably use some sort of software that can use the diff file posted above, but I did manually. You can see there where the lines that need replacement or adding are and there are (+) and (-) signs where you need to add or cut stuff from the original file.

Anyway, since you probably need the final code, here it is, in case you want to skip patching:

Code: Select all
from direct.directbase.DirectStart import *
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
from direct.gui.OnscreenText import OnscreenText
import sys, math, random

# Function to put title on the screen.
def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1,1,1,1),
            pos=(1.0,0.8), align=TextNode.ARight, scale = .08)

# Function to put instructions on the screen.
def addInstructions(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(-1.23, pos), align=TextNode.ALeft, scale = .07)
           
# Function to put output on the screen.
def addOutput(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(-1.1, pos), align=TextNode.ALeft, scale = .06)
           
# Function to put information to the screen.
def addInfo (msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
            pos=(0, -0.8), align=TextNode.ACenter, scale = .06)

class World (DirectObject):
    def __init__(self):
        base.win.setClearColor(Vec4(0,0,0,1))
        base.setBackgroundColor(0,0,0,1)

        # Post the instructions

        self.title = addTitle("Impulse")
        self.inst1 = addInstructions(0.85, "[ESC]: Quit")
        self.inst2 = addInstructions(0.75, "[W]:   Applies +Y force")
        self.inst3 = addInstructions(0.65, "[E]:   Applies -Y force")
        self.inst4 = addInfo ("The force is applied as long as the key is held down, i.e., the rockets are on and the impulse is increasing")
        self.output1 = addOutput(0.30, "")
        self.output2 = addOutput(0.20, "")
        self.output3 = addOutput(0.10, "")
        self.output4 = addOutput(0, "")
       
       
       
        #ambient light
        self.alight = AmbientLight('alight')
        self.alight.setColor(VBase4(0.5,0.5, 0.5, 1))
        self.alnp = render.attachNewNode(self.alight)
        render.setLight(self.alnp)
       
        #light used as a reference point for 0,0,0
        self.plight = PointLight('plight')
        self.plight.setColor(VBase4(1, 1, 1, 1))
        self.plight.setAttenuation(Point3(0,0.2, 0))
        self.plnp = render.attachNewNode(self.plight)
        render.setLight(self.plnp)

        #object to apply a force to
        self.cubeBoy = loader.loadModel('box')

        #'camera'
        base.oobe()
        base.disableMouse()
        self.camDist =20
        self.camHeight = 10
        base.camera.setPos(0,-self.camDist,self.camHeight)
        base.camera.lookAt(self.cubeBoy)
        base.camera.reparentTo(self.cubeBoy)
           
        #initialize variables
        self.Yforce = 40
        self.thisPImpulse = 0
        self.totalPImpulse = 0
        self.thisNImpulse = 0
        self.totalNImpulse = 0
        self.totalImpulse = 0
        self.currentImpulse = 0
        self.timeForceApplied = 0
       
        #setup a force along Y axis
        self.setupForce()
       
        #keys to add positive and negative impulse
        #
        #as long as the key is held down, the force is applied
        #i.e., the rockets are on
        #impulse = force X time
        #if you increase the amount of time the force is applied
        #you increase the impulse, which is an increase in acceleration
       
        self.accept("w", self.PYimpulse, [1])
        self.accept("w-up", self.PYimpulse, [-1])
        self.accept("e", self.NYimpulse, [1])
        self.accept("e-up", self.NYimpulse, [-1])
        self.accept("escape", sys.exit)

        #task to update output
        taskMgr.add(self.updateT, "updateT")
       
       
    def updateT(self,task):
        self.output1 ['text'] = "Cube Position:           %0.2f" % self.cubeBoy.getY(self.plnp)
        self.output2 ['text'] = "Last applied impulse:    %0.2f" % self.currentImpulse
        self.output3 ['text'] = "Force of 40 applied for: %0.4f seconds " % self.timeForceApplied
        self.output4 ['text'] = "Total impulse:           %0.2f" % self.totalImpulse
        return Task.cont
   
    def PYimpulse(self, arg):
        if arg ==1:
            self.pull = LinearVectorForce(0, self.Yforce, 0)
            self.fn.addForce(self.pull)
            self.an.getPhysical(0).addLinearForce(self.pull)
           
            self.startTime = globalClock.getFrameTime()
        else:
            self.fn.removeForce(self.pull)
            self.an.getPhysical(0).removeLinearForce(self.pull)
           
            self.endTime = globalClock.getFrameTime()
            self.timeForceApplied = self.endTime - self.startTime
 
            self.thisPImpulse = self.timeForceApplied * self.Yforce
            self.currentImpulse = self.thisPImpulse
            self.totalPImpulse+=self.thisPImpulse #ever applied
            self.totalImpulse = self.totalPImpulse + self.totalNImpulse #current net impulse

           
           

       
    def NYimpulse(self, arg):
        if arg ==1:
            self.pull = LinearVectorForce(0, -self.Yforce, 0)
            self.fn.addForce(self.pull)
            self.an.getPhysical(0).addLinearForce(self.pull)
           
            self.startTime = globalClock.getFrameTime()
        else:
            self.fn.removeForce(self.pull)
            self.an.getPhysical(0).removeLinearForce(self.pull)
           
            self.endTime = globalClock.getFrameTime()
            self.timeForceApplied = self.endTime - self.startTime
            self.startTime =0
           
            self.thisNImpulse = self.timeForceApplied * -self.Yforce
            self.currentImpulse = self.thisNImpulse
            self.totalNImpulse+=self.thisNImpulse
            self.totalImpulse = self.totalPImpulse + self.totalNImpulse
       


    def setupForce(self):
        # This method must be called to enable the task that computes the
        # PhysicsManager every frame.
        base.enableParticles()

        # The physical object, that is, the object which is acted upon by
        # the physics system, needs an ActorNode to define the coordinate
        # system in which it is moving, and to manifest the object's
        # actions. 
        self.an = ActorNode('actor node')
        self.anp = render.attachNewNode(self.an)

        # A model is parented to the ActorNode just so we can see what
        # it's doing.
       
        self.cubeBoy.reparentTo(self.anp)
   
        #light to highlight object
        self.plight1 = PointLight('plight1')
        self.plight1.setColor(VBase4(1, 1, 1, 1))
        self.plight1.setAttenuation(Point3(0,0.2, 0))
        self.plnp1 = self.cubeBoy.attachNewNode(self.plight1)
        self.plnp1.setPos(0,0,1)
        render.setLight(self.plnp1)

        # We need to attach it to a PhysicsManager to do the actual work
        # of moving it around. 
        base.physicsMgr.attachPhysicalNode(self.an)

        # Now apply a force to the ActorNode.  The Force needs a
        # ForceNode, to define the coordinate system in which it is
        # applied.
        self.fn = ForceNode('pull')
        self.fnp = render.attachNewNode(self.fn)
        self.pull = LinearVectorForce(0, 0, 0)
        self.fn.addForce(self.pull)

        # Here we make it a local force that affects only this one
        # ActorNode.  We could make it a global force instead by adding it
        # to base.physicsMgr.
        self.an.getPhysical(0).addLinearForce(self.pull)
   
w=World()
run()
Adopt a Panda (thread)
and get your funky Panda avatar here
User avatar
radu
 
Posts: 153
Joined: Fri Nov 06, 2009 11:09 am
Location: Bucharest

Thank you!

Postby huttarl » Tue Apr 26, 2011 4:43 pm

I've been searching for examples of addLinearForce(), and couldn't find any. The official doc pages are woefully skimpy.

Thank you for providing a working example!
huttarl
 
Posts: 4
Joined: Fri Jan 21, 2011 4:29 pm


Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 0 guests