Просмотр исходного кода

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

David Rose 18 лет назад
Родитель
Сommit
8488c504d6
1 измененных файлов с 93 добавлено и 76 удалено
  1. 93 76
      direct/src/actor/Actor.py

+ 93 - 76
direct/src/actor/Actor.py

@@ -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():     
-            bundleDict[trueName].partBundle.freezeJoint(jointName, transform)
-            
+            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.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" %