Browse Source

half-body animations

David Rose 20 years ago
parent
commit
4b35c0715d
1 changed files with 182 additions and 47 deletions
  1. 182 47
      direct/src/actor/Actor.py

+ 182 - 47
direct/src/actor/Actor.py

@@ -3,6 +3,7 @@
 from direct.showbase.DirectObject import *
 from direct.showbase.DirectObject import *
 from pandac.PandaModules import LODNode
 from pandac.PandaModules import LODNode
 import types
 import types
+import copy as copy_module
 
 
 class Actor(DirectObject, NodePath):
 class Actor(DirectObject, NodePath):
     """
     """
@@ -89,6 +90,7 @@ class Actor(DirectObject, NodePath):
 
 
         # create data structures
         # create data structures
         self.__partBundleDict = {}
         self.__partBundleDict = {}
+        self.__subpartDict = {}
         self.__sortedLODNames = []
         self.__sortedLODNames = []
         self.__animControlDict = {}
         self.__animControlDict = {}
         self.__controlJoints = {}
         self.__controlJoints = {}
@@ -199,6 +201,7 @@ class Actor(DirectObject, NodePath):
             
             
             # copy the part dictionary from other
             # copy the part dictionary from other
             self.__copyPartBundles(other)
             self.__copyPartBundles(other)
+            self.__subpartDict = copy_module.deepcopy(other.__subpartDict)
             
             
             # copy the anim dictionary from other
             # copy the anim dictionary from other
             self.__copyAnimControls(other)
             self.__copyAnimControls(other)
@@ -245,6 +248,48 @@ class Actor(DirectObject, NodePath):
         return "Actor: partBundleDict = %s,\n animControlDict = %s" % \
         return "Actor: partBundleDict = %s,\n animControlDict = %s" % \
                (self.__partBundleDict, self.__animControlDict)
                (self.__partBundleDict, self.__animControlDict)
 
 
+    def listJoints(self, partName="modelRoot", lodName="lodRoot"):
+        """Handy utility function to list the joint hierarchy of the
+        actor. """
+
+        partBundleDict = self.__partBundleDict.get(lodName)
+        if not partBundleDict:
+            Actor.notify.error("no lod named: %s" % (lodName))
+
+        truePartName = partName
+        subset = PartSubset()
+        subpartDef = self.__subpartDict.get(partName)
+        if subpartDef:
+            truePartName, subset = subpartDef
+
+        bundle = partBundleDict.get(truePartName)
+        if bundle == None:
+            Actor.notify.error("no part named: %s" % (partName))
+
+        self.__doListJoints(0, bundle.node().getBundle(),
+                            subset.isEmpty(), subset)
+
+    def __doListJoints(self, indentLevel, part, isIncluded, subset):
+        name = part.getName()
+        if subset.matchesInclude(name):
+            isIncluded = True
+        elif subset.matchesExclude(name):
+            isIncluded = False
+
+        if isIncluded:
+            value = ''
+            if hasattr(part, 'outputValue'):
+                lineStream = LineStream.LineStream()
+                part.outputValue(lineStream)
+                value = lineStream.getLine()
+
+            print ' ' * indentLevel, part.getName(), value
+
+        for i in range(part.getNumChildren()):
+            self.__doListJoints(indentLevel + 2, part.getChild(i),
+                                isIncluded, subset)
+        
+
     def getActorInfo(self):
     def getActorInfo(self):
         """
         """
         Utility function to create a list of information about an actor.
         Utility function to create a list of information about an actor.
@@ -255,7 +300,8 @@ class Actor(DirectObject, NodePath):
             partDict = self.__animControlDict[lodName]
             partDict = self.__animControlDict[lodName]
             partInfo = []
             partInfo = []
             for partName in partDict.keys():
             for partName in partDict.keys():
-                partBundle = self.__partBundleDict[lodName][partName]
+                truePartName = self.__subpartDict.get(partName, [partName])[0]
+                partBundle = self.__partBundleDict[lodName][truePartName]
                 animDict = partDict[partName]
                 animDict = partDict[partName]
                 animInfo = []
                 animInfo = []
                 for animName in animDict.keys():
                 for animName in animDict.keys():
