Pārlūkot izejas kodu

*** empty log message ***

gregw 25 gadi atpakaļ
vecāks
revīzija
8109a44405
1 mainītis faili ar 496 papildinājumiem un 218 dzēšanām
  1. 496 218
      direct/src/actor/Actor.py

+ 496 - 218
direct/src/actor/Actor.py

@@ -1,14 +1,16 @@
 """Actor module: contains the Actor class"""
 """Actor module: contains the Actor class"""
 
 
 from PandaObject import *
 from PandaObject import *
+import LODNode
 
 
 class Actor(PandaObject, NodePath):
 class Actor(PandaObject, NodePath):
     """Actor class: Contains methods for creating, manipulating
     """Actor class: Contains methods for creating, manipulating
     and playing animations on characters"""
     and playing animations on characters"""
 
 
-    #create the Actor class DirectNotify category
+    #create the Actor class globals (ewww!)
     notify = directNotify.newCategory("Actor")
     notify = directNotify.newCategory("Actor")
     partPrefix = "__Actor_"
     partPrefix = "__Actor_"
+
     
     
     #special methods
     #special methods
     
     
@@ -56,8 +58,11 @@ class Actor(PandaObject, NodePath):
             a.attach("head", "torso", "joint-head")
             a.attach("head", "torso", "joint-head")
             a.attach("torso", "legs", "joint-hips")
             a.attach("torso", "legs", "joint-hips")
 
 
+        #
+        # ADD LOD COMMENT HERE!
+        #
 
 
-        Other useful Acotr class functions:
+        Other useful Actor class functions:
 
 
             #fix actor eye rendering
             #fix actor eye rendering
             a.drawInFront("joint-pupil?", "eyes*")
             a.drawInFront("joint-pupil?", "eyes*")
@@ -68,64 +73,117 @@ class Actor(PandaObject, NodePath):
             
             
         """
         """
 
 
-        try:
-            self.Actor_initialized
-        except:
-            self.Actor_initialized = 1
-            # initial our NodePath essence
-            NodePath.__init__(self)
-            
-            # create data structures
-            self.__partBundleDict = {}
-            self.__animControlDict = {}
+        # initialize our NodePath essence
+        NodePath.__init__(self)
+
+        # create data structures
+        self.__partBundleDict = {}
+        self.__animControlDict = {}
+
+        if (other == None):
+            # act like a normal contructor
+
+            # create base hierarchy
+            self.assign(hidden.attachNewNode('actor'))
+            self.setGeomNode(self.attachNewNode('actorGeom'))
+            self.__LODNode = None
+            self.__hasLOD = 0
             
             
-            if (other == None):
-                # act like a normal contructor
-                
-                # create base hierarchy
-                self.assign(hidden.attachNewNode('actor'))
-                self.setGeomNode(self.attachNewNode('actorGeom'))
-                
-                # load models
-                # make sure we have models
-                if (models):
-                    # if this is a dictionary
-                    if (type(models)==type({})):
-                        # then it must be multipart actor
+            # load models
+            #
+            # four cases:
+            #
+            #   models, anims{} = single part actor
+            #   models{}, anims{} =  single part actor w/ LOD
+            #   models{}, anims{}{} = multi-part actor
+            #   models{}{}, anims{}{} = multi-part actor w/ LOD
+            #
+            # make sure we have models
+            if (models):
+                # do we have a dictionary of models?
+                if (type(models)==type({})):
+                    # if this is a dictionary of dictionaries
+                    if (type(models[models.keys()[0]]) == type({})):
+                        # then it must be a multipart actor w/LOD
+                        self.setLODNode()
+                        # preserve numerical order for lod's
+                        # this will make it easier to set ranges
+                        sortedKeys = models.keys()
+                        sortedKeys.sort()
+                        for lodName in sortedKeys:
+                            # make a node under the LOD switch
+                            # for each lod (just because!)
+                            self.addLOD(str(lodName))
+                            # iterate over both dicts
+                            for modelName in models[lodName].keys():
+                                self.loadModel(models[lodName][modelName],
+                                               modelName, lodName)
+                    # then if there is a dictionary of dictionaries of anims
+                    elif (type(anims[anims.keys()[0]])==type({})):
+                        # then this is a multipart actor w/o LOD
                         for partName in models.keys():
                         for partName in models.keys():
+                            # pass in each part
                             self.loadModel(models[partName], partName)
                             self.loadModel(models[partName], partName)
                     else:
                     else:
