rework controlJoint() and freezeJoint() to reduce memory footprint

This commit is contained in:
David Rose 2007-07-03 17:09:56 +00:00
parent af9e6e0510
commit 8488c504d6
1 changed files with 93 additions and 76 deletions

View File

@ -158,8 +158,6 @@ class Actor(DirectObject, NodePath):
self.__subpartDict = {}
self.__sortedLODNames = []
self.__animControlDict = {}
self.__controlJoints = {}
self.__frozenJoints = {}
self.__subpartsComplete = False
@ -444,7 +442,7 @@ class Actor(DirectObject, NodePath):
Actor cleanup function
"""
self.stop(None)
self.__frozenJoints = {}
self.clearPythonData()
self.flush()
if(self.__geomNode):
self.__geomNode.removeNode()
@ -462,7 +460,6 @@ class Actor(DirectObject, NodePath):
self.__subpartDict = {}
self.__sortedLODNames = []
self.__animControlDict = {}
self.__controlJoints = {}
def flush(self):
"""
@ -1037,62 +1034,78 @@ class Actor(DirectObject, NodePath):
def controlJoint(self, node, partName, jointName, lodName="lodRoot"):
"""controlJoint(self, NodePath, string, string, key="lodRoot")
The converse of exposeJoint: this associates the joint with
"""The converse of exposeJoint: this associates the joint with
the indicated node, so that the joint transform will be copied
from the node to the joint each frame. This can be used for
programmer animation of a particular joint at runtime.
This must be called before any animations are played. Once an
animation has been loaded and bound to the character, it will
be too late to add a new control during that animation.
"""
partBundleDict=self.__partBundleDict.get(lodName)
The parameter node should be the NodePath for the node whose
transform will animate the joint. If node is None, a new node
will automatically be created and loaded with the joint's
initial transform. In either case, the node used will be
returned.
if not partBundleDict:
Actor.notify.warning("no lod named: %s" % (lodName))
return None
It used to be necessary to call this before any animations
have been loaded and bound, but that is no longer so.
"""
# Temporary condition for old Pandas.
if not hasattr(PartBundle, 'controlJoint'):
if node == None:
node = self.attachNewNode(jointName)
return node
subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
partDef = partBundleDict.get(subpartDef.truePartName)
if partDef:
bundle = partDef.partBundle
bundle.setModifiesAnimBundles(1)
else:
Actor.notify.warning("no part named %s!" % (partName))
return None
trueName = subpartDef.truePartName
anyGood = False
for bundleDict in self.__partBundleDict.values():
bundle = bundleDict[trueName].partBundle
if node == None:
node = self.attachNewNode(jointName)
joint = bundle.findChild(jointName)
if joint and joint.getType().isDerivedFrom(MovingPartMatrix.getClassType()):
node.setMat(joint.getInitialValue())
joint = bundle.findChild(jointName)
if joint == None:
Actor.notify.warning("no joint named %s!" % (jointName))
return None
if bundle.controlJoint(jointName, node.node()):
anyGood = True
if node == None:
node = self.attachNewNode(jointName)
if joint.getType().isDerivedFrom(MovingPartMatrix.getClassType()):
node.setMat(joint.getInitialValue())
# Store a dictionary of jointName: node to list the controls
# requested for joints. The controls will actually be applied
# later, when we load up the animations in bindAnim().
if self.__controlJoints.has_key(bundle.this):
self.__controlJoints[bundle.this][jointName] = node
else:
self.__controlJoints[bundle.this] = { jointName: node }
if not anyGood:
self.notify.warning("Cannot control joint %s" % (jointName))
return node
#This is an alternate method to control joints, which can be copied
#This function is optimal in a non control jointed actor
def freezeJoint(self, partName, jointName, pos=Vec3(0,0,0), hpr=Vec3(0,0,0), scale=Vec3(1,1,1)):
transform=Mat4(TransformState.makePosHprScale(pos,hpr,scale).getMat())
#trueName = self.__subpartDict[partName].truePartName
def freezeJoint(self, partName, jointName, transform = None,
pos=Vec3(0,0,0), hpr=Vec3(0,0,0), scale=Vec3(1,1,1)):
"""Similar to controlJoint, but the transform assigned is
static, and may not be animated at runtime (without another
subsequent call to freezeJoint). This is slightly more
optimal than controlJoint() for cases in which the transform
is not intended to be animated during the lifetime of the
Actor. """
# Temporary condition for old Pandas.
if not hasattr(PartBundle, 'freezeJoint'):
return
if transform == None:
transform = TransformState.makePosHprScale(pos, hpr, scale)
subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
trueName = subpartDef.truePartName
anyGood = False
for bundleDict in self.__partBundleDict.values():
if bundleDict[trueName].partBundle.freezeJoint(jointName, transform):
anyGood = True
if not anyGood:
self.notify.warning("Cannot freeze joint %s" % (jointName))
def releaseJoint(self, partName, jointName):
"""Undoes a previous call to controlJoint() or freezeJoint()
and restores the named joint to its normal animation. """
subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
trueName = subpartDef.truePartName
for bundleDict in self.__partBundleDict.values():
bundleDict[trueName].partBundle.freezeJoint(jointName, transform)
bundleDict[trueName].partBundle.releaseJoint(jointName)
def instance(self, path, partName, jointName, lodName="lodRoot"):
"""instance(self, NodePath, string, string, key="lodRoot")
@ -1699,7 +1712,6 @@ class Actor(DirectObject, NodePath):
# A model loaded from disk will always have just one bundle.
assert(node.getNumBundles() == 1)
bundle = node.getBundle(0)
self.__frozenJoints[bundle.this]={}
if (needsDict):
bundleDict[partName] = Actor.PartDef(bundleNP, bundle, model.node())
self.__partBundleDict[lodName] = bundleDict
@ -1919,6 +1931,36 @@ class Actor(DirectObject, NodePath):
ac = self.__bindAnimToPart(animName, thisPart, thisLod)
def bindAllAnims(self):
"""Loads and binds all animations that have been defined for
the Actor. """
for lodName, partDict in self.__animControlDict.items():
# Now, build the list of partNames and the corresponding
# 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!
@ -1947,6 +1989,8 @@ class Actor(DirectObject, NodePath):
if anim.animControl:
return anim.animControl
bundle = self.__partBundleDict[lodName][subpartDef.truePartName].partBundle
# fetch a copy from the modelPool, or if we weren't careful
# enough to preload, fetch from disk
animPath = anim.filename
@ -1955,36 +1999,12 @@ class Actor(DirectObject, NodePath):
# 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()
bundle = self.__partBundleDict[lodName][subpartDef.truePartName].partBundle
#NOTE: this is up here until we can guarantee anim bundle copying at a lower level
# Before we apply any control joints, we have to make a
# copy of the bundle hierarchy, so we don't modify other
# Actors that share the same bundle.
# Are there any controls requested for joints in this bundle?
# If so, apply them.
assert Actor.notify.debug('actor bundle %s, %s'% (bundle,bundle.this))
controlDict = self.__controlJoints.get(bundle.this, None)
animBundle = animBundle.copyBundle()
if controlDict:
for jointName, node in controlDict.items():
if node:
joint = animBundle.makeChildDynamic(jointName)
if joint:
joint.setValueNode(node.node())
else:
Actor.notify.error("controlled joint %s is not present" % jointName)
animModel = animNode.node()
# bind anim
animControl = bundle.bindAnim(animBundle, -1, subpartDef.subset)
@ -1994,7 +2014,7 @@ class Actor(DirectObject, NodePath):
else:
# store the animControl
anim.animControl = animControl
anim.animModel = animNode.node()
anim.animModel = animModel
assert Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
(animName, partName, lodName))
return animControl
@ -2027,9 +2047,6 @@ class Actor(DirectObject, NodePath):
# store the part bundle
assert bundleNP.node().getNumBundles() == 1
bundle = bundleNP.node().getBundle(0)
otherFixed=other.__frozenJoints.get(partDef.partBundle.this,None)
if(otherFixed is not None):
self.__frozenJoints[bundle.this]=copy.copy(otherFixed)
self.__partBundleDict[lodName][partName] = Actor.PartDef(bundleNP, bundle, model)
else:
Actor.notify.error("lod: %s has no matching part: %s" %