bindAnim() improvements

This commit is contained in:
David Rose 2008-08-28 05:50:12 +00:00
parent 8c92a5e207
commit 63a948749a

View File

@ -1535,12 +1535,13 @@ class Actor(DirectObject, NodePath):
except: except:
return None return None
def getAnimControl(self, animName, partName=None, lodName=None): def getAnimControl(self, animName, partName=None, lodName=None,
allowAsyncBind = True):
""" """
getAnimControl(self, string, string, string="lodRoot") getAnimControl(self, string, string, string="lodRoot")
Search the animControl dictionary indicated by lodName for Search the animControl dictionary indicated by lodName for
a given anim and part. If none specified, try the first part and lod. a given anim and part. If none specified, try the first part and lod.
Return the animControl if present, or None otherwise Return the animControl if present, or None otherwise.
""" """
if not partName: if not partName:
@ -1571,21 +1572,31 @@ class Actor(DirectObject, NodePath):
else: else:
# bind the animation first if we need to # bind the animation first if we need to
if not anim.animControl: if not anim.animControl:
self.__bindAnimToPart(animName, partName, lodName) self.__bindAnimToPart(animName, partName, lodName,
allowAsyncBind = allowAsyncBind)
return anim.animControl return anim.animControl
return None return None
def getAnimControls(self, animName=None, partName=None, lodName=None): def getAnimControls(self, animName=None, partName=None, lodName=None,
allowAsyncBind = True):
"""getAnimControls(self, string, string=None, string=None) """getAnimControls(self, string, string=None, string=None)
Returns a list of the AnimControls that represent the given Returns a list of the AnimControls that represent the given
animation for the given part and the given lod. If animName animation for the given part and the given lod.
is omitted, the currently-playing animation (or all
currently-playing animations) is returned. If partName is If animName is None or omitted, the currently-playing
omitted, all parts are returned (or possibly the one overall animation (or all currently-playing animations) is returned.
Actor part, according to the subpartsComplete flag). If If animName is True, all animations are returned. If animName
lodName is omitted, all LOD's are returned. is a single string name, that particular animation is
returned. If animName is a list of string names, all of the
names animations are returned.
If partName is None or omitted, all parts are returned (or
possibly the one overall Actor part, according to the
subpartsComplete flag).
If lodName is None or omitted, all LOD's are returned.
""" """
if partName == None and self.__subpartsComplete: if partName == None and self.__subpartsComplete:
@ -1642,38 +1653,50 @@ class Actor(DirectObject, NodePath):
else: else:
animDictItems.append((pName, animDict)) animDictItems.append((pName, animDict))
if animName == None: if animName is None:
# get all playing animations # get all playing animations
for thisPart, animDict in animDictItems: for thisPart, animDict in animDictItems:
for anim in animDict.values(): for anim in animDict.values():
if anim.animControl and anim.animControl.isPlaying(): if anim.animControl and anim.animControl.isPlaying():
controls.append(anim.animControl) controls.append(anim.animControl)
else: else:
# get the named animation only. # get the named animation(s) only.
if isinstance(animName, types.StringType):
# A single animName
animNameList = [animName]
else:
# A list of animNames, or True to indicate all anims.
animNameList = animName
for thisPart, animDict in animDictItems: for thisPart, animDict in animDictItems:
anim = animDict.get(animName) names = animNameList
if anim == None and partName != None: if animNameList is True:
for pName in partNameList: names = animDict.keys()
# Maybe it's a subpart that hasn't been bound yet. for animName in names:
subpartDef = self.__subpartDict.get(pName) anim = animDict.get(animName)
if subpartDef: if anim == None and partName != None:
truePartName = subpartDef.truePartName for pName in partNameList:
anim = partDict[truePartName].get(animName) # Maybe it's a subpart that hasn't been bound yet.
if anim: subpartDef = self.__subpartDict.get(pName)
anim = anim.makeCopy() if subpartDef:
animDict[animName] = anim truePartName = subpartDef.truePartName
anim = partDict[truePartName].get(animName)
if anim:
anim = anim.makeCopy()
animDict[animName] = anim
if anim == None: if anim == None:
# anim was not present # anim was not present
assert Actor.notify.debug("couldn't find anim: %s" % (animName)) assert Actor.notify.debug("couldn't find anim: %s" % (animName))
pass pass
else: else:
# bind the animation first if we need to # bind the animation first if we need to
animControl = anim.animControl animControl = anim.animControl
if animControl == None: if animControl == None:
animControl = self.__bindAnimToPart(animName, thisPart, lodName) animControl = self.__bindAnimToPart(
if animControl: animName, thisPart, lodName,
controls.append(animControl) allowAsyncBind = allowAsyncBind)
if animControl:
controls.append(animControl)
return controls return controls
@ -2027,65 +2050,45 @@ class Actor(DirectObject, NodePath):
except: except:
return return
def bindAnim(self, animName, partName="modelRoot", lodName="lodRoot"): def bindAnim(self, animName, partName = None, lodName = None,
"""bindAnim(self, string, string='modelRoot', string='lodRoot') allowAsyncBind = False):
Bind the named animation to the named part and lod
""" """
if lodName == None or self.mergeLODBundles: Binds the named animation to the named part and/or lod. If
lodNames = self.__animControlDict.keys() allowAsyncBind is False, this guarantees that the animation is
else: bound immediately--the animation is never bound in a
lodNames = [lodName] sub-thread; it will be loaded and bound in the main thread, so
it will be available by the time this method returns.
# loop over all lods The parameters are the same as that for getAnimControls(). In
for thisLod in lodNames: fact, this method is a thin wrapper around that other method.
if partName == None:
partNames = self.__partBundleDict[thisLod].keys()
else:
partNames = [partName]
# loop over all parts
for thisPart in partNames:
ac = self.__bindAnimToPart(animName, thisPart, thisLod)
Use this method if you need to ensure that an animation is
available before you start to play it, and you don't mind
holding up the render for a frame or two until the animation
is available.
"""
self.getAnimControls(animName = animName, partName = partName,
lodName = lodName,
allowAsyncBind = allowAsyncBind)
def bindAllAnims(self): def bindAllAnims(self, allowAsyncBind = False):
"""Loads and binds all animations that have been defined for """Loads and binds all animations that have been defined for
the Actor. """ the Actor. """
self.getAnimControls(animName = True, allowAsyncBind = allowAsyncBind)
for lodName, partDict in self.__animControlDict.items(): def __bindAnimToPart(self, animName, partName, lodName,
# Now, build the list of partNames and the corresponding allowAsyncBind = True):
# animDicts.
for thisPart, animDict in partDict.items():
if animDict == None:
# Maybe it's a subpart that hasn't been bound yet.
subpartDef = self.__subpartDict.get(pName)
if subpartDef:
animDict = {}
partDict[pName] = animDict
for animName, anim in animDict.items():
if anim == None and partName != None:
for pName in partNameList:
# Maybe it's a subpart that hasn't been bound yet.
subpartDef = self.__subpartDict.get(pName)
if subpartDef:
truePartName = subpartDef.truePartName
anim = partDict[truePartName].get(animName)
if anim:
anim = anim.makeCopy()
animDict[animName] = anim
if anim.animControl == None:
self.__bindAnimToPart(animName, thisPart, lodName)
def __bindAnimToPart(self, animName, partName, lodName):
""" """
for internal use only! Binds the named animation to the named part/lod and returns
the associated animControl. The animation is loaded and bound
in a sub-thread, if allowAsyncBind is True,
self.allowAsyncBind is True, threading is enabled, and the
animation has a preload table generated for it (e.g. via
"egg-optchar -preload"). Even though the animation may or may
not be yet bound at the time this function returns, a usable
animControl is returned, or None if the animation could not be
bound.
""" """
# Temporary check for old Pandas.
if not hasattr(PartBundle, 'loadBindAnim'):
return self.__tempHackDoNotUseBindAnimToPart(animName, partName, lodName)
# make sure this anim is in the dict # make sure this anim is in the dict
subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName)) subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
@ -2120,7 +2123,7 @@ class Actor(DirectObject, NodePath):
# will still return a usable AnimControl. # will still return a usable AnimControl.
animControl = bundle.loadBindAnim( animControl = bundle.loadBindAnim(
loader.loader, Filename(anim.filename), -1, loader.loader, Filename(anim.filename), -1,
subpartDef.subset, self.allowAsyncBind) subpartDef.subset, allowAsyncBind and self.allowAsyncBind)
if not animControl: if not animControl:
# Couldn't bind. (This implies the binding operation was # Couldn't bind. (This implies the binding operation was
@ -2133,69 +2136,6 @@ class Actor(DirectObject, NodePath):
(animName, partName, lodName)) (animName, partName, lodName))
return animControl return animControl
def __tempHackDoNotUseBindAnimToPart(self, animName, partName, lodName):
""" This method exists only temporarily to support old Pandas
that don't yet have PartBundle.loadBindAnim(). This method is
the old implementation of __bindAnimToPart(), from before we
added asynchronous support. """
# make sure this anim is in the dict
subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
partDict = self.__animControlDict[lodName]
animDict = partDict.get(partName)
if animDict == None:
# It must be a subpart that hasn't been bound yet.
animDict = {}
partDict[partName] = animDict
anim = animDict.get(animName)
if anim == None:
# It must be a subpart that hasn't been bound yet.
anim = partDict[subpartDef.truePartName].get(animName)
anim = anim.makeCopy()
animDict[animName] = anim
if anim == None:
Actor.notify.error("actor has no animation %s", animName)
# only bind if not already bound!
if anim.animControl:
return anim.animControl
if self.mergeLODBundles:
bundle = self.__commonBundleHandles[subpartDef.truePartName].getBundle()
else:
bundle = self.__partBundleDict[lodName][subpartDef.truePartName].getBundle()
# fetch a copy from the modelPool, or if we weren't careful
# enough to preload, fetch from disk
animPath = anim.filename
loaderOptions = self.animLoaderOptions
if not self.__autoCopy:
# If copy = 0, then we should always hit the disk.
loaderOptions = LoaderOptions(loaderOptions)
loaderOptions.setFlags(loaderOptions.getFlags() & ~LoaderOptions.LFNoRamCache)
animNode = loader.loadModel(animPath, loaderOptions = loaderOptions)
if animNode == None:
return None
animBundle = (animNode.find("**/+AnimBundleNode").node()).getBundle()
animModel = animNode.node()
# bind anim
animControl = bundle.bindAnim(animBundle, -1, subpartDef.subset)
if (animControl == None):
Actor.notify.error("Null AnimControl: %s" % (animName))
else:
# store the animControl
anim.animControl = animControl
anim.animModel = animModel
assert Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
(animName, partName, lodName))
return animControl
def __copyPartBundles(self, other): def __copyPartBundles(self, other):
"""__copyPartBundles(self, Actor) """__copyPartBundles(self, Actor)
Copy the part bundle dictionary from another actor as this Copy the part bundle dictionary from another actor as this