-                        # else it is a single part actor
-                        self.loadModel(models)
-
-                # load anims
-                # make sure the actor has animations
-                if (anims):
-                    if (len(anims) >= 1):
-                        # if so, does it have a dictionary of dictionaries
-                        if (type(anims[anims.keys()[0]])==type({})):
-                            # then it must be multipart
-                            for partName in anims.keys():
-                                self.loadAnims(anims[partName], partName)
-                        else:
-                            # else it is not multipart
-                            self.loadAnims(anims)
+                        # it is a single part actor w/LOD
+                        self.setLODNode()
+                        # preserve order of LOD's
+                        sortedKeys = models.keys()
+                        sortedKeys.sort()
+                        for lodName in sortedKeys:
+                            self.addLOD(str(lodName))
+                            # pass in dictionary of parts
+                            self.loadModel(models[lodName], lodName=lodName)
+                else:
+                    # else it is a single part actor
+                    self.loadModel(models)
+
+            # load anims
+            # make sure the actor has animations
+            if (anims):
+                if (len(anims) >= 1):
+                    # if so, does it have a dictionary of dictionaries?
+                    if (type(anims[anims.keys()[0]])==type({})):
+                        # are the models a dict of dicts too?
+                        if (type(models)==type({})):
+                            if (type(models[models.keys()[0]]) == type({})):
+                                # then we have a multi-part w/ LOD
+                                sortedKeys = models.keys()
+                                sortedKeys.sort()
+                                for lodName in sortedKeys:
+                                    # iterate over both dicts
+                                    for partName in anims.keys():
+                                        self.loadAnims(
+                                            anims[partName], partName, lodName)
+                            else:
+                                # then it must be multi-part w/o LOD
+                                for partName in anims.keys():
+                                    self.loadAnims(anims[partName], partName)
+                    elif (type(models)==type({})):
+                        # then we have single-part w/ LOD
+                        sortedKeys = models.keys()
+                        sortedKeys.sort()
+                        for lodName in sortedKeys:
+                            self.loadAnims(anims, lodName=lodName)
+                    else:
+                        # else it is single-part w/o LOD
+                        self.loadAnims(anims)
 
 
-            else:
-                # act like a copy constructor
-                
-                # copy the scene graph elements of other
-                otherCopy = other.copyTo(hidden)
-                # assign these elements to ourselve
-                self.assign(otherCopy)
-                self.setGeomNode(otherCopy.getChild(0))
-                
-                # copy the part dictionary from other
-                self.__copyPartBundles(other)
-                
-                # copy the anim dictionary from other
-                self.__copyAnimControls(other)
-        return None
+        else:
+            # act like a copy constructor
+
+            # copy the scene graph elements of other
+            otherCopy = other.copyTo(hidden)
+            # assign these elements to ourselve
+            self.assign(otherCopy)
+            self.setGeomNode(otherCopy.getChild(0))
+            
+            # copy the part dictionary from other
+            self.__copyPartBundles(other)
+            
+            # copy the anim dictionary from other
+            self.__copyAnimControls(other)
+            
  
  
     def __str__(self):
     def __str__(self):
         """__str__(self)
         """__str__(self)
@@ -136,11 +194,17 @@ class Actor(PandaObject, NodePath):
 
 
     # accessing
     # accessing
     
     
+    def getLODNames(self):
+        """getLODNames(self):
+        Return list of Actor LOD names. If not an LOD actor,
+        returns 'lodRoot'"""
+        return self.__partBundleDict.keys()
+    
     def getPartNames(self):
     def getPartNames(self):
         """getPartNames(self):
         """getPartNames(self):
-        Return list of Actor part names. If not multipart,
-        returns modelRoot"""
-        return self.__partBundleDict.keys()
+        Return list of Actor part names. If not an multipart actor,
+        returns 'modelRoot' NOTE: returns parts of first LOD"""
+        return self.__partBundleDict[0].keys()
     
     
     def getGeomNode(self):
     def getGeomNode(self):
         """getGeomNode(self)
         """getGeomNode(self)
@@ -152,19 +216,115 @@ class Actor(PandaObject, NodePath):
         Set the node that contains all actor geometry"""
         Set the node that contains all actor geometry"""
         self.__geomNode = node
         self.__geomNode = node
 
 