@@ -301,6 +347,7 @@ class Actor(DirectObject, NodePath):
         self.stop()
         self.stop()
 
 
         self.__partBundleDict = {}
         self.__partBundleDict = {}
+        self.__subpartDict = {}
         self.__sortedLODNames = []
         self.__sortedLODNames = []
         self.__animControlDict = {}
         self.__animControlDict = {}
         self.__controlJoints = {}
         self.__controlJoints = {}
@@ -353,7 +400,8 @@ class Actor(DirectObject, NodePath):
         Return list of Actor part names. If not an multipart actor,
         Return list of Actor part names. If not an multipart actor,
         returns 'modelRoot' NOTE: returns parts of arbitrary LOD
         returns 'modelRoot' NOTE: returns parts of arbitrary LOD
         """
         """
-        return self.__partBundleDict.values()[0].keys()
+        return self.__partBundleDict.values()[0].keys() + self.__subpartDict.keys()
+
     
     
     def getGeomNode(self):
     def getGeomNode(self):
         """
         """
@@ -626,7 +674,8 @@ class Actor(DirectObject, NodePath):
         if not partBundleDict:
         if not partBundleDict:
             Actor.notify.warning("no lod named: %s" % (lodName))
             Actor.notify.warning("no lod named: %s" % (lodName))
             return None
             return None
-        return partBundleDict.get(partName)
+        truePartName = self.__subpartDict.get(partName, [partName])[0]
+        return partBundleDict.get(truePartName)
 
 
     def removePart(self, partName, lodName="lodRoot"):
     def removePart(self, partName, lodName="lodRoot"):
         """
         """
@@ -646,14 +695,14 @@ class Actor(DirectObject, NodePath):
             del(partBundleDict[partName])
             del(partBundleDict[partName])
 
 
         # find the corresponding anim control dict
         # find the corresponding anim control dict
-        animControlDict = self.__animControlDict.get(lodName)
-        if not animControlDict:
+        partDict = self.__animControlDict.get(lodName)
+        if not partDict:
             Actor.notify.warning("no lod named: %s" % (lodName))
             Actor.notify.warning("no lod named: %s" % (lodName))
             return
             return
 
 
         # remove the animations
         # remove the animations
-        if (animControlDict.has_key(partName)):
-            del(animControlDict[partName])
+        if (partDict.has_key(partName)):
+            del(partDict[partName])
             
             
     def hidePart(self, partName, lodName="lodRoot"):
     def hidePart(self, partName, lodName="lodRoot"):
         """
         """
@@ -716,7 +765,8 @@ class Actor(DirectObject, NodePath):
             Actor.notify.warning("no lod named: %s" % (lodName))
             Actor.notify.warning("no lod named: %s" % (lodName))
             return None
             return None
 
 
-        part = partBundleDict.get(partName)
+        truePartName = self.__subpartDict.get(partName, [partName])[0]
+        part = partBundleDict.get(truePartName)
         if part:
         if part:
             bundle = part.node().getBundle()
             bundle = part.node().getBundle()
         else:
         else:
@@ -749,7 +799,8 @@ class Actor(DirectObject, NodePath):
             Actor.notify.warning("no lod named: %s" % (lodName))
             Actor.notify.warning("no lod named: %s" % (lodName))
             return None
             return None
 
 
-        part = partBundleDict.get(partName)
+        truePartName = self.__subpartDict.get(partName, [partName])[0]
+        part = partBundleDict.get(truePartName)
         if part:
         if part:
             bundle = part.node().getBundle()
             bundle = part.node().getBundle()
         else:
         else:
@@ -782,7 +833,8 @@ class Actor(DirectObject, NodePath):
             Actor.notify.warning("no lod named: %s" % (lodName))
             Actor.notify.warning("no lod named: %s" % (lodName))
             return None
             return None
 
 
-        part = partBundleDict.get(partName)
+        truePartName = self.__subpartDict.get(partName, [partName])[0]
+        part = partBundleDict.get(truePartName)
         if part:
         if part:
             bundle = part.node().getBundle()
             bundle = part.node().getBundle()
         else:
         else:
@@ -814,7 +866,8 @@ class Actor(DirectObject, NodePath):
         Instance a nodePath to an actor part at a joint called jointName"""
         Instance a nodePath to an actor part at a joint called jointName"""
         partBundleDict = self.__partBundleDict.get(lodName)
         partBundleDict = self.__partBundleDict.get(lodName)
         if partBundleDict:
         if partBundleDict:
