make ActorInterval more efficient

This commit is contained in:
David Rose 2002-07-30 17:48:35 +00:00
parent 82158d1b8b
commit e0291f7303
2 changed files with 180 additions and 271 deletions

View File

@ -442,175 +442,119 @@ class Actor(PandaObject, NodePath):
def getFrameRate(self, animName=None, partName=None): def getFrameRate(self, animName=None, partName=None):
"""getFrameRate(self, string, string=None) """getFrameRate(self, string, string=None)
Return duration of given anim name and given part. Return actual frame rate of given anim name and given part.
If no anim specified, use the currently playing anim. If no anim specified, use the currently playing anim.
If no part specified, return anim durations of first part. If no part specified, return anim durations of first part.
NOTE: returns info only for the first LOD""" NOTE: returns info only for an arbitrary LOD"""
# use the first LOD
lodName = self.__animControlDict.keys()[0] lodName = self.__animControlDict.keys()[0]
controls = self.getAnimControls(animName, partName)
if (partName == None): if len(controls) == 0:
partName = self.__animControlDict[lodName].keys()[0]
if (animName==None):
animName = self.getCurrentAnim(partName)
# get duration for named part only
if (self.__animControlDict[lodName].has_key(partName)):
animControl = self.getAnimControl(animName, partName, lodName)
if (animControl != None):
return animControl.getFrameRate()
else:
Actor.notify.warning("no part named %s" % (partName))
return None return None
return controls[0].getFrameRate()
def getBaseFrameRate(self, animName=None, partName=None): def getBaseFrameRate(self, animName=None, partName=None):
"""getBaseFrameRate(self, string, string=None) """getBaseFrameRate(self, string, string=None)
Return duration of given anim name and given part, unmodified Return frame rate of given anim name and given part, unmodified
by any play rate in effect. by any play rate in effect.
""" """
lodName = self.__animControlDict.keys()[0] lodName = self.__animControlDict.keys()[0]
controls = self.getAnimControls(animName, partName)
if (partName == None): if len(controls) == 0:
partName = self.__animControlDict[lodName].keys()[0]
if (animName==None):
animName = self.getCurrentAnim(partName)
# get duration for named part only
if (self.__animControlDict[lodName].has_key(partName)):
animControl = self.getAnimControl(animName, partName, lodName)
if (animControl != None):
return animControl.getAnim().getBaseFrameRate()
else:
Actor.notify.warning("no part named %s" % (partName))
return None return None
return controls[0].getAnim().getBaseFrameRate()
def getPlayRate(self, animName=None, partName=None): def getPlayRate(self, animName=None, partName=None):
"""getPlayRate(self, string=None, string=None) """getPlayRate(self, string=None, string=None)
Return the play rate of given anim for a given part. Return the play rate of given anim for a given part.
If no part is given, assume first part in dictionary. If no part is given, assume first part in dictionary.
If no anim is given, find the current anim for the part. If no anim is given, find the current anim for the part.
NOTE: Returns info only for the first LOD""" NOTE: Returns info only for an arbitrary LOD"""
# use the first lod # use the first lod
lodName = self.__animControlDict.keys()[0] lodName = self.__animControlDict.keys()[0]
controls = self.getAnimControls(animName, partName)
if (partName==None): if len(controls) == 0:
partName = self.__animControlDict[lodName].keys()[0]
if (animName==None):
animName = self.getCurrentAnim(partName)
animControl = self.getAnimControl(animName, partName, lodName)
if (animControl != None):
return animControl.getPlayRate()
else:
return None return None
return controls[0].getPlayRate()
def setPlayRate(self, rate, animName=None, partName=None): def setPlayRate(self, rate, animName=None, partName=None):
"""getPlayRate(self, float, string=None, string=None) """getPlayRate(self, float, string=None, string=None)
Set the play rate of given anim for a given part. Set the play rate of given anim for a given part.
If no part is given, set for all parts in dictionary. If no part is given, set for all parts in dictionary.
If no anim is given, find the current anim for the part. If no anim is given, find the current anim for the part.
NOTE: sets play rate on all LODs""" NOTE: sets play rate on all LODs"""
# make a list of partNames for loop below for control in self.getAnimControls(animName, partName):
for lodName in self.__animControlDict.keys(): control.setPlayRate(rate)
animControlDict = self.__animControlDict[lodName]
if (partName==None):
partNames = animControlDict.keys()
else:
partNames = []
partNames.append(partName)
# for each part in list, set play rate on given or current anim
for thisPart in partNames:
if (animName==None):
thisAnim = self.getCurrentAnim(thisPart)
else:
thisAnim = animName
animControl = self.getAnimControl(thisAnim, thisPart, lodName)
if (animControl != None):
animControl.setPlayRate(rate)
def getDuration(self, animName=None, partName=None): def getDuration(self, animName=None, partName=None):
"""getDuration(self, string, string=None) """getDuration(self, string, string=None)
Return duration of given anim name and given part. Return duration of given anim name and given part.
If no anim specified, use the currently playing anim. If no anim specified, use the currently playing anim.
If no part specified, return anim duration of first part. If no part specified, return anim duration of first part.
NOTE: returns info for first LOD only""" NOTE: returns info for arbitrary LOD"""
lodName = self.__animControlDict.keys()[0] lodName = self.__animControlDict.keys()[0]
if (partName == None): controls = self.getAnimControls(animName, partName)
partName = self.__animControlDict[lodName].keys()[0] if len(controls) == 0:
if (animName==None):
animName = self.getCurrentAnim(partName)
# get duration for named part only
if (self.__animControlDict[lodName].has_key(partName)):
animControl = self.getAnimControl(animName, partName, lodName)
if (animControl != None):
return (animControl.getNumFrames() / \
animControl.getFrameRate())
else:
Actor.notify.warning("no part named %s" % (partName))
return None return None
animControl = controls[0]
return (animControl.getNumFrames() / animControl.getFrameRate())
def getNumFrames(self, animName=None, partName=None): def getNumFrames(self, animName=None, partName=None):
""" getNumFrames(animName, partName) """ getNumFrames(animName, partName)
""" """
lodName = self.__animControlDict.keys()[0] lodName = self.__animControlDict.keys()[0]
if (partName == None): controls = self.getAnimControls(animName, partName)
partName = self.__animControlDict[lodName].keys()[0] if len(controls) == 0:
if (animName == None): return None
animName = self.getCurrentAnim(partName)
if (self.__animControlDict[lodName].has_key(partName)): return controls[0].getNumFrames()
animControl = self.getAnimControl(animName, partName, lodName)
if (animControl != None):
return animControl.getNumFrames()
else:
Actor.notify.error('no anim control!')
else:
Actor.notify.warning('no part named: %s' % (partName))
def getCurrentAnim(self, partName=None): def getCurrentAnim(self, partName=None):
"""getCurrentAnim(self, string=None) """getCurrentAnim(self, string=None)
Return the anim current playing on the actor. If part not Return the anim currently playing on the actor. If part not
specified return current anim of first part in dictionary. specified return current anim of an arbitrary part in dictionary.
NOTE: only returns info for the first LOD""" NOTE: only returns info for an arbitrary LOD"""
lodName = self.__animControlDict.keys()[0] lodName, animControlDict = self.__animControlDict.items()[0]
if (partName==None): if partName == None:
partName = self.__animControlDict[lodName].keys()[0] partName, animDict = animControlDict.items()[0]
else:
animDict = animControlDict.get(partName)
if animDict == None:
# part was not present
Actor.notify.warning("couldn't find part: %s" % (partName))
return None
# loop through all anims for named part and find if any are playing # loop through all anims for named part and find if any are playing
if (self.__animControlDict[lodName].has_key(partName)): for animName, anim in animDict.items():
for animName in self.__animControlDict[lodName][partName].keys(): if isinstance(anim[1], AnimControl) and anim[1].isPlaying():
if (self.getAnimControl(animName, partName, lodName).isPlaying()):
return animName return animName
else:
Actor.notify.warning("no part named %s" % (partName))
# we must have found none, or gotten an error # we must have found none, or gotten an error
return None return None
def getCurrentFrame(self, animName=None, partName=None): def getCurrentFrame(self, animName=None, partName=None):
"""getCurrentAnim(self, string=None) """getCurrentAnim(self, string=None)
Return the anim current playing on the actor. If part not Return the current frame number of the anim current playing on
specified return current anim of first part in dictionary. the actor. If part not specified return current anim of first
NOTE: only returns info for the first LOD""" part in dictionary.
if animName == None: NOTE: only returns info for an arbitrary LOD"""
animName = self.getCurrentAnim(partName) lodName, animControlDict = self.__animControlDict.items()[0]
if partName == None:
lodName = self.__animControlDict.keys()[0] partName, animDict = animControlDict.items()[0]
if (partName==None):
partName = self.__animControlDict[lodName].keys()[0]
# check the part name
if (self.__animControlDict[lodName].has_key(partName)):
animControl = self.getAnimControl(animName, partName, lodName)
return animControl.getFrame()
else: else:
Actor.notify.warning("no part named %s" % (partName)) animDict = animControlDict.get(partName)
if animDict == None:
# part was not present
Actor.notify.warning("couldn't find part: %s" % (partName))
return None
# loop through all anims for named part and find if any are playing
for animName, anim in animDict.items():
if isinstance(anim[1], AnimControl) and anim[1].isPlaying():
return anim[1].getFrame()
# we must have found none, or gotten an error # we must have found none, or gotten an error
return None return None
@ -941,53 +885,20 @@ class Actor(PandaObject, NodePath):
Stop named animation on the given part of the actor. Stop named animation on the given part of the actor.
If no name specified then stop all animations on the actor. If no name specified then stop all animations on the actor.
NOTE: stops all LODs""" NOTE: stops all LODs"""
for thisLod in self.__animControlDict.keys(): for control in self.getAnimControls(animName, partName):
animControlDict = self.__animControlDict[thisLod] control.stop()
# assemble lists of parts and anims
if (partName == None):
partNames = animControlDict.keys()
else:
partNames = [partName]
if (animName == None):
animNames = animControlDict[partNames[0]].keys()
else:
animNames = [animName]
# loop over all parts
for thisPart in partNames:
for thisAnim in animNames:
# only stop if it's bound
if isinstance(animControlDict[thisPart][thisAnim][1],
AnimControl):
animControlDict[thisPart][thisAnim][1].stop()
def play(self, animName, partName=None, fromFrame=None, toFrame=None): def play(self, animName, partName=None, fromFrame=None, toFrame=None):
"""play(self, string, string=None) """play(self, string, string=None)
Play the given animation on the given part of the actor. Play the given animation on the given part of the actor.
If no part is specified, try to play on all parts. NOTE: If no part is specified, try to play on all parts. NOTE:
plays over ALL LODs""" plays over ALL LODs"""
for thisLod in self.__animControlDict.keys(): if fromFrame == None:
animControlDict = self.__animControlDict[thisLod] for control in self.getAnimControls(animName, partName):
if (partName == None): control.play(restart)
# play all parts
for thisPart in animControlDict.keys():
animControl = self.getAnimControl(animName, thisPart,
thisLod)
if (animControl != None):
if (fromFrame == None):
animControl.play()
else: else:
animControl.play(fromFrame, toFrame) for control in self.getAnimControls(animName, partName):
control.play(restart, fromFrame, toFrame)
else:
animControl = self.getAnimControl(animName, partName,
thisLod)
if (animControl != None):
if (fromFrame == None):
animControl.play()
else:
animControl.play(fromFrame, toFrame)
def loop(self, animName, restart=1, partName=None, def loop(self, animName, restart=1, partName=None,
fromFrame=None, toFrame=None): fromFrame=None, toFrame=None):
@ -996,27 +907,12 @@ class Actor(PandaObject, NodePath):
restarting at zero frame if requested. If no part name restarting at zero frame if requested. If no part name
is given then try to loop on all parts. NOTE: loops on is given then try to loop on all parts. NOTE: loops on
all LOD's""" all LOD's"""
for thisLod in self.__animControlDict.keys(): if fromFrame == None:
animControlDict = self.__animControlDict[thisLod] for control in self.getAnimControls(animName, partName):
if (partName == None): control.loop(restart)
# loop all parts
for thisPart in animControlDict.keys():
animControl = self.getAnimControl(animName, thisPart,
thisLod)
if (animControl != None):
if (fromFrame == None):
animControl.loop(restart)
else: else:
animControl.loop(restart, fromFrame, toFrame) for control in self.getAnimControls(animName, partName):
else: control.loop(restart, fromFrame, toFrame)
# loop a specific part
animControl = self.getAnimControl(animName, partName,
thisLod)
if (animControl != None):
if (fromFrame == None):
animControl.loop(restart)
else:
animControl.loop(restart, fromFrame, toFrame)
def pingpong(self, animName, fromFrame, toFrame, restart=1, partName=None): def pingpong(self, animName, fromFrame, toFrame, restart=1, partName=None):
"""pingpong(self, string, fromFrame, toFrame, int=1, string=None) """pingpong(self, string, fromFrame, toFrame, int=1, string=None)
@ -1024,42 +920,16 @@ class Actor(PandaObject, NodePath):
restarting at zero frame if requested. If no part name restarting at zero frame if requested. If no part name
is given then try to loop on all parts. NOTE: loops on is given then try to loop on all parts. NOTE: loops on
all LOD's""" all LOD's"""
for thisLod in self.__animControlDict.keys(): for control in self.getAnimControls(animName, partName):
animControlDict = self.__animControlDict[thisLod] control.pingpong(restart, fromFrame, toFrame)
if (partName == None):
# loop all parts
for thisPart in animControlDict.keys():
animControl = self.getAnimControl(animName, thisPart,
thisLod)
if (animControl != None):
animControl.pingpong(restart, fromFrame, toFrame)
else:
# loop a specific part
animControl = self.getAnimControl(animName, partName,
thisLod)
if (animControl != None):
animControl.pingpong(restart, fromFrame, toFrame)
def pose(self, animName, frame, partName=None): def pose(self, animName, frame, partName=None, lodName=None):
"""pose(self, string, int, string=None) """pose(self, string, int, string=None)
Pose the actor in position found at given frame in the specified Pose the actor in position found at given frame in the specified
animation for the specified part. If no part is specified attempt animation for the specified part. If no part is specified attempt
to apply pose to all parts. NOTE: poses all LODs""" to apply pose to all parts."""
for thisLod in self.__animControlDict.keys(): for control in self.getAnimControls(animName, partName, lodName):
animControlDict = self.__animControlDict[thisLod] control.pose(frame)
if (partName==None):
# pose all parts
for thisPart in animControlDict.keys():
animControl = self.getAnimControl(animName, thisPart,
thisLod)
if (animControl != None):
animControl.pose(frame)
else:
# pose a specific part
animControl = self.getAnimControl(animName, partName,
thisLod)
if (animControl != None):
animControl.pose(frame)
def enableBlend(self, partName = None): def enableBlend(self, partName = None):
"""Enables blending of multiple animations simultaneously. """Enables blending of multiple animations simultaneously.
@ -1106,32 +976,8 @@ class Actor(PandaObject, NodePath):
animations; it only makes sense to call this after a previous animations; it only makes sense to call this after a previous
call to enableBlend(). call to enableBlend().
""" """
if lodName == None: for control in self.getAnimControls(animName, partName, lodName):
for lodName, controlDict in self.__animControlDict.items(): control.getPart().setControlEffect(control, effect)
if partName == None:
for part in controlDict.keys():
ac = self.getAnimControl(animName, part, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
ac = self.getAnimControl(animName, partName, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
if partName == None:
controlDict = self.__animControlDict.get(lodName)
if controlDict != None:
for part in controlDict.keys():
ac = self.getAnimControl(animName, part, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
Actor.notify.warning("couldn't find lod: %s" % (lodName))
else:
ac = self.getAnimControl(animName, partName, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
def getAnimControl(self, animName, partName, lodName="lodRoot"): def getAnimControl(self, animName, partName, lodName="lodRoot"):
"""getAnimControl(self, string, string, string="lodRoot") """getAnimControl(self, string, string, string="lodRoot")
@ -1139,26 +985,90 @@ class Actor(PandaObject, NodePath):
a given anim and part. Return the animControl if present, a given anim and part. Return the animControl if present,
or None otherwise or None otherwise
""" """
if (self.__animControlDict.has_key(lodName)): animControlDict = self.__animControlDict.get(lodName)
animControlDict = self.__animControlDict[lodName] # if this assertion fails, named lod was not present
if (animControlDict.has_key(partName)): assert(animControlDict != None)
if (animControlDict[partName].has_key(animName)):
# make sure the anim is bound first animDict = animControlDict.get(partName)
self.bindAnim(animName, partName, lodName) if animDict == None:
return animControlDict[partName][animName][1]
else:
# anim was not present
Actor.notify.warning("couldn't find anim: %s" % (animName))
else:
# part was not present # part was not present
Actor.notify.warning("couldn't find part: %s" % (partName)) Actor.notify.warning("couldn't find part: %s" % (partName))
else: else:
# lod was not present anim = animDict.get(animName)
Actor.notify.warning("couldn't find lod: %s" % (lodName)) if anim == None:
assert(0) # anim was not present
Actor.notify.warning("couldn't find anim: %s" % (animName))
else:
# bind the animation first if we need to
if not isinstance(anim[1], AnimControl):
self.__bindAnimToPart(animName, partName, lodName)
return anim[1]
return None return None
def getAnimControls(self, animName=None, partName=None, lodName=None):
"""getAnimControls(self, string, string=None, string=None)
Returns a list of the AnimControls that represent the given
animation for the given part and the given lod. If animName
is omitted, the currently-playing animation (or all
currently-playing animations) is returned. If partName is
omitted, all parts are returned. If lodName is omitted, all
LOD's are returned.
"""
controls = []
# build list of lodNames and corresponding animControlDicts
# requested.
if lodName == None:
# Get all LOD's
animControlDictItems = self.__animControlDict.items()
else:
animControlDict = self.__animControlDict.get(lodName)
if animControlDict == None:
Actor.notify.warning("couldn't find lod: %s" % (lodName))
animControlDictItems = []
else:
animControlDictItems = [(lodName, animControlDict)]
for lodName, animControlDict in animControlDictItems:
# Now, build the list of partNames and the corresponding
# animDicts.
if partName == None:
# Get all parts
animDictItems = animControlDict.items()
else:
# Get a specific part
animDict = animControlDict.get(partName)
if animDict == None:
# part was not present
Actor.notify.warning("couldn't find part: %s" % (partName))
animDictItems = []
else:
animDictItems = [(partName, animDict)]
if animName == None:
# get all playing animations
for thisPart, animDict in animDictItems:
for anim in animDict.values():
if isinstance(anim[1], AnimControl) and anim[1].isPlaying():
controls.append(anim[1])
else:
# get the named animation only.
for thisPart, animDict in animDictItems:
anim = animDict.get(animName)
if anim == None:
# anim was not present
Actor.notify.warning("couldn't find anim: %s" % (animName))
else:
# bind the animation first if we need to
if not isinstance(anim[1], AnimControl):
if self.__bindAnimToPart(animName, thisPart, lodName):
controls.append(anim[1])
else:
controls.append(anim[1])
return controls
def loadModel(self, modelPath, partName="modelRoot", lodName="lodRoot", copy = 1): def loadModel(self, modelPath, partName="modelRoot", lodName="lodRoot", copy = 1):
"""loadModel(self, string, string="modelRoot", string="lodRoot", """loadModel(self, string, string="modelRoot", string="lodRoot",
@ -1327,6 +1237,8 @@ class Actor(PandaObject, NodePath):
# enough to preload, fetch from disk :( # enough to preload, fetch from disk :(
animPath = self.__animControlDict[lodName][partName][animName][0] animPath = self.__animControlDict[lodName][partName][animName][0]
anim = loader.loadModelOnce(animPath) anim = loader.loadModelOnce(animPath)
if anim == None:
return None
animBundle = \ animBundle = \
(anim.find("**/+AnimBundleNode").node()).getBundle() (anim.find("**/+AnimBundleNode").node()).getBundle()

View File

@ -32,9 +32,11 @@ class ActorInterval(Interval.Interval):
# Record class specific variables # Record class specific variables
self.actor = actor self.actor = actor
self.animName = animName self.animName = animName
self.controls = self.actor.getAnimControls(self.animName)
assert(len(self.controls) > 0)
self.loopAnim = loop self.loopAnim = loop
self.frameRate = self.actor.getBaseFrameRate(self.animName) * playRate self.frameRate = self.controls[0].getAnim().getBaseFrameRate() * playRate
self.numFrames = self.actor.getNumFrames(self.animName) self.numFrames = self.controls[0].getNumFrames()
# Compute start time # Compute start time
self.startTime = startTime self.startTime = startTime
# If no name specified, use id as name # If no name specified, use id as name
@ -85,10 +87,13 @@ class ActorInterval(Interval.Interval):
# Calc integer frame number # Calc integer frame number
frame = self.calcFrame(t) frame = self.calcFrame(t)
# Pose anim # Pose anim
self.actor.pose(self.animName, frame)
# Print debug information # We use our pre-computed list of animControls for
self.notify.debug('goToT() - %s pose to frame: %d' % # efficiency's sake, rather than going through the relatively
(self.name,frame)) # expensive Actor interface every frame.
for control in self.controls:
control.pose(frame)
return frame return frame
def updateFunc(self, t, event=Interval.IVAL_NONE): def updateFunc(self, t, event=Interval.IVAL_NONE):
@ -100,15 +105,11 @@ class ActorInterval(Interval.Interval):
return return
# Update animation based upon current time # Update animation based upon current time
# Pose or stop anim # Pose or stop anim
if (t >= self.getDuration()): if (t >= self.duration):
self.actor.stop(self.animName) self.actor.stop(self.animName)
frame = self.goToT(self.getDuration()) frame = self.goToT(self.duration)
if self.loopAnim: if self.loopAnim:
self.ignore(self.stopEvent) self.ignore(self.stopEvent)
# Print debug information
self.notify.debug(
'updateFunc() - %s stoping at frame: ' % self.name +
'%d Num frames: %d' % (frame, self.numFrames))
elif self.loopAnim == 1: elif self.loopAnim == 1:
if event == Interval.IVAL_INIT: if event == Interval.IVAL_INIT:
# Pose anim # Pose anim
@ -116,10 +117,6 @@ class ActorInterval(Interval.Interval):
# And start loop, restart flag says continue from current frame # And start loop, restart flag says continue from current frame
self.actor.loop(self.animName, restart=0) self.actor.loop(self.animName, restart=0)
self.acceptOnce(self.stopEvent, self.actor.stop) self.acceptOnce(self.stopEvent, self.actor.stop)
# Print debug information
self.notify.debug(
'updateFunc() - IVAL_INIT %s looping anim' %
self.name)
else: else:
# Pose anim # Pose anim
self.goToT(t) self.goToT(t)
@ -162,7 +159,7 @@ class LerpAnimInterval(Interval.Interval):
return return
# First, normalize t into the range 0 .. 1, and apply the blendType. # First, normalize t into the range 0 .. 1, and apply the blendType.
t = self.blendType(float(t) / self.getDuration()) t = self.blendType(float(t) / self.duration)
# Then compute the current weight based on the time elapsed so far. # Then compute the current weight based on the time elapsed so far.
w = self.startWeight + t * self.deltaWeight w = self.startWeight + t * self.deltaWeight