+    def getLODNode(self):
+        """getLODNode(self)
+        Return the node that switches actor geometry in and out"""
+        return self.__LODNode.node()
+
+    def setLODNode(self, node=None):
+        """setLODNode(self, LODNode=None)
+        Set the node that switches actor geometry in and out.
+        If one is not supplied as an argument, make one"""
+        if (node == None):
+            lod = LODNode.LODNode("lod")
+            self.__LODNode = self.__geomNode.attachNewNode(lod)
+        else:
+            self.__LODNode = self.__geomNode.attachNewNode(node)            
+        self.__hasLOD = 1
+        self.switches = {}
+
+    def useLOD(self, lodName):
+        """useLOD(self, string)
+        Make the Actor ONLY display the given LOD"""
+        # make sure we don't call this twice in a row
+        # and pollute the the switches dictionary
+        self.resetLOD()
+        # store the data in the switches for later use
+        sortedKeys = self.switches.keys()
+        sortedKeys.sort()
+        for eachLod in sortedKeys:
+            index = sortedKeys.index(eachLod)
+            # set the switches to not display ever
+            self.__LODNode.node().setSwitch(index, 0, 10000)
+        # turn the given LOD on 'always'
+        index = sortedKeys.index(lodName)
+        self.__LODNode.node().setSwitch(index, 10000, 0)
+
+    def printLOD(self):
+        sortedKeys = self.switches.keys()
+        sortedKeys.sort()
+        for eachLod in sortedKeys:
+            print "python switches for %s: in: %d, out %d" % (eachLod,
+                                              self.switches[eachLod][0],
+                                              self.switches[eachLod][1])
+
+        switchNum = self.__LODNode.node().getNumSwitches()
+        for eachSwitch in range(0, switchNum):
+            print "c++ switches for %d: in: %d, out: %d" % (eachSwitch,
+                   self.__LODNode.node().getIn(eachSwitch),
+                   self.__LODNode.node().getOut(eachSwitch))
+                                                        
+            
+    def resetLOD(self):
+        """resetLOD(self)
+        Restore all switch distance info (usually after a useLOD call)"""
+        sortedKeys = self.switches.keys()
+        sortedKeys.sort()
+        for eachLod in sortedKeys:
+            index = sortedKeys.index(eachLod)
+            self.__LODNode.node().setSwitch(index, self.switches[eachLod][0],
+                                     self.switches[eachLod][1])
+                    
+    def addLOD(self, lodName, inDist=0, outDist=0):
+        """addLOD(self, string) 
+        Add a named node under the LODNode to parent all geometry
+        of a specific LOD under."""
+        self.__LODNode.attachNewNode(str(lodName))
+        # save the switch distance info
+        self.switches[lodName] = [inDist, outDist]
+        # add the switch distance info
+        self.__LODNode.node().addSwitch(inDist, outDist)
+
+    def setLOD(self, lodName, inDist=0, outDist=0):
+        """setLOD(self, string)
+        Set the switch distance for given LOD"""
+        # save the switch distance info
+        self.switches[lodName] = [inDist, outDist]
+        # add the switch distance info
+        sortedKeys = self.switches.keys()
+        sortedKeys.sort()
+        index = sortedKeys.index(lodName)
+        self.__LODNode.node().setSwitch(index, inDist, outDist)
+
+    def getLOD(self, lodName):
+        """getLOD(self, string)
+        Get the named node under the LOD to which we parent all LOD
+        specific geometry to. Returns 'None' if not found"""
+        lod = self.__LODNode.find("**/" + str(lodName))
+        if lod.isEmpty():
+            return None
+        else:
+            return lod
+        
+    def hasLOD(self):
+        """hasLOD(self)
+        Return 1 if the actor has LODs, 0 otherwise"""
+        return self.__hasLOD
+    
     def getFrameRate(self, animName=None, partName=None):
     def getFrameRate(self, animName=None, partName=None):
         """getFrameRate(self, string, string=None)
         """getFrameRate(self, string, string=None)
         Return duration of given anim name and given part.
         Return duration of given anim name and given part.
         If no anim specified, use the currently playing anim.
         If no anim specified, use the currently playing anim.
-        If no part specified, return anim durations of first part"""
+        If no part specified, return anim durations of first part.
+        NOTE: returns info only for the first LOD"""
         if (partName == None):
         if (partName == None):
-            partName = self.__animControlDict.keys()[0]
+            partName = self.__animControlDict[0].keys()[0]
     
     
         if (animName==None):
         if (animName==None):
             animName = self.getCurrentAnim(partName)
             animName = self.getCurrentAnim(partName)
 
 
         # get duration for named part only
         # get duration for named part only
-        if (self.__animControlDict.has_key(partName)):        
+        if (self.__animControlDict[0].has_key(partName)):        
             animControl = self.__getAnimControl(animName, partName)
             animControl = self.__getAnimControl(animName, partName)
             if (animControl != None):
             if (animControl != None):
                 return animControl.getFrameRate()
                 return animControl.getFrameRate()
@@ -177,9 +337,10 @@ class Actor(PandaObject, NodePath):
         """getPlayRate(self, string=None, string=None)
         """getPlayRate(self, string=None, string=None)
         Return the play rate of given anim for a given part.
         Return the play rate of given anim for a given part.
         If no part is given, assume first part in dictionary.
         If no part is given, assume first part in dictionary.
-        If no anim is given, find the current anim for the part"""
+        If no anim is given, find the current anim for the part.
+        NOTE: Returns info only for the first LOD"""
         if (partName==None):
         if (partName==None):
-            partName = self.__animControlDict.keys()[0]
+            partName = self.__animControlDict[0].keys()[0]
 
 
         if (animName==None):
         if (animName==None):
             animName = self.getCurrentAnim(partName)
             animName = self.getCurrentAnim(partName)
@@ -194,35 +355,39 @@ class Actor(PandaObject, NodePath):
         """getPlayRate(self, float, string=None, string=None)
         """getPlayRate(self, float, string=None, string=None)
         Set the play rate of given anim for a given part.
         Set the play rate of given anim for a given part.
         If no part is given, set for all parts in dictionary.
         If no part is given, set for all parts in dictionary.