-            part = partBundleDict.get(partName)
+            truePartName = self.__subpartDict.get(partName, [partName])[0]
+            part = partBundleDict.get(truePartName)
             if part:
             if part:
                 joint = part.find("**/" + jointName)
                 joint = part.find("**/" + jointName)
                 if (joint.isEmpty()):
                 if (joint.isEmpty()):
@@ -831,7 +884,8 @@ class Actor(DirectObject, NodePath):
         Attach one actor part to another at a joint called jointName"""
         Attach one actor part to another at a joint called jointName"""
         partBundleDict = self.__partBundleDict.get(lodName)
         partBundleDict = self.__partBundleDict.get(lodName)
         if partBundleDict:
         if partBundleDict:
-            part = partBundleDict.get(partName)
+            truePartName = self.__subpartDict.get(partName, [partName])[0]
+            part = partBundleDict.get(truePartName)
             if part:
             if part:
                 anotherPart = partBundleDict.get(anotherPartName)
                 anotherPart = partBundleDict.get(anotherPartName)
                 if anotherPart:
                 if anotherPart:
@@ -1069,7 +1123,8 @@ class Actor(DirectObject, NodePath):
                 for partBundle in bundleDict.values():
                 for partBundle in bundleDict.values():
                     partBundle.node().getBundle().setBlendType(blendType)
                     partBundle.node().getBundle().setBlendType(blendType)
             else:
             else:
-                partBundle = bundleDict.get(partName)
+                truePartName = self.__subpartDict.get(partName, [partName])[0]
+                partBundle = bundleDict.get(truePartName)
                 if partBundle != None:
                 if partBundle != None:
                     partBundle.node().getBundle().setBlendType(blendType)
                     partBundle.node().getBundle().setBlendType(blendType)
                 else:
                 else:
@@ -1099,11 +1154,11 @@ class Actor(DirectObject, 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
         """
         """
-        animControlDict = self.__animControlDict.get(lodName)
+        partDict = self.__animControlDict.get(lodName)
         # if this assertion fails, named lod was not present
         # if this assertion fails, named lod was not present
-        assert animControlDict != None
+        assert partDict != None
 
 
-        animDict = animControlDict.get(partName)
+        animDict = partDict.get(partName)
         if animDict == None:
         if animDict == None:
             # 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))
@@ -1138,28 +1193,39 @@ class Actor(DirectObject, NodePath):
             # Get all LOD's
             # Get all LOD's
             animControlDictItems = self.__animControlDict.items()
             animControlDictItems = self.__animControlDict.items()
         else:
         else:
-            animControlDict = self.__animControlDict.get(lodName)
-            if animControlDict == None:
+            partDict = self.__animControlDict.get(lodName)
+            if partDict == None:
                 Actor.notify.warning("couldn't find lod: %s" % (lodName))
                 Actor.notify.warning("couldn't find lod: %s" % (lodName))
                 animControlDictItems = []
                 animControlDictItems = []
             else:
             else:
-                animControlDictItems = [(lodName, animControlDict)]
+                animControlDictItems = [(lodName, partDict)]
 
 
-        for lodName, animControlDict in animControlDictItems:
+        for lodName, partDict in animControlDictItems:
             # Now, build the list of partNames and the corresponding
             # Now, build the list of partNames and the corresponding
             # animDicts.
             # animDicts.
             if partName == None:
             if partName == None:
                 # Get all parts
                 # Get all parts
-                animDictItems = animControlDict.items()
+                animDictItems = partDict.items()
             else:
             else:
-                if isinstance(partName, types.StringType):
+                if isinstance(partName, types.StringTypes):
                     partNameList = [partName]
                     partNameList = [partName]
                 else:
                 else:
                     partNameList = partName
                     partNameList = partName
                     
                     
                 animDictItems = []
                 animDictItems = []
                 for partName in partNameList:
                 for partName in partNameList:
-                    animDict = animControlDict.get(partName)
+                    animDict = partDict.get(partName)
+                    if animDict == None:
+                        # Maybe it's a subpart that hasn't been bound yet.
+                        subpartDef = self.__subpartDict.get(partName)
+                        if subpartDef:
+                            truePartName = subpartDef[0]
+                            dupAnimDict = partDict.get(truePartName)
+                            animDict = {}
+                            for name, anim in dupAnimDict.items():
+                                animDict[name] = [anim[0], None]
+
+
                     if animDict == None:
                     if animDict == None:
                         # 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))
@@ -1181,11 +1247,11 @@ class Actor(DirectObject, NodePath):
                         Actor.notify.debug("couldn't find anim: %s" % (animName))
                         Actor.notify.debug("couldn't find anim: %s" % (animName))
                     else:
                     else:
                         # bind the animation first if we need to
                         # 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])
+                        animControl = anim[1]
+                        if animControl == None:
+                            animControl = self.__bindAnimToPart(animName, thisPart, lodName)
+                        if animControl:
+                            controls.append(animControl)
 
 
         return controls
         return controls
             
             
@@ -1194,8 +1260,10 @@ class Actor(DirectObject, NodePath):
         bool = 0)
         bool = 0)
         Actor model loader. Takes a model name (ie file path), a part
         Actor model loader. Takes a model name (ie file path), a part
         name(defaults to "modelRoot") and an lod name(defaults to "lodRoot").
         name(defaults to "modelRoot") and an lod name(defaults to "lodRoot").
