Parcourir la source

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

David Rose il y a 18 ans
Parent
commit
8488c504d6
1 fichiers modifiés avec 93 ajouts et 76 suppressions
  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.__subpartDict = {}
         self.__sortedLODNames = []
         self.__sortedLODNames = []
         self.__animControlDict = {}
         self.__animControlDict = {}
-        self.__controlJoints = {}
-        self.__frozenJoints = {}        
 
 
         self.__subpartsComplete = False
         self.__subpartsComplete = False
 
 
@@ -444,7 +442,7 @@ class Actor(DirectObject, NodePath):
         Actor cleanup function
         Actor cleanup function
         """
         """
         self.stop(None)
         self.stop(None)
-        self.__frozenJoints = {}
+        self.clearPythonData()
         self.flush()
         self.flush()
         if(self.__geomNode):
         if(self.__geomNode):
             self.__geomNode.removeNode()
             self.__geomNode.removeNode()
@@ -462,7 +460,6 @@ class Actor(DirectObject, NodePath):
         self.__subpartDict = {}
         self.__subpartDict = {}
         self.__sortedLODNames = []
         self.__sortedLODNames = []
         self.__animControlDict = {}
         self.__animControlDict = {}
-        self.__controlJoints = {}
         
         
     def flush(self):
     def flush(self):
         """
         """
@@ -1037,62 +1034,78 @@ class Actor(DirectObject, NodePath):
 
 
 
 
     def controlJoint(self, node, partName, jointName, lodName="lodRoot"):
     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
         the indicated node, so that the joint transform will be copied
         from the node to the joint each frame.  This can be used for
         from the node to the joint each frame.  This can be used for
         programmer animation of a particular joint at runtime.
         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))
         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
         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))
         subpartDef = self.__subpartDict.get(partName, Actor.SubpartDef(partName))
         trueName = subpartDef.truePartName
         trueName = subpartDef.truePartName
+        anyGood = False
         for bundleDict in self.__partBundleDict.values():     
         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"):
     def instance(self, path, partName, jointName, lodName="lodRoot"):
         """instance(self, NodePath, string, string, key="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.
         # A model loaded from disk will always have just one bundle.
         assert(node.getNumBundles() == 1)
         assert(node.getNumBundles() == 1)
         bundle = node.getBundle(0)
         bundle = node.getBundle(0)
-        self.__frozenJoints[bundle.this]={}
         if (needsDict):
         if (needsDict):
             bundleDict[partName] = Actor.PartDef(bundleNP, bundle, model.node())
             bundleDict[partName] = Actor.PartDef(bundleNP, bundle, model.node())
             self.__partBundleDict[lodName] = bundleDict
             self.__partBundleDict[lodName] = bundleDict
@@ -1919,6 +1931,36 @@ class Actor(DirectObject, NodePath):
                 ac = self.__bindAnimToPart(animName, thisPart, thisLod)
                 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):
     def __bindAnimToPart(self, animName, partName, lodName):
         """
         """
         for internal use only!
         for internal use only!
@@ -1947,6 +1989,8 @@ class Actor(DirectObject, NodePath):
         if anim.animControl:
         if anim.animControl:
             return anim.animControl
             return anim.animControl
 
 
+        bundle = self.__partBundleDict[lodName][subpartDef.truePartName].partBundle
+
         # fetch a copy from the modelPool, or if we weren't careful
         # fetch a copy from the modelPool, or if we weren't careful
         # enough to preload, fetch from disk
         # enough to preload, fetch from disk
         animPath = anim.filename
         animPath = anim.filename
@@ -1955,36 +1999,12 @@ class Actor(DirectObject, NodePath):
             # If copy = 0, then we should always hit the disk.
             # If copy = 0, then we should always hit the disk.
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions = LoaderOptions(loaderOptions)
             loaderOptions.setFlags(loaderOptions.getFlags() & ~LoaderOptions.LFNoRamCache)
             loaderOptions.setFlags(loaderOptions.getFlags() & ~LoaderOptions.LFNoRamCache)
-            
+
         animNode = loader.loadModel(animPath, loaderOptions = loaderOptions)
         animNode = loader.loadModel(animPath, loaderOptions = loaderOptions)
         if animNode == None:
         if animNode == None:
             return None
             return None
         animBundle = (animNode.find("**/+AnimBundleNode").node()).getBundle()
         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
         # bind anim
         animControl = bundle.bindAnim(animBundle, -1, subpartDef.subset)
         animControl = bundle.bindAnim(animBundle, -1, subpartDef.subset)
@@ -1994,7 +2014,7 @@ class Actor(DirectObject, NodePath):
         else:
         else:
             # store the animControl
             # store the animControl
             anim.animControl = animControl
             anim.animControl = animControl
-            anim.animModel = animNode.node()
+            anim.animModel = animModel
             assert Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
             assert Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
                                       (animName, partName, lodName))
                                       (animName, partName, lodName))
         return animControl
         return animControl
@@ -2027,9 +2047,6 @@ class Actor(DirectObject, NodePath):
                     # store the part bundle
                     # store the part bundle
                     assert bundleNP.node().getNumBundles() == 1
                     assert bundleNP.node().getNumBundles() == 1
                     bundle = bundleNP.node().getBundle(0)
                     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)
                     self.__partBundleDict[lodName][partName] = Actor.PartDef(bundleNP, bundle, model)
                 else:
                 else:
                     Actor.notify.error("lod: %s has no matching part: %s" %
                     Actor.notify.error("lod: %s has no matching part: %s" %