-        If no anim is given, find the current anim for the part"""
+        If no anim is given, find the current anim for the part.
+        NOTE: sets play rate on all LODs"""
         # make a list of partNames for loop below
         # make a list of partNames for loop below
-        if (partName==None):
-            partNames = self.__animControlDict.keys()
-        else:
-            partNames = []
-            partNames.append(partName)
+        for lodName in self.__animControlDict.keys():
+            animControlDict = self.__animControlDict[lodName]
+            if (partName==None):
+                partNames = animControlDict.keys()
+            else:
+                partNames = []
+                partNames.append(partName)
 
 
-        # for each part in list, set play rate on given or current anim
-        for partName in partNames:
-            if (animName==None):
-                animName = self.getCurrentAnim(partName)          
-            animControl = self.__getAnimControl(animName, partName)
-            if (animControl != None):
-                animControl.setPlayRate(rate)
+            # for each part in list, set play rate on given or current anim
+            for partName in partNames:
+                if (animName==None):
+                    animName = self.getCurrentAnim(partName)          
+                animControl = self.__getAnimControl(animName, partName)
+                if (animControl != None):
+                    animControl.setPlayRate(rate)
             
             
     def getDuration(self, animName=None, partName=None):
     def getDuration(self, animName=None, partName=None):
         """getDuration(self, string, string=None)
         """getDuration(self, string, string=None)
         Return duration of given anim name and given part.
         Return duration of given anim name and given part.
         If no anim specified, use the currently playing anim.
         If no anim specified, use the currently playing anim.
-        If no part specified, return anim duration of first part"""
+        If no part specified, return anim duration of first part.
+        NOTE: returns info for first LOD only"""
         if (partName == None):
         if (partName == None):
-            partName = self.__animControlDict.keys()[0]
+            partName = self.__animControlDict[0].keys()[0]
 
 
         if (animName==None):
         if (animName==None):
             animName = self.getCurrentAnim(partName)          
             animName = self.getCurrentAnim(partName)          
 
 
         # get duration for named part only
         # get duration for named part only