-        If copy is set to 0, do a lodModelOnce instead of a loadModelCopy.
+        If copy is set to 0, do a loadModel instead of a loadModelCopy.
         """
         """
+        assert partName not in self.__subpartDict
+
         Actor.notify.debug("in loadModel: %s , part: %s, lod: %s, copy: %s" % \
         Actor.notify.debug("in loadModel: %s , part: %s, lod: %s, copy: %s" % \
             (modelPath, partName, lodName, copy))
             (modelPath, partName, lodName, copy))
 
 
@@ -1260,9 +1328,11 @@ class Actor(DirectObject, NodePath):
             model.removeNode()        
             model.removeNode()        
 
 
     def prepareBundle(self, bundle, partName="modelRoot", lodName="lodRoot"):
     def prepareBundle(self, bundle, partName="modelRoot", lodName="lodRoot"):
+        assert partName not in self.__subpartDict
+        
         # Rename the node at the top of the hierarchy, if we
         # Rename the node at the top of the hierarchy, if we
         # haven't already, to make it easier to identify this
         # haven't already, to make it easier to identify this
-        # actor in the scene graph.
+        # actor in the scene graph.        
         if not self.gotName:
         if not self.gotName:
             self.node().setName(bundle.node().getName())
             self.node().setName(bundle.node().getName())
             self.gotName = 1
             self.gotName = 1
@@ -1293,6 +1363,49 @@ class Actor(DirectObject, NodePath):
         else:
         else:
             self.__partBundleDict[lodName][partName] = bundle
             self.__partBundleDict[lodName][partName] = bundle
 
 
+    def makeSubpart(self, partName, includeJoints, excludeJoints = [],
+                    parent="modelRoot"):
+
+        """Defines a new "part" of the Actor that corresponds to the
+        same geometry as the named parent part, but animates only a
+        certain subset of the joints.  This can be used for
+        partial-body animations, for instance to animate a hand waving
+        while the rest of the body continues to play its walking
+        animation.
+
+        includeJoints is a list of joint names that are to be animated
+        by the subpart.  Each name can include globbing characters
+        like '?' or '*', which will match one or any number of
+        characters, respectively.  Including a joint by naming it in
+        includeJoints implicitly includes all of the descendents of
+        that joint as well, except for excludeJoints, below.
+
+        excludeJoints is a list of joint names that are *not* to be
+        animated by the subpart.  As in includeJoints, each name can
+        include globbing characters.  If a joint is named by
+        excludeJoints, it will not be included (and neither will any
+        of its descendents), even if a parent joint was named by
+        includeJoints.
+
+        parent is the actual partName that this subpart is based
+        on."""
+
+        assert partName not in self.__subpartDict
+
+        truePartName = partName
+        prevSubset = PartSubset()
+        subpartDef = self.__subpartDict.get(partName)
+        if subpartDef:
+            truePartName, subset = subpartDef
+
+        subset = PartSubset(prevSubset)
+        for name in includeJoints:
+            subset.addIncludeJoint(GlobPattern(name))
+        for name in excludeJoints:
+            subset.addExcludeJoint(GlobPattern(name))
+
+        self.__subpartDict[partName] = (parent, subset)
+
     def loadAnims(self, anims, partName="modelRoot", lodName="lodRoot"):
     def loadAnims(self, anims, partName="modelRoot", lodName="lodRoot"):
         """loadAnims(self, string:string{}, string='modelRoot',
         """loadAnims(self, string:string{}, string='modelRoot',
         string='lodRoot')
         string='lodRoot')