-        if (self.__animControlDict.has_key(partName)):        
+        if (self.__animControlDict[0].has_key(partName)):        
             animControl = self.__getAnimControl(animName, partName)
             animControl = self.__getAnimControl(animName, partName)
             if (animControl != None):
             if (animControl != None):
                 return (animControl.getNumFrames() / \
                 return (animControl.getNumFrames() / \
@@ -235,12 +400,13 @@ class Actor(PandaObject, NodePath):
     def getCurrentAnim(self, partName=None):
     def getCurrentAnim(self, partName=None):
         """getCurrentAnim(self, string=None)
         """getCurrentAnim(self, string=None)
         Return the anim current playing on the actor. If part not
         Return the anim current playing on the actor. If part not
-        specified return current anim of first part in dictionary"""
+        specified return current anim of first part in dictionary.
+        NOTE: only returns info for the first LOD"""
         if (partName==None):
         if (partName==None):
-            partName = self.__animControlDict.keys()[0]
+            partName = self.__animControlDict[0].keys()[0]
 
 
         # loop through all anims for named part and find if any are playing
         # loop through all anims for named part and find if any are playing
-        if (self.__animControlDict.has_key(partName)):
+        if (self.__animControlDict[0].has_key(partName)):
             for animName in self.__animControlDict[partName].keys():
             for animName in self.__animControlDict[partName].keys():
                 if (self.__getAnimControl(animName, partName).isPlaying()):
                 if (self.__getAnimControl(animName, partName).isPlaying()):
                     return animName
                     return animName
@@ -251,102 +417,156 @@ class Actor(PandaObject, NodePath):
         return None
         return None
 
 
 
 
-
     # arranging
     # arranging
 
 
-    def getPart(self, partName):
-        """getPart(self, string)
-        Find the named part in the partBundleDict and return it, or
+    def getPart(self, partName, lodName="lodRoot"):
+        """getPart(self, string, key="lodRoot")
+        Find the named part in the optional named lod and return it, or
         return None if not present"""
         return None if not present"""
-        if (self.__partBundleDict.has_key(partName)):
-            return self.__partBundleDict[partName]
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+        else:
+            Actor.notify.warning("no lod named: %s" % (lodName))
+            return None
+                
+        if (partBundleDict.has_key(partName)):
+            return partBundleDict[partName]
         else:
         else:
             return None
             return None
 
 
-    def removePart(self, partName):
-        """removePart(Self, string)
-        Remove the geometry and animations of the named part if present
-        NOTE: this will remove parented geometry also!"""
-        # remove the geometry
-        if (self.__partBundleDict.has_key(partName)):
-            self.__partBundleDict[partName].removeNode()
-            del(self.__partBundleDict[partName])
-        # remove the animations
-        if (self.__animControlDict.has_key(partName)):
-            del(self.__animControlDict[partName])
+    def removePart(self, partName, lodName="lodRoot"):
+        """removePart(self, string, key="lodRoot")
+        Remove the geometry and animations of the named part of the
+        optional named lod if present.
+        NOTE: this will remove child geometry also!"""
+        # find the corresponding part bundle dict
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+        else:
+            Actor.notify.warning("no lod named: %s" % (lodName))
+            return None
+
+        # find the corresponding anim control dict
+        if (self.__animControlDict.has_key(lodName)):
+            animControlDict = self.__animControlDict[lodName]
+        else:
+            Actor.notify.warning("no lod named: %s" % (lodName))
+            return None
 
 
+        # remove the part
+        if (partBundleDict.has_key(partName)):
+            partBundleDict[partName].removeNode()
+            del(partBundleDict[partName])
+
+        # remove the animations
+        if (animControlDict.has_key(partName)):
+            del(animControlDict[partName])
             
             
-    def hidePart(self, partName):
-        """hidePart(self, string)
-        Make the given part not render, even though still in the tree.
-        NOTE: this functionality will be effected by the 'attach' method"""
-        if (self.__partBundleDict.has_key(partName)):
-            self.__partBundleDict[partName].hide()
+    def hidePart(self, partName, lodName="lodName"):
+        """hidePart(self, string, key="lodName")
+        Make the given part of the optional given lod not render,
+        even though still in the tree.
+        NOTE: this will affect child geometry"""
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+        else:
+            Actor.notify.warning("no lod named: %s" % (lodName))
+            return None
+
+        if (partBundleDict.has_key(partName)):
+            partBundleDict[partName].hide()
         else:
         else:
             Actor.notify.warning("no part named %s!" % (partName))
             Actor.notify.warning("no part named %s!" % (partName))
 
 
-    def showPart(self, partName):
-        """showPart(self, string)
+    def showPart(self, partName, lodName="lodRoot"):
+        """showPart(self, string, key="lodRoot")
         Make the given part render while in the tree.
         Make the given part render while in the tree.
-        NOTE: this functionality will be effected by the 'attach' method"""
-        if (self.__partBundleDict.has_key(partName)):
-            self.__partBundleDict[partName].show()
+        NOTE: this will affect child geometry"""
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+        else:
+            Actor.notify.warning("no lod named: %s" % (lodName))
+            return None
+
+        if (partBundleDict.has_key(partName)):
+            partBundleDict[partName].show()
         else:
         else:
             Actor.notify.warning("no part named %s!" % (partName))
             Actor.notify.warning("no part named %s!" % (partName))
             
             
-    def instance(self, partName, anotherPart, jointName):
-        """instance(self, string, string, string)
+    def instance(self, partName, anotherPart, jointName, lodName="lodRoot"):
+        """instance(self, string, string, string, key="lodRoot")
         Instance one actor part to another at a joint called jointName"""
         Instance one actor part to another at a joint called jointName"""
-        if (self.__partBundleDict.has_key(partName)):
-            if (self.__partBundleDict.has_key(anotherPart)):
-                joint = NodePath(self.__partBundleDict[anotherPart], \
-                                 "**/" + jointName)
-                if (joint.isEmpty()):
-                    Actor.notify.warning("%s not found!" % (jointName))
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+            if (partBundleDict.has_key(partName)):
+                if (partBundleDict.has_key(anotherPart)):
+                    joint = NodePath(partBundleDict[anotherPart],
+                                     "**/" + jointName)
+                    if (joint.isEmpty()):
+                        Actor.notify.warning("%s not found!" % (jointName))
+                    else:
+                        return partBundleDict[partName].instanceTo(joint)
                 else:
                 else:
-                    return self.__partBundleDict[partName].instanceTo(joint)
+                    Actor.notify.warning("no part named %s!" % (anotherPart))
             else:
             else:
-                Actor.notify.warning("no part named %s!" % (anotherPart))
+                Actor.notify.warning("no part named %s!" % (partName))
         else:
         else:
-            Actor.notify.warning("no part named %s!" % (partName))
+            Actor.notify.warning("no lod named %s!" % (lodName))
 
 
-
-    def attach(self, partName, anotherPart, jointName):
-        """attach(self, string, string, string)
+    def attach(self, partName, anotherPart, jointName, lodName="lodRoot"):
+        """attach(self, string, string, string, key="lodRoot")
         Attach one actor part to another at a joint called jointName"""
         Attach one actor part to another at a joint called jointName"""
-        if (self.__partBundleDict.has_key(partName)):
-            if (self.__partBundleDict.has_key(anotherPart)):
-                joint = NodePath(self.__partBundleDict[anotherPart], \
+        if (self.__partBundleDict.has_key(lodName)):
+            partBundleDict = self.__partBundleDict[lodName]
+            if (partBundleDict.has_key(partName)):
+                if (partBundleDict.has_key(anotherPart)):
+                    joint = NodePath(partBundleDict[anotherPart],
                                  "**/" + jointName)
                                  "**/" + jointName)
-                if (joint.isEmpty()):
-                    Actor.notify.warning("%s not found!" % (jointName))
+                    if (joint.isEmpty()):
+                        Actor.notify.warning("%s not found!" % (jointName))
+                    else:
+                        partBundleDict[partName].reparentTo(joint)
                 else:
                 else:
-                    self.__partBundleDict[partName].reparentTo(joint)
+                    Actor.notify.warning("no part named %s!" % (anotherPart))
             else:
             else:
-                Actor.notify.warning("no part named %s!" % (anotherPart))
+                Actor.notify.warning("no part named %s!" % (partName))
         else:
         else:
-            Actor.notify.warning("no part named %s!" % (partName))
+            Actor.notify.warning("no lod named %s!" % (lodName))
 
 
                     
                     
-    def drawInFront(self, frontPartName, backPartName, root=None):
-        """drawInFront(self, string, string=None)
+    def drawInFront(self, frontPartName, backPartName, root=None,
+                    lodName=None):
+        """drawInFront(self, string, string=None, key=None)
         Arrange geometry so the frontPart is drawn properly wrt backPart.
         Arrange geometry so the frontPart is drawn properly wrt backPart.
         Takes an optional argument root as the start of the search for the
         Takes an optional argument root as the start of the search for the
-        given parts"""
+        given parts. Also takes optional lod name to refine search for the
+        named parts. If root and lod are defined, we search for the given
+        root under the given lod."""
+
+        # check to see if we are working within an lod
+        if (lodName != None):
+            # find the named lod node
+            lodRoot = self.find("**/" + str(lodName))
+            if (root == None):
+                # no need to look further
+                root = lodRoot
+            else:
+                # look for root under lod
+                root = lodRoot.find("**/" + root)
+        else:
+            # start search from self if no root and no lod given
+            if (root == None):
+                root = self
 
 
-        # start search from self if no root given
-        if (root==None):
-            root = self
-            
         # make the back part have the proper transition
         # make the back part have the proper transition
-        backPart = NodePath(root, "**/"+backPartName)
+        backPart = NodePath(root, "**/" + backPartName)
         if (backPart.isEmpty()):
         if (backPart.isEmpty()):
             Actor.notify.warning("no part named %s!" % (backPartName))
             Actor.notify.warning("no part named %s!" % (backPartName))
         else:
         else:
             (backPart.getBottomArc()).setTransition(DirectRenderTransition())
             (backPart.getBottomArc()).setTransition(DirectRenderTransition())
 
 
-        #reparent the front parts to the back parts
-        frontParts = self.findAllMatches( "**/"+frontPartName)
+        #reparent the front parts to the back part
+        frontParts = root.findAllMatches( "**/" + frontPartName)
         numFrontParts = frontParts.getNumPaths()
         numFrontParts = frontParts.getNumPaths()
         for partNum in range(0, numFrontParts):
         for partNum in range(0, numFrontParts):
             (frontParts.getPath(partNum)).reparentTo(backPart)
             (frontParts.getPath(partNum)).reparentTo(backPart)
@@ -406,140 +626,198 @@ class Actor(PandaObject, NodePath):
     def stop(self, animName=None, partName=None):
     def stop(self, animName=None, partName=None):
         """stop(self, string=None, string=None)
         """stop(self, string=None, string=None)
         Stop named animation on the given part of the actor.
         Stop named animation on the given part of the actor.
-        If no name specified then stop all animations on the actor"""
-        if (animName == None):
-            #loop and stop ALL anims
-            for animControl in self.__animControlDict[partName].keys():
-                self.__animControlDict[partName][animControl].stop()
-        else:
-            #stop the specified anim
-            animControl = self.__getAnimControl(animName, partName)
-            if (animControl != None):
-                animControl.stop()
+        If no name specified then stop all animations on the actor.
+        NOTE: stops all LODs"""
+        for lodName in self.__animControlDict.keys():
+            animControlDict = self.__animControlDict[lodName]
+            if (animName == None):
+                # loop and stop all anims
+                if (partName == None):
+                    # loop over all parts
+                    for thisPart in animControlDict.keys():
+                        for thisAnim in animControlDict[thisPart].keys():
+                            animControlDict[thisPart][thisAnim].stop()
+                else:
+                    # stop just this part
+                    for thisAnim in animControlDict[partName].keys():
+                        animControlDict[partName][thisAnim].stop()
+            else:
+                # stop the specified anim
+                if (partName == None):
+                    # loop over all parts
+                    for thisPart in animControlDict.keys():
+                        animControlDict[thisPart][animName].stop()
+                else:
+                    animControlDict[partName][animName].stop()
+                    
         
         
     def play(self, animName, partName=None):
     def play(self, animName, partName=None):
         """play(self, string, string=None)
         """play(self, string, string=None)
         Play the given animation on the given part of the actor.
         Play the given animation on the given part of the actor.
-        If no part is specified, try to play on all parts"""
-        if (partName == None):
-            # loop all parts
-            for partName in self.__animControlDict.keys():            
-                animControl = self.__getAnimControl(animName, partName)
+        If no part is specified, try to play on all parts. NOTE:
+        plays over ALL LODs"""
+        for lodName in self.__animControlDict.keys():
+            animControlDict = self.__animControlDict[lodName]
+            if (partName == None):
+                # loop all parts
+                for thisPart in animControlDict.keys():            
+                    animControl = self.__getAnimControl(animName, thisPart,
+                                                        lodName)
+                    if (animControl != None):
+                        animControl.play()
+
+            else:
+                animControl = self.__getAnimControl(animName, partName,
+                                                    lodName)
                 if (animControl != None):
                 if (animControl != None):
                     animControl.play()
                     animControl.play()
-        else:
-            animControl = self.__getAnimControl(animName, partName)
-            if (animControl != None):
-                animControl.play()
 
 
 
 
     def loop(self, animName, restart=1, partName=None):
     def loop(self, animName, restart=1, partName=None):
         """loop(self, string, int=1, string=None)
         """loop(self, string, int=1, string=None)
         Loop the given animation on the given part of the actor,
         Loop the given animation on the given part of the actor,
         restarting at zero frame if requested. If no part name
         restarting at zero frame if requested. If no part name
-        is given then try to loop on all parts"""
-        if (partName == None):
-            # loop all parts
-            for partName in self.__animControlDict.keys():
-                animControl = self.__getAnimControl(animName, partName)
+        is given then try to loop on all parts. NOTE: loops on
+        all LOD's"""
+        for lodName in self.__animControlDict.keys():
+            animControlDict = self.__animControlDict[lodName]
+            if (partName == None):
+                # loop all parts
+                for thisPart in animControlDict.keys():
+                    animControl = self.__getAnimControl(animName, thisPart,
+                                                        lodName)
+                    if (animControl != None):
+                        animControl.loop(restart)
+            else:
+                # loop a specific part
+                animControl = self.__getAnimControl(animName, partName,
+                                                    lodName)
                 if (animControl != None):
                 if (animControl != None):
                     animControl.loop(restart)
                     animControl.loop(restart)
-        else:
-            # loop a specific part
-            animControl = self.__getAnimControl(animName, partName)
-            if (animControl != None):
-                animControl.loop(restart)
         
         
     def pose(self, animName, frame, partName=None):
     def pose(self, animName, frame, partName=None):
         """pose(self, string, int, string=None)
         """pose(self, string, int, string=None)
         Pose the actor in position found at given frame in the specified
         Pose the actor in position found at given frame in the specified
         animation for the specified part. If no part is specified attempt
         animation for the specified part. If no part is specified attempt
-        to apply pose to all parts"""
-        if (partName==None):
-            # pose all parts
-            for partName in self.__animControlDict.keys():
-                animControl = self.__getAnimControl(animName, partName)
+        to apply pose to all parts. NOTE: poses all LODs"""
+        for lodName in self.__animControlDict.keys():
+            animControlDict = self.__animControlDict[lodName]
+            if (partName==None):
+                # pose all parts
+                for thisPart in animControlDict.keys():
+                    animControl = self.__getAnimControl(animName, thisPart,
+                                                        lodName)
+                    if (animControl != None):
+                        animControl.pose(frame)
+            else:
+                # pose a specific part
+                animControl = self.__getAnimControl(animName, partName,
+                                                    lodName)
                 if (animControl != None):
                 if (animControl != None):
                     animControl.pose(frame)
                     animControl.pose(frame)
-        else:
-            # pose a specific part
-            animControl = self.__getAnimControl(animName, partName)
-            if (animControl != None):
-                animControl.pose(frame)
         
         
-
     #private
     #private
     
     
-    def __getAnimControl(self, animName, partName):
-        """__getAnimControl(self, string, string)
-        Search the animControl dictionary for given anim and part.
-        Return the animControl if present, or None otherwise"""
-        if (self.__animControlDict.has_key(partName)):
-            if (self.__animControlDict[partName].has_key(animName)):
-                return self.__animControlDict[partName][animName]
+    def __getAnimControl(self, animName, partName, lodName="lodRoot"):
+        """__getAnimControl(self, string, string, string="lodRoot")
+        Search the animControl dictionary indicated by lodName for
+        a given anim and part. Return the animControl if present,
+        or None otherwise
+        """
+        if (self.__animControlDict.has_key(lodName)):
+            animControlDict = self.__animControlDict[lodName]
+            if (animControlDict.has_key(partName)):
+                if (animControlDict[partName].has_key(animName)):
+                    return animControlDict[partName][animName]
+                else:
+                    # anim was not present
+                    Actor.notify.warning("couldn't find anim: %s" % (animName))
             else:
             else:
-                # anim was not present
-                Actor.notify.warning("couldn't find anim: %s" % (animName))
+                # part was not present
+                Actor.notify.warning("couldn't find part: %s" % (partName))
         else:
         else:
-            # part was not present
-            Actor.notify.warning("couldn't find part: %s" % (partName))
-                
+            # lod was not present
+            Actor.notify.warning("couldn't find lod: %s" % (lodName))
+            
         return None
         return None
 
 
             
             
-    def loadModel(self, modelPath, partName="modelRoot"):
-        """loadModel(self, string, string="modelRoot")
-        Actor model loader. Takes a model name (ie file path) and
-        a partName (defaults to "modelRoot")"""
-        
-        Actor.notify.info("in loadModel: %s , part: %s" % \
-            (modelPath, partName))
+    def loadModel(self, modelPath, partName="modelRoot", lodName="lodRoot"):
+        """loadModel(self, string, string="modelRoot", string="base")
+        Actor model loader. Takes a model name (ie file path), a part
+        name(defaults to "modelRoot") and an lod name(defaults to "lodRoot").
+        """
+        Actor.notify.warning("in loadModel: %s , part: %s, lod: %s" % \
+            (modelPath, partName, lodName))
 
 
         # load the model and extract its part bundle
         # load the model and extract its part bundle
         model = loader.loadModelCopy(modelPath)
         model = loader.loadModelCopy(modelPath)
-
-
         bundle = NodePath(model, "**/+PartBundleNode")
         bundle = NodePath(model, "**/+PartBundleNode")
         if (bundle.isEmpty()):
         if (bundle.isEmpty()):
             Actor.notify.warning("%s is not a character!" % (modelPath))
             Actor.notify.warning("%s is not a character!" % (modelPath))
         else:
         else:
             # we rename this node to make Actor copying easier
             # we rename this node to make Actor copying easier
             bundle.node().setName(Actor.partPrefix + partName)
             bundle.node().setName(Actor.partPrefix + partName)
-            bundle.reparentTo(self.__geomNode)
+
+            if (self.__partBundleDict.has_key(lodName) == 0):
+                # make a dictionary to store these parts in
+                needsDict = 1
+                bundleDict = {}
+            else:
+                needsDict = 0
+                
+            if (lodName!="lodRoot"):
+                # reparent to appropriate node under LOD switch
+                bundle.reparentTo(self.__LODNode.find("**/" + str(lodName)))
+            else:
+                bundle.reparentTo(self.__geomNode)
+
+            if (needsDict):
+                bundleDict[partName] = bundle
+                self.__partBundleDict[lodName] = bundleDict
+            else:
+                self.__partBundleDict[lodName][partName] = bundle
+
             model.removeNode()        
             model.removeNode()        
 
 
-        #make this mimic mutli-part by giving it a default part anme
-        self.__partBundleDict[partName] = bundle
-        
 
 
-    def loadAnims(self, anims, partName="modelRoot"):
-        """loadAnims(self, string:string{}, string="modelRoot")
+    def loadAnims(self, anims, partName="modelRoot", lodName="lodRoot"):
+        """loadAnims(self, string:string{}, string='modelRoot',
+        string='lodRoot')
         Actor anim loader. Takes an optional partName (defaults to
         Actor anim loader. Takes an optional partName (defaults to
-        'modelRoot' for non-multipart actors) and dict of corresponding
+        'modelRoot' for non-multipart actors) and lodName (defaults
+        to 'lodRoot' for non-LOD actors) and dict of corresponding
         anims in the form animName:animPath{}"""
         anims in the form animName:animPath{}"""
         
         
-        Actor.notify.info("in loadAnims: %s, part: %s" % (anims, partName))
+        Actor.notify.warning("in loadAnims: %s, part: %s, lod: %s" %
+                          (anims, partName, lodName))
 
 
         animDict = {}
         animDict = {}
 
 
         for animName in anims.keys():
         for animName in anims.keys():
             
             
-            #load the anim and get its anim bundle
-            anim = loader.loadModelCopy(anims[animName])
+            # load the anim and get its anim bundle
+            anim = loader.loadModelOnce(anims[animName])
             animBundle = \
             animBundle = \
                 (NodePath(anim, "**/+AnimBundleNode").node()).getBundle()
                 (NodePath(anim, "**/+AnimBundleNode").node()).getBundle()
 
 
-            #bind anim
-            bundleNode = (self.__partBundleDict[partName]).node()
+            # bind anim
+            bundleNode = (
+                self.__partBundleDict[lodName][partName]).node()
+                
             animControl = (bundleNode.getBundle()).bindAnim(animBundle, -1)
             animControl = (bundleNode.getBundle()).bindAnim(animBundle, -1)
             if (animControl == None):
             if (animControl == None):
                 Actor.notify.error("Null AnimControl: %s" % (animName))
                 Actor.notify.error("Null AnimControl: %s" % (animName))
             else:
             else:
                 animDict[animName] = animControl
                 animDict[animName] = animControl
             
             
-        # add this part's dictionary to animation dictionary
-        self.__animControlDict[partName] = animDict
-    
+            # add this part's dictionary to animation dictionary
+            if not (self.__animControlDict.has_key(lodName)):
+                lodDict = {}
+                self.__animControlDict[lodName] = lodDict
+                
+            self.__animControlDict[lodName][partName] = animDict
+                
 
 
     def __copyPartBundles(self, other):
     def __copyPartBundles(self, other):
         """__copyPartBundles(self, Actor)
         """__copyPartBundles(self, Actor)