@@ -1370,7 +1483,7 @@ class Actor(DirectObject, NodePath):
         # loop over all lods
         # loop over all lods
         for thisLod in lodNames:
         for thisLod in lodNames:
             if partName == None:
             if partName == None:
-                partNames = animControlDict[lodName].keys()
+                partNames = self.__partBundleDict[thisLod].keys()
             else:
             else:
                 partNames = [partName]
                 partNames = [partName]
             # loop over all parts
             # loop over all parts
@@ -1383,26 +1496,49 @@ class Actor(DirectObject, NodePath):
         for internal use only!
         for internal use only!
         """
         """
         # make sure this anim is in the dict
         # make sure this anim is in the dict
-        if not self.__animControlDict[lodName][partName].has_key(animName):
-            Actor.notify.debug("actor has no animation %s", animName)
+
+        subpartDef = self.__subpartDict.get(partName)
+        truePartName = partName
+        subset = PartSubset()
+        if subpartDef:
+            truePartName, subset = subpartDef
+
+
+        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.
+            if subpartDef != None:
+                anim = partDict[truePartName].get(animName)
+                anim = [anim[0], None]
+                animDict[animName] = anim
+
+        if anim == None:
+            Actor.notify.error("actor has no animation %s", animName)
             
             
         # only bind if not already bound!
         # only bind if not already bound!
-        if isinstance(self.__animControlDict[lodName][partName][animName][1],
-                      AnimControl):
-            return None
+        if anim[1]:
+            return anim[1]
 
 
         # 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 = self.__animControlDict[lodName][partName][animName][0]
+        animPath = anim[0]
         if self.__autoCopy:
         if self.__autoCopy:
-            anim = loader.loadModelOnce(animPath)
+            animNode = loader.loadModelOnce(animPath)
         else:
         else:
-            anim = loader.loadModel(animPath,
-                                    loaderOptions = self.animLoaderOptions)
-        if anim == None:
+            animNode = loader.loadModel(animPath,
+                                        loaderOptions = self.animLoaderOptions)
+        if animNode == None:
             return None
             return None
-        animBundle = (anim.find("**/+AnimBundleNode").node()).getBundle()
-        bundle = self.__partBundleDict[lodName][partName].node().getBundle()
+        animBundle = (animNode.find("**/+AnimBundleNode").node()).getBundle()
+
+        bundle = self.__partBundleDict[lodName][truePartName].node().getBundle()
 
 
         # Are there any controls requested for joints in this bundle?
         # Are there any controls requested for joints in this bundle?
         # If so, apply them.
         # If so, apply them.
@@ -1417,16 +1553,15 @@ class Actor(DirectObject, NodePath):
                         Actor.notify.error("controlled joint %s is not present" % jointName)
                         Actor.notify.error("controlled joint %s is not present" % jointName)
 
 
         # bind anim
         # bind anim
-        animControl = bundle.bindAnim(animBundle, -1)
+        animControl = bundle.bindAnim(animBundle, -1, subset)
 
 
         if (animControl == None):
         if (animControl == None):
             Actor.notify.error("Null AnimControl: %s" % (animName))
             Actor.notify.error("Null AnimControl: %s" % (animName))
         else:
         else:
             # store the animControl
             # store the animControl
-            self.__animControlDict[lodName][partName][animName][1] = \
-                                                                   animControl
+            anim[1] = animControl
             Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
             Actor.notify.debug("binding anim: %s to part: %s, lod: %s" %
-                           (animName, partName, lodName))
+                               (animName, partName, lodName))
         return animControl
         return animControl
 
 
     def __copyPartBundles(self, other):
     def __copyPartBundles(self, other):