Browse Source

Initial revision

David Rose 25 years ago
parent
commit
c07eb09341
85 changed files with 8382 additions and 0 deletions
  1. 18 0
      direct/Config.pp
  2. 34 0
      direct/Package.pp
  3. 10 0
      direct/Sources.pp
  4. 7 0
      direct/metalibs/Sources.pp
  5. 20 0
      direct/metalibs/direct/Sources.pp
  6. 9 0
      direct/metalibs/direct/direct.cxx
  7. 4 0
      direct/src/Sources.pp
  8. 580 0
      direct/src/actor/Actor.py
  9. 3 0
      direct/src/actor/Sources.pp
  10. 1 0
      direct/src/configfiles/Configrc
  11. 2 0
      direct/src/configfiles/Sources.pp
  12. 7 0
      direct/src/configfiles/direct.init
  13. 34 0
      direct/src/dcparse/Makefile.gnu
  14. 29 0
      direct/src/dcparse/Sources.pp
  15. 176 0
      direct/src/dcparse/dc.dsp
  16. 29 0
      direct/src/dcparse/dc.dsw
  17. 240 0
      direct/src/dcparse/dcAtomicField.cxx
  18. 74 0
      direct/src/dcparse/dcAtomicField.h
  19. 340 0
      direct/src/dcparse/dcClass.cxx
  20. 76 0
      direct/src/dcparse/dcClass.h
  21. 192 0
      direct/src/dcparse/dcFile.cxx
  22. 53 0
      direct/src/dcparse/dcFile.h
  23. 415 0
      direct/src/dcparse/dcLexer.lxx
  24. 20 0
      direct/src/dcparse/dcLexerDefs.h
  25. 92 0
      direct/src/dcparse/dcMolecularField.cxx
  26. 46 0
      direct/src/dcparse/dcMolecularField.h
  27. 295 0
      direct/src/dcparse/dcParser.yxx
  28. 43 0
      direct/src/dcparse/dcParserDefs.h
  29. 43 0
      direct/src/dcparse/dcSubatomicType.cxx
  30. 40 0
      direct/src/dcparse/dcSubatomicType.h
  31. 59 0
      direct/src/dcparse/dcbase.h
  32. 30 0
      direct/src/dcparse/dcparse.cxx
  33. 18 0
      direct/src/dcparse/indent.cxx
  34. 29 0
      direct/src/dcparse/indent.h
  35. 21 0
      direct/src/dcparse/test.dc
  36. 10 0
      direct/src/directbase/Sources.pp
  37. 6 0
      direct/src/directbase/directbase.cxx
  38. 18 0
      direct/src/directbase/directbase.h
  39. 30 0
      direct/src/directbase/directsymbols.h
  40. 21 0
      direct/src/distributed/ClientDistClass.py
  41. 84 0
      direct/src/distributed/ClientRepository.py
  42. 8 0
      direct/src/distributed/DistributedActor.py
  43. 7 0
      direct/src/distributed/DistributedNode.py
  44. 7 0
      direct/src/distributed/DistributedObject.py
  45. 90 0
      direct/src/distributed/ServerRepository.py
  46. 3 0
      direct/src/distributed/Sources.pp
  47. 479 0
      direct/src/extensions/NodePath-extensions.py
  48. 3 0
      direct/src/extensions/Sources.pp
  49. 31 0
      direct/src/ffi/FFIConstants.py
  50. 23 0
      direct/src/ffi/FFIEnvironment.py
  51. 130 0
      direct/src/ffi/FFIExternalObject.py
  52. 655 0
      direct/src/ffi/FFIInterrogateDatabase.py
  53. 280 0
      direct/src/ffi/FFIOverload.py
  54. 145 0
      direct/src/ffi/FFIRename.py
  55. 499 0
      direct/src/ffi/FFISpecs.py
  56. 767 0
      direct/src/ffi/FFITypes.py
  57. 3 0
      direct/src/ffi/Sources.pp
  58. 107 0
      direct/src/ffi/generatePythonCode
  59. 4 0
      direct/src/showbase/AudioManagerGlobal.py
  60. 65 0
      direct/src/showbase/DirectNotify.py
  61. 6 0
      direct/src/showbase/DirectNotifyGlobal.py
  62. 26 0
      direct/src/showbase/DirectObject.py
  63. 87 0
      direct/src/showbase/EventManager.py
  64. 3 0
      direct/src/showbase/EventManagerGlobal.py
  65. 140 0
      direct/src/showbase/FSM.py
  66. 14 0
      direct/src/showbase/LerpBlendHelpers.py
  67. 96 0
      direct/src/showbase/Loader.py
  68. 93 0
      direct/src/showbase/Logger.py
  69. 5 0
      direct/src/showbase/LoggerGlobal.py
  70. 126 0
      direct/src/showbase/Messenger.py
  71. 5 0
      direct/src/showbase/MessengerGlobal.py
  72. 127 0
      direct/src/showbase/Notifier.py
  73. 55 0
      direct/src/showbase/OnscreenText.py
  74. 10 0
      direct/src/showbase/PandaObject.py
  75. 22 0
      direct/src/showbase/PythonUtil.py
  76. 140 0
      direct/src/showbase/ShowBase.py
  77. 16 0
      direct/src/showbase/ShowBaseGlobal.py
  78. 14 0
      direct/src/showbase/Sources.pp
  79. 136 0
      direct/src/showbase/State.py
  80. 335 0
      direct/src/showbase/Task.py
  81. 4 0
      direct/src/showbase/TaskManagerGlobal.py
  82. 21 0
      direct/src/showbase/TkGlobal.py
  83. 36 0
      direct/src/showbase/ppython
  84. 262 0
      direct/src/showbase/showBase.cxx
  85. 39 0
      direct/src/showbase/showBase.h

+ 18 - 0
direct/Config.pp

@@ -0,0 +1,18 @@
+//
+// Config.pp
+//
+// This file defines certain configuration variables that are written
+// into the various make scripts.  It is processed by ppremake (along
+// with the Sources.pp files in each of the various directories) to
+// generate build scripts appropriate to each environment.
+//
+// There are not too many variables to declare at this level; most of
+// them are defined in the DTOOL-specific Config.pp.
+
+
+// Where should we find PANDA?  This will come from the environment
+// variable if it is set.
+#if $[eq $[PANDA],]
+  #define PANDA /usr/local/panda
+#endif
+

+ 34 - 0
direct/Package.pp

@@ -0,0 +1,34 @@
+//
+// Package.pp
+//
+// This file defines certain configuration variables that are to be
+// written into the various make scripts.  It is processed by ppremake
+// (along with the Sources.pp files in each of the various
+// directories) to generate build scripts appropriate to each
+// environment.
+//
+// This is the package-specific file, which should be at the top of
+// every source hierarchy.  It generally gets the ball rolling, and is
+// responsible for explicitly including all of the relevent Config.pp
+// files.
+
+
+
+// What is the name and version of this source tree?
+#if $[eq $[PACKAGE],]
+  #define PACKAGE direct
+  #define VERSION 0.80
+#endif
+
+
+// Pull in the package-level Config file.  This contains a few
+// configuration variables that the user might want to fine-tune.
+#include $[THISDIRPREFIX]Config.pp
+
+
+// Also get the PANDA Package file and everything that includes.
+#if $[not $[isdir $[PANDA]]]
+  #error Directory defined by $PANDA not found!  Are you attached properly?
+#endif
+
+#include $[PANDA]/Package.pp

+ 10 - 0
direct/Sources.pp

@@ -0,0 +1,10 @@
+// This is the toplevel directory.  It contains configure.in and other
+// stuff.
+
+#define DIR_TYPE toplevel
+
+#define SAMPLE_SOURCE_FILE src/directbase/directbase.cxx
+#define REQUIRED_TREES dtool panda
+
+#define EXTRA_DIST \
+    Sources.pp Config.pp Package.pp

+ 7 - 0
direct/metalibs/Sources.pp

@@ -0,0 +1,7 @@
+// This is a group directory: a directory level above a number of
+// source subdirectories.
+
+#define DIR_TYPE group
+
+// The metalibs directory always depends on the src directory.
+#define DEPENDS src

+ 20 - 0
direct/metalibs/direct/Sources.pp

@@ -0,0 +1,20 @@
+// DIR_TYPE "metalib" indicates we are building a shared library that
+// consists mostly of references to other shared libraries.  Under
+// Windows, this directly produces a DLL (as opposed to the regular
+// src libraries, which don't produce anything but a pile of OBJ files
+// under Windows).
+
+#define DIR_TYPE metalib
+#define BUILDING_DLL BUILDING_DIRECT
+
+#define COMPONENT_LIBS \
+   directbase dcparse showbase
+
+#define OTHER_LIBS panda dtool
+
+#begin metalib_target
+  #define TARGET direct
+
+  #define SOURCES direct.cxx
+#end metalib_target
+

+ 9 - 0
direct/metalibs/direct/direct.cxx

@@ -0,0 +1,9 @@
+// Filename: direct.cxx
+// Created by:  drose (18May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+// This is a dummy file whose sole purpose is to give the compiler
+// something to compile when making libdirect.so in NO_DEFER mode,
+// which generates an empty library that itself links with all the
+// other shared libraries that make up libdirect.

+ 4 - 0
direct/src/Sources.pp

@@ -0,0 +1,4 @@
+// This is a group directory: a directory level above a number of
+// source subdirectories.
+
+#define DIR_TYPE group

+ 580 - 0
direct/src/actor/Actor.py

@@ -0,0 +1,580 @@
+"""Actor module: contains the Actor class"""
+
+from PandaObject import *
+
+class Actor(PandaObject, ShowBase.NodePath):
+    """Actor class: Contains methods for creating, manipulating
+    and playing animations on characters"""
+
+    #create the Actor class DirectNotify category
+    notify = directNotify.newCategory("Actor")
+    partPrefix = "__Actor_"
+    
+    #special methods
+    
+    def __init__(self, models=None, anims=None, other=None):
+        """__init__(self, string | string:string{}, string:string{} |
+        string:(string:string{}){}, Actor=None)
+        Actor constructor: can be used to create single or multipart
+        actors. If another Actor is supplied as an argument this
+        method acts like a copy constructor. Single part actors are
+        created by calling with a model and animation dictionary
+        (animName:animPath{}) as follows:
+
+           a = Actor("panda-3k.egg", {"walk":"panda-walk.egg" \
+                                      "run":"panda-run.egg"})
+
+        This could be displayed and animated as such:
+
+           a.reparentTo(render)
+           a.loop("walk")
+           a.stop()
+
+        Multipart actors expect a dictionary of parts and a dictionary
+        of animation dictionaries (partName:(animName:animPath{}){}) as
+        below:
+        
+            a = Actor(
+        
+                # part dictionary
+                {"head":"char/dogMM/dogMM_Shorts-head-mod", \
+                 "torso":"char/dogMM/dogMM_Shorts-torso-mod", \
+                 "legs":"char/dogMM/dogMM_Shorts-legs-mod"} , \
+
+                # dictionary of anim dictionaries
+                {"head":{"walk":"char/dogMM/dogMM_Shorts-head-walk", \
+                         "run":"char/dogMM/dogMM_Shorts-head-run"}, \
+                 "torso":{"walk":"char/dogMM/dogMM_Shorts-torso-walk", \
+                          "run":"char/dogMM/dogMM_Shorts-torso-run"}, \
+                 "legs":{"walk":"char/dogMM/dogMM_Shorts-legs-walk", \
+                         "run":"char/dogMM/dogMM_Shorts-legs-run"} \
+                 })
+
+        In addition multipart actor parts need to be connected together
+        in a meaningful fashion:
+        
+            a.attach("head", "torso", "joint-head")
+            a.attach("torso", "legs", "joint-hips")
+
+
+        Other useful Acotr class functions:
+
+            #fix actor eye rendering
+            a.drawInFront("joint-pupil?", "eyes*")
+
+            #fix bounding volumes - this must be done after drawing
+            #the actor for a few frames, otherwise it has no effect
+            a.fixBounds()
+            
+        """
+
+        # initial 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'))
+        
+            # load models
+            # make sure we have models
+            if (models):
+                # if this is a dictionary
+                if (type(models)==type({})):
+                    # then it must be multipart actor
+                    for partName in models.keys():
+                        self.loadModel(models[partName], partName)
+                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)
+
+        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):
+        """__str__(self)
+        Actor print function"""
+        return "Actor: partBundleDict = %s, animControlDict = %s" % \
+               (self.__partBundleDict, self.__animControlDict)
+        
+
+    # accessing
+    
+    def getPartNames(self):
+        """getPartNames(self):
+        Return list of Actor part names. If not multipart,
+        returns modelRoot"""
+        return self.__partBundleDict.keys()
+    
+    def getGeomNode(self):
+        """getGeomNode(self)
+        Return the node that contains all actor geometry"""
+        return self.__geomNode
+
+    def setGeomNode(self, node):
+        """setGeomNode(self, node)
+        Set the node that contains all actor geometry"""
+        self.__geomNode = node
+
+    def getFrameRate(self, animName=None, partName=None):
+        """getFrameRate(self, string, string=None)
+        Return duration of given anim name and given part.
+        If no anim specified, use the currently playing anim.
+        If no part specified, return anim durations of first part"""
+        if (partName == None):
+            partName = self.__animControlDict.keys()[0]
+    
+        if (animName==None):
+            animName = self.getCurrentAnim(partName)
+
+        # get duration for named part only
+        if (self.__animControlDict.has_key(partName)):        
+            animControl = self.__getAnimControl(animName, partName)
+            if (animControl != None):
+                return animControl.getFrameRate()
+        else:
+            Actor.notify.warning("no part named %s" % (partName))
+
+        return None
+
+    def getPlayRate(self, animName=None, partName=None):
+        """getPlayRate(self, string=None, string=None)
+        Return the play rate of given anim for a given part.
+        If no part is given, assume first part in dictionary.
+        If no anim is given, find the current anim for the part"""
+        if (partName==None):
+            partName = self.__animControlDict.keys()[0]
+
+        if (animName==None):
+            animName = self.getCurrentAnim(partName)
+            
+        animControl = self.__getAnimControl(animName, partName)
+        if (animControl != None):
+            return animControl.getPlayRate()
+        else:
+            return None
+    
+    def setPlayRate(self, rate, animName=None, partName=None):
+        """getPlayRate(self, float, string=None, string=None)
+        Set the play rate of given anim for a given part.
+        If no part is given, set for all parts in dictionary.
+        If no anim is given, find the current anim for the part"""
+        # make a list of partNames for loop below
+        if (partName==None):
+            partNames = self.__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)
+            
+    def getDuration(self, animName=None, partName=None):
+        """getDuration(self, string, string=None)
+        Return duration of given anim name and given part.
+        If no anim specified, use the currently playing anim.
+        If no part specified, return anim duration of first part"""
+        if (partName == None):
+            partName = self.__animControlDict.keys()[0]
+
+        if (animName==None):
+            animName = self.getCurrentAnim(partName)          
+
+        # get duration for named part only
+        if (self.__animControlDict.has_key(partName)):        
+            animControl = self.__getAnimControl(animName, partName)
+            if (animControl != None):
+                return (animControl.getNumFrames() / \
+                        animControl.getFrameRate())
+        else:
+            Actor.notify.warning("no part named %s" % (partName))
+
+        return None
+        
+    def getCurrentAnim(self, partName=None):
+        """getCurrentAnim(self, string=None)
+        Return the anim current playing on the actor. If part not
+        specified return current anim of first part in dictionary"""
+        if (partName==None):
+            partName = self.__animControlDict.keys()[0]
+
+        # loop through all anims for named part and find if any are playing
+        if (self.__animControlDict.has_key(partName)):
+            for animName in self.__animControlDict[partName].keys():
+                if (self.__getAnimControl(animName, partName).isPlaying()):
+                    return animName
+        else:
+            Actor.notify.warning("no part named %s" % (partName))
+
+        # we must have found none, or gotten an error
+        return None
+
+
+
+    # arranging
+
+    def getPart(self, partName):
+        """getPart(self, string)
+        Find the named part in the partBundleDict and return it, or
+        return None if not present"""
+        if (self.__partBundleDict.has_key(partName)):
+            return self.__partBundleDict[partName]
+        else:
+            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 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()
+        else:
+            Actor.notify.warning("no part named %s!" % (partName))
+
+    def showPart(self, partName):
+        """showPart(self, string)
+        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()
+        else:
+            Actor.notify.warning("no part named %s!" % (partName))
+            
+    def instance(self, partName, anotherPart, jointName):
+        """instance(self, string, string, string)
+        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))
+                else:
+                    return self.__partBundleDict[partName].instanceTo(joint)
+            else:
+                Actor.notify.warning("no part named %s!" % (anotherPart))
+        else:
+            Actor.notify.warning("no part named %s!" % (partName))
+
+
+    def attach(self, partName, anotherPart, jointName):
+        """attach(self, string, string, string)
+        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], \
+                                 "**/" + jointName)
+                if (joint.isEmpty()):
+                    Actor.notify.warning("%s not found!" % (jointName))
+                else:
+                    self.__partBundleDict[partName].reparentTo(joint)
+            else:
+                Actor.notify.warning("no part named %s!" % (anotherPart))
+        else:
+            Actor.notify.warning("no part named %s!" % (partName))
+
+                    
+    def drawInFront(self, frontPartName, backPartName, root=None):
+        """drawInFront(self, string, string=None)
+        Arrange geometry so the frontPart is drawn properly wrt backPart.
+        Takes an optional argument root as the start of the search for the
+        given parts"""
+
+        # start search from self if no root given
+        if (root==None):
+            root = self
+            
+        # make the back part have the proper transition
+        backPart = NodePath(root, "**/"+backPartName)
+        if (backPart.isEmpty()):
+            Actor.notify.warning("no part named %s!" % (backPartName))
+        else:
+            (backPart.getBottomArc()).setTransition(DirectRenderTransition())
+
+        #reparent the front parts to the back parts
+        frontParts = self.findAllMatches( "**/"+frontPartName)
+        numFrontParts = frontParts.getNumPaths()
+        for partNum in range(0, numFrontParts):
+            (frontParts.getPath(partNum)).reparentTo(backPart)
+
+
+    def fixBounds(self, part=None):
+        """fixBounds(self, nodePath=None)
+        Force recomputation of bounding spheres for all geoms
+        in a given part. If no part specified, fix all geoms
+        in this actor"""
+        
+        # if no part name specified fix all parts
+        if (part==None):
+            part = self
+
+        # update all characters first
+        charNodes = part.findAllMatches("**/+Character")
+        numCharNodes = charNodes.getNumPaths()
+        for charNum in range(0, numCharNodes):
+            (charNodes.getPath(charNum)).node().update()
+
+        # for each geomNode, iterate through all geoms and force update
+        # of bounding spheres by marking current bounds as stale
+        geomNodes = part.findAllMatches("**/+GeomNode")
+        numGeomNodes = geomNodes.getNumPaths()
+        for nodeNum in range(0, numGeomNodes):
+            thisGeomNode = geomNodes.getPath(nodeNum)
+            numGeoms = thisGeomNode.node().getNumGeoms()
+            for geomNum in range(0, numGeoms):
+                thisGeom = thisGeomNode.node().getGeom(geomNum)
+                thisGeom.markBoundStale()
+                Actor.notify.info("fixing bounds for node %s, geom %s" % \
+                                  (nodeNum, geomNum))
+            thisGeomNode.node().markBoundStale()
+
+    def showBounds(self):
+        """showBounds(self)
+        Show the bounds of all actor geoms"""
+        geomNodes = self.findAllMatches("**/+GeomNode")
+        numGeomNodes = geomNodes.getNumPaths()
+
+        for nodeNum in range(0, numGeomNodes):
+            geomNodes.getPath(nodeNum).showBounds()
+
+    def hideBounds(self):
+        """hideBounds(self)
+        Hide the bounds of all actor geoms"""
+        geomNodes = self.findAllMatches("**/+GeomNode")
+        numGeomNodes = geomNodes.getNumPaths()
+
+        for nodeNum in range(0, numGeomNodes):
+            geomNodes.getPath(nodeNum).hideBounds()
+                    
+
+    # actions
+    
+    def stop(self, animName=None, partName=None):
+        """stop(self, string=None, string=None)
+        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()
+        
+    def play(self, animName, partName=None):
+        """play(self, string, string=None)
+        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 (animControl != None):
+                    animControl.play()
+        else:
+            animControl = self.__getAnimControl(animName, partName)
+            if (animControl != None):
+                animControl.play()
+
+
+    def loop(self, animName, restart=1, partName=None):
+        """loop(self, string, int=1, string=None)
+        Loop the given animation on the given part of the actor,
+        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)
+                if (animControl != None):
+                    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):
+        """pose(self, string, int, string=None)
+        Pose the actor in position found at given frame in the specified
+        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)
+                if (animControl != None):
+                    animControl.pose(frame)
+        else:
+            # pose a specific part
+            animControl = self.__getAnimControl(animName, partName)
+            if (animControl != None):
+                animControl.pose(frame)
+        
+
+    #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]
+            else:
+                # anim was not present
+                Actor.notify.warning("couldn't find anim: %s" % (animName))
+        else:
+            # part was not present
+            Actor.notify.warning("couldn't find part: %s" % (partName))
+                
+        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))
+
+        # load the model and extract its part bundle
+        model = loader.loadModelCopy(modelPath)
+
+
+        bundle = NodePath(model, "**/+PartBundleNode")
+        if (bundle.isEmpty()):
+            Actor.notify.warning("%s is not a character!" % (modelPath))
+        else:
+            # we rename this node to make Actor copying easier
+            bundle.node().setName(Actor.partPrefix + partName)
+            bundle.reparentTo(self.__geomNode)
+            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")
+        Actor anim loader. Takes an optional partName (defaults to
+        'modelRoot' for non-multipart actors) and dict of corresponding
+        anims in the form animName:animPath{}"""
+        
+        Actor.notify.info("in loadAnims: %s, part: %s" % (anims, partName))
+
+        animDict = {}
+
+        for animName in anims.keys():
+            
+            #load the anim and get its anim bundle
+            anim = loader.loadModelCopy(anims[animName])
+            animBundle = \
+                (NodePath(anim, "**/+AnimBundleNode").node()).getBundle()
+
+            #bind anim
+            bundleNode = (self.__partBundleDict[partName]).node()
+            animControl = (bundleNode.getBundle()).bindAnim(animBundle, -1)
+            if (animControl == None):
+                Actor.notify.error("Null AnimControl: %s" % (animName))
+            else:
+                animDict[animName] = animControl
+            
+        # add this part's dictionary to animation dictionary
+        self.__animControlDict[partName] = animDict
+    
+
+    def __copyPartBundles(self, other):
+        """__copyPartBundles(self, Actor)
+        Copy the part bundle dictionary from another actor as this
+        instance's own. NOTE: this method does not actually copy geometry"""
+        for partName in other.__partBundleDict.keys():
+            print("copyPart: copying part named = %s" % (partName))
+            # find the part in our tree
+            partBundle = self.find("**/" + Actor.partPrefix + partName) 
+            if (partBundle != None):
+                # store the part bundle
+                self.__partBundleDict[partName] = partBundle
+            else:
+                Actor.notify.error("couldn't find matching part:  %s" % \
+                                   partName)
+
+
+    def __copyAnimControls(self, other):
+        """__copyAnimControls(self, Actor)
+        Get the anims from the anim control's in the anim control
+        dictionary of another actor. Bind these anim's to the part
+        bundles in our part bundle dict that have matching names, and
+        store the resulting anim controls in our own part bundle dict"""
+        for partName in other.__animControlDict.keys():
+            print("copyAnim: partName = %s" % (partName))
+            self.__animControlDict[partName] = {}
+            for animName in other.__animControlDict[partName].keys():
+                print("    anim: %s" % (animName))
+                # get the anim
+                animBundle = \
+                    other.__animControlDict[partName][animName].getAnim()
+                # get the part
+                partBundleNode = (self.__partBundleDict[partName].node())
+                # bind the anim
+                animControl = \
+                    (partBundleNode.getBundle().bindAnim(animBundle, -1))
+                if (animControl == None):
+                    Actor.notify.error("Null animControl: %s" % (animName))
+                else:
+                    # store the anim control
+                    self.__animControlDict[partName][animName] = animControl
+                

+ 3 - 0
direct/src/actor/Sources.pp

@@ -0,0 +1,3 @@
+// For now, since we are not installing Python files, this file can
+// remain empty.
+

+ 1 - 0
direct/src/configfiles/Configrc

@@ -0,0 +1 @@
+model-path      /beta/direct/install

+ 2 - 0
direct/src/configfiles/Sources.pp

@@ -0,0 +1,2 @@
+#define INSTALL_DATA \
+  direct.init Configrc

+ 7 - 0
direct/src/configfiles/direct.init

@@ -0,0 +1,7 @@
+ATTACH panda
+MODREL ETC_PATH etc
+SEP PYTHONPATH :
+MODREL PYTHONPATH lib
+MODREL PYTHONPATH lib/py
+MODREL PYTHONPATH src/all/python
+

+ 34 - 0
direct/src/dcparse/Makefile.gnu

@@ -0,0 +1,34 @@
+OFILES = \
+  dcParser.o dcLexer.o \
+  dcAtomicField.o dcClass.o dcFile.o dcMolecularField.o \
+  dcSubatomicType.o indent.o \
+  dcparse.o
+
+# Cheesy dependencies.
+HFILES = \
+  dcAtomicField.h dcClass.h dcClassDescription.h dcFile.h	\
+  dcLexerDefs.h dcMolecularField.h dcParser.h dcParserDefs.h	\
+  dcSubatomicType.h dcbase.h indent.h
+
+dcparse : $(OFILES)
+	g++ -o $@ $(OFILES)
+
+dcParser.cxx : dcParser.yxx
+	bison -d --name-prefix=dcyy -o $@ $<
+	mv [email protected] dcParser.h
+
+dcLexer.cxx : dcLexer.lxx
+	flex -Pdcyy -ot.lex $<
+	sed '/#include <unistd.h>/d' t.lex >$@
+	rm -f t.lex
+
+%.o: %.cxx $(HFILES)
+	g++ -c -g -Wall -o $@ $<
+
+clean:
+	rm -f *.o dcparse
+
+zip:
+	rm -f dcparse.zip
+	zip dcparse.zip Makefile.gnu *.cxx *.h *.lxx *.yxx *.dc \
+             dc.dsp dc.dsw

+ 29 - 0
direct/src/dcparse/Sources.pp

@@ -0,0 +1,29 @@
+#define OTHER_LIBS interrogatedb:c dconfig:c dtoolutil:c dtoolbase:c dtool:m
+#define YACC_PREFIX dcyy
+
+#begin lib_target
+  #define TARGET dcparse
+
+  #define SOURCES \
+    dcAtomicField.cxx dcAtomicField.h dcClass.cxx dcClass.h \
+    dcFile.cxx dcFile.h dcLexer.lxx dcLexerDefs.h \
+    dcMolecularField.cxx dcMolecularField.h dcParser.yxx \
+    dcParserDefs.h dcSubatomicType.cxx dcSubatomicType.h dcbase.h \
+    indent.cxx indent.h
+
+  #define EXTRA_DIST test.dc dc.dsp dc.dsw
+
+  #define IGATESCAN all
+#end lib_target
+
+#begin test_bin_target
+  #define TARGET dcparse
+  #define LOCAL_LIBS dcparse
+  #define OTHER_LIBS $[OTHER_LIBS] pystub
+
+  #define SOURCES \
+    dcparse.cxx
+
+  #define EXTRA_DIST test.dc dc.dsp dc.dsw
+#end test_bin_target
+

+ 176 - 0
direct/src/dcparse/dc.dsp

@@ -0,0 +1,176 @@
+# Microsoft Developer Studio Project File - Name="dc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=dc - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "dc.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "dc.mak" CFG="dc - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "dc - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "dc - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "dc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "dc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "dc - Win32 Release"
+# Name "dc - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\dcAtomicField.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcClass.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcFile.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcLexer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcMolecularField.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcparse.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcParser.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcSubatomicType.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\indent.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\dcAtomicField.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcbase.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcClass.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcClassDescription.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcLexerDefs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcMolecularField.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcParser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcParserDefs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dcSubatomicType.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\indent.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project

+ 29 - 0
direct/src/dcparse/dc.dsw

@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "dc"=.\dc.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

+ 240 - 0
direct/src/dcparse/dcAtomicField.cxx

@@ -0,0 +1,240 @@
+// Filename: dcAtomicField.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcAtomicField.h"
+#include "indent.h"
+
+#include <assert.h>
+
+ostream &
+operator << (ostream &out, const DCAtomicField::ElementType &et) {
+  out << et._type;
+  if (et._divisor != 1) {
+    out << " / " << et._divisor;
+  }
+  return out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::get_number
+//       Access: Public
+//  Description: Returns a unique index number associated with this
+//               field.  This is defined implicitly when the .dc
+//               file(s) are read.
+////////////////////////////////////////////////////////////////////
+int DCAtomicField::
+get_number() const {
+  return _number;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::get_name
+//       Access: Public
+//  Description: Returns the name of this field.
+////////////////////////////////////////////////////////////////////
+string DCAtomicField::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::get_num_elements
+//       Access: Public
+//  Description: Returns the number of elements of the atomic field.
+////////////////////////////////////////////////////////////////////
+int DCAtomicField::
+get_num_elements() const {
+  return _elements.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::get_element_type
+//       Access: Public
+//  Description: Returns the numeric type of the nth element of the
+//               field.
+////////////////////////////////////////////////////////////////////
+DCSubatomicType DCAtomicField::
+get_element_type(int n) const {
+  assert(n >= 0 && n < (int)_elements.size());
+  return _elements[n]._type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::get_element_divisor
+//       Access: Public
+//  Description: Returns the divisor associated with the nth element
+//               of the field.  This implements an implicit
+//               fixed-point system; floating-point values are to be
+//               multiplied by this value before encoding into a
+//               packet, and divided by this number after decoding.
+////////////////////////////////////////////////////////////////////
+int DCAtomicField::
+get_element_divisor(int n) const {
+  assert(n >= 0 && n < (int)_elements.size());
+  return _elements[n]._divisor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_required
+//       Access: Public
+//  Description: Returns true if the "required" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_required() const {
+  return (_flags & F_required) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_broadcast
+//       Access: Public
+//  Description: Returns true if the "broadcast" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_broadcast() const {
+  return (_flags & F_broadcast) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_p2p
+//       Access: Public
+//  Description: Returns true if the "p2p" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_p2p() const {
+  return (_flags & F_p2p) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_ram
+//       Access: Public
+//  Description: Returns true if the "ram" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_ram() const {
+  return (_flags & F_ram) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_db
+//       Access: Public
+//  Description: Returns true if the "db" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_db() const {
+  return (_flags & F_db) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_clsend
+//       Access: Public
+//  Description: Returns true if the "clsend" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_clsend() const {
+  return (_flags & F_clsend) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_clrecv
+//       Access: Public
+//  Description: Returns true if the "clrecv" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_clrecv() const {
+  return (_flags & F_clrecv) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_aisend
+//       Access: Public
+//  Description: Returns true if the "aisend" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_aisend() const {
+  return (_flags & F_aisend) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::is_airecv
+//       Access: Public
+//  Description: Returns true if the "airecv" flag is set for this
+//               field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+is_airecv() const {
+  return (_flags & F_airecv) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCAtomicField::
+DCAtomicField() {
+  _number = 0;
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::write
+//       Access: Public
+//  Description: Generates a parseable description of the object to
+//               the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void DCAtomicField::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << _name << "(";
+
+  if (!_elements.empty()) {
+    Elements::const_iterator ei = _elements.begin();
+    out << (*ei);
+    ++ei;
+    while (ei != _elements.end()) {
+      out << ", " << (*ei);
+      ++ei;
+    }
+  }
+  out << ")";
+
+  if ((_flags & F_required) != 0) {
+    out << " required";
+  }
+  if ((_flags & F_broadcast) != 0) {
+    out << " broadcast";
+  }
+  if ((_flags & F_p2p) != 0) {
+    out << " p2p";
+  }
+  if ((_flags & F_ram) != 0) {
+    out << " ram";
+  }
+  if ((_flags & F_db) != 0) {
+    out << " db";
+  }
+  if ((_flags & F_clsend) != 0) {
+    out << " clsend";
+  }
+  if ((_flags & F_clrecv) != 0) {
+    out << " clrecv";
+  }
+  if ((_flags & F_aisend) != 0) {
+    out << " aisend";
+  }
+  if ((_flags & F_airecv) != 0) {
+    out << " airecv";
+  }
+
+  out << ";  // atomic " << _number << "\n";
+}

+ 74 - 0
direct/src/dcparse/dcAtomicField.h

@@ -0,0 +1,74 @@
+// Filename: dcAtomicField.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCATOMICFIELD_H
+#define DCATOMICFIELD_H
+
+#include "dcbase.h"
+#include "dcSubatomicType.h"
+
+#include <vector>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DCAtomicField
+// Description : A single atomic field of a Distributed Class, as read
+//               from a .dc file.  This defines an interface to the
+//               Distributed Class, and is always implemented as a
+//               remote procedure method.
+////////////////////////////////////////////////////////////////////
+class DCAtomicField {
+PUBLISHED:
+  int get_number() const;
+  string get_name() const;
+
+  int get_num_elements() const;
+  DCSubatomicType get_element_type(int n) const;
+  int get_element_divisor(int n) const;
+
+  bool is_required() const;
+  bool is_broadcast() const;
+  bool is_p2p() const;
+  bool is_ram() const;
+  bool is_db() const;
+  bool is_clsend() const;
+  bool is_clrecv() const;
+  bool is_aisend() const;
+  bool is_airecv() const;
+
+public:
+  DCAtomicField();
+  void write(ostream &out, int indent_level = 0) const;
+
+public:
+  // These members define the primary interface to the atomic field
+  // definition as read from the file.
+  int _number;
+  string _name;
+
+  class ElementType {
+  public:
+    DCSubatomicType _type;
+    int _divisor;
+  };
+
+  typedef vector<ElementType> Elements;
+  Elements _elements;
+
+  enum Flags {
+    F_required        = 0x0001,
+    F_broadcast       = 0x0002,
+    F_p2p             = 0x0004,
+    F_ram             = 0x0008,
+    F_db              = 0x0010,
+    F_clsend          = 0x0020,
+    F_clrecv          = 0x0040,
+    F_aisend          = 0x0080,
+    F_airecv          = 0x0100
+  };
+
+  int _flags;  // A bitmask union of any of the above values.
+};
+
+#endif

+ 340 - 0
direct/src/dcparse/dcClass.cxx

@@ -0,0 +1,340 @@
+// Filename: dcClass.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcClass.h"
+#include "indent.h"
+
+#include <assert.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_number
+//       Access: Public
+//  Description: Returns a unique index number associated with this
+//               class.  This is defined implicitly when the .dc
+//               file(s) are read.
+////////////////////////////////////////////////////////////////////
+int DCClass::
+get_number() const {
+  return _number;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_name
+//       Access: Public
+//  Description: Returns the name of this class.
+////////////////////////////////////////////////////////////////////
+string DCClass::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::has_parent
+//       Access: Public
+//  Description: Returns true if this class inherits from some other
+//               class, false if it does not.
+////////////////////////////////////////////////////////////////////
+bool DCClass::
+has_parent() const {
+  return !_parents.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_parent
+//       Access: Public
+//  Description: Returns the parent class this class inherits from, if
+//               any.  It is an error to call this unless has_parent()
+//               returned true.
+////////////////////////////////////////////////////////////////////
+DCClass *DCClass::
+get_parent() const {
+  assert(has_parent());
+  return _parents.front();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_num_atomics
+//       Access: Public
+//  Description: Returns the number of atomic fields defined directly
+//               in this class, ignoring inheritance.
+////////////////////////////////////////////////////////////////////
+int DCClass::
+get_num_atomics() {
+  return _atomic_fields.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_atomic
+//       Access: Public
+//  Description: Returns the nth atomic field in the class.  This is
+//               not necessarily the field with index n; this is the
+//               nth field defined in the class directly, ignoring
+//               inheritance.
+////////////////////////////////////////////////////////////////////
+DCAtomicField *DCClass::
+get_atomic(int n) {
+  assert(n >= 0 && n < (int)_atomic_fields.size());
+  return _atomic_fields[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_atomic_by_name
+//       Access: Public
+//  Description: Returns a pointer to the DCAtomicField that shares
+//               the indicated name.  If the named field is not found
+//               in the current class, the parent classes will be
+//               searched, so the value returned may not actually be a
+//               field within this class.  Returns NULL if there is no
+//               such field defined.
+////////////////////////////////////////////////////////////////////
+DCAtomicField *DCClass::
+get_atomic_by_name(const string &name) {
+  AtomicsByName::const_iterator ni;
+  ni = _atomics_by_name.find(name);
+  if (ni != _atomics_by_name.end()) {
+    return (*ni).second;
+  }
+
+  // We didn't have such a field, so check our parents.
+  Parents::iterator pi;
+  for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
+    DCAtomicField *result = (*pi)->get_atomic_by_name(name);
+    if (result != (DCAtomicField *)NULL) {
+      return result;
+    }
+  }
+
+  // Nobody knew what this field is.
+  return (DCAtomicField *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_num_inherited_atomics
+//       Access: Public
+//  Description: Returns the total number of atomic fields defined in
+//               this class and all ancestor classes.
+////////////////////////////////////////////////////////////////////
+int DCClass::
+get_num_inherited_atomics() {
+  if (!_parents.empty()) {
+    // This won't work for multiple dclass inheritance.
+    return _parents.front()->get_num_atomics() + get_num_atomics();
+  }
+  return get_num_atomics();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_inherited_atomic
+//       Access: Public
+//  Description: Returns the nth atomic field in the class and all of
+//               its ancestors.  This *is* the field corresponding to
+//               the given index number, since the fields are ordered
+//               consecutively beginning at the earliest inherited
+//               fields.
+////////////////////////////////////////////////////////////////////
+DCAtomicField *DCClass::
+get_inherited_atomic(int n) {
+  if (!_parents.empty()) {
+    // This won't work for multiple dclass inheritance.
+    n -= _parents.front()->get_num_inherited_atomics();
+  }
+  return get_atomic(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_num_moleculars
+//       Access: Public
+//  Description: Returns the number of molecular fields defined directly
+//               in this class, ignoring inheritance.
+////////////////////////////////////////////////////////////////////
+int DCClass::
+get_num_moleculars() {
+  return _molecular_fields.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_molecular
+//       Access: Public
+//  Description: Returns the nth molecular field in the class.  This is
+//               not necessarily the field with index n; this is the
+//               nth field defined in the class directly, ignoring
+//               inheritance.
+////////////////////////////////////////////////////////////////////
+DCMolecularField *DCClass::
+get_molecular(int n) {
+  assert(n >= 0 && n < (int)_molecular_fields.size());
+  return _molecular_fields[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_molecular_by_name
+//       Access: Public
+//  Description: Returns a pointer to the DCMolecularField that shares
+//               the indicated name.  If the named field is not found
+//               in the current class, the parent classes will be
+//               searched, so the value returned may not actually be a
+//               field within this class.  Returns NULL if there is no
+//               such field defined.
+////////////////////////////////////////////////////////////////////
+DCMolecularField *DCClass::
+get_molecular_by_name(const string &name) {
+  MolecularsByName::const_iterator ni;
+  ni = _moleculars_by_name.find(name);
+  if (ni != _moleculars_by_name.end()) {
+    return (*ni).second;
+  }
+
+  // We didn't have such a field, so check our parents.
+  Parents::iterator pi;
+  for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
+    DCMolecularField *result = (*pi)->get_molecular_by_name(name);
+    if (result != (DCMolecularField *)NULL) {
+      return result;
+    }
+  }
+
+  // Nobody knew what this field is.
+  return (DCMolecularField *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_num_inherited_moleculars
+//       Access: Public
+//  Description: Returns the total number of molecular fields defined in
+//               this class and all ancestor classes.
+////////////////////////////////////////////////////////////////////
+int DCClass::
+get_num_inherited_moleculars() {
+  if (!_parents.empty()) {
+    // This won't work for multiple dclass inheritance.
+    return _parents.front()->get_num_moleculars() + get_num_moleculars();
+  }
+  return get_num_moleculars();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_inherited_molecular
+//       Access: Public
+//  Description: Returns the nth molecular field in the class and all of
+//               its ancestors.  This *is* the field corresponding to
+//               the given index number, since the fields are ordered
+//               consecutively beginning at the earliest inherited
+//               fields.
+////////////////////////////////////////////////////////////////////
+DCMolecularField *DCClass::
+get_inherited_molecular(int n) {
+  if (!_parents.empty()) {
+    // This won't work for multiple dclass inheritance.
+    n -= _parents.front()->get_num_inherited_moleculars();
+  }
+  return get_molecular(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCClass::
+DCClass() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCClass::
+~DCClass() {
+  AtomicFields::iterator ai;
+  for (ai = _atomic_fields.begin(); ai != _atomic_fields.end(); ++ai) {
+    delete (*ai);
+  }
+  MolecularFields::iterator mi;
+  for (mi = _molecular_fields.begin(); mi != _molecular_fields.end(); ++mi) {
+    delete (*mi);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::write
+//       Access: Public
+//  Description: Generates a parseable description of the object to
+//               the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << "dclass " << _name;
+
+  if (!_parents.empty()) {
+    Parents::const_iterator pi = _parents.begin();
+    out << " : " << (*pi)->_name;
+    ++pi;
+    while (pi != _parents.end()) {
+      out << ", " << (*pi)->_name;
+      ++pi;
+    }
+  }
+  out << " {  // index " << _number << "\n";
+
+  AtomicFields::const_iterator ai;
+  for (ai = _atomic_fields.begin(); ai != _atomic_fields.end(); ++ai) {
+    (*ai)->write(out, indent_level + 2);
+  }
+
+  MolecularFields::const_iterator mi;
+  for (mi = _molecular_fields.begin(); mi != _molecular_fields.end(); ++mi) {
+    (*mi)->write(out, indent_level + 2);
+  }
+
+  indent(out, indent_level) << "};\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::add_field
+//       Access: Public
+//  Description: Adds the newly-allocated atomic field to the class.
+//               The class becomes the owner of the pointer and will
+//               delete it when it destructs.  Returns true if the
+//               field is successfully added, or false if there was a
+//               name conflict.
+////////////////////////////////////////////////////////////////////
+bool DCClass::
+add_field(DCAtomicField *field) {
+  bool inserted = _atomics_by_name.insert
+    (AtomicsByName::value_type(field->_name, field)).second;
+
+  if (!inserted) {
+    return false;
+  }
+
+  field->_number = get_num_inherited_atomics();
+  _atomic_fields.push_back(field);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::add_field
+//       Access: Public
+//  Description: Adds the newly-allocated molecular field to the class.
+//               The class becomes the owner of the pointer and will
+//               delete it when it destructs.  Returns true if the
+//               field is successfully added, or false if there was a
+//               name conflict.
+////////////////////////////////////////////////////////////////////
+bool DCClass::
+add_field(DCMolecularField *field) {
+  bool inserted = _moleculars_by_name.insert
+    (MolecularsByName::value_type(field->_name, field)).second;
+
+  if (!inserted) {
+    return false;
+  }
+
+  field->_number = get_num_inherited_moleculars();
+  _molecular_fields.push_back(field);
+  return true;
+}

+ 76 - 0
direct/src/dcparse/dcClass.h

@@ -0,0 +1,76 @@
+// Filename: dcClass.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCCLASS_H
+#define DCCLASS_H
+
+#include "dcbase.h"
+#include "dcAtomicField.h"
+#include "dcMolecularField.h"
+
+#include <vector>
+#include <map>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DCClass
+// Description : Defines a particular DistributedClass as read from an
+//               input .dc file.
+////////////////////////////////////////////////////////////////////
+class DCClass {
+PUBLISHED:
+  int get_number() const;
+  string get_name() const;
+
+  bool has_parent() const;
+  DCClass *get_parent() const;
+
+  int get_num_atomics();
+  DCAtomicField *get_atomic(int n);
+  DCAtomicField *get_atomic_by_name(const string &name);
+
+  int get_num_inherited_atomics();
+  DCAtomicField *get_inherited_atomic(int n);
+
+  int get_num_moleculars();
+  DCMolecularField *get_molecular(int n);
+  DCMolecularField *get_molecular_by_name(const string &name);
+
+  int get_num_inherited_moleculars();
+  DCMolecularField *get_inherited_molecular(int n);
+
+public:
+  DCClass();
+  ~DCClass();
+
+  void write(ostream &out, int indent_level = 0) const;
+  bool add_field(DCAtomicField *field);
+  bool add_field(DCMolecularField *field);
+
+public:
+  // These members define the primary interface to the distributed
+  // class as read from the file.
+  int _number;
+  string _name;
+
+  typedef vector<DCClass *> Parents;
+  Parents _parents;
+
+  typedef vector<DCAtomicField *> AtomicFields;
+  AtomicFields _atomic_fields;
+
+  typedef vector<DCMolecularField *> MolecularFields;
+  MolecularFields _molecular_fields;
+
+public:
+  // These members are built up during parsing for the convenience of
+  // the parser.
+  typedef map<string, DCAtomicField *> AtomicsByName;
+  AtomicsByName _atomics_by_name;
+
+  typedef map<string, DCMolecularField *> MolecularsByName;
+  MolecularsByName _moleculars_by_name;
+};
+
+#endif

+ 192 - 0
direct/src/dcparse/dcFile.cxx

@@ -0,0 +1,192 @@
+// Filename: dcFile.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcFile.h"
+#include "dcParserDefs.h"
+#include "dcLexerDefs.h"
+
+#include <fstream>
+#include <assert.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCFile::
+DCFile() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCFile::
+~DCFile() {
+  Classes::iterator ci;
+  for (ci = _classes.begin(); ci != _classes.end(); ++ci) {
+    delete (*ci);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::read
+//       Access: Public
+//  Description: Opens and reads the indicated .dc file by name.  The
+//               distributed classes defined in the file will be
+//               appended to the set of distributed classes already
+//               recorded, if any.
+//
+//               Returns true if the file is successfully read, false
+//               if there was an error (in which case the file might
+//               have been partially read).
+////////////////////////////////////////////////////////////////////
+bool DCFile::
+read(const string &filename) {
+  ifstream in(filename.c_str());
+
+  if (!in) {
+    cerr << "Cannot open " << filename << " for reading.\n";
+    return false;
+  }
+
+  return read(in, filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::read
+//       Access: Public
+//  Description: Parses the already-opened input stream for
+//               distributed class descriptions.  The filename
+//               parameter is optional and is only used when reporting
+//               errors.
+//
+//               The distributed classes defined in the file will be
+//               appended to the set of distributed classes already
+//               recorded, if any.
+//
+//               Returns true if the file is successfully read, false
+//               if there was an error (in which case the file might
+//               have been partially read).
+////////////////////////////////////////////////////////////////////
+bool DCFile::
+read(istream &in, const string &filename) {
+  dc_init_parser(in, filename, *this);
+  dcyyparse();
+  dc_cleanup_parser();
+
+  return (dc_error_count() == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::write
+//       Access: Public
+//  Description: Opens the indicated filename for output and writes a
+//               parseable description of all the known distributed
+//               classes to the file.
+//
+//               Returns true if the description is successfully
+//               written, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCFile::
+write(const string &filename) const {
+  ofstream out(filename.c_str());
+
+  if (!out) {
+    cerr << "Can't open " << filename << " for output.\n";
+    return false;
+  }
+  return write(out, filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::write
+//       Access: Public
+//  Description: Writes a parseable description of all the known
+//               distributed classes to the file.  The filename
+//               parameter is optional and is only used when reporting
+//               errors.
+//
+//               Returns true if the description is successfully
+//               written, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCFile::
+write(ostream &out, const string &filename) const {
+  Classes::const_iterator ci;
+  for (ci = _classes.begin(); ci != _classes.end(); ++ci) {
+    (*ci)->write(out);
+    out << "\n";
+  }
+
+  if (out.fail()) {
+    cerr << "I/O error writing " << filename << ".\n";
+    return false;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_num_classes
+//       Access: Public
+//  Description: Returns the number of classes read from the .dc
+//               file(s).
+////////////////////////////////////////////////////////////////////
+int DCFile::
+get_num_classes() {
+  return _classes.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_class
+//       Access: Public
+//  Description: Returns the nth class read from the .dc file(s).
+////////////////////////////////////////////////////////////////////
+DCClass *DCFile::
+get_class(int n) {
+  assert(n >= 0 && n < (int)_classes.size());
+  return _classes[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_class_by_name
+//       Access: Public
+//  Description: Returns the class that has the indicated name, or
+//               NULL if there is no such class.
+////////////////////////////////////////////////////////////////////
+DCClass *DCFile::
+get_class_by_name(const string &name) {
+  ClassesByName::const_iterator ni;
+  ni = _classes_by_name.find(name);
+  if (ni != _classes_by_name.end()) {
+    return (*ni).second;
+  }
+
+  return (DCClass *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::add_class
+//       Access: Public
+//  Description: Adds the newly-allocated distributed class definition
+//               to the file.  The DCFile becomes the owner of the
+//               pointer and will delete it when it destructs.
+//               Returns true if the class is successfully added, or
+//               false if there was a name conflict.
+////////////////////////////////////////////////////////////////////
+bool DCFile::
+add_class(DCClass *dclass) {
+  bool inserted = _classes_by_name.insert
+    (ClassesByName::value_type(dclass->_name, dclass)).second;
+
+  if (!inserted) {
+    return false;
+  }
+
+  dclass->_number = get_num_classes();
+  _classes.push_back(dclass);
+  return true;
+}

+ 53 - 0
direct/src/dcparse/dcFile.h

@@ -0,0 +1,53 @@
+// Filename: dcFile.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCFILE_H
+#define DCFILE_H
+
+#include "dcbase.h"
+#include "dcClass.h"
+
+#include <vector>
+#include <map>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DCFile
+// Description : Represents the complete list of Distributed Class
+//               descriptions as read from a .dc file.
+////////////////////////////////////////////////////////////////////
+class DCFile {
+PUBLISHED:
+  DCFile();
+  ~DCFile();
+
+  bool read(const string &filename);
+  bool read(istream &in, const string &filename = string());
+
+  bool write(const string &filename) const;
+  bool write(ostream &out, const string &filename = string()) const;
+
+  int get_num_classes();
+  DCClass *get_class(int n);
+
+  DCClass *get_class_by_name(const string &name);
+
+public:
+  bool add_class(DCClass *dclass);
+
+public:
+  // This vector is the primary interface to the distributed classes
+  // read from the file.
+  typedef vector<DCClass *> Classes;
+  Classes _classes;
+
+public:
+  // This map is built up during parsing for the convenience of the parser.
+  typedef map<string, DCClass *> ClassesByName;
+  ClassesByName _classes_by_name;
+};
+
+#endif
+
+

+ 415 - 0
direct/src/dcparse/dcLexer.lxx

@@ -0,0 +1,415 @@
+/*
+// Filename: dcLexer.lxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+*/
+
+%{
+#include "dcLexerDefs.h"
+#include "dcParserDefs.h"
+#include "dcParser.h"
+#include "indent.h"
+
+#include <assert.h>
+
+static int yyinput(void);        // declared by flex.
+extern "C" int dcyywrap();
+
+////////////////////////////////////////////////////////////////////
+// Static variables
+////////////////////////////////////////////////////////////////////
+
+// We'll increment line_number and col_number as we parse the file, so
+// that we can report the position of an error.
+static int line_number = 0;
+static int col_number = 0;
+
+// current_line holds as much of the current line as will fit.  Its
+// only purpose is for printing it out to report an error to the user.
+static const int max_error_width = 1024;
+static char current_line[max_error_width + 1];
+
+static int error_count = 0;
+static int warning_count = 0;
+
+// This is the pointer to the current input stream.
+static istream *inp = NULL;
+
+// This is the name of the dc file we're parsing.  We keep it so we
+// can print it out for error messages.
+static string dc_filename;
+
+
+////////////////////////////////////////////////////////////////////
+// Defining the interface to the lexer.
+////////////////////////////////////////////////////////////////////
+
+void
+dc_init_lexer(istream &in, const string &filename) {
+  inp = &in;
+  dc_filename = filename;
+  line_number = 0;
+  col_number = 0;
+  error_count = 0;
+  warning_count = 0;
+}
+
+int
+dc_error_count() {
+  return error_count;
+}
+
+int
+dc_warning_count() {
+  return warning_count;
+}
+
+
+////////////////////////////////////////////////////////////////////
+// Internal support functions.
+////////////////////////////////////////////////////////////////////
+
+int
+dcyywrap(void) {
+  return 1;
+}
+
+void
+dcyyerror(const string &msg) {
+  cerr << "\nError";
+  if (!dc_filename.empty()) {
+    cerr << " in " << dc_filename;
+  }
+  cerr 
+    << " at line " << line_number << ", column " << col_number << ":\n"
+    << current_line << "\n";
+  indent(cerr, col_number-1) 
+    << "^\n" << msg << "\n\n";
+  
+  error_count++;
+}
+
+void
+dcyywarning(const string &msg) {
+  cerr << "\nWarning";
+  if (!dc_filename.empty()) {
+    cerr << " in " << dc_filename;
+  }
+  cerr 
+    << " at line " << line_number << ", column " << col_number << ":\n"
+    << current_line << "\n";
+  indent(cerr, col_number-1) 
+    << "^\n" << msg << "\n\n";
+
+  warning_count++;
+}
+
+// Now define a function to take input from an istream instead of a
+// stdio FILE pointer.  This is flex-specific.
+static void
+input_chars(char *buffer, int &result, int max_size) {
+  assert(inp != NULL);
+  if (*inp) {
+    inp->read(buffer, max_size);
+    result = inp->gcount();
+
+    if (line_number == 0) {
+      // This is a special case.  If we are reading the very first bit
+      // from the stream, copy it into the current_line array.  This
+      // is because the \n.* rule below, which fills current_line
+      // normally, doesn't catch the first line.
+      strncpy(current_line, yytext, max_error_width);
+      current_line[max_error_width] = '\0';
+      line_number++;
+      col_number = 0;
+
+      // Truncate it at the newline.
+      char *end = strchr(current_line, '\n');
+      if (end != NULL) {
+	*end = '\0';
+      }
+    }
+
+  } else {
+    // End of file or I/O error.
+    result = 0;
+  }
+}
+#undef YY_INPUT
+#define YY_INPUT(buffer, result, max_size) input_chars(buffer, result, max_size)
+
+// read_char reads and returns a single character, incrementing the
+// supplied line and column numbers as appropriate.  A convenience
+// function for the scanning functions below.
+static int
+read_char(int &line, int &col) {
+  int c = yyinput();
+  if (c == '\n') {
+    line++;
+    col = 0;
+  } else {
+    col++;
+  }
+  return c;
+}
+
+// scan_quoted_string reads a string delimited by quotation marks and
+// returns it.
+static string
+scan_quoted_string() {
+  string result;
+
+  // We don't touch the current line number and column number during
+  // scanning, so that if we detect an error while scanning the string
+  // (e.g. an unterminated string), we'll report the error as
+  // occurring at the start of the string, not at the end--somewhat
+  // more convenient for the user.
+
+  // Instead of adjusting the global line_number and col_number
+  // variables, we'll operate on our own local variables for the
+  // interim.
+  int line = line_number;
+  int col = col_number;
+
+  int c;
+  c = read_char(line, col);
+  while (c != '"' && c != EOF) {
+    result += c;
+    c = read_char(line, col);
+  }
+
+  if (c == EOF) {
+    dcyyerror("This quotation mark is unterminated.");
+  }
+
+  line_number = line;
+  col_number = col;
+
+  return result;
+}
+
+// eat_c_comment scans past all characters up until the first */
+// encountered.
+static void
+eat_c_comment() {
+  // As above, we'll operate on our own local copies of line_number
+  // and col_number within this function.
+
+  int line = line_number;
+  int col = col_number;
+
+  int c, last_c;
+  
+  last_c = '\0';
+  c = read_char(line, col);
+  while (c != EOF && !(last_c == '*' && c == '/')) {
+    if (last_c == '/' && c == '*') {
+      dcyywarning("This comment contains a nested /* symbol--possibly unclosed?");
+    }
+    last_c = c;
+    c = read_char(line, col);
+  }
+
+  if (c == EOF) {
+    dcyyerror("This comment marker is unclosed.");
+  }
+
+  line_number = line;
+  col_number = col;
+}
+
+
+
+// accept() is called below as each piece is pulled off and
+// accepted by the lexer; it increments the current column number.
+inline void accept() {
+  col_number += yyleng;
+}
+
+%}
+
+INTEGERNUM       ([+-]?([0-9]+))
+REALNUM          ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
+
+%%
+
+%{
+%}
+
+\n.* {
+  // New line.  Save a copy of the line so we can print it out for the
+  // benefit of the user in case we get an error.
+
+  strncpy(current_line, yytext+1, max_error_width);
+  current_line[max_error_width] = '\0';
+  line_number++;
+  col_number=0;
+
+  // Return the whole line to the lexer, except the newline character,
+  // which we eat.
+  yyless(1);
+}
+
+[ \t] { 
+  // Eat whitespace.
+  accept();
+}
+
+"//".* { 
+  // Eat C++-style comments.
+  accept();
+}
+
+"/*" {
+  // Eat C-style comments.
+  accept();
+  eat_c_comment(); 
+}
+
+
+"dclass" {
+  accept();
+  return KW_DCLASS;
+}
+
+"int8" {
+  accept();
+  return KW_INT8;
+}
+
+"int16" {
+  accept();
+  return KW_INT16;
+}
+
+"int32" {
+  accept();
+  return KW_INT32;
+}
+
+"int64" {
+  accept();
+  return KW_INT64;
+}
+
+"uint8" {
+  accept();
+  return KW_UINT8;
+}
+
+"uint16" {
+  accept();
+  return KW_UINT16;
+}
+
+"uint32" {
+  accept();
+  return KW_UINT32;
+}
+
+"uint64" {
+  accept();
+  return KW_UINT64;
+}
+
+"float64" {
+  accept();
+  return KW_FLOAT64;
+}
+
+"string" {
+  accept();
+  return KW_STRING;
+}
+
+mol[0-9]+ {
+  // A molecular keyword.
+  accept();
+  dcyylval.u.integer = atoi(dcyytext + 3); 
+  dcyylval.str = yytext;
+  return KW_MOL; 
+}
+
+"required" {
+  accept();
+  return KW_REQUIRED;
+}
+
+"broadcast" {
+  accept();
+  return KW_BROADCAST;
+}
+
+"p2p" {
+  accept();
+  return KW_P2P;
+}
+
+"ram" {
+  accept();
+  return KW_RAM;
+}
+
+"db" {
+  accept();
+  return KW_DB;
+}
+
+"clsend" {
+  accept();
+  return KW_CLSEND;
+}
+
+"clrecv" {
+  accept();
+  return KW_CLRECV;
+}
+
+"aisend" {
+  accept();
+  return KW_AISEND;
+}
+
+"airecv" {
+  accept();
+  return KW_AIRECV;
+}
+
+{INTEGERNUM} { 
+  // An integer number.
+  accept(); 
+  dcyylval.u.integer = atoi(dcyytext); 
+  dcyylval.str = yytext;
+  return INTEGER; 
+}
+
+{REALNUM} { 
+  // A floating-point number.
+  accept(); 
+  dcyylval.u.real = atof(dcyytext); 
+  dcyylval.str = yytext;
+  return REAL; 
+}
+
+["] {
+  // Quoted string.
+  accept();
+  dcyylval.str = scan_quoted_string();
+  return STRING;
+}
+
+[A-Za-z_][A-Za-z_0-9]* { 
+  // Identifier.
+  accept();
+  dcyylval.str = yytext;
+  return IDENTIFIER;
+}
+
+
+. {
+  // Send any other printable character as itself.
+  accept(); 
+  return dcyytext[0];
+}
+  

+ 20 - 0
direct/src/dcparse/dcLexerDefs.h

@@ -0,0 +1,20 @@
+// Filename: dcLexerDefs.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCLEXERDEFS_H
+#define DCLEXERDEFS_H
+
+#include "dcbase.h"
+
+void dc_init_lexer(istream &in, const string &filename);
+int dc_error_count();
+int dc_warning_count();
+
+void dcyyerror(const string &msg);
+void dcyywarning(const string &msg);
+
+int dcyylex();
+
+#endif

+ 92 - 0
direct/src/dcparse/dcMolecularField.cxx

@@ -0,0 +1,92 @@
+// Filename: dcMolecularField.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcMolecularField.h"
+#include "dcAtomicField.h"
+#include "indent.h"
+
+#include <assert.h>
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::get_number
+//       Access: Public
+//  Description: Returns a unique index number associated with this
+//               field.  This is defined implicitly when the .dc
+//               file(s) are read.
+////////////////////////////////////////////////////////////////////
+int DCMolecularField::
+get_number() const {
+  return _number;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::get_name
+//       Access: Public
+//  Description: Returns the name of this field.
+////////////////////////////////////////////////////////////////////
+string DCMolecularField::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::get_num_atomics
+//       Access: Public
+//  Description: Returns the number of atomic fields that make up this
+//               molecular field.
+////////////////////////////////////////////////////////////////////
+int DCMolecularField::
+get_num_atomics() const {
+  return _fields.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::get_atomic
+//       Access: Public
+//  Description: Returns the nth atomic field that makes up this
+//               molecular field.  This may or may not be a field of
+//               this particular class; it might be defined in a
+//               parent class.
+////////////////////////////////////////////////////////////////////
+DCAtomicField *DCMolecularField::
+get_atomic(int n) const {
+  assert(n >= 0 && n < (int)_fields.size());
+  return _fields[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DCMolecularField::
+DCMolecularField() {
+  _number = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::write
+//       Access: Public
+//  Description: Generates a parseable description of the object to
+//               the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void DCMolecularField::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << _name;
+
+  if (!_fields.empty()) {
+    Fields::const_iterator fi = _fields.begin();
+    out << " : " << (*fi)->_name;
+    ++fi;
+    while (fi != _fields.end()) {
+      out << ", " << (*fi)->_name;
+      ++fi;
+    }
+  }
+
+  out << ";  // molecular " << _number << "\n";
+}
+

+ 46 - 0
direct/src/dcparse/dcMolecularField.h

@@ -0,0 +1,46 @@
+// Filename: dcMolecularField.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCMOLECULARFIELD_H
+#define DCMOLECULARFIELD_H
+
+#include "dcbase.h"
+
+#include <vector>
+
+class DCAtomicField;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DCMolecularField
+// Description : A single molecular field of a Distributed Class, as
+//               read from a .dc file.  This represents a combination
+//               of two or more related atomic fields, that will often
+//               be treated as a unit.
+////////////////////////////////////////////////////////////////////
+class DCMolecularField {
+PUBLISHED:
+  int get_number() const;
+  string get_name() const;
+
+  int get_num_atomics() const;
+  DCAtomicField *get_atomic(int n) const;
+
+public:
+  DCMolecularField();
+  void write(ostream &out, int indent_level = 0) const;
+
+public:
+  // These members define the primary interface to the molecular field
+  // definition as read from the file.
+  int _number;
+  string _name;
+
+  typedef vector<DCAtomicField *> Fields;
+  Fields _fields;
+};
+
+#endif
+
+  

+ 295 - 0
direct/src/dcparse/dcParser.yxx

@@ -0,0 +1,295 @@
+// Filename: dcParser.yxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+%{
+#include "dcLexerDefs.h"
+#include "dcParserDefs.h"
+#include "dcFile.h"
+#include "dcClass.h"
+#include "dcAtomicField.h"
+
+// Because our token type contains objects of type string, which
+// require correct copy construction (and not simply memcpying), we
+// cannot use bison's built-in auto-stack-grow feature.  As an easy
+// solution, we ensure here that we have enough yacc stack to start
+// with, and that it doesn't ever try to grow.
+#define YYINITDEPTH 1000
+#define YYMAXDEPTH 1000
+
+static DCFile *dc_file = (DCFile *)NULL;
+static DCClass *current_class = (DCClass *)NULL;
+static DCAtomicField *current_atomic = (DCAtomicField *)NULL;
+static DCMolecularField *current_molecular = (DCMolecularField *)NULL;
+
+////////////////////////////////////////////////////////////////////
+// Defining the interface to the parser.
+////////////////////////////////////////////////////////////////////
+
+void
+dc_init_parser(istream &in, const string &filename, DCFile &file) {
+  dc_file = &file;
+  dc_init_lexer(in, filename);
+}
+
+void
+dc_cleanup_parser() {
+  dc_file = (DCFile *)NULL;
+}
+
+%}
+
+%token <u.integer> INTEGER
+%token <u.real> REAL
+%token <str> STRING IDENTIFIER
+
+%token KW_DCLASS 
+
+%token KW_INT8
+%token KW_INT16
+%token KW_INT32
+%token KW_INT64
+%token KW_UINT8
+%token KW_UINT16
+%token KW_UINT32
+%token KW_UINT64
+%token KW_FLOAT64
+%token KW_STRING
+
+%token KW_MOL
+
+%token KW_REQUIRED
+%token KW_BROADCAST
+%token KW_P2P
+%token KW_RAM
+%token KW_DB
+%token KW_CLSEND
+%token KW_CLRECV
+%token KW_AISEND
+%token KW_AIRECV
+
+%type <u.dclass> dclass_name
+%type <u.atomic> atomic_name
+%type <u.subatomic> type_token
+
+%%
+
+dc:
+	empty
+	| dc ';'
+	| dc dclass
+	;
+
+dclass:
+	KW_DCLASS IDENTIFIER 
+{
+  current_class = new DCClass;
+  current_class->_name = $2;
+  if (!dc_file->add_class(current_class)) {
+    yyerror("Duplicate class name: " + current_class->_name);
+  }
+}
+	dclass_derivation '{' dclass_fields '}'
+	;
+
+dclass_name:
+	IDENTIFIER
+{
+  DCFile::ClassesByName::const_iterator ni;
+  ni = dc_file->_classes_by_name.find($1);
+  if (ni == dc_file->_classes_by_name.end()) {
+    yyerror("Unknown class: " + $1);
+    $$ = (DCClass *)NULL;
+  } else {
+    $$ = (*ni).second;
+  }
+}
+
+dclass_derivation:
+	empty
+	| ':' base_list
+	;
+
+base_list:
+	dclass_name
+{
+  if ($1 != (DCClass *)NULL) {
+    current_class->_parents.push_back($1);
+  }
+}
+	| base_list ',' dclass_name
+{
+  if ($3 != (DCClass *)NULL) {
+    current_class->_parents.push_back($3);
+  }
+}
+	;
+
+dclass_fields:
+	empty
+	| dclass_fields ';'
+	| dclass_fields atomic_field
+	| dclass_fields molecular_field
+	;
+
+atomic_field:
+	IDENTIFIER '('
+{
+  current_atomic = new DCAtomicField;
+  current_atomic->_name = $1;
+  if (!current_class->add_field(current_atomic)) {
+    yyerror("Duplicate atomic field name: " + current_atomic->_name);
+  }
+}
+	parameter_list ')' atomic_flags
+	;
+
+atomic_name:
+	IDENTIFIER
+{
+  $$ = current_class->get_atomic_by_name($1);
+  if ($$ == (DCAtomicField *)NULL) {
+    yyerror("Unknown atomic field: " + $1);
+  }
+}
+
+parameter_list:
+	empty
+	| nonempty_parameter_list
+	;
+
+nonempty_parameter_list:
+	subatomic_type
+	| nonempty_parameter_list ',' subatomic_type
+	;
+
+subatomic_type:
+	type_token
+{
+  DCAtomicField::ElementType et;
+  et._type = $1;
+  et._divisor = 1;
+  current_atomic->_elements.push_back(et);
+}
+	| type_token '/' INTEGER
+{
+  DCAtomicField::ElementType et;
+  et._type = $1;
+  et._divisor = $3;
+  current_atomic->_elements.push_back(et);
+}
+	;
+
+type_token:
+	KW_INT8
+{
+  $$ = ST_int8;
+}
+	| KW_INT16
+{
+  $$ = ST_int16;
+}
+	| KW_INT32
+{
+  $$ = ST_int32;
+}
+	| KW_INT64
+{
+  $$ = ST_int64;
+}
+	| KW_UINT8
+{
+  $$ = ST_uint8;
+}
+	| KW_UINT16
+{
+  $$ = ST_uint16;
+}
+	| KW_UINT32
+{
+  $$ = ST_uint32;
+}
+	| KW_UINT64
+{
+  $$ = ST_uint64;
+}
+	| KW_FLOAT64
+{
+  $$ = ST_float64;
+}
+	| KW_STRING
+{
+  $$ = ST_string;
+}
+	;
+
+atomic_flags:
+	empty
+	| atomic_flags KW_REQUIRED
+{
+  current_atomic->_flags |= DCAtomicField::F_required;
+}
+	| atomic_flags KW_BROADCAST
+{
+  current_atomic->_flags |= DCAtomicField::F_broadcast;
+}
+	| atomic_flags KW_P2P
+{
+  current_atomic->_flags |= DCAtomicField::F_p2p;
+}
+	| atomic_flags KW_RAM
+{
+  current_atomic->_flags |= DCAtomicField::F_ram;
+}
+	| atomic_flags KW_DB
+{
+  current_atomic->_flags |= DCAtomicField::F_db;
+}
+	| atomic_flags KW_CLSEND
+{
+  current_atomic->_flags |= DCAtomicField::F_clsend;
+}
+	| atomic_flags KW_CLRECV
+{
+  current_atomic->_flags |= DCAtomicField::F_clrecv;
+}
+	| atomic_flags KW_AISEND
+{
+  current_atomic->_flags |= DCAtomicField::F_aisend;
+}
+	| atomic_flags KW_AIRECV
+{
+  current_atomic->_flags |= DCAtomicField::F_airecv;
+}
+	;
+
+molecular_field:
+	IDENTIFIER ':'
+{
+  current_molecular = new DCMolecularField;
+  current_molecular->_name = $1;
+  if (!current_class->add_field(current_molecular)) {
+    yyerror("Duplicate molecular field name: " + current_molecular->_name);
+  }
+}
+	molecular_atom_list
+	;
+
+molecular_atom_list:
+	atomic_name
+{
+  if ($1 != (DCAtomicField *)NULL) {
+    current_molecular->_fields.push_back($1);
+  }
+}
+	| molecular_atom_list ',' atomic_name
+{
+  if ($3 != (DCAtomicField *)NULL) {
+    current_molecular->_fields.push_back($3);
+  }
+}
+	;
+
+empty:
+	;

+ 43 - 0
direct/src/dcparse/dcParserDefs.h

@@ -0,0 +1,43 @@
+// Filename: dcParserDefs.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCPARSERDEFS_H
+#define DCPARSERDEFs_H
+
+#include "dcbase.h"
+#include "dcSubatomicType.h"
+
+class DCFile;
+class DCClass;
+class DCAtomicField;
+
+void dc_init_parser(istream &in, const string &filename, DCFile &file);
+void dc_cleanup_parser();
+int dcyyparse();
+
+// This structure holds the return value for each token.
+// Traditionally, this is a union, and is declared with the %union
+// declaration in the parser.y file, but unions are pretty worthless
+// in C++ (you can't include an object that has member functions in a
+// union), so we'll use a class instead.  That means we need to
+// declare it externally, here.
+
+class DCTokenType {
+public:
+  union U {
+    int integer;
+    double real;
+    DCClass *dclass;
+    DCAtomicField *atomic;
+    DCSubatomicType subatomic;
+  } u;
+  string str;
+};
+
+// The yacc-generated code expects to use the symbol 'YYSTYPE' to
+// refer to the above class.
+#define YYSTYPE DCTokenType
+
+#endif

+ 43 - 0
direct/src/dcparse/dcSubatomicType.cxx

@@ -0,0 +1,43 @@
+// Filename: dcSubatomicType.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcSubatomicType.h"
+
+ostream &
+operator << (ostream &out, DCSubatomicType type) {
+  switch (type) {
+  case ST_int8:
+    return out << "int8";
+
+  case ST_int16:
+    return out << "int16";
+
+  case ST_int32:
+    return out << "int32";
+
+  case ST_int64:
+    return out << "int64";
+
+  case ST_uint8:
+    return out << "uint8";
+
+  case ST_uint16:
+    return out << "uint16";
+
+  case ST_uint32:
+    return out << "uint32";
+
+  case ST_uint64:
+    return out << "uint64";
+
+  case ST_float64:
+    return out << "float64";
+
+  case ST_string:
+    return out << "string";
+  }
+
+  return out << "invalid type: " << (int)type;
+}

+ 40 - 0
direct/src/dcparse/dcSubatomicType.h

@@ -0,0 +1,40 @@
+// Filename: dcSubatomicType.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCSUBATOMICTYPE_H
+#define DCSUBATOMICTYPE_H
+
+#include "dcbase.h"
+
+BEGIN_PUBLISH
+////////////////////////////////////////////////////////////////////
+// 	  Enum : DCSubatomicType
+// Description : This defines the numeric type of each element of a
+//               DCAtomicField; that is, the particular values that
+//               will get added to the message when the atomic field
+//               method is called.
+////////////////////////////////////////////////////////////////////
+enum DCSubatomicType {
+  ST_int8,
+  ST_int16,
+  ST_int32,
+  ST_int64,
+
+  ST_uint8,
+  ST_uint16,
+  ST_uint32,
+  ST_uint64,
+
+  ST_float64,
+
+  ST_string,
+};
+END_PUBLISH
+
+ostream &operator << (ostream &out, DCSubatomicType type);
+
+#endif
+
+  

+ 59 - 0
direct/src/dcparse/dcbase.h

@@ -0,0 +1,59 @@
+// Filename: dcbase.h
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCBASE_H
+#define DCBASE_H
+
+// This file defines a few headers and stuff necessary for compilation
+// of this project.  Most compiler-specific decisions should be
+// grouped up here.
+
+#ifdef WIN32
+/* C4786: 255 char debug symbols */
+#pragma warning (disable : 4786)
+/* C4503: decorated name length exceeded */
+#pragma warning (disable : 4503)
+#endif  /* WIN32_VC */
+
+#include <iostream>
+#include <string>
+
+// These header files are needed to compile dcLexer.cxx, the output
+// from flex.  flex doesn't create a perfectly windows-friendly source
+// file right out of the box.
+#ifdef WIN32
+#include <io.h>
+#include <malloc.h>
+#else
+#include <unistd.h>
+#endif
+
+using namespace std;
+
+#ifdef CPPPARSER
+// We define the macro PUBLISHED to mark C++ methods that are to be
+// published via interrogate to scripting languages.  However, if
+// we're not running the interrogate pass (CPPPARSER isn't defined),
+// this maps to public.
+#define PUBLISHED __published
+#else
+#define PUBLISHED public
+#endif
+
+/*
+ We define the macros BEGIN_PUBLISH and END_PUBLISH to bracket
+ functions and global variable definitions that are to be published
+ via interrogate to scripting languages.
+ */
+#ifdef CPPPARSER
+#define BEGIN_PUBLISH __begin_publish
+#define END_PUBLISH __end_publish
+#else
+#define BEGIN_PUBLISH
+#define END_PUBLISH
+#endif
+
+#endif
+

+ 30 - 0
direct/src/dcparse/dcparse.cxx

@@ -0,0 +1,30 @@
+// Filename: dcparse.cxx
+// Created by:  drose (05Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "dcbase.h"
+#include "dcFile.h"
+
+int
+main(int argc, char *argv[]) {
+  if (argc < 2) {
+    cerr <<
+      "dcparse - a simple program to read one or more .dc files and report their\n"
+      "contents to standard output.\n\n";
+    return (1);
+  }
+
+  DCFile file;
+  for (int i = 1; i < argc; i++) {
+    if (!file.read(argv[i])) {
+      return (1);
+    }
+  }
+
+  if (!file.write(cout, "standard output")) {
+    return (1);
+  }
+
+  return (0);
+}

+ 18 - 0
direct/src/dcparse/indent.cxx

@@ -0,0 +1,18 @@
+// Filename: indent.cxx
+// Created by:  drose (05May00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "indent.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: indent 
+//  Description:
+////////////////////////////////////////////////////////////////////
+ostream &
+indent(ostream &out, int indent_level) {
+  for (int i = 0; i < indent_level; i++) {
+    out << ' ';
+  }
+  return out;
+}

+ 29 - 0
direct/src/dcparse/indent.h

@@ -0,0 +1,29 @@
+// Filename: indent.h
+// Created by:  drose (16Jan99)
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef INDENT_H
+#define INDENT_H
+
+#include "dcbase.h"
+
+// We rename indent() so it won't clash with the similar function
+// defined in Panda.
+#define indent dcindent
+
+////////////////////////////////////////////////////////////////////
+//     Function: indent
+//  Description: A handy function for doing text formatting.  This
+//               function simply outputs the indicated number of
+//               spaces to the given output stream, returning the
+//               stream itself.  Useful for indenting a series of
+//               lines of text by a given amount.
+////////////////////////////////////////////////////////////////////
+ostream &
+indent(ostream &out, int indent_level);
+
+#endif
+
+ 
+

+ 21 - 0
direct/src/dcparse/test.dc

@@ -0,0 +1,21 @@
+// Define a distributed object with a position.
+
+dclass Movable {
+
+  // Declare a few atomic fields
+  set_xyz(int16 / 1000, int16 / 1000, int16 / 1000) required broadcast ram;
+  set_hpr(int16, int16, int16) required broadcast ram;
+
+  // And a molecular field
+  set_xyzhpr : set_xyz, set_hpr;
+};
+
+// Define a distributed object with a position and an attitude.
+dclass Attitude : Movable {
+  // methods are inherited from parent class.
+
+  set_attitude(int8);
+
+  // This molecular field uses some inherited atomic fields.
+  set_xyzhprattitude : set_xyz, set_hpr, set_attitude;
+};

+ 10 - 0
direct/src/directbase/Sources.pp

@@ -0,0 +1,10 @@
+#begin lib_target
+  #define TARGET directbase
+  
+  #define SOURCES \
+    directbase.cxx directbase.h directsymbols.h \
+
+  #define INSTALL_HEADERS \
+    directbase.h directsymbols.h
+
+#end lib_target

+ 6 - 0
direct/src/directbase/directbase.cxx

@@ -0,0 +1,6 @@
+// Filename: directbase.cc
+// Created by:  drose (15Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "directbase.h"

+ 18 - 0
direct/src/directbase/directbase.h

@@ -0,0 +1,18 @@
+/*
+ * Filename: directbase.h
+ * Created by:  drose (12Sep00)
+ *
+ */
+
+/* This file is included at the beginning of every header file and/or
+   C or C++ file.  It must be compilable for C as well as C++ files,
+   so no C++-specific code or syntax can be put here. */
+
+#ifndef DIRECTBASE_H
+#define DIRECTBASE_H
+
+#include <pandabase.h>
+#include "directsymbols.h"
+
+#endif
+

+ 30 - 0
direct/src/directbase/directsymbols.h

@@ -0,0 +1,30 @@
+/*
+// Filename: directsymbols.h
+// Created by:  drose (18Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+*/
+
+#ifndef DIRECTSYMBOLS_H
+#define DIRECTSYMBOLS_H
+
+/* See dtoolsymbols.h for a rant on the purpose of this file.  */
+
+#if defined(PENV_WIN32) && !defined(CPPPARSER)
+
+#ifdef BUILDING_DIRECT
+  #define EXPCL_DIRECT __declspec(dllexport)
+  #define EXPTP_DIRECT
+#else
+  #define EXPCL_DIRECT __declspec(dllimport)
+  #define EXPTP_DIRECT extern
+#endif
+
+#else   /* !PENV_WIN32 */
+
+#define EXPCL_DIRECT
+#define EXPTP_DIRECT
+
+#endif  /* PENV_WIN32 */
+
+#endif

+ 21 - 0
direct/src/distributed/ClientDistClass.py

@@ -0,0 +1,21 @@
+"""ClientDistClass module: contains the ClientDistClass class"""
+
+from PandaModules import *
+import DirectNotifyGlobal
+
+class ClientDistClass:
+	
+    def __init__(self, dcClass):
+        self.number = dcClass.get_number()
+        self.name = dcClass.get_name()
+        self.atomicFields=[]
+        self.molecularFields=[]
+        self.parseAtomicFields(dcClass)
+        self.parseMolecularFields(dcClass)
+        return None
+
+    def parseAtomicFields(dcClass):
+        for i in range(0,dcClass.get_num_inherited_atomics()):
+            self.atomicFields.append((dcClass.get_inherited_atomic(i))
+            
+        

+ 84 - 0
direct/src/distributed/ClientRepository.py

@@ -0,0 +1,84 @@
+"""ClientRepository module: contains the ClientRepository class"""
+
+from PandaModules import *
+from TaskManagerGlobal import *
+import Task
+import DirectNotifyGlobal
+
+class ClientRepository:
+    defaultServerPort = 5150
+    notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository")
+
+    def __init__(self, dcFileName):
+        self.number2cdc={}
+        self.name2cdc={}
+        self.parseDcFile(dcFileName)
+        return None
+
+    def parseDcFile(dcFileName):
+        dcFile = DCFile()
+        dcFile.read(dcFileName)
+        return self.parseDcClasses(dcFile)
+
+    def parseDcClasses(dcFile):
+        numClasses = dcFile.get_num_classes()
+        for i in range(0, numClasses):
+            # Create a clientDistClass from the dcClass
+            clientDistClass = self.parseClass(dcFile.getClass())
+            # List the cdc in the number and name dictionaries
+            self.number2cdc[clientDistClass.getNumber()]=clientDistClass
+            self.name2cdc[clientDistClass.getName()]=clientDistClass
+        return None
+
+    def parseClass(dcClass):
+        
+
+    def connect(self, serverName="localhost",
+                serverPort=defaultServerPort):
+        self.qcm=QueuedConnectionManager()
+        self.tcpConn = self.qcm.openTCPClientConnection(
+            serverName, serverPort, 1000)
+        self.qcr=QueuedConnectionReader(self.qcm, 0)
+        self.cw=ConnectionWriter(self.qcm, 0)
+        self.startReaderPollTask()
+
+    def startReaderPollTask(self):
+        task = Task.Task(self.readerPollUntilEmpty)
+        taskMgr.spawnTaskNamed(task, "readerPollTask")
+        return None
+
+    def readerPollUntilEmpty(self, task):
+        while self.readerPollOnce():
+            pass
+        return Task.cont
+
+    def readerPollOnce(self):
+        availGetVal = self.qcr.dataAvailable()
+        if availGetVal:
+            datagram = NetDatagram()
+            readRetVal = self.qcr.getData(datagram)
+            if readRetVal:
+                self.handleDatagram(datagram)
+            else:
+                ClientRepository.notify.warning("getData returned false")
+        return availGetVal
+
+    def handleDatagram(datagram):
+        print 'Got it!'
+        return None
+
+    def sendLoginMsg(self):
+        datagram = Datagram()
+        # Add message type
+        datagram.addUint16(1)
+        # Add swid
+        datagram.addString("1234567890123456789012345678901234")
+        # Add IP Address
+        datagram.addUint32(0)
+        # Add UDP port
+        datagram.addUint16(5150)
+        # Send the message
+        self.cw.send(datagram, self.tcpConn)
+
+        
+        

+ 8 - 0
direct/src/distributed/DistributedActor.py

@@ -0,0 +1,8 @@
+"""DistributedActor module: contains the DistributedActor class"""
+
+from DistributedNode import *
+import Actor
+
+class DistributedActor(DistributedNode, Actor.Actor):
+    """Distributed Actor class:"""
+    pass

+ 7 - 0
direct/src/distributed/DistributedNode.py

@@ -0,0 +1,7 @@
+"""DistributedNode module: contains the DistributedNode class"""
+
+from DistributedObject import *
+
+class DistributedNode(DistributedObject, ShowBase.NodePath):
+    """Distributed Node class:"""
+    pass

+ 7 - 0
direct/src/distributed/DistributedObject.py

@@ -0,0 +1,7 @@
+"""DistributedObject module: contains the DistributedObject class"""
+
+from PandaObject import *
+
+class DistributedObject(PandaObject):
+    """Distributed Object class:"""
+    pass

+ 90 - 0
direct/src/distributed/ServerRepository.py

@@ -0,0 +1,90 @@
+"""ServerRepository module: contains the ServerRepository class"""
+
+from PandaModules import *
+from TaskManagerGlobal import *
+import Task
+import DirectNotifyGlobal
+
+class ServerRepository:
+
+    def __init__(self, tcpPort, udpPort):
+        self.qcm = QueuedConnectionManager()
+        self.qcl = QueuedConnectionListener(self.qcm, 0)
+        self.qcr = QueuedConnectionReader(self.qcm, 0)
+        self.cw = ConnectionWriter(self.qcm,0)
+        self.tcpRendezvous = self.qcm.openTCPServerRendezvous(tcpPort, 10)
+        print self.tcpRendezvous
+        self.qcl.addConnection(self.tcpRendezvous)
+        self.startListenerPollTask()
+        self.startReaderPollTask()
+        self.startResetPollTask()
+        return None
+
+    def startListenerPollTask(self):
+        task = Task.Task(self.listenerPoll)
+        taskMgr.spawnTaskNamed(task, "serverListenerPollTask")
+        return None
+
+    def listenerPoll(self, task):
+        if self.qcl.newConnectionAvailable():
+            print "New connection is available"
+            rendezvous = PointerToConnection()
+            netAddress = NetAddress()
+            newConnection = PointerToConnection()
+            retVal = self.qcl.getNewConnection(rendezvous, netAddress,
+                                               newConnection)
+            if retVal:
+                # Crazy dereferencing
+                newConnection=newConnection.p()
+                self.qcr.addConnection(newConnection)
+                print "Got a connection!"
+                self.lastConnection = newConnection
+            else:
+                ServerRepository.notify.warning(
+                    "getNewConnection returned false")
+        return Task.cont
+
+    def startReaderPollTask(self):
+        task = Task.Task(self.readerPollUntilEmpty)
+        taskMgr.spawnTaskNamed(task, "serverReaderPollTask")
+        return None
+
+    def readerPollUntilEmpty(self, task):
+        while self.readerPollOnce():
+            pass
+        return Task.cont
+
+    def readerPollOnce(self):
+        availGetVal = self.qcr.dataAvailable()
+        if availGetVal:
+            datagram = NetDatagram()
+            readRetVal = self.qcr.getData(datagram)
+            if readRetVal:
+                self.handleDatagram(datagram)
+            else:
+                ClientRepository.notify.warning("getData returned false")
+        return availGetVal
+
+    def handleDatagram(self, datagram):
+        print "Server got a datagram!"
+        dgi = DatagramIterator(datagram)
+        print dgi.getUint16()
+        print dgi.getString()
+        print dgi.getUint32()
+        print dgi.getUint16()
+
+        newDatagram = Datagram()
+        datagram.addUint16(2)
+        datagram.addUint8(ord('s'))
+        self.cw.send(datagram, self.lastConnection)
+        return None
+
+    def startResetPollTask(self):
+        return None
+    
+    def resetPollUntilEmpty(self):
+        return None
+
+    def resetPollOnce(self):
+        return None
+    

+ 3 - 0
direct/src/distributed/Sources.pp

@@ -0,0 +1,3 @@
+// For now, since we are not installing Python files, this file can
+// remain empty.
+

+ 479 - 0
direct/src/extensions/NodePath-extensions.py

@@ -0,0 +1,479 @@
+    """
+    NodePath-extensions module: contains methods to extend functionality
+    of the NodePath class
+    """
+
+
+    # private methods
+    
+    def __getBlend(self, blendType):
+        """__getBlend(self, string)
+        Return the C++ blend class corresponding to blendType string
+        """
+        import LerpBlendHelpers
+
+        if (blendType == "easeIn"):
+            return LerpBlendHelpers.LerpBlendHelpers.easeIn
+        elif (blendType == "easeOut"):
+            return LerpBlendHelpers.LerpBlendHelpers.easeOut
+        elif (blendType == "easeInOut"):
+            return LerpBlendHelpers.LerpBlendHelpers.easeInOut
+        elif (blendType == "noBlend"):
+            return LerpBlendHelpers.LerpBlendHelpers.noBlend
+        else:
+            raise Exception("Error: NodePath.__getBlend: Unknown blend type")
+
+            
+    def __lerp(self, functor, time, blendType, taskName=None):
+        """__lerp(self, functor, float, string, string)
+        Basic lerp functionality used by other lerps.
+        Fire off a lerp. Make it a task if taskName given."""
+        import Lerp
+        # make the lerp
+        lerp = Lerp.Lerp(functor, time, (self.__getBlend(blendType)))
+
+        from TaskManagerGlobal import *
+        # make the task function
+        def lerpTaskFunc(task):
+            import Task
+            import ClockObject
+            dt = ClockObject.ClockObject.getGlobalClock().getDt()
+            task.lerp.setStepSize(dt)
+            task.lerp.step()
+            if (task.lerp.isDone()):
+                return(Task.done)
+            else:
+                return(Task.cont)
+
+        # make the lerp task
+        lerpTask = Task.Task(lerpTaskFunc)
+        lerpTask.lerp = lerp
+        
+        if (taskName == None):
+            # don't spawn a task, return one instead
+            return lerpTask
+        else:
+            # spawn the lerp task
+            taskMgr.spawnTaskNamed(lerpTask, taskName)
+            return lerpTask
+
+    def __autoLerp(self, functor, time, blendType, taskName):
+        """_autoLerp(self, functor, float, string, string)
+        This lerp uses C++ to handle the stepping. Bonus is
+        its more efficient, trade-off is there is less control"""
+        import AutonomousLerp
+        from ShowBaseGlobal import *
+
+        # make a lerp that lives in C++ land
+        lerp = AutonomousLerp.AutonomousLerp(functor, time,
+                              self.__getBlend(blendType),
+                              base.eventHandler)
+        lerp.start()
+        return lerp
+
+
+    # user callable lerp methods
+    def lerpColor(self, *posArgs, **keyArgs):
+        """lerpColor(self, *positionArgs, **keywordArgs)
+        determine which lerpColor* to call based on arguments
+        """
+        if (len(posArgs) == 2):
+            return apply(self.lerpColorVBase4, posArgs, keyArgs)
+        elif (len(posArgs) == 3):
+            return apply(self.lerpColorVBase4VBase4, posArgs, keyArgs)
+        elif (len(posArgs) == 5):
+            return apply(self.lerpColorRGBA, posArgs, keyArgs)
+        elif (len(posArgs) == 9):
+            return apply(self.lerpColorRGBARGBA, posArgs, keyArgs)
+        else:
+            # bad args
+            raise Exception("Error: NodePath.lerpColor: bad number of args")
+
+            
+    def lerpColorRGBA(self, r, g, b, a, time, blendType="noBlend",
+                      auto=None, task=None):
+        """lerpColorRGBA(self, float, float, float, float, float,
+        string="noBlend", string=none, string=none)
+        """
+        import ColorLerpFunctor
+        # just end rgba values, use current color rgba values for start
+        startColor = self.getColor()
+        functor = ColorLerpFunctor.ColorLerpFunctor(self,
+                                   startColor[0], startColor[1],
+                                   startColor[2], startColor[3],
+                                   r, g, b, a)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpColorRGBARGBA(self, sr, sg, sb, sa, er, eg, eb, ea, time,
+                          blendType="noBlend", auto=None, task=None):
+        """lerpColorRGBARGBA(self, float, float, float, float, float,
+        float, float, float, float, string="noBlend", string=none, string=none)
+        """
+        import ColorLerpFunctor
+        # start and end rgba values
+        functor = ColorLerpFunctor.ColorLerpFunctor(self, sr, sg, sb, sa,
+                                                    er, eg, eb, ea)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpColorVBase4(self, endColor, time, blendType="noBlend",
+                      auto=None, task=None):
+        """lerpColorVBase4(self, VBase4, float, string="noBlend", string=none,
+        string=none)
+        """
+        import ColorLerpFunctor
+        # just end vec4, use current color for start
+        startColor = self.getColor()
+        functor = ColorLerpFunctor.ColorLerpFunctor(self, startColor, endColor)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpColorVBase4VBase4(self, startColor, endColor, time,
+                          blendType="noBlend", auto=None, task=None):
+        """lerpColorVBase4VBase4(self, VBase4, VBase4, float, string="noBlend",
+        string=none, string=none)
+        """
+        import ColorLerpFunctor
+        # start color and end vec
+        functor = ColorLerpFunctor.ColorLerpFunctor(self, startColor, endColor)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+            
+
+    def lerpHpr(self, *posArgs, **keyArgs):
+        """lerpHpr(self, *positionArgs, **keywordArgs)
+        Determine whether to call lerpHprHPR or lerpHprVBase3
+        based on first argument
+        """
+        # check to see if lerping with
+        # three floats or a VBase3
+        if (len(posArgs) == 4):
+            return apply(self.lerpHprHPR, posArgs, keyArgs)
+        elif(len(posArgs) == 2):
+            return apply(self.lerpHprVBase3, posArgs, keyArgs)
+        else:
+            # bad args
+            raise Exception("Error: NodePath.lerpHpr: bad number of args")
+    
+    def lerpHprHPR(self, h, p, r, time, blendType="noBlend", auto=None,
+                   task=None, other=None):
+        """lerpHprHPR(self, float, float, float, float, string="noBlend",
+        string=none, string=none, NodePath=none)
+        Perform a hpr lerp with three floats as the end point
+        """
+        import HprLerpFunctor
+        # it's individual hpr components
+        if (other != None):
+            # lerp wrt other
+            startHpr = self.getHpr(other)
+            functor = HprLerpFunctor.HprLerpFunctor(self,
+                                     startHpr[0], startHpr[1], startHpr[2],
+                                     h, p, r, other)
+        else:
+            startHpr = self.getHpr()
+            functor = HprLerpFunctor.HprLerpFunctor(self,
+                                     startHpr[0], startHpr[1], startHpr[2],
+                                     h, p, r)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+        
+    
+    def lerpHprVBase3(self, hpr, time, blendType="noBlend", auto=None,
+                    task=None, other=None):
+        """lerpHprVBase3(self, VBase3, float, string="noBlend", string=none,
+        string=none, NodePath=None)
+        Perform a hpr lerp with a VBase3 as the end point
+        """
+        import HprLerpFunctor
+        # it's a vbase3 hpr
+        if (other != None):
+            # lerp wrt other
+            functor = HprLerpFunctor.HprLerpFunctor(self, (self.getHpr(other)),
+                                                    hpr, other)
+        else:
+            functor = HprLerpFunctor.HprLerpFunctor(self, (self.getHpr()),
+                                                    hpr)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+        
+
+    def lerpPos(self, *posArgs, **keyArgs):
+        """lerpPos(self, *positionArgs, **keywordArgs)
+        Determine whether to call lerpPosXYZ or lerpPosPoint3
+        based on the first argument
+        """
+        # check to see if lerping with three
+        # floats or a Point3
+        if (len(posArgs) == 4):
+            return apply(self.lerpPosXYZ, posArgs, keyArgs)
+        elif(len(posArgs) == 2):
+            return apply(self.lerpPosPoint3, posArgs, keyArgs)
+        else:
+            # bad number off args
+            raise Exception("Error: NodePath.lerpPos: bad number of args")
+        
+    def lerpPosXYZ(self, x, y, z, time, blendType="noBlend", auto=None,
+                   task=None, other=None):
+        """lerpPosXYZ(self, float, float, float, float, string="noBlend",
+        string=None, NodePath=None)
+        Perform a pos lerp with three floats as the end point
+        """
+        import PosLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            startPos = self.getPos(other)
+            functor = PosLerpFunctor.PosLerpFunctor(self,
+                                     startPos[0], startPos[1], startPos[2],
+                                     x, y, z, other)
+        else:
+            startPos = self.getPos()
+            functor = PosLerpFunctor.PosLerpFunctor(self, startPos[0],
+                                     startPos[1], startPos[2], x, y, z)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return  self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpPosPoint3(self, pos, time, blendType="noBlend", auto=None,
+                    task=None, other=None):
+        """lerpPosPoint3(self, Point3, float, string="noBlend", string=None,
+        string=None, NodePath=None)
+        Perform a pos lerp with a Point3 as the end point
+        """
+        import PosLerpFunctor
+        if (other != None):
+            #lerp wrt other
+            functor = PosLerpFunctor.PosLerpFunctor(self, (self.getPos(other)),
+                                                    pos, other)
+        else:
+            functor = PosLerpFunctor.PosLerpFunctor(self, (self.getPos()), pos)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+
+    def lerpPosHpr(self, *posArgs, **keyArgs):
+        """lerpPosHpr(self, *positionArgs, **keywordArgs)
+        Determine whether to call lerpPosHprXYZHPR or lerpHprPoint3VBase3
+        based on first argument
+        """
+        # check to see if lerping with
+        # six floats or a Point3 and a VBase3
+        if (len(posArgs) == 7):
+            return apply(self.lerpPosHprXYZHPR, posArgs, keyArgs)
+        elif(len(posArgs) == 3):
+            return apply(self.lerpPosHprPoint3VBase3, posArgs, keyArgs)
+        else:
+            # bad number off args
+            raise Exception("Error: NodePath.lerpPosHpr: bad number of args")
+
+    def lerpPosHprPoint3VBase3(self, pos, hpr, time, blendType="noBlend",
+                             auto=None, task=None, other=None):
+        """lerpPosHprPoint3VBase3(self, Point3, VBase3, string="noBlend",
+        string=none, string=none, NodePath=None)
+        """
+        import PosHprLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            startPos = self.getPos(other)
+            startHpr = self.getHpr(other)
+            functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
+                                                          startPos, pos,
+                                                          startHpr, hpr, other)
+        else:
+            startPos = self.getPos()
+            startHpr = self.getHpr()
+            functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
+                                                          startPos, pos,
+                                                          startHpr, hpr)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpPosHprXYZHPR(self, x, y, z, h, p, r, time, blendType="noBlend",
+                         auto=None, task=None, other=None):
+        """lerpPosHpr(self, float, string="noBlend", string=none,
+        string=none, NodePath=None)
+        """
+        import PosHprLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            startPos = self.getPos(other)
+            startHpr = self.getHpr(other)
+            functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
+                                        startPos[0], startPos[1],
+                                        startPos[2], x, y, z,
+                                        startHpr[0], startHpr[1],
+                                        startHpr[2], h, p, r,
+                                        other)
+        else:
+            startPos = self.getPos()
+            startHpr = self.getHpr()
+            functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
+                                        startPos[0], startPos[1],
+                                        startPos[2], x, y, z,
+                                        startHpr[0], startHpr[1],
+                                        startHpr[2], h, p, r)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+
+    def lerpPosHprScale(self, pos, hpr, scale, time, blendType="noBlend",
+                        auto=None, task=None, other=None):
+        """lerpPosHpr(self, Point3, VBase3, float, float, string="noBlend",
+        string=none, string=none, NodePath=None)
+        Only one case, no need for extra args. Call the appropriate lerp
+        (auto, spawned, or blocking) based on how(if) a task name is given
+        """
+        import PosHprScaleLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            startPos = self.getPos(other)
+            startHpr = self.getHpr(other)
+            startScale = self.getScale(other)
+            functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
+                                             startPos, pos,
+                                             startHpr, hpr,
+                                             startScale, scale, other)
+        else:
+            startPos = self.getPos()
+            startHpr = self.getHpr()
+            startScale = self.getScale()
+            functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
+                                             startPos, pos,
+                                             startHpr, hpr,
+                                             startScale, scale)
+            
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+
+    def lerpScale(self, *posArgs, **keyArgs):
+        """lerpSclae(self, *positionArgs, **keywordArgs)
+        Determine whether to call lerpScaleXYZ or lerpScaleaseV3
+        based on the first argument
+        """
+        # check to see if lerping with three
+        # floats or a Point3
+        if (len(posArgs) == 4):
+            return apply(self.lerpScaleXYZ, posArgs, keyArgs)
+        elif(len(posArgs) == 2):
+            return apply(self.lerpScaleVBase3, posArgs, keyArgs)
+        else:
+            # bad number off args
+            raise Exception("Error: NodePath.lerpScale: bad number of args")
+
+    def lerpScaleVBase3(self, scale, time, blendType="noBlend", auto=None,
+                      task=None, other=None):
+        """lerpPos(self, VBase3, float, string="noBlend", string=none,
+        string=none, NodePath=None)
+        """
+        import ScaleLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
+                                       (self.getScale(other)),
+                                       scale, other)
+        else:
+            functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
+                                       (self.getScale()), scale)
+
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+
+    def lerpScaleXYZ(self, sx, sy, sz, time, blendType="noBlend",
+                     auto=None, task=None, other=None):
+        """lerpPos(self, float, float, float, float, string="noBlend",
+        string=none, string=none, NodePath=None)
+        """
+        import ScaleLerpFunctor
+        if (other != None):
+            # lerp wrt other
+            startScale = self.getScale(other)
+            functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
+                                       startScale[0], startScale[1],
+                                       startScale[2], sx, sy, sz, other)
+        else:
+            startScale = self.getScale()
+            functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
+                                       startScale[0], startScale[1],
+                                       startScale[2], sx, sy, sz)
+        #determine whether to use auto, spawned, or blocking lerp
+        if (auto != None):
+            return self.__autoLerp(functor, time, blendType, auto)
+        elif (task != None):
+            return self.__lerp(functor, time, blendType, task)
+        else:
+            return self.__lerp(functor, time, blendType)
+            
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 3 - 0
direct/src/extensions/Sources.pp

@@ -0,0 +1,3 @@
+// For now, since we are not installing Python files, this file can
+// remain empty.
+

+ 31 - 0
direct/src/ffi/FFIConstants.py

@@ -0,0 +1,31 @@
+
+# create a DirectNotify category for FFI modules
+from  DirectNotifyGlobal import *
+notify = directNotify.newCategory("FFI")
+
+# This is the name of the file that the global functions and values
+# will be stored
+globalModuleName = 'PandaGlobals'
+
+# This is the name of the file that the importing code will be stored
+importModuleName = 'PandaModules'
+
+# This is the name of the file where the static helper class will be stored
+staticModuleName = 'PandaStatic'
+
+# A header for all the generated files
+generatedHeader = '# This file is automatically generated. It would be unwise to edit.\n\n'
+
+# These modules should come from somewhere outside this program
+# Maybe in an environment variable, or by looking at what you are
+# attached to?
+CodeModuleNameList = []
+
+# This is the module that contains the interrogate functions
+InterrogateModuleName = None
+
+# Should FFI output C++ comments with the source code?
+wantComments = 1
+
+# Should FFI output type assertions?
+wantTypeChecking = 1

+ 23 - 0
direct/src/ffi/FFIEnvironment.py

@@ -0,0 +1,23 @@
+import FFIConstants
+
+class FFIEnvironment:
+    def __init__(self):
+        self.types = {}
+        self.globalFunctions = []
+        self.globalValues = []
+    
+    def addType(self, typeDescriptor, name):
+        if self.types.has_key(name):
+            FFIConstants.notify.warning('Redefining type named: ' + name)
+        self.types[name] = typeDescriptor
+    
+    def getTypeNamed(self, name):
+        try:
+            self.types[name]
+        except KeyError:
+            raise 'Type not found in FFIEnvironment'
+    
+    def addGlobalFunction(self, typeDescriptor):
+        self.globalFunctions.append(typeDescriptor)
+    def addGlobalValue(self, typeDescriptor):
+        self.globalValues.append(typeDescriptor)

+ 130 - 0
direct/src/ffi/FFIExternalObject.py

@@ -0,0 +1,130 @@
+
+import FFIConstants
+import TypedObject
+
+WrapperClassMap = {}
+
+
+
+def getDowncastFunctions(thisClass, baseClass, chain):
+    if (thisClass == baseClass):
+        # Found it, return true
+        return 1
+    elif (len(thisClass.__bases__) == 0):
+        # Not here, return 0
+        return 0
+    else:
+        # Look recursively in the classes thisClass inherits from
+        for base in thisClass.__bases__:
+            # If it finds it, append the base class's downcast function
+            # to the chain if it has one
+            if getDowncastFunctions(base, baseClass, chain):
+                downcastFuncName = 'downcastTo' + thisClass.__name__
+                if base.__dict__.has_key(downcastFuncName):
+                    FFIConstants.notify.info('Found downcast function %s in %s'                        % (downcastFuncName, base.__name__))
+                    chain.append(base.__dict__[downcastFuncName])
+                return chain
+
+
+class FFIExternalObject:
+    def __init__(self, *_args):
+        # By default, we do not manage our own memory
+        self.userManagesMemory = 0
+        # Start with a null this pointer
+        self.this = 0
+        
+    def asExactType(self):
+        return self.getType()
+    
+    def isTypedObject(self):
+        return isinstance(self, TypedObject.TypedObject)
+    
+    def setPointer(self):
+        if (self.this == 0):
+            # Null pointer, return None
+            return None
+        # If it is not a typed object, our work is done, just return the object
+        if (not self.isTypedObject()):
+            return self
+        # Ok, it is a typed object. See what type it really is and downcast
+        # to that type (if necessary)
+        exactWrapperClass = self.wrapperClassForTypeHandle(self.asExactType())
+        # We do not need to downcast if we already have the same class
+        if (exactWrapperClass and (exactWrapperClass != self.__class__)):
+            # Create a new wrapper class instance
+            exactObject = exactWrapperClass(None)
+            # Get the downcast pointer that has had all the downcast funcs called
+            downcastObject = self.downcast(exactWrapperClass)
+            exactObject.this = downcastObject.this
+            exactObject.userManagesMemory = downcastObject.userManagesMemory
+            # Make sure the original downcast object does not get garbage collected
+            # so that the exactObject will not get gc'd thereby transferring ownership
+            # of the object to this new exactObject
+            downcastObject.userManagesMemory = 0
+            return exactObject
+        else:
+            return self
+ 
+    def wrapperClassForTypeHandle(self, aTypeHandle):
+        if WrapperClassMap.has_key(aTypeHandle.getIndex()):
+            return WrapperClassMap[aTypeHandle.getIndex()]
+        else:
+            return None
+        
+    def registerInTypeMap(self):
+        global WrapperClassMap
+        if self.isTypedObject():
+            typeIndex = self.__class__.getClassType().getIndex()
+            WrapperClassMap[typeIndex] = self.__class__
+
+    def downcast(self, specificClass):
+        FFIConstants.notify.info('downcasting from %s to %s' % \
+            (self.__class__.__name__, specificClass.__name__))
+        downcastChain = getDowncastFunctions(specificClass, self.__class__, [])
+        FFIConstants.notify.info(downcastChain)
+        newObject = self
+        if (downcastChain == None):
+            return newObject
+        elif (downcastChain == 1):
+            return newObject
+        elif (downcastChain == 0):
+            return newObject
+        else:
+            for downcastFunc in downcastChain:
+                FFIConstants.notify.info('Downcasting %s using %s' % \
+                    (newObject.__class__.__name__, downcastFunc))
+                newObject = downcastFunc(newObject)
+            return newObject
+
+    def compareTo(self, other):
+        # By default, we compare the C++ pointers
+        # Some classes will override the compareTo operator with their own
+        # logic in C++ (like vectors and matrices for instance)
+        try:
+            if self.this < other.this:
+                return -1
+            if self.this > other.this:
+                return 1
+            else:
+                return 0
+        except:
+            return 1
+
+    def __cmp__(self, other):
+        # Only use the C++ compareTo if they are the same class
+        if isinstance(other, self.__class__):
+            return self.compareTo(other)
+        # Otherwise, they must not be the same
+        else:
+            return 1
+
+    def __hash__(self):
+        return self.this
+
+
+
+
+
+
+
+    

+ 655 - 0
direct/src/ffi/FFIInterrogateDatabase.py

@@ -0,0 +1,655 @@
+
+# Note: do not import this file directly, it is meant to be used as part of
+# a Python script (generatePythonCode) that sets up variables that this
+# module depends on
+
+import string
+import os
+import compileall
+
+import FFIEnvironment
+import FFITypes
+import FFISpecs
+import FFIRename
+import FFIConstants
+import FFIOverload
+
+
+# FFIConstants.notify.setDebug(1)
+FFIConstants.notify.info('Importing interrogate library: ' + FFIConstants.InterrogateModuleName)
+# Note: we do a from lib import * here because we do not want
+# to be dependent on the name of the interrogate library in this code
+exec('from ' + FFIConstants.InterrogateModuleName + ' import *')
+
+# Import all the C++ modules
+for CModuleName in FFIConstants.CodeModuleNameList:
+    FFIConstants.notify.info('Importing code library: ' + CModuleName)
+    exec('import ' + CModuleName)
+
+def constructGlobalFile(codeDir):
+    """
+    Open a file that will hold the global values and functions code
+    """
+    file = open(os.path.join(codeDir, FFIConstants.globalModuleName + '.py'), 'w')
+    return file
+
+def constructImportFile(codeDir):
+    """
+    Open a file that will hold the global values and functions code
+    """
+    file = open(os.path.join(codeDir, FFIConstants.importModuleName + '.py'), 'w')
+    return file
+
+def outputGlobalFileImports(file, methodList):
+    # Print the standard header
+    file.write(FFIConstants.generatedHeader)
+
+    # Import the C modules
+    for CModuleName in FFIConstants.CodeModuleNameList:
+        file.write('import ' + CModuleName + '\n')
+
+    moduleList = []
+    for method in methodList:
+        returnType = method.typeDescriptor.returnType.recursiveTypeDescriptor()
+        if (not (returnType.foreignTypeName in moduleList)):
+            if (returnType.__class__ == FFITypes.ClassTypeDescriptor):
+                moduleList.append(returnType.foreignTypeName)
+    
+    for moduleName in moduleList:
+        file.write('import ' + moduleName + '\n')
+    
+    file.write('\n')
+
+
+def outputImportFileImports(file, typeList):
+    """
+    This is the file that we will import to get all the panda modules
+    """
+    
+    # Print the standard header
+    file.write(FFIConstants.generatedHeader)
+    
+    file.write('# Import the interrogate module\n')
+    file.write('import ' + FFIConstants.InterrogateModuleName + '\n')
+    file.write('\n')
+    
+    file.write('# Import the C modules\n')
+    for CModuleName in FFIConstants.CodeModuleNameList:
+        file.write('import ' + CModuleName + '\n')
+    file.write('\n')
+
+    # Filter out only the class and enum type descriptors (not const, pointers, etc)
+    classTypeList = []
+    enumTypeList = []
+    for type in typeList:
+        if (type.__class__ == FFITypes.ClassTypeDescriptor):
+            if (not type.isNested):
+                classTypeList.append(type)
+        elif (type.__class__ == FFITypes.EnumTypeDescriptor):
+            if (not type.isNested):
+                enumTypeList.append(type)
+            
+    # Sort the types based on inheritance, most generic first
+    classTypeList.sort(FFIOverload.inheritanceLevelSort)
+
+    moduleList = []
+    for type in classTypeList:    
+        moduleList.append(type.foreignTypeName)
+
+    file.write('import FFIExternalObject\n')
+    file.write('\n')
+
+    file.write('# Import enums into the global name space\n')
+    for type in enumTypeList:
+        file.write('from ' + type.enumName + ' import *\n')
+    file.write('\n')
+
+    file.write('# Import classes\n')
+    for moduleName in moduleList:
+        file.write('import ' + moduleName + '\n')    
+    file.write('\n')
+
+    file.write('# Import the global module file into our name space\n')
+    file.write('from ' + FFIConstants.globalModuleName + ' import *\n')
+    file.write('\n')
+
+    file.write('# Now generate the classes\n')
+    for moduleName in moduleList:
+        file.write(moduleName + '.generateClass_' + moduleName + '()\n')
+    file.write('\n')
+        
+    file.write('# Now put the classes in the wrapper class map\n')
+    for moduleName in moduleList:
+        file.write('obj = ' + moduleName + '.' + moduleName + '(None)\n')
+        file.write('obj.registerInTypeMap()\n')
+    file.write('\n')
+
+    file.write('# Now copy the classes into our own namespace\n')
+    for moduleName in moduleList:
+        file.write(moduleName + ' = ' + moduleName + '.' + moduleName + '\n')
+    file.write('\n')
+
+def generateStaticClass(codeDir):
+    """
+    Create a file that will hold the static class definition
+    """
+    file = open(os.path.join(codeDir, FFIConstants.staticModuleName + '.py'), 'w')
+    # Print the standard header
+    file.write(FFIConstants.generatedHeader)
+    file.write('class ' + FFIConstants.staticModuleName + ':\n')
+    file.write('    def __init__(self, function):\n')
+    file.write('        self.__call__ = function\n')
+    file.close()
+    return file
+
+def getTypeName(typeIndex, scoped=0):
+    """
+    Return a fully specified type name for this type index
+    Return the scoped name if asked for it
+    """
+    nameComponents = []
+    name = ''
+
+    
+    if scoped:
+        typeName = interrogate_type_scoped_name(typeIndex)
+    else:        
+        typeName = interrogate_type_name(typeIndex)
+
+    if typeIndex == 0:
+        FFIConstants.notify.debug('typeIndex 0: ' + typeName)
+        
+    if interrogate_type_is_wrapped(typeIndex):
+        typeName = getTypeName(interrogate_type_wrapped_type(typeIndex))
+    if interrogate_type_is_const(typeIndex):
+        nameComponents.append('const')
+    if interrogate_type_is_pointer(typeIndex):
+        nameComponents.append('ptr')
+    if interrogate_type_is_signed(typeIndex):
+        # signed is now built into the type name
+        #nameComponents.append('signed')
+        pass
+    if interrogate_type_is_unsigned(typeIndex):
+        # unsigned is now built into the type name
+        #nameComponents.append('unsigned')
+        pass
+    if interrogate_type_is_long(typeIndex):
+        nameComponents.append('long')
+    if interrogate_type_is_longlong(typeIndex):
+        nameComponents.append('longLong')
+    if interrogate_type_is_short(typeIndex):
+        nameComponents.append('short')
+    if (len(nameComponents) > 0):
+        typeName = string.capitalize(typeName[0]) + typeName[1:]
+    nameComponents.append(typeName)
+    for i in range(len(nameComponents)):
+        if (i == 0):
+            name = name + nameComponents[i]
+        else:
+            name = name + string.capitalize(nameComponents[i][0]) + nameComponents[i][1:]
+
+    FFIConstants.notify.debug('typeIndex: ' + `typeIndex` + ' typeName: ' + typeName + ' has name: ' + name)
+
+    if not name:
+        FFIConstants.notify.warning('typeIndex: ' + `typeIndex` + ' typeName: ' + typeName + ' has no name')
+
+    return name
+
+
+class FFIInterrogateDatabase:
+
+    def __init__(self):
+        self.typeIndexMap = {}
+        self.environment = FFIEnvironment.FFIEnvironment()
+
+    def isDefinedType(self, typeIndex):
+        return self.typeIndexMap.has_key(typeIndex)
+    
+    def constructDescriptor(self, typeIndex):
+        if interrogate_type_is_atomic(typeIndex):
+            return self.constructPrimitiveTypeDescriptor(typeIndex)
+        
+        elif interrogate_type_is_enum(typeIndex):
+            return self.constructEnumTypeDescriptor(typeIndex)
+        
+        elif interrogate_type_is_wrapped(typeIndex):
+            if interrogate_type_is_pointer(typeIndex):
+                return self.constructPointerTypeDescriptor(typeIndex)
+            elif interrogate_type_is_const(typeIndex):
+                return self.constructConstTypeDescriptor(typeIndex)
+        
+        elif (interrogate_type_is_class(typeIndex) or
+              interrogate_type_is_struct(typeIndex) or
+              interrogate_type_is_union(typeIndex)):
+            return self.constructClassTypeDescriptor(typeIndex)
+
+        elif (not interrogate_type_is_fully_defined(typeIndex)):
+            return  self.constructClassTypeDescriptor(typeIndex)
+        
+        else:
+            raise 'A type in the interrogate database was not recognized: '+ `typeIndex`
+    
+    def constructPrimitiveTypeDescriptor(self, typeIndex):
+        if self.isDefinedType(typeIndex):
+            return self.typeIndexMap[typeIndex]
+        else:
+            descriptor = FFITypes.PrimitiveTypeDescriptor()
+            #descriptor.environment = self.environment
+            descriptor.atomicType = interrogate_type_atomic_token(typeIndex)
+            descriptor.foreignTypeName = \
+                FFIRename.nonClassNameFromCppName(getTypeName(typeIndex))
+            descriptor.typeIndex = typeIndex
+            self.typeIndexMap[typeIndex] = descriptor
+            return descriptor
+    
+    def constructEnumTypeDescriptor(self, typeIndex):
+        if self.isDefinedType(typeIndex):
+            return self.typeIndexMap[typeIndex]
+        else:
+            descriptor = FFITypes.EnumTypeDescriptor()
+            #descriptor.environment = self.environment
+            descriptor.isNested = interrogate_type_is_nested(typeIndex)
+            if descriptor.isNested:
+                outerTypeIndex = interrogate_type_outer_class(typeIndex)
+                descriptor.outerType = self.constructDescriptor(outerTypeIndex)
+            # Enums are ints in C++ but we do not want to redefine the int type
+            # So we will just call them enums
+            descriptor.enumName = FFIRename.classNameFromCppName(getTypeName(typeIndex))
+            descriptor.foreignTypeName = '__enum__' + descriptor.enumName
+            numValues = interrogate_type_number_of_enum_values(typeIndex)
+
+            # Store the names and values of the enum in a dictionary
+            for i in range(numValues):
+                value = interrogate_type_enum_value(typeIndex, i)
+                name = FFIRename.classNameFromCppName(
+                    interrogate_type_enum_value_name(typeIndex, i))
+                scopedName = FFIRename.classNameFromCppName(
+                    interrogate_type_enum_value_scoped_name(typeIndex, i))
+                descriptor.values[name] = value
+            
+            descriptor.typeIndex = typeIndex
+            self.typeIndexMap[typeIndex] = descriptor
+            return descriptor
+
+    def constructPointerTypeDescriptor(self, typeIndex):
+        if self.isDefinedType(typeIndex):
+            return self.typeIndexMap[typeIndex]
+        descriptor = FFITypes.PointerTypeDescriptor()
+        #descriptor.environment = self.environment
+        descriptor.isNested = interrogate_type_is_nested(typeIndex)
+        if descriptor.isNested:
+            outerTypeIndex = interrogate_type_outer_class(typeIndex)
+            descriptor.outerType = self.constructDescriptor(outerTypeIndex)
+        descriptor.foreignTypeName = \
+             FFIRename.nonClassNameFromCppName(getTypeName(typeIndex))
+        descriptor.typeIndex = typeIndex
+        wrappedTypeIndex = interrogate_type_wrapped_type(typeIndex)
+        wrappedTypeDescriptor = self.constructDescriptor(wrappedTypeIndex)
+        descriptor.typeDescriptor = wrappedTypeDescriptor
+        self.typeIndexMap[typeIndex] = descriptor
+        return descriptor
+    
+    def constructConstTypeDescriptor(self, typeIndex):
+        if self.isDefinedType(typeIndex):
+            return self.typeIndexMap[typeIndex]
+        descriptor = FFITypes.ConstTypeDescriptor()
+        #descriptor.environment = self.environment
+        descriptor.isNested = interrogate_type_is_nested(typeIndex)
+        if descriptor.isNested:
+            outerTypeIndex = interrogate_type_outer_class(typeIndex)
+            descriptor.outerType = self.constructDescriptor(outerTypeIndex)
+        descriptor.foreignTypeName = \
+             FFIRename.nonClassNameFromCppName(getTypeName(typeIndex))
+        descriptor.typeIndex = typeIndex
+        wrappedTypeIndex = interrogate_type_wrapped_type(typeIndex)
+        wrappedTypeDescriptor = self.constructDescriptor(wrappedTypeIndex)
+        descriptor.typeDescriptor = wrappedTypeDescriptor
+        self.typeIndexMap[typeIndex] = descriptor
+        return descriptor
+
+    def constructParentTypeDescriptors(self, typeIndex):
+        numParents = interrogate_type_number_of_derivations(typeIndex)
+        descriptors = []
+        for i in range(numParents):
+            parentTypeIndex = interrogate_type_get_derivation(typeIndex, i)
+            if self.isDefinedType(parentTypeIndex):
+                parentTypeDescriptor = self.typeIndexMap[parentTypeIndex]
+            else:
+                parentTypeDescriptor = self.constructDescriptor(parentTypeIndex)
+            descriptors.append(parentTypeDescriptor)
+        return descriptors
+
+    def constructNestedTypeDescriptors(self, typeIndex):
+        nestedTypes = []
+        numNestedTypes = interrogate_type_number_of_nested_types(typeIndex)
+        for i in range(numNestedTypes):
+            nestedTypeIndex = interrogate_type_get_nested_type(typeIndex, i)
+            descriptor = self.constructDescriptor(nestedTypeIndex)
+            nestedTypes.append(descriptor)
+        return nestedTypes
+    
+    def constructClassTypeDescriptor(self, typeIndex):
+        if self.isDefinedType(typeIndex):
+            return self.typeIndexMap[typeIndex]
+        descriptor = FFITypes.ClassTypeDescriptor()
+        self.typeIndexMap[typeIndex] = descriptor
+        #descriptor.environment = self.environment
+        descriptor.isNested = interrogate_type_is_nested(typeIndex)
+        if descriptor.isNested:
+            outerTypeIndex = interrogate_type_outer_class(typeIndex)
+            descriptor.outerType = self.constructDescriptor(outerTypeIndex)
+        descriptor.foreignTypeName = FFIRename.classNameFromCppName(getTypeName(typeIndex))
+        if FFIConstants.wantComments:
+            if interrogate_type_has_comment(typeIndex):
+                descriptor.comment = interrogate_type_comment(typeIndex)
+        descriptor.typeIndex = typeIndex
+        descriptor.instanceMethods = self.constructMemberFunctionSpecifications(typeIndex)
+        descriptor.upcastMethods = self.constructUpcastFunctionSpecifications(typeIndex)
+        # Constructing downcasts does not return the functions, it just puts them in the class
+        # See the comment in that function
+        self.constructDowncastFunctionSpecifications(typeIndex)
+        descriptor.filterOutStaticMethods()
+        descriptor.constructors = self.constructConstructorSpecifications(typeIndex)
+        descriptor.destructor = self.constructDestructorSpecification(typeIndex)
+        descriptor.parentTypes = self.constructParentTypeDescriptors(typeIndex)
+        descriptor.nestedTypes = self.constructNestedTypeDescriptors(typeIndex)
+        return descriptor
+
+    def constructFunctionTypeDescriptors(self, functionIndex):
+
+        # Store these values because they will be the same for all the wrappers
+        isVirtual = interrogate_function_is_virtual(functionIndex)
+        #environment = self.environment
+        foreignTypeName = interrogate_function_name(functionIndex)
+        if FFIConstants.wantComments:
+            prototype = interrogate_function_prototype(functionIndex)
+            if interrogate_function_has_comment(functionIndex):
+                comment = interrogate_function_comment(functionIndex)
+            else:
+                comment = ''
+        # Prepend lib to the module name it reports because that will be the name of
+        # the Python module we import. This is apparently stems from a makefile
+        # discrepency in the way we build the libraries
+        moduleName = 'lib' + interrogate_function_module_name(functionIndex)
+        typeIndex = functionIndex
+
+        # Look at the Python wrappers for this function
+        numPythonWrappers = interrogate_function_number_of_python_wrappers(functionIndex)
+        
+        if numPythonWrappers == 0:
+            # If there are no Python wrappers, it is because interrogate could not handle
+            # something about the function. Just return an empty list
+            return []
+
+        wrapperDescriptors = []
+
+        # Iterate over the wrappers constructing a FunctionTypeDescriptor for each
+        for i in range(numPythonWrappers):
+            descriptor = FFITypes.FunctionTypeDescriptor()
+            descriptor.isVirtual = isVirtual
+            #descriptor.environment = environment
+            descriptor.foreignTypeName = foreignTypeName
+            if FFIConstants.wantComments:
+                descriptor.comment = comment
+                descriptor.prototype = prototype
+            descriptor.moduleName = moduleName
+            descriptor.typeIndex = typeIndex
+            pythonFunctionIndex = interrogate_function_python_wrapper(functionIndex, i)
+            descriptor.wrapperName = interrogate_wrapper_name(pythonFunctionIndex)
+            # Even if it does not have a return value, it reports void which is better
+            # for generating code, so I will not even ask here
+            # if interrogate_wrapper_has_return_value(pythonFunctionIndex):
+            returnType = interrogate_wrapper_return_type(pythonFunctionIndex)
+            descriptor.returnType = self.constructDescriptor(returnType)
+            descriptor.argumentTypes = self.constructFunctionArgumentTypes(pythonFunctionIndex)
+            descriptor.userManagesMemory = interrogate_wrapper_caller_manages_return_value(pythonFunctionIndex)
+            descriptor.returnValueDestructor = interrogate_wrapper_return_value_destructor(pythonFunctionIndex)
+            wrapperDescriptors.append(descriptor)
+            
+        return wrapperDescriptors
+    
+    def constructFunctionArgumentTypes(self, functionIndex):
+        numArgs = interrogate_wrapper_number_of_parameters(functionIndex)
+        arguments = []
+        for argIndex in range(numArgs):
+            if interrogate_wrapper_parameter_has_name(functionIndex, argIndex):
+                name =  FFIRename.nonClassNameFromCppName(
+                    interrogate_wrapper_parameter_name(functionIndex, argIndex))
+            else:
+                name = ('parameter' + `argIndex`)
+            descriptor = self.constructDescriptor(
+                interrogate_wrapper_parameter_type(functionIndex, argIndex))
+            
+            argSpec = FFISpecs.MethodArgumentSpecification()
+            if interrogate_wrapper_parameter_is_this(functionIndex, argIndex):
+                argSpec.isThis = 1
+            argSpec.name = name
+            argSpec.typeDescriptor = descriptor
+            arguments.append(argSpec)
+        return arguments
+        
+    def constructMemberFunctionSpecifications(self, typeIndex):
+        funcSpecs = []
+        numFuncs = interrogate_type_number_of_methods(typeIndex)
+        for i in range(numFuncs):
+            funcIndex = interrogate_type_get_method(typeIndex, i)
+            typeDescs = self.constructFunctionTypeDescriptors(funcIndex)
+            for typeDesc in typeDescs:
+                funcSpec = FFISpecs.MethodSpecification()
+                funcSpec.name = FFIRename.methodNameFromCppName(
+                    interrogate_function_name(funcIndex))
+                funcSpec.typeDescriptor = typeDesc
+                funcSpec.index = funcIndex
+                funcSpecs.append(funcSpec)
+        return funcSpecs
+
+    def constructUpcastFunctionSpecifications(self, typeIndex):
+        funcSpecs = []
+        numFuncs = interrogate_type_number_of_derivations(typeIndex)
+        for i in range(numFuncs):
+            if interrogate_type_derivation_has_upcast(typeIndex, i):
+                funcIndex = interrogate_type_get_upcast(typeIndex, i)
+                typeDescs = self.constructFunctionTypeDescriptors(funcIndex)
+                for typeDesc in typeDescs:
+                    funcSpec = FFISpecs.MethodSpecification()
+                    funcSpec.name = FFIRename.methodNameFromCppName(
+                        interrogate_function_name(funcIndex))
+                    funcSpec.typeDescriptor = typeDesc
+                    funcSpec.index = funcIndex
+                    funcSpecs.append(funcSpec)
+        return funcSpecs
+    
+    def constructDowncastFunctionSpecifications(self, typeIndex):
+        """
+        The strange thing about downcast functions is that they appear in the
+        class they are being downcast TO, not downcast FROM. But they should be
+        built into the class they are being downcast from. For instance, a method
+        downcastToNode(ptrBoundedObject) will appear in Node's list of methods
+        but should be compiled into BoundedObject's class
+        """
+        numFuncs = interrogate_type_number_of_derivations(typeIndex)
+        for i in range(numFuncs):
+            # Make sure this downcast is possible
+            if (not interrogate_type_derivation_downcast_is_impossible(typeIndex, i)):
+                if interrogate_type_derivation_has_downcast(typeIndex, i):
+                    funcIndex = interrogate_type_get_downcast(typeIndex, i)
+                    typeDescs = self.constructFunctionTypeDescriptors(funcIndex)
+                    for typeDesc in typeDescs:
+                        funcSpec = FFISpecs.MethodSpecification()
+                        funcSpec.name = FFIRename.methodNameFromCppName(
+                            interrogate_function_name(funcIndex))
+                        funcSpec.typeDescriptor = typeDesc
+                        funcSpec.index = funcIndex
+                        # Here we look for the class in the first argument
+                        fromClass = typeDesc.argumentTypes[0].typeDescriptor.recursiveTypeDescriptor()
+                        # Append this funcSpec to that class's downcast methods
+                        fromClass.downcastMethods.append(funcSpec)
+    
+    def constructConstructorSpecifications(self, typeIndex):
+        funcSpecs = []
+        numFuncs = interrogate_type_number_of_constructors(typeIndex)
+        for i in range(numFuncs):
+            funcIndex = interrogate_type_get_constructor(typeIndex, i)
+            typeDescs = self.constructFunctionTypeDescriptors(funcIndex)
+            for typeDesc in typeDescs:
+                funcSpec = FFISpecs.MethodSpecification()
+                funcSpec.name = 'constructor'
+                # funcSpec.name = FFIRename.methodNameFromCppName(
+                #    interrogate_function_name(funcIndex))
+                funcSpec.typeDescriptor = typeDesc
+                funcSpec.index = funcIndex            
+                funcSpecs.append(funcSpec)
+        return funcSpecs
+    
+    def constructDestructorSpecification(self, typeIndex):
+        if (not interrogate_type_has_destructor(typeIndex)):
+            return None
+        funcIndex = interrogate_type_get_destructor(typeIndex)
+        typeDescs = self.constructFunctionTypeDescriptors(funcIndex)
+        if (len(typeDescs) == 0):
+            return None
+        for typeDesc in typeDescs:
+            funcSpec = FFISpecs.MethodSpecification()
+            funcSpec.name = 'destructor'
+            # funcSpec.name = FFIRename.methodNameFromCppName(
+            #    interrogate_function_name(funcIndex))
+            funcSpec.typeDescriptor = typeDesc
+            funcSpec.index = funcIndex
+            return funcSpec
+    
+    def addTypes(self):
+        for i in range(interrogate_number_of_global_types()):
+            self.constructDescriptor(interrogate_get_global_type(i))
+
+    def addEnvironmentTypes(self):
+        for descriptor in self.typeIndexMap.values():
+            self.environment.addType(descriptor, descriptor.foreignTypeName)
+    
+    def constructGlobal(self, globalIndex):
+        # We really do not need the descriptor for the value, just
+        # the getter and setter
+        # descriptor = self.typeIndexMap[interrogate_element_type(globalIndex)]
+        if interrogate_element_has_getter(globalIndex):
+            getterIndex = interrogate_element_getter(globalIndex)
+            getter = self.constructGlobalFunction(getterIndex)
+        else:
+            getter = None
+
+        if interrogate_element_has_setter(globalIndex):
+            setterIndex = interrogate_element_setter(globalIndex)
+            setter = self.constructGlobalFunction(setterIndex)
+        else:
+            setter = None
+        globalSpec = FFISpecs.GlobalValueSpecification()
+        globalSpec.getter = getter
+        globalSpec.setter = setter
+        # globalSpec.typeDescriptor = descriptor
+        cppName = interrogate_element_name(globalIndex)
+        globalSpec.name = FFIRename.classNameFromCppName(cppName)
+        return globalSpec
+
+    def constructGlobalFunction(self, globalIndex):
+        descriptors = self.constructFunctionTypeDescriptors(globalIndex)
+        if (len(descriptors) == 0):
+            return None
+        for descriptor in descriptors:
+            funcSpec = FFISpecs.GlobalFunctionSpecification()
+            funcSpec.typeDescriptor = descriptor
+            funcSpec.name = FFIRename.methodNameFromCppName(
+                funcSpec.typeDescriptor.foreignTypeName)
+            funcSpec.index = globalIndex
+            return funcSpec
+        
+    def addGlobalFunctions(self):
+        numGlobals = interrogate_number_of_global_functions()
+        for i in range(numGlobals):
+            funcIndex = interrogate_get_global_function(i)
+            newGlob = self.constructGlobalFunction(funcIndex)
+            if newGlob:
+                self.environment.addGlobalFunction(newGlob)
+
+        # Take all the global functions that have a Panda Class as their
+        # first argument and make them class methods on that class
+        # For example the global function
+        #    get_distance(node1, node2)
+        # becomes:
+        #    node1.getDistance(node2)
+       
+        # Functions that do not get moved will be stored here temporarily
+        tempGlobalFunctions = []
+        for funcSpec in self.environment.globalFunctions:
+            # If there are any arguments
+            if (len(funcSpec.typeDescriptor.argumentTypes) > 0):
+                # If the first argument is a class type descriptor
+                methodArgSpec = funcSpec.typeDescriptor.argumentTypes[0]
+                argBaseType = methodArgSpec.typeDescriptor.recursiveTypeDescriptor()
+                if isinstance(argBaseType, FFITypes.ClassTypeDescriptor):
+                    # Move this global function into the class
+                    argBaseType.globalMethods.append(funcSpec)
+                else:
+                    # Copy this function into the temp list
+                    tempGlobalFunctions.append(funcSpec)
+            else:
+                # Copy this function into the temp list
+                tempGlobalFunctions.append(funcSpec)
+        # Now copy the temp list back over the real list
+        self.environment.globalFunctions = tempGlobalFunctions
+                    
+    def addGlobalValues(self):
+        numGlobals = interrogate_number_of_globals()
+        for i in range(numGlobals):
+            globalIndex = interrogate_get_global(i)
+            newGlob = self.constructGlobal(globalIndex)
+            self.environment.addGlobalValue(newGlob)
+
+    def generateCode(self, codeDir, extensionsDir):
+        FFIConstants.notify.info( 'Generating static class...')
+        generateStaticClass(codeDir)
+
+        FFIConstants.notify.info( 'Generating type code...')
+        for type in self.environment.types.values():
+            # Do not generate code for nested types at the top level
+            if (not type.isNested):
+                type.generateGlobalCode(codeDir, extensionsDir)
+            
+        FFIConstants.notify.info( 'Generating global value code...')
+        globalFile = constructGlobalFile(codeDir)
+
+        # Make a list of all the global functions. This includes the normal
+        # global functions as well as the getters and setters on all the
+        # global values. This list is used to figure out what files to import
+        globalFunctions = self.environment.globalFunctions
+        for globalValue in self.environment.globalValues:
+            if globalValue.getter:
+                globalFunctions.append(globalValue.getter)
+            if globalValue.setter:
+                globalFunctions.append(globalValue.setter)
+        # Output all the imports based on this list of functions
+        outputGlobalFileImports(globalFile, globalFunctions)
+
+        for type in self.environment.globalValues:
+            type.generateGlobalCode(globalFile)
+            
+        FFIConstants.notify.info( 'Generating global function code...')
+        for type in self.environment.globalFunctions:
+            type.generateGlobalCode(globalFile)
+
+        globalFile.close()
+
+        FFIConstants.notify.info( 'Generating import code...')
+        importFile = constructImportFile(codeDir)
+        outputImportFileImports(importFile, self.environment.types.values())
+
+        
+        compileall.compile_dir(codeDir)
+        
+
+    def updateBindings(self):
+        FFIConstants.notify.info( 'Updating Bindings')
+        FFIConstants.notify.info( 'Adding Types...')
+        self.addTypes()
+        FFIConstants.notify.info( 'Adding global values...')
+        self.addGlobalValues()
+        FFIConstants.notify.info( 'Adding global functions...')
+        self.addGlobalFunctions()
+        self.addEnvironmentTypes()

+ 280 - 0
direct/src/ffi/FFIOverload.py

@@ -0,0 +1,280 @@
+
+from PythonUtil import *    
+from types import *
+import string
+import FFIConstants
+
+"""
+Things that are not supported:
+ - Overloading a function based on an enum being differentiated from an int
+ - Type names from C++ cannot begin with __enum__
+"""
+
+AT_not_atomic = 0
+AT_int = 1
+AT_float = 2
+AT_double = 3
+AT_bool = 4
+AT_char = 5
+AT_void = 6
+AT_string = 7
+
+def getTypeName(classTypeDesc, typeDesc):
+    """
+    Map the interrogate primitive type names to python type names.
+    We assume that the module using this has imported the types module.
+    It is valid to pass in None for classTypeDesc if we are not in a class
+    """
+
+    typeName = typeDesc.getFullNestedName()
+
+    # Atomic C++ types are type checked against the builtin
+    # Python types. This code sorts out the mapping
+    if typeDesc.isAtomic():
+        
+        # Ints and bools are treated as ints.
+        # Enums are special and are not atomic, see below
+        if ((typeDesc.atomicType == AT_int) or
+            (typeDesc.atomicType == AT_bool)):
+            return 'types.IntType'
+        
+        # Floats and doubles are both floats in Python
+        elif ((typeDesc.atomicType == AT_float) or
+            (typeDesc.atomicType == AT_double)):
+            return 'types.FloatType'
+
+        # Strings and individual chars are treated as Python strings
+        elif ((typeDesc.atomicType == AT_char) or
+            (typeDesc.atomicType == AT_string)):
+            return 'types.StringType'
+        
+        elif (typeDesc.atomicType == AT_void):
+            # Convert the void type to None type... I guess...
+            # So far we do not have any code that uses this
+            return 'types.NoneType'
+        
+    # If the type is an enum, we really want to treat it like an int
+    # To handle this, the type will have __enum__ prepended to the name
+    elif (typeName[0:8] == '__enum__'):
+        return 'types.IntType'
+
+    # If it was not atomic or enum, it must be a class which is a
+    # bit trickier because we output different things depending on the
+    # scoping of the type. 
+    else:
+        # Assuming the class and the module are the same name, return
+        # typeName.typeName (ie Node.Node)
+        # Unless we are in the same module. For instance, in Node.py,
+        # Node.Node is not defined, so just return Node.
+        nestedTypes = string.split(typeName, '.')
+        if (classTypeDesc and (classTypeDesc.foreignTypeName in nestedTypes)):
+            # Return the last type (SubClass) in the nested types
+            return nestedTypes[-1]
+        else:
+            # Return the full Module.Class.SubClass
+            return (nestedTypes[0] + '.' + typeName)
+
+
+def inheritsFrom(type1, type2):
+    """
+    Return true if type1 inherits from type2
+    This works by recursively checking parentTypes for type1
+    """
+    if type1.parentTypes:
+        if type2 in type1.parentTypes:
+            return 1
+        else:
+            result = 0
+            for type in type1.parentTypes:
+                result = (result or inheritsFrom(type, type2))
+            return result
+    else:
+        return 0
+
+def getInheritanceLevel(type):
+#    if (len(type.parentTypes) == 0):
+#        return 0
+    level = 0
+    for parentType in type.parentTypes:
+        level = max(level, 1+getInheritanceLevel(parentType))
+    for nestedType in type.nestedTypes:
+        level = max(level, 1+getInheritanceLevel(nestedType))
+    return level
+
+def inheritanceLevelSort(type1, type2):
+    level1 = getInheritanceLevel(type1)
+    level2 = getInheritanceLevel(type2)
+    if (level1 == level2):
+        return 0
+    elif (level1 < level2):
+        return -1
+    elif (level1 > level2):
+        return 1
+
+
+def subclass(type1, type2):
+    """
+    Helper funcion used in sorting classes by inheritance
+    """
+    # If the types are the same, return 0
+    if type1 == type2:
+	return 0
+    # If you have no args, sort you first
+    elif (type1 == 0):
+        return 1
+    elif (type2 == 0):
+        return -1
+    # If class1 inherits from class2 return 1
+    elif inheritsFrom(type1, type2):
+	return 1
+    # If class2 inherits from class1 return -1
+    elif inheritsFrom(type2, type1):
+        return -1
+    else:
+        # This is the dont care case. We must specify a sorting
+        # rule just so it is not arbitrary
+        if (type1.foreignTypeName > type2.foreignTypeName):
+            return -1
+        else:
+            return 1
+
+
+class FFIMethodArgumentTreeCollection:
+    def __init__(self, classTypeDesc, methodSpecList):
+        self.classTypeDesc = classTypeDesc
+        self.methodSpecList = methodSpecList
+        self.methodDict = {}
+        self.treeDict = {}
+        
+    def outputOverloadedMethodHeader(self, file):
+        indent(file, 1, 'def ' +  self.methodSpecList[0].name
+                   + '(self, *_args):\n')
+        indent(file, 2, 'numArgs = len(_args)\n')
+        
+    def outputOverloadedMethodFooter(self, file):
+        # If the overloaded function got all the way through the if statements
+        # it must have had the wrong number or type of arguments
+        indent(file, 2, "raise TypeError, 'Invalid arguments'\n\n")
+    
+    def setup(self):
+        for method in self.methodSpecList:
+            numArgs = len(method.typeDescriptor.thislessArgTypes())
+            numArgsList = ifAbsentPut(self.methodDict, numArgs, [])
+            numArgsList.append(method)
+        for numArgs in self.methodDict.keys():
+            methodList = self.methodDict[numArgs]
+            tree = FFIMethodArgumentTree(self.classTypeDesc, methodList)
+            treeList = ifAbsentPut(self.treeDict, numArgs, [])
+            treeList.append(tree)
+        
+    def generateCode(self, file, nesting):
+        self.setup()
+        self.outputOverloadedMethodHeader(file)
+        numArgsKeys = self.treeDict.keys()
+        numArgsKeys.sort()
+        for numArgs in numArgsKeys:
+            trees = self.treeDict[numArgs]
+            for tree in trees:
+                indent(file, 2, 'if (numArgs == ' + `numArgs` + '):\n')
+                tree.setup()
+                tree.traverse(file)
+        self.outputOverloadedMethodFooter(file)
+
+class FFIMethodArgumentTree:
+    """
+    Tree is made from nested dictionaries.
+    The keys are methodNamed.
+    The values are [tree, methodSpec]
+    methodSpec may be None at any level
+    If tree is None, it is a leaf node and methodSpec will be defined
+    """
+    def __init__(self, classTypeDesc, methodSpecList):
+        self.argSpec = None
+        self.classTypeDesc = classTypeDesc
+        self.methodSpecList = methodSpecList
+        # The actual tree is implemented as nested dictionaries
+        self.tree = {}
+
+    def setup(self):
+        for methodSpec in self.methodSpecList:
+            argTypes = methodSpec.typeDescriptor.thislessArgTypes()
+            self.fillInArgTypes(argTypes, methodSpec)
+    
+    def fillInArgTypes(self, argTypes, methodSpec):
+        # If the method takes no arguments, we will assign a type index of 0
+        if (len(argTypes) == 0):
+            self.tree[0] = [
+                FFIMethodArgumentTree(self.classTypeDesc,
+                                      self.methodSpecList),
+                methodSpec]
+        
+        else:
+            self.argSpec = argTypes[0]
+            typeDesc = self.argSpec.typeDescriptor.recursiveTypeDescriptor()
+            
+            if (len(argTypes) == 1):
+                # If this is the last parameter, we are a leaf node, so store the
+                # methodSpec in this dictionary
+                self.tree[typeDesc] = [None, methodSpec]
+            else:
+                if self.tree.has_key(typeDesc):
+                    # If there already is a tree here, jump into and pass the
+                    # cdr of the arg list
+                    subTree = self.tree[typeDesc][0]
+                    subTree.fillInArgTypes(argTypes[1:], methodSpec)
+                else:
+                    # Add a subtree for the rest of the arg list
+                    subTree = FFIMethodArgumentTree(self.classTypeDesc,
+                                                    self.methodSpecList)
+                    subTree.fillInArgTypes(argTypes[1:], methodSpec)
+                    # This subtree has no method spec
+                    self.tree[typeDesc] = [subTree, None]
+
+    def traverse(self, file, level=1):
+        # Make a copy of the keys so we can sort them in place
+        sortedKeys = self.tree.keys()
+        # Sort the keys based on inheritance hierarchy, most generic classes first
+        sortedKeys.sort(subclass)
+        for typeDesc in sortedKeys:
+            # See if this takes no arguments
+            if (typeDesc == 0):
+                # Output the function
+                methodSpec = self.tree[0][1]
+                indent(file, level+2, 'return ')
+                methodSpec.outputOverloadedCall(file, 0)
+            else:
+                typeName = getTypeName(self.classTypeDesc, typeDesc)
+                indent(file, level+2, 'if (isinstance(_args[' + `level-1` + '], '
+                       + typeName
+                       + '))')
+                # If it is looking for a float, make it accept an integer too
+                if (typeName == 'types.FloatType'):
+                    file.write(' or (isinstance(_args[' + `level-1` + '], '
+                               + 'types.IntType'
+                               + '))')
+                file.write(':\n')
+                if (self.tree[typeDesc][0] != None):
+                    self.tree[typeDesc][0].traverse(file, level+1)
+                else:
+                    # Output the function
+                    methodSpec = self.tree[typeDesc][1]
+                    indent(file, level+3, 'return ')
+                    numArgs = level
+                    methodSpec.outputOverloadedCall(file, numArgs)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 145 - 0
direct/src/ffi/FFIRename.py

@@ -0,0 +1,145 @@
+import FFIConstants
+from string import *
+
+
+pythonKeywords = ['and','del','for','is','raise','assert','elif','from','lambda','return','break','else','global','not','try','class','except','if','or','while','continue','exec','import','pass','def','finally','in','print']
+
+
+methodRenameDictionary = {
+    'operator=='  : 'eq',
+    'operator!='  : 'ne',
+    'operator<<'  : '__lshift__',
+    'operator>>'  : '__rshift__',
+    'operator<'   : 'lessThan',
+    'operator>'   : 'greaterThan',
+    'operator<='  : 'lessThanOrEqual',
+    'operator>='  : 'greaterThanOrEqual',
+    'operator='   : 'assign',
+    'operator()'  : '__call__',
+    'operator[]'  : '__getitem__',
+    'operator++'  : 'increment',
+    'operator--'  : 'decrement',
+    'operator^'   : '__xor__',
+    'operator%'   : '__mod__',
+    'operator!'   : 'logicalNot',
+    'operator~'   : 'bitwiseNot',
+    'operator&'   : '__and__',
+    'operator&&'  : 'logicalAnd',
+    'operator|'   : '__or__',
+    'operator||'  : 'logicalOr',
+    'operator+'   : '__add__',
+    'operator-'   : '__sub__',
+    'operator*'   : '__mul__',
+    'operator/'   : '__div__',
+    'operator+='  : 'addEqual',
+    'operator-='  : 'subtractEqual',
+    'operator*='  : 'multiplyEqual',
+    'operator/='  : 'divEqual',
+    'operator,'   : 'concatenate',
+    'operator|='  : 'bitwiseOrEqual',
+    'operator&='  : 'bitwiseAndEqual',
+    'operator^='  : 'bitwiseXorEqual',
+    'operator~='  : 'bitwiseNotEqual',
+    'operator->'  : 'dereference',
+    'operator<<=' : 'leftShiftEqual',
+    'operator>>=' : 'rightShiftEqual',
+    'print'       : 'Cprint'
+    }
+    
+classRenameDictionary = {
+    'Loader'                    : 'PandaLoader',
+    'String'                    : 'CString',
+    'LMatrix4f'                 : 'Mat4',
+    'LMatrix3f'                 : 'Mat3',
+    'LVecBase4f'                : 'VBase4',
+    'LVector4f'                 : 'Vec4',
+    'LPoint4f'                  : 'Point4',
+    'LVecBase3f'                : 'VBase3',
+    'LVector3f'                 : 'Vec3',
+    'LPoint3f'                  : 'Point3',
+    'LVecBase2f'                : 'VBase2',
+    'LVector2f'                 : 'Vec2',
+    'LPoint2f'                  : 'Point2',
+    'LMatrix4d'                 : 'Mat4D',
+    'LMatrix3d'                 : 'Mat3D',
+    'LVecBase4d'                : 'VBase4D',
+    'LVector4d'                 : 'Vec4D',
+    'LPoint4d'                  : 'Point4D',
+    'LVecBase3d'                : 'VBase3D',
+    'LVector3d'                 : 'Vec3D',
+    'LPoint3d'                  : 'Point3D',
+    'LVecBase2d'                : 'VBase2D',
+    'LVector2d'                 : 'Vec2D',
+    'LPoint2d'                  : 'Point2D',
+    'Plane'                     : 'PlaneBase',
+    'Planef'                    : 'Plane',
+    'Planed'                    : 'PlaneD',
+    'Frustum'                   : 'FrustumBase',
+    'Frustumf'                  : 'Frustum',
+    'Frustumd'                  : 'FrustumD'
+    }
+
+
+def checkKeyword(cppName):
+    if cppName in pythonKeywords:
+        cppName = '_' + cppName
+    return cppName
+
+# TODO: Make faster - this thing is horribly slow    
+def classNameFromCppName(cppName):
+    # initialize to empty string
+    className = ''
+    # These are the characters we want to strip out of the name
+    badChars = '!@#$%^&*()<>,.-=+~{}? '
+    nextCap = 0
+    firstChar = 1
+    for char in cppName:
+        if (char in badChars):
+            continue
+        elif (char == '_'):
+            nextCap = 1
+            continue
+        elif (nextCap or firstChar):
+            className = className + capitalize(char)
+            nextCap = 0
+            firstChar = 0
+        else:
+            className = className + char
+    if classRenameDictionary.has_key(className):
+        className = classRenameDictionary[className]
+
+    if (className == ''):
+        FFIConstants.notify.warning('Renaming class: ' + cppName + ' to empty string')
+    # FFIConstants.notify.debug('Renaming class: ' + cppName + ' to: ' + className)
+    # Note we do not have to check for keywords because class name are capitalized
+    return className
+    
+def nonClassNameFromCppName(cppName):
+    className = classNameFromCppName(cppName)
+    # Make the first character lowercase
+    newName = lower(className[0])+className[1:]
+    # Mangle names that happen to be python keywords so they are not anymore
+    newName = checkKeyword(newName)
+    return newName
+
+def methodNameFromCppName(cppName):
+    methodName = ''
+    badChars = ' '
+    nextCap = 0
+    for char in cppName:
+        if (char in badChars):
+            continue
+        elif (char == '_'):
+            nextCap = 1
+            continue
+        elif nextCap:
+            methodName = methodName + capitalize(char)
+            nextCap = 0
+        else:
+            methodName = methodName + char    
+    if methodRenameDictionary.has_key(methodName):
+        methodName = methodRenameDictionary[methodName]
+    # Mangle names that happen to be python keywords so they are not anymore
+    methodName = checkKeyword(methodName)
+    return methodName
+

+ 499 - 0
direct/src/ffi/FFISpecs.py

@@ -0,0 +1,499 @@
+
+import FFIConstants
+import FFITypes
+import FFIOverload
+import string
+
+from PythonUtil import *
+
+class FunctionSpecification:
+    def __init__(self):
+        self.name = ''
+        self.typeDescriptor = None
+        self.index = 0
+        self.overloaded = 0
+
+    def outputTypeChecking(self, methodClass, args, file, nesting):
+        """
+        Output an assert statement to check the type of each arg in this method
+        This can be turned off with a command line parameter in generatePythonCode
+        It is valid to pass in None for methodClass if you are not in any methodClass
+        """
+        if FFIConstants.wantTypeChecking:
+            for methodArgSpec in args:
+                typeDesc = methodArgSpec.typeDescriptor.recursiveTypeDescriptor()
+                typeName = FFIOverload.getTypeName(methodClass, typeDesc)
+
+                # Special case:
+                # If it is looking for a float, accept an int as well
+                # C++ will cast it properly, and it is much more convenient
+                if (typeName == 'types.FloatType'):
+                    indent(file, nesting, 'assert((isinstance(' +
+                           methodArgSpec.name + ', types.FloatType) or isinstance(' +
+                           methodArgSpec.name + ', types.IntType)))\n')
+                else:
+                    indent(file, nesting, 'assert(isinstance(' +
+                           methodArgSpec.name + ', ' + typeName + '))\n')
+
+    def outputCFunctionComment(self, file, nesting):
+        """
+        Output a docstring to the file describing the C++ call with type info
+        Also output the C++ comment from interrogate.
+        """
+        if FFIConstants.wantComments:
+            indent(file, nesting, '"""\n')
+
+            # Output the function prototype
+            if self.typeDescriptor.prototype:
+                indent(file, nesting, self.typeDescriptor.prototype + '\n')
+
+            # Output the function comment
+            if self.typeDescriptor.comment:
+                # To insert tabs into the comment, replace all newlines with a newline+tabs
+                comment = string.replace(self.typeDescriptor.comment,
+                                         '\n', ('\n' + ('    ' * nesting)))
+                indent(file, nesting, comment)
+
+            indent(file, 0, '\n')
+            indent(file, nesting, '"""\n')
+        
+    def getFinalName(self):
+        """
+        Return the name of the function given that it might be overloaded
+        If it is overloaded, prepend "overloaded", then append the types of
+        each argument to make it unique.
+
+        So "getChild(int)" becomes "overloaded_getChild_int(int)"
+        """
+        if self.overloaded:
+            name = 'overloaded_' + self.name
+            for methodArgSpec in self.typeDescriptor.argumentTypes:
+                name = name + '_' + methodArgSpec.typeDescriptor.foreignTypeName
+            return name
+        else:
+            return self.name
+            
+    def outputOverloadedCall(self, file, numArgs):
+        """
+        Write the function call to call this overloaded method
+        For example:
+          self.overloaded_setPos_ptrNodePath_float_float_float(_args[0], _args[1], _args[2])
+        """
+        indent(file, 0, 'self.' + self.getFinalName() + '(')
+        for i in range(numArgs):
+            file.write('_args[' + `i` + ']')
+            if (i != (numArgs - 1)):
+                file.write(', ')
+        file.write(')\n')
+
+
+class GlobalFunctionSpecification(FunctionSpecification):
+    def __init__(self):
+        FunctionSpecification.__init__(self)
+
+    # Use generateCode when creating a global (non-class) function
+    def generateGlobalCode(self, file):
+        self.outputHeader(file)
+        self.outputBody(file)
+        self.outputFooter(file)
+    # Use generateMethodCode when creating a global->class function
+    def generateMethodCode(self, methodClass, file, nesting):
+        self.outputMethodHeader(methodClass, file, nesting)
+        self.outputMethodBody(methodClass, file, nesting)
+        self.outputMethodFooter(methodClass, file, nesting)
+    def generateInheritedUpcastMethodCode(self, methodClass, parentClass, file, nesting):
+        self.outputInheritedUpcastMethodHeader(methodClass, parentClass, file, nesting)
+        self.outputInheritedUpcastMethodBody(methodClass, parentClass, file, nesting)
+        self.outputInheritedUpcastMethodFooter(methodClass, parentClass, file, nesting)
+        
+    ##################################################
+    ## Global Function Code Generation
+    ##################################################
+    def outputHeader(self, file):
+        argTypes = self.typeDescriptor.argumentTypes
+        indent(file, 0, 'def ' + self.getFinalName() + '(')
+        for i in range(len(argTypes)):
+            file.write(argTypes[i].name)
+            if (i < (len(argTypes)-1)):
+                file.write(', ')
+        file.write('):\n')
+    def outputBody(self, file):
+        # The method body will look something like
+        #     returnValue = PandaGlobal.method(arg)
+        #     returnObject = NodePath()
+        #     returnObject.this = returnValue
+        #     returnObject.userManagesMemory = 1  (optional)
+        #     return returnObject
+        self.outputCFunctionComment(file, 1)
+        argTypes = self.typeDescriptor.argumentTypes
+        self.outputTypeChecking(None, argTypes, file, 1)
+        indent(file, 1, 'returnValue = ' + self.typeDescriptor.moduleName
+                   + '.' + self.typeDescriptor.wrapperName + '(')
+        for i in range(len(argTypes)):
+            file.write(argTypes[i].passName())
+            if (i < (len(argTypes)-1)):
+                file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, 1, 1)
+        
+    def outputFooter(self, file):
+        indent(file, 0, '\n')
+        
+    ##################################################
+    ## Class Method Code Generation
+    ##################################################
+    def outputMethodHeader(self, methodClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(')
+        for i in range(len(argTypes)):
+            # Instead of the first argument, put self
+            if (i == 0):
+                file.write('self')
+            else:
+                file.write(argTypes[i].name)
+            if (i < (len(argTypes)-1)):
+                file.write(', ')
+        file.write('):\n')
+        
+    def outputMethodBody(self, methodClass, file, nesting):
+        # The method body will look something like
+        #     returnValue = PandaGlobal.method(self.this, arg)
+        #     returnValue.userManagesMemory = 1  (optional)
+        #     return returnValue
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        self.outputTypeChecking(methodClass, argTypes[1:], file, nesting+2)
+        indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName
+                   + '.' + self.typeDescriptor.wrapperName + '(')
+        for i in range(len(argTypes)):
+            # Instead of the first argument, put self.this
+            if (i == 0):
+                file.write('self.this')
+            else:
+                file.write(argTypes[i].passName())
+            if (i < (len(argTypes)-1)):
+                file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, 1, nesting+2)
+        
+    def outputMethodFooter(self, methodClass, file, nesting):
+        indent(file, nesting+1, '\n')
+
+    ##################################################
+    ## Upcast Class Method Code Generation
+    ##################################################
+    def outputInheritedUpcastMethodHeader(self, methodClass, parentClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].name)
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputInheritedUpcastMethodBody(self, methodClass, parentClass, file, nesting):
+        # The method body will look something like
+        #     upcastSelf = self.upcastToParentClass()
+        #     returnValue = ParentClass.method(upcastSelf, arg)
+        #     returnValue.userManagesMemory = 1  (optional)
+        #     return returnValue
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
+        if self.typeDescriptor.userManagesMemory:
+            indent(file, nesting+2, 'self.userManagesMemory = 1\n')
+        indent(file, nesting+2, 'upcastSelf = self.upcast' + 'To'
+                   + parentClass.foreignTypeName + '()\n')
+        indent(file, nesting+2, 'returnValue = ' + parentClass.foreignTypeName
+                   + '.' + self.typeDescriptor.wrapperName + '(upcastSelf.this')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].passName())
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        # Generate the return value code with no downcast instructions
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, 1, nesting+2)
+
+    def outputInheritedUpcastMethodFooter(self, methodClass, parentClass, file, nesting):
+        pass
+
+
+class MethodSpecification(FunctionSpecification):
+    def __init__(self):
+        FunctionSpecification.__init__(self)
+    def isStatic(self):
+        for arg in self.typeDescriptor.argumentTypes:
+            if arg.isThis:
+                return 0
+        return 1
+
+    def generateConstructorCode(self, methodClass, file, nesting):
+        self.outputConstructorHeader(methodClass, file, nesting)
+        self.outputConstructorBody(methodClass, file, nesting)
+        self.outputConstructorFooter(methodClass, file, nesting)
+
+    def generateDestructorCode(self, methodClass, file, nesting):
+        self.outputDestructorHeader(methodClass, file, nesting)
+        self.outputDestructorBody(methodClass, file, nesting)
+        self.outputDestructorFooter(methodClass, file, nesting)
+
+    def generateMethodCode(self, methodClass, file, nesting):
+        self.outputMethodHeader(methodClass, file, nesting)
+        self.outputMethodBody(methodClass, file, nesting)
+        self.outputMethodFooter(methodClass, file, nesting)
+
+    def generateStaticCode(self, methodClass, file, nesting):
+        self.outputStaticHeader(methodClass, file, nesting)
+        self.outputStaticBody(methodClass, file, nesting)
+        self.outputStaticFooter(methodClass, file, nesting)
+
+    def generateInheritedUpcastMethodCode(self, methodClass, parentClass, file, nesting):
+        self.outputInheritedUpcastMethodHeader(methodClass, parentClass, file, nesting)
+        self.outputInheritedUpcastMethodBody(methodClass, parentClass, file, nesting)
+        self.outputInheritedUpcastMethodFooter(methodClass, parentClass, file, nesting)
+        
+    def generateDowncastMethodCode(self, methodClass, file, nesting):
+        # The downcast method code is just like regular code, but the
+        # return value wrapper does not have downcasting instructions in
+        # it to prevent an infinite loop of downcasting
+        self.outputMethodHeader(methodClass, file, nesting)
+        self.outputMethodBody(methodClass, file, nesting, 0) # no downcast
+        self.outputMethodFooter(methodClass, file, nesting)
+
+    def generateUpcastMethodCode(self, methodClass, file, nesting):
+        # The upcast method code is just like regular code, but the
+        # return value wrapper does not have downcasting instructions
+        self.outputMethodHeader(methodClass, file, nesting)
+        self.outputMethodBody(methodClass, file, nesting, 0) # no downcast
+        self.outputMethodFooter(methodClass, file, nesting)
+
+    ##################################################
+    ## Constructor Code Generation
+    ##################################################
+    def outputConstructorHeader(self, methodClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].name)
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputConstructorBody(self, methodClass, file, nesting):
+        # The method body will look something like
+        #     self.this = panda.Class_constructor(arg)
+        #     self.userManagesMemory = 1  (optional)
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
+        indent(file, nesting+2, 'self.this = ' + self.typeDescriptor.moduleName + '.'
+                   + self.typeDescriptor.wrapperName + '(')
+        # Do not pass self into the constructor
+        for i in range(len(thislessArgTypes)):
+            file.write(thislessArgTypes[i].passName())
+            if (i < (len(thislessArgTypes)-1)):
+                file.write(', ')
+        file.write(')\n')       
+        indent(file, nesting+2, 'assert(self.this != 0)\n')
+        if self.typeDescriptor.userManagesMemory:
+            indent(file, nesting+2, 'self.userManagesMemory = 1\n')
+    def outputConstructorFooter(self, methodClass, file, nesting):
+        indent(file, nesting+1, '\n')
+
+
+    ##################################################
+    ## Destructor Code Generation
+    ##################################################
+    def outputDestructorHeader(self, methodClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].name)
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputDestructorBody(self, methodClass, file, nesting):
+        # The method body will look something like
+        #     panda.Class_destructor(self.this)
+        self.outputCFunctionComment(file, nesting+2)
+        indent(file, nesting+2, self.typeDescriptor.moduleName + '.'
+                   + self.typeDescriptor.wrapperName + '(self.this)\n')
+
+    def outputDestructorFooter(self, methodClass, file, nesting):
+        indent(file, nesting+1, '\n')
+
+    ##################################################
+    ## Method Code Generation
+    ##################################################
+    def outputMethodHeader(self, methodClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].name)
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputMethodBody(self, methodClass, file, nesting, needsDowncast=1):
+        # The method body will look something like
+        #     returnValue = panda.Class_method(self.this, arg)
+        #     returnValue.userManagesMemory = 1  (optional)
+        #     return returnValue
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
+        indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName + '.'
+                   + self.typeDescriptor.wrapperName + '(')
+        file.write('self.this')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].passName())
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, needsDowncast, nesting+2)
+ 
+    def outputMethodFooter(self, methodClass, file, nesting):
+        indent(file, nesting+1, '\n')
+        
+
+    ##################################################
+    ## Static Method Code Generation
+    ##################################################
+    def outputStaticHeader(self, methodClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(')
+        for i in range(len(argTypes)):
+            file.write(argTypes[i].name)
+            if (i < (len(argTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputStaticBody(self, methodClass, file, nesting):
+        # The method body will look something like
+        #     returnValue = panda.class_method(self.this, arg)
+        #     returnValue.userManagesMemory = 1  (optional)
+        #     return returnValue
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
+        indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName + '.'
+                   + self.typeDescriptor.wrapperName + '(')
+        # Static methods do not take the this parameter
+        if (len(thislessArgTypes) > 0):
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].passName())
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, 1, nesting+2)
+
+    def outputStaticFooter(self, methodClass, file, nesting):
+        indent(file, nesting+1, self.getFinalName() + ' = '
+                   + FFIConstants.staticModuleName + '.' + FFIConstants.staticModuleName
+                   + '(' + self.getFinalName() + ')\n')
+        indent(file, nesting+1, '\n')
+        
+
+
+    ##################################################
+    ## Upcast Method Code Generation
+    ##################################################
+    def outputInheritedUpcastMethodHeader(self, methodClass, parentClass, file, nesting):
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].name)
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write('):\n')
+
+    def outputInheritedUpcastMethodBody(self, methodClass, parentClass, file, nesting):
+        # The method body will look something like
+        #     upcastSelf = self.upcastToParentClass()
+        #     returnValue = libpanda.method(upcastSelf.this, arg)
+        #     returnValue.userManagesMemory = 1  (optional)
+        #     return returnValue
+        self.outputCFunctionComment(file, nesting+2)
+        argTypes = self.typeDescriptor.argumentTypes
+        thislessArgTypes = self.typeDescriptor.thislessArgTypes()
+        self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
+        indent(file, nesting+2, 'upcastSelf = self.upcast' + 'To'
+                   + parentClass.foreignTypeName + '()\n')
+        indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName
+                   + '.' + self.typeDescriptor.wrapperName + '(upcastSelf.this')
+        if (len(thislessArgTypes) > 0):
+            file.write(', ')
+            for i in range(len(thislessArgTypes)):
+                file.write(thislessArgTypes[i].passName())
+                if (i < (len(thislessArgTypes)-1)):
+                    file.write(', ')
+        file.write(')\n')
+        returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
+        # Generate the return value code with no downcast instructions
+        returnType.generateReturnValueWrapper(file, self.typeDescriptor.userManagesMemory, 1, nesting+2)
+
+    def outputInheritedUpcastMethodFooter(self, methodClass, parentClass, file, nesting):
+        indent(file, nesting+1, '\n')        
+
+
+class GlobalValueSpecification:
+    def __init__(self):
+        self.name = ''
+        # We really do not need the descriptor for the value, just
+        # the getter and setter
+        # self.typeDescriptor = None
+        # To be filled in with a GlobalFunctionSpecification
+        self.getter = None
+        # To be filled in with a GlobalFunctionSpecification
+        self.setter = None
+        
+    def generateGlobalCode(self, file):
+        indent(file, 0, '# Global value: ' + self.name + '\n')
+        if self.getter:
+            self.getter.generateGlobalCode(file)
+        if self.setter:
+            self.setter.generateGlobalCode(file)
+        indent(file, 0, '\n')
+
+class MethodArgumentSpecification:
+    def __init__(self):
+        self.name = ''
+        self.typeDescriptor = None
+        # By default it is not the this pointer
+        self.isThis = 0
+        
+    def passName(self):
+        if (self.typeDescriptor.recursiveTypeDescriptor().__class__ == \
+            FFITypes.ClassTypeDescriptor):
+            return self.name + '.this'
+        else:
+            return self.name

+ 767 - 0
direct/src/ffi/FFITypes.py

@@ -0,0 +1,767 @@
+
+"""
+Type Descriptors
+
+Type Descriptors are used for code generation of C++ types. They
+know everything they need to know about themselves to generate code.
+They get constructed by and stored in FFIInterrogateDatabase.
+
+"""
+
+import sys
+import os
+import string
+import FFIConstants
+import FFIOverload
+
+
+from PythonUtil import *
+
+class BaseTypeDescriptor:
+    """
+    A type descriptor contains everything you need to know about a C++ function,
+    class, or primitive.
+    """
+    def __init__(self):
+        # The pythonified name from C++
+        self.foreignTypeName = ''
+
+        # The typeIndex for lookup in the typeIndexMap
+        self.typeIndex = 0
+
+        # The C++ prototype for this type
+        self.prototype = ''
+
+        # The C++ comment for this type
+        self.comment = ''
+
+        # Is this a nested type?
+        self.isNested = 0
+
+        # If we are nested, this is the typeDescriptor we are nested in
+        self.outerType = None
+
+        # The type descriptors for the types we derive from
+        self.parentTypes = []
+        
+        # atomicType may be one of the following
+        # AT_not_atomic = 0
+        # AT_int = 1
+        # AT_float = 2
+        # AT_double = 3
+        # AT_bool = 4
+        # AT_char = 5
+        # AT_void = 6
+        # AT_string = 7
+        # By default this type is not atomic
+        self.atomicType = 0
+
+    def isAtomic(self):
+        return (self.atomicType != 0)
+        
+    def generateGlobalCode(self, dir, extensionsDir):
+        # By default generate no code
+        pass
+    def recursiveTypeDescriptor(self):
+        """
+        Attempt to get to the bottom of a type descriptor
+        Since we are at the bottom when we get here, just return self
+        """
+        return self
+    def recordOverloadedMethods(self):
+        # By default do nothing
+        pass
+    def generateReturnValueWrapper(self, file, userManagesMemory,
+                                   needsDowncast, nesting):
+        # By default do nothing
+        pass
+    def getFullNestedName(self):
+        """
+        If this type is nested, it will return the fully specified name
+        For example:  OuterClass.InnerClass.ReallyInnerClass
+        """
+        if self.isNested:
+            return self.outerType.getFullNestedName() + '.' + self.foreignTypeName
+        else:
+            return self.foreignTypeName
+
+
+class PrimitiveTypeDescriptor(BaseTypeDescriptor):
+    """
+    Primitive type descriptors include int, float, char, etc.
+    These get mapped to Python types like IntType, FloatType, StringType
+    """
+    def __init__(self):
+        BaseTypeDescriptor.__init__(self)
+                
+    def generateReturnValueWrapper(self, file, userManagesMemory,
+                                   needsDowncast, nesting):
+        """
+        Write code to the file that will return a primitive to the caller.
+        Pretty simple since there is no extra work needed here
+        """
+        indent(file, nesting, 'return returnValue\n')
+
+
+
+class EnumTypeDescriptor(PrimitiveTypeDescriptor):
+    """
+    EnumTypeDescriptors represent enums in C++
+    """
+    def __init__(self):
+        PrimitiveTypeDescriptor.__init__(self)
+        # A dictionary of name, value pairs for this enum
+        self.values = {}
+        # The enum name is different than the foreignTypeName because
+        # we record the foreignTypeName as enum (int)
+        self.enumName = ''
+        # Specify that we do not have any parent or nested types to make
+        # the sorting based on inheritance happy. Essentially, we do not
+        # inherit from anybody or have any nested types
+        self.parentTypes = []
+        self.nestedTypes = []
+
+    def generateGlobalCode(self, dir, extensionsDir):
+        """
+        Generate enum code for this type.
+        """
+        fileName = self.enumName + '.py'
+        file = open(os.path.join(dir, fileName), 'w')
+        indent(file, 0, FFIConstants.generatedHeader)
+        self.generateCode(file, 0)
+
+    def generateCode(self, file, nesting):
+        self.outputComment(file, nesting)
+        self.outputValues(file, nesting)
+        
+
+    def outputComment(self, file, nesting):
+        indent(file, nesting, '\n')
+        indent(file, nesting, '##################################################\n')
+        indent(file, nesting, '#  Enum ' + self.enumName + '\n')
+        indent(file, nesting, '##################################################\n')
+        indent(file, nesting, '\n')
+
+    def outputValues(self, file, nesting):
+        """
+        For each entry in the dictionary, output a line for name, value pairs
+        Example:
+        off = 0
+        on = 1
+        """
+        for key in self.values.keys():
+            indent(file, nesting, key + ' = ' + `self.values[key]` + '\n')
+            
+
+class DerivedTypeDescriptor(BaseTypeDescriptor):
+    """
+    DerivedTypeDescriptor is a wrapper around a primitive or class type
+    For instance const, or pointer to.
+    """
+    def __init__(self):
+        BaseTypeDescriptor.__init__(self)
+        self.typeDescriptor = None
+        
+    def recursiveTypeDescriptor(self):
+        """
+        Attempt to get to the bottom of a type descriptor by
+        recursively unravelling typeDescriptors until you get to
+        a type that is not derived (primitive or class) in which
+        case the base class will just return self.
+        """
+        return self.typeDescriptor.recursiveTypeDescriptor()
+
+class PointerTypeDescriptor(DerivedTypeDescriptor):
+    """
+    Points to another type descriptor
+    """
+    def __init__(self):
+        DerivedTypeDescriptor.__init__(self)
+
+class ConstTypeDescriptor(DerivedTypeDescriptor):
+    """
+    Const version of another type descriptor
+    """
+    def __init__(self):
+        DerivedTypeDescriptor.__init__(self)
+
+class ClassTypeDescriptor(BaseTypeDescriptor):
+    """
+    This describes a C++ class. It holds lists of all its methods too.
+    It can also generate Python shadow class code for itself.
+    """
+    def __init__(self):
+        BaseTypeDescriptor.__init__(self)
+        
+        # Methods interrogate told us were constructors
+        self.constructors = []
+        
+        # A method interrogate told us is the destructor
+        self.destructor = None
+        
+        # Methods interrogate told us were instance methods
+        # Note: the methods without the this pointer get moved into staticMethods
+        self.instanceMethods = []
+        
+        # Methods interrogate told us were upcast methods
+        self.upcastMethods = []
+        
+        # Methods interrogate told us were downcast methods
+        self.downcastMethods = []
+        
+        # Instance methods that had no this pointer are moved into here
+        self.staticMethods = []
+        
+        # Global methods that take this class as the first parameter are just
+        # stored with the class because it is more useable that way
+        self.globalMethods = []
+
+        # These are dictionaries used to temporarily hold methods for
+        # overloading while generating code
+        self.overloadedClassMethods = {}
+        self.overloadedInstanceMethods = {}
+
+        # Nested typeDescriptors inside this class
+        self.nestedTypes = []
+
+    def getExtensionModuleName(self):
+        """
+        Return a filename for the extensions for this class
+        Example: NodePath extensions would be found in NodePath-extensions.py
+        """
+        return self.foreignTypeName + '-extensions.py'
+
+    def getCModules(self):
+        """
+        Return a list of all the C modules this class references
+        """
+        moduleList = []
+        for method in (self.constructors + [self.destructor] + self.instanceMethods
+                       + self.upcastMethods + self.downcastMethods 
+                       + self.staticMethods + self.globalMethods):
+            if method:
+                if (not (method.typeDescriptor.moduleName in moduleList)):
+                    moduleList.append(method.typeDescriptor.moduleName)
+
+        # Now look at all the methods that we might inherit if we are at
+        # a multiple inheritance node and get their C modules
+        if (len(self.parentTypes) >= 2):
+            for parentType in self.parentTypes:
+                for method in parentType.instanceMethods:
+                    if (not (method.typeDescriptor.moduleName in moduleList)):
+                        moduleList.append(method.typeDescriptor.moduleName)
+                for method in parentType.upcastMethods:
+                    if (not (method.typeDescriptor.moduleName in moduleList)):
+                        moduleList.append(method.typeDescriptor.moduleName)
+                for method in parentType.globalMethods:
+                    if (not (method.typeDescriptor.moduleName in moduleList)):
+                        moduleList.append(method.typeDescriptor.moduleName)
+                    
+        return moduleList
+
+
+    def getReturnTypeModules(self):
+        """
+        Return a list of all the other shadow class modules this
+        class references.
+        Be careful about nested types
+        """
+        moduleList = []
+
+        upcastMethods = []
+        if (len(self.parentTypes) >= 2):
+            for parentType in self.parentTypes:
+                for method in parentType.instanceMethods:
+                    upcastMethods.append(method)
+                for method in parentType.upcastMethods:
+                    upcastMethods.append(method)
+                for method in parentType.globalMethods:
+                    upcastMethods.append(method)
+                    
+        for method in (self.constructors + [self.destructor] + self.instanceMethods
+                       + self.upcastMethods + self.downcastMethods 
+                       + self.staticMethods + self.globalMethods + upcastMethods):
+            if method:
+                # Get the real return type (not derived)
+                returnType = method.typeDescriptor.returnType.recursiveTypeDescriptor()
+                if (not returnType.isNested):
+                    returnTypeName = returnType.foreignTypeName
+                    # Do not put our own module in the import list
+                    if ((returnTypeName != self.foreignTypeName) and
+                        # Do not put modules already in the list (like a set)
+                        (not (returnTypeName in moduleList))):
+                        # If this is a class (not a primitive), put it on the list
+                        if (returnType.__class__ == ClassTypeDescriptor):
+                            moduleList.append(returnTypeName)
+                        
+                # Now look at all the arguments
+                argTypes = method.typeDescriptor.argumentTypes
+                for argType in argTypes:
+                    # Get the real return type (not derived)
+                    argType = argType.typeDescriptor.recursiveTypeDescriptor()
+                    if (not argType.isNested):
+                        argTypeName = argType.foreignTypeName
+                        # Do not put our own module in the import list
+                        if ((argTypeName != self.foreignTypeName) and
+                            # Do not put modules already in the list (like a set)
+                            (not (argTypeName in moduleList))):
+                            # If this is a class (not a primitive), put it on the list
+                            if (argType.__class__ == ClassTypeDescriptor):
+                                moduleList.append(argTypeName)
+                   
+        return moduleList
+    
+    def recordClassMethod(self, methodSpec):
+        """
+        Record all class methods in a 2 level dictionary so we can go
+        through them and see which are overloaded
+        { className : {methodName : [methodSpec, methodSpec, methodSpec]}}
+        """
+        methodList = ifAbsentPut(self.overloadedClassMethods, methodSpec.name, [])
+        methodList.append(methodSpec)
+    
+    def recordInstanceMethod(self, methodSpec):
+        """
+        Record all instance methods in a 2 level dictionary so we can go
+        through them and see which are overloaded
+        { className : {methodName : [methodSpec, methodSpec, methodSpec]}}
+        """
+        methodList = ifAbsentPut(self.overloadedInstanceMethods, methodSpec.name, [])
+        methodList.append(methodSpec)
+
+    def cullOverloadedMethods(self):
+        """
+        Find all the entries that have multiple indexes for the same method name
+        Get rid of all others.
+        """
+        tmpDict = {}
+        # For each class
+        for methodName in self.overloadedClassMethods.keys():
+            methodList = self.overloadedClassMethods[methodName]
+            # See if this method has more than one function index (overloaded)
+            if (len(methodList) > 1):
+                tmpDict[methodName] = methodList
+                # Mark all the method specifications as overloaded
+                for methodSpec in methodList:
+                    methodSpec.overloaded = 1
+        
+        # Now we are done. Jam the tmpDict into the real one
+        self.overloadedClassMethods = tmpDict
+        
+        # Now do the same for instance methods
+        tmpDict = {}
+        # For each class
+        for methodName in self.overloadedInstanceMethods.keys():
+            methodList = self.overloadedInstanceMethods[methodName]
+            # See if this method has more than one function index (overloaded)
+            if (len(methodList) > 1):
+                # Copy over the method list
+                tmpDict[methodName] = methodList
+                # Mark all the method specifications as overloaded
+                for methodSpec in methodList:
+                    methodSpec.overloaded = 1
+        
+        # Now we are done. Jam the tmpDict into the real one
+        self.overloadedInstanceMethods = tmpDict
+
+    def filterOutStaticMethods(self):
+        """
+        Run through the list of instance methods and filter out the
+        ones that are static class methods. We can tell this because they
+        do not have a this pointer in their arg list. Those methods that
+        are static are then placed in a new staticMethods list and the ones
+        that are left are stored back in the instanceMethods list. We are
+        avoiding modifying the instanceMethods list in place while traversing it.
+        Do not check upcast or downcast methods because we know they are not static.
+        """
+        newInstanceMethods = []
+        for method in self.instanceMethods:
+            if method.isStatic():
+                self.staticMethods.append(method)
+            else:
+                newInstanceMethods.append(method)
+        self.instanceMethods = newInstanceMethods
+
+    def recordOverloadedMethods(self):
+        """
+        Record all the methods in dictionaries based on method name
+        so we can see if they are overloaded
+        """
+        classMethods = self.constructors + self.staticMethods
+        if self.destructor:
+            classMethods = classMethods + [self.destructor]
+        for method in classMethods:
+            self.recordClassMethod(method)
+
+        instanceMethods = (self.instanceMethods + self.globalMethods
+                           + self.upcastMethods + self.downcastMethods)
+        for method in instanceMethods:
+            self.recordInstanceMethod(method)
+
+    def generateOverloadedMethods(self, file, nesting):
+        """
+        Generate code for all the overloaded methods of this class
+        """
+
+        if (len(self.overloadedClassMethods.values()) or
+            len(self.overloadedInstanceMethods.values())):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Overloaded methods                            #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+        
+        for methodSpecList in self.overloadedClassMethods.values():
+            treeColl = FFIOverload.FFIMethodArgumentTreeCollection(self, methodSpecList)
+            treeColl.generateCode(file, nesting)
+        for methodSpecList in self.overloadedInstanceMethods.values():
+            treeColl = FFIOverload.FFIMethodArgumentTreeCollection(self, methodSpecList)
+            treeColl.generateCode(file, nesting)
+
+    def generateGlobalCode(self, dir, extensionsDir):
+        """
+        Generate shadow class code for this type.
+        We make our own file form our foreignTypeName and put it in the dir
+        passed in.
+        """
+        fileName = self.foreignTypeName + '.py'
+        file = open(os.path.join(dir, fileName), 'w')
+        indent(file, 0, FFIConstants.generatedHeader)
+        self.outputBaseImports(file)
+        self.generateCode(file, 0)
+
+        # Copy in any extensions we may have
+        self.copyExtensions(extensionsDir, file, 0)
+        self.outputClassFooter(file)
+        file.close()
+
+    def generateCode(self, file, nesting):
+        self.recordOverloadedMethods()
+        self.cullOverloadedMethods()        
+        self.outputImports(file, nesting)
+        self.outputClassHeader(file, nesting)
+        self.outputClassComment(file, nesting)
+
+        self.outputNestedTypes(file, nesting)
+
+        indent(file, nesting+1, '\n')
+        indent(file, nesting+1, '##################################################\n')
+        indent(file, nesting+1, '#  Constructors                                  #\n')
+        indent(file, nesting+1, '##################################################\n')
+        indent(file, nesting+1, '\n')
+        self.outputBaseConstructor(file, nesting)
+        if self.constructors:
+            for method in self.constructors:
+                method.generateConstructorCode(self, file, nesting)
+        else:
+            self.outputEmptyConstructor(file, nesting)
+
+        indent(file, nesting+1, '\n')
+        indent(file, nesting+1, '##################################################\n')
+        indent(file, nesting+1, '#  Destructor                                    #\n')
+        indent(file, nesting+1, '##################################################\n')
+        indent(file, nesting+1, '\n')
+        self.outputBaseDestructor(file, nesting)
+        if self.destructor:
+            self.destructor.generateDestructorCode(self, file, nesting)
+        else:
+            self.outputEmptyDestructor(file, nesting)
+
+        if len(self.staticMethods):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Static Methods                                #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for method in self.staticMethods:
+                method.generateStaticCode(self, file, nesting)
+
+        if len(self.instanceMethods):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Instance methods                              #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for method in self.instanceMethods:
+                method.generateMethodCode(self, file, nesting)
+
+        if len(self.upcastMethods):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Upcast methods                                #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for method in self.upcastMethods:
+                method.generateUpcastMethodCode(self, file, nesting)
+
+        if len(self.downcastMethods):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Downcast methods                              #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for method in self.downcastMethods:
+                method.generateDowncastMethodCode(self, file, nesting)
+
+        if len(self.globalMethods):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Global methods                                #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for method in self.globalMethods:
+                method.generateMethodCode(self, file, nesting)
+
+        # At multiple inheritance nodes, copy all the parent methods into
+        # this class and call them after upcasting us to that class
+        if (len(self.parentTypes) >= 2):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Upcast inherited instance method wrappers     #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for parentType in self.parentTypes:
+                # Copy all the parents instance methods
+                for method in parentType.instanceMethods:
+                    method.generateInheritedUpcastMethodCode(self, parentType, file, nesting)
+                # Copy all the parents upcast methods so we transitively pick them up
+                for method in parentType.upcastMethods:
+                    method.generateInheritedUpcastMethodCode(self, parentType, file, nesting)
+                # Do not copy the downcast methods
+
+        # At multiple inheritance nodes, copy all the parent methods into
+        # this class and call them after upcasting us to that class
+        if (len(self.parentTypes) >= 2):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Upcast global method wrappers                 #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            for parentType in self.parentTypes:
+                for method in parentType.globalMethods:
+                    method.generateInheritedUpcastMethodCode(self, parentType, file, nesting)
+
+        self.generateOverloadedMethods(file, nesting)
+
+    def outputNestedTypes(self, file, nesting):
+        if (len(self.nestedTypes) > 0):
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Nested Types                                  #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+        # Output code in this same file for all our nested types
+        for nestedType in self.nestedTypes:
+            nestedType.generateCode(file, nesting+1)
+
+    def copyExtensions(self, extensionsDir, file, nesting):
+        """
+        Copy in the extension file for this class if one exists
+        If you want to extend a C++ file, create a file in the extensions directory and
+        this will append that extension file to the generated code file.
+        """
+        extensionFileName = self.getExtensionModuleName()
+        extensionFilePath = os.path.join(extensionsDir, extensionFileName)
+        if os.path.exists(extensionFilePath):
+            FFIConstants.notify.info( 'Found extensions for class: ' + self.foreignTypeName)
+            extensionFile = open(extensionFilePath)
+            indent(file, nesting+1, '\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '#  Extension methods                             #\n')
+            indent(file, nesting+1, '##################################################\n')
+            indent(file, nesting+1, '\n')
+            # Copy the contents of the extensions file to the class file verbatim
+            indent(file, nesting, extensionFile.read())
+        else:
+            # No extensions for this class
+            pass
+        
+
+    def outputBaseImports(self, file):
+        indent(file, 0, 'import ' + FFIConstants.staticModuleName + '\n')
+        # Everybody imports types for type checking
+        indent(file, 0, 'import types\n')
+        indent(file, 0, '\n')
+
+        indent(file, 0, '# Import all the C modules this class uses\n')
+        for moduleName in self.getCModules():
+            indent(file, 0, 'import ' + moduleName + '\n')            
+        indent(file, 0, '\n')
+        indent(file, 0, 'import FFIExternalObject\n')
+        
+        
+    def outputImports(self, file, nesting):
+        """
+        Generate code that imports the modules we need for this class
+        """
+
+        if len(self.parentTypes):
+            indent(file, nesting, '# Import everybody we inherit from\n')
+            for parent in self.parentTypes:
+                indent(file, nesting, 'import ' + parent.foreignTypeName + '\n')
+            indent(file, nesting, '\n')
+
+        returnTypeModules = self.getReturnTypeModules()
+        if len(returnTypeModules):
+            indent(file, nesting, '# Import all the shadow class modules this class uses\n')
+            for moduleName in returnTypeModules:
+                indent(file, nesting, 'import ' + moduleName + '\n')
+            
+        # an extra line just for spacing
+        indent(file, nesting, '\n')
+
+    def outputClassComment(self, file, nesting):
+        """
+        Output the class comment to the file
+        """
+        if FFIConstants.wantComments:
+            if self.comment:
+                indent(file, nesting+1, ('"' * 3) + '\n')
+                # To insert tabs into the comment, replace all newlines with a newline+tabs
+                comment = string.replace(self.comment,
+                                         '\n', ('\n' + ('    ' * (nesting+1))))
+                indent(file, nesting+1, comment)
+                file.write('\n')
+                indent(file, nesting+1, ('"' * 3) + '\n\n')
+    
+    def outputClassHeader(self, file, nesting):
+        """
+        Output the class definition to the file
+        """
+
+        if (self.foreignTypeName == ''):
+            FFIConstants.notify.warning('Class with no name')
+
+        # If this is the toplevel, we need to delay the generation of this
+        # class to avoid circular imports, so put the entire class in a function
+        # that we will call later
+        if (nesting==0):
+            indent(file, nesting, '# Delay the definition of this class until all the imports are done\n')
+            indent(file, nesting, 'def generateClass_' + self.foreignTypeName + '():\n')
+            # Start the class definition indented a space to account for the function
+            indent(file, nesting, ' class ' + self.foreignTypeName)
+        else:
+            # Start the class definition
+            indent(file, nesting, 'class ' + self.foreignTypeName)
+
+        # Everybody inherits from FFIExternalObject
+        file.write('(')
+        # Also inherit from all of your parentTypes
+        for i in range(len(self.parentTypes)):
+            parentTypeName = self.parentTypes[i].foreignTypeName
+            # assuming the type "Node" is stored in module "Node.py"
+            # and we have done an "import Node", we need to then
+            # inherit from Node.Node
+            file.write(parentTypeName + '.' + parentTypeName)
+            file.write(', ')
+        file.write('FFIExternalObject.FFIExternalObject):\n')
+        # Store the class C modules for the class so they do not
+        # get garbage collected before we do
+        indent(file, nesting+1, '__CModules__ = [')
+        for moduleName in self.getCModules():
+            file.write(moduleName + ',')
+        file.write(']\n')
+            
+
+    def outputClassFooter(self, file):
+        indent(file, 0, " # When this class gets defined, put it in this module's namespace\n")
+        indent(file, 0, " globals()['" + self.foreignTypeName + "'] = " + self.foreignTypeName + '\n')
+    
+    def outputBaseConstructor(self, file, nesting):
+        """
+        Output the __init__ constructor for this class.
+        There is special login if you pass in None to the constructor, you
+        will not get an actual C object with memory, you will just get the
+        shadow class shell object. This is useful for functions that want
+        to return this type that already have a this pointer and just need
+        to construct a shadow object to contain it.
+        """
+
+        indent(file, nesting+1, 'def __init__(self, *_args):\n')
+        indent(file, nesting+2, '# Initialize the super class\n')
+        indent(file, nesting+2, 'FFIExternalObject.FFIExternalObject.__init__(self)\n')
+        indent(file, nesting+2, '# If you want an empty shadow object, pass in None\n')
+        indent(file, nesting+2, 'if ((len(_args) == 1) and (_args[0] == None)):\n')
+        indent(file, nesting+3, 'return\n')
+        indent(file, nesting+2, '# Otherwise, call the C constructor\n')
+        indent(file, nesting+2, 'apply(self.constructor, _args)\n')
+        indent(file, nesting+2, '\n')
+
+    def outputEmptyConstructor(self, file, nesting):
+        # If there is no C++ constructor, we just output this
+        # empty one instead
+        indent(file, nesting+1, 'def constructor(self):\n')
+        indent(file, nesting+2, 'pass\n')
+
+    def outputBaseDestructor(self, file, nesting):
+        # This destructor overwrites the builtin Python destructor
+        # using the __del__ method. This will get called whenever a
+        # Python object is garbage collected. We are going to overwrite
+        # it with special cleanup for Panda.
+        indent(file, nesting+1, 'def __del__(self):\n')
+
+        # Reference counting is now handled in the C++ code
+        # indent(file, nesting+2, 'if isinstance(self, ReferenceCount):\n')
+        # indent(file, nesting+3, 'self.unref()\n')
+        # indent(file, nesting+3, 'if (self.getCount() == 0):\n')
+        # indent(file, nesting+4, 'self.destructor()\n')
+
+        # If the scripting language owns the memory for this object,
+        # we need to call the C++ destructor when Python frees the
+        # shadow object, but only if the userManagesMemory flag is set.
+        # Also make sure we are not destructing a null pointer
+        indent(file, nesting+2, 'if (self.userManagesMemory and (self.this != 0)):\n')
+        indent(file, nesting+3, 'self.destructor()\n')
+
+    def outputEmptyDestructor(self, file, nesting):
+        # If there is no C++ destructor, we just output this
+        # empty one instead
+        indent(file, nesting+1, 'def destructor(self):\n')
+        indent(file, nesting+2, 'pass\n')
+
+    def generateReturnValueWrapper(self, file, userManagesMemory,
+                                   needsDowncast, nesting):
+        """
+        Generate code that creates a shadow object of this type
+        then sets the this pointer and returns the object. We call the
+        class destructor with None as the only parameter to get an
+        empty shadow object.
+        """
+        indent(file, nesting, 'returnObject = ')
+        # Do not put Class.Class if this file is the file that defines Class
+        if (os.path.basename(file.name)[:-3] == self.foreignTypeName):
+            file.write(self.foreignTypeName)
+        else:
+            file.write(self.foreignTypeName + '.' + self.foreignTypeName)
+        file.write('(None)\n')
+        indent(file, nesting, 'returnObject.this = returnValue\n')
+        if userManagesMemory:
+            indent(file, nesting, 'returnObject.userManagesMemory = 1\n')
+        if needsDowncast:
+            indent(file, nesting, 'downcastObject = returnObject.setPointer()\n')
+            indent(file, nesting, 'return downcastObject\n')
+        else:
+            indent(file, nesting, 'return returnObject\n')
+            
+
+
+class FunctionTypeDescriptor(BaseTypeDescriptor):
+    """
+    A C++ function type. It knows its returnType, arguments, etc.
+    """
+    def __init__(self):
+        BaseTypeDescriptor.__init__(self)
+        self.returnType = None
+        self.argumentTypes = []
+        self.userManagesMemory = 0
+        self.isVirtual = 0
+        self.moduleName = ''
+        self.wrapperName = ''
+        self.returnValueDestructor = None
+    def thislessArgTypes(self):
+        """
+        It is often useful to know the list of arguments excluding the
+        this parameter (if there was one)
+        """
+        return filter(lambda type: (not type.isThis), self.argumentTypes)
+

+ 3 - 0
direct/src/ffi/Sources.pp

@@ -0,0 +1,3 @@
+// For now, since we are not installing Python files, this file can
+// remain empty.
+

+ 107 - 0
direct/src/ffi/generatePythonCode

@@ -0,0 +1,107 @@
+#!/usr/local/bin/python
+
+
+import getopt
+import sys
+import os
+import FFIConstants
+
+# Define a help string for the user
+helpString ="""
+generatePythonCode [opts] -i libtool libcode1 libcode2 ...
+
+Generates Python code for the C++ libraries listed.
+
+Example:
+ generatePythonCode -v -d $DIRECT/lib/py -e $DIRECT/src/all/python -i libdtool libpandaexpress libpanda libdirect
+
+Options:
+  -h          print this message
+  -v          verbose
+  -d dir      directory to write output code       
+  -e dir      directory to pull extension code from       
+  -i lib      interrogate library
+  -O          no C++ comments or assertion statements
+"""
+    
+# Initialize variables
+outputDir = ''
+extensionsDir = ''
+interrogateLib = ''
+codeLibs = []
+    
+
+# Extract the args the user passed in
+try:
+    opts, pargs = getopt.getopt(sys.argv[1:], 'hvOd:e:i:')
+except Exception, e:
+    # User passed in a bad option, print the error and the help, then exit
+    print e
+    print helpString
+    sys.exit()
+
+if len(opts)==0:
+    print helpString
+    sys.exit()
+
+
+# Store the option values into our variables
+for opt in opts:
+    flag, value = opt
+    if (flag == '-h'):
+        print helpString
+        sys.exit()
+    elif (flag == '-v'):
+        FFIConstants.notify.setVerbose(1)
+    elif (flag == '-d'):
+        outputDir = value
+    elif (flag == '-e'):
+        extensionsDir = value
+    elif (flag == '-i'):
+        interrogateLib = value
+    elif (flag == '-O'):
+        FFIConstants.wantComments = 0
+        FFIConstants.wantTypeChecking = 0
+    else:
+        FFIConstants.notify.error('illegal option: ' + flag)
+
+# Store the program arguments into the codeLibs
+codeLibs = pargs
+
+# Now do some error checking and verbose output
+if (not interrogateLib):
+    FFIConstants.notify.error('You must specify an interrogate library (-i lib)')
+else:
+    FFIConstants.notify.info('Setting interrogate library to: ' + interrogateLib)
+    FFIConstants.InterrogateModuleName = interrogateLib
+
+if (not outputDir):
+    FFIConstants.notify.info('Setting output directory to current directory')
+    outputDir = '.'
+elif (not os.path.exists(outputDir)):
+    FFIConstants.notify.error('Directory does not exists: ' + outputDir)
+else:
+    FFIConstants.notify.info('Setting output directory to: ' + outputDir)
+
+
+if (not extensionsDir):
+    FFIConstants.notify.info('Setting extensions directory to current directory')
+    extensionsDir = '.'
+elif (not os.path.exists(extensionsDir)):
+    FFIConstants.notify.error('Directory does not exists: ' + extensionsDir)
+else:
+    FFIConstants.notify.info('Setting extensions directory to: ' + extensionsDir)
+
+
+if (not codeLibs):
+    FFIConstants.notify.error('You must specify one or more libraries to generate code from')
+else:
+    FFIConstants.notify.info('Generating code for: ' + `codeLibs`)
+    FFIConstants.CodeModuleNameList = codeLibs
+
+# Ok, now we can start generating code
+import FFIInterrogateDatabase
+db = FFIInterrogateDatabase.FFIInterrogateDatabase()
+db.updateBindings()
+db.generateCode(outputDir, extensionsDir)
+

+ 4 - 0
direct/src/showbase/AudioManagerGlobal.py

@@ -0,0 +1,4 @@
+"""AudioManagerGlobal module: contains the global audio manager"""
+import AudioManager
+
+audioMgr = AudioManager.AudioManager()

+ 65 - 0
direct/src/showbase/DirectNotify.py

@@ -0,0 +1,65 @@
+
+"""DirectNotify module: this module contains the DirectNotify class"""
+
+import Notifier
+import Logger
+
+class DirectNotify:
+    """DirectNotify class: this class contains methods for creating
+    mulitple notify categories via a dictionary of Notifiers."""
+    
+    def __init__(self):
+        """__init__(self)
+        DirectNotify class keeps a dictionary of Notfiers"""
+        self.__categories = { }
+        # create a default log file
+        self.logger = Logger.Logger()
+
+    def __str__(self):
+        """__str__(self)
+        Print handling routine"""
+        return "DirectNotify categories: %s" % (self.__categories)
+
+    #getters and setters
+    def getCategories(self):
+        """getCategories(self)
+        Return list of category dictionary keys"""
+        return (self.__categories.keys())
+
+    def getCategory(self, categoryName):
+        """getCategory(self, string)
+        Return the category with given name if present, None otherwise"""
+        return(self.__categories.get(categoryName, None))
+
+    def addCategory(self, categoryName, category):
+        """addCategory(self, Notifier)
+        Add a given Notifier with given name to the category dictionary
+        and return 0 if not present, else return 0. """
+        if (self.__categories.has_key(categoryName)):
+            print "Warning: DirectNotify: category '%s' already exists" % \
+                  (categoryName)
+            return(0)
+        else:
+            self.__categories[categoryName] = category
+            return(1)
+
+    def newCategory(self, categoryName):
+        """newCategory(self, string)
+        Make a new notify category named categoryName. Return new category
+        if no such category exists, else return existing category"""
+        if (not self.__categories.has_key(categoryName)):
+            self.__categories[categoryName] = Notifier.Notifier(categoryName)
+        else:
+            print "Warning: DirectNotify: category '%s' already exists" % \
+                  (categoryName)
+        return(self.getCategory(categoryName))
+
+           
+
+#global DirectNotify for public access
+directNotify = DirectNotify()
+
+
+
+
+

+ 6 - 0
direct/src/showbase/DirectNotifyGlobal.py

@@ -0,0 +1,6 @@
+"""instantiate global DirectNotfiy used in Direct"""
+
+import DirectNotify
+
+directNotify = DirectNotify.DirectNotify()
+

+ 26 - 0
direct/src/showbase/DirectObject.py

@@ -0,0 +1,26 @@
+
+from MessengerGlobal import *
+from DirectNotifyGlobal import *
+
+class DirectObject:
+    """
+    This is the class that all Direct/SAL classes should inherit from
+    """
+    # Event Handling
+
+    # object.accept('mouse', object.handleMouse)
+    # object.accept('mouse', 'handleMouse')
+    # object.accept('mouse', 'handleMouse', [1,2])
+    
+    def accept(self, event, method, extraArgs=[]):
+        messenger.accept(event, self, method, extraArgs, 1)
+    def acceptOnce(self, event, method, extraArgs=[]):
+        messenger.accept(event, self, method, extraArgs, 0)
+    def ignore(self, event):
+        messenger.ignore(event, self)
+    def isAccepting(self, event):
+        return messenger.isAccepting(event, self)
+    def isIgnoring(self, event):
+        return messenger.isIgnoring(event, self)
+    
+

+ 87 - 0
direct/src/showbase/EventManager.py

@@ -0,0 +1,87 @@
+
+from PandaModules import *
+from MessengerGlobal import *
+from TaskManagerGlobal import *
+from DirectNotifyGlobal import *
+
+class EventManager:
+
+    notify = None
+    
+    def __init__(self):
+        """
+        Create a C++ event queue and handler
+        """
+
+        # Make a notify category for this class (unless there already is one)
+        if (EventManager.notify == None):
+            EventManager.notify = directNotify.newCategory("EventManager")
+
+        self.eventQueue = EventQueue.getGlobalEventQueue()
+        self.eventHandler = EventHandler(self.eventQueue)
+
+    def eventLoop(self, state):
+        """
+        Process all the events on the C++ event queue
+        """
+        while (not self.eventQueue.isQueueEmpty()):
+            event = self.eventQueue.dequeueEvent()
+            self.processEvent(event)
+        return Task.cont
+
+    def parseEventParameter(self, eventParameter):
+        """
+        Extract the actual data from the eventParameter
+        """
+        if (eventParameter.isInt()):
+            return eventParameter.getIntValue()
+        elif (eventParameter.isDouble()):
+            return eventParameter.getDoubleValue()
+        elif (eventParameter.isString()):
+            return eventParameter.getStringValue()
+        # Must be some user defined type, return the ptr
+        # which will be downcast to that type
+        else:
+            return eventParameter.getPtr()
+        
+            
+
+    def processEvent(self, event):
+        """
+        Process a C++ event
+        """
+        # If the event has a name, throw a Python event with the Pythonified name
+	if event.hasName():
+            # Get the event name
+            eventName = event.getName()
+
+            numParameters = event.getNumParameters()
+            paramList = []
+            for i in range(numParameters):
+                eventParameter = event.getParameter(i)
+                eventParameterData = self.parseEventParameter(eventParameter)
+                paramList.append(eventParameterData)
+
+            EventManager.notify.debug('received C++ event named: ' + eventName +
+                                      ' parameters: ' + `paramList`)
+
+
+            # Send the event, we used to send it with the event 
+            # name as a parameter, but now you can use extraArgs for that
+            if paramList:
+                messenger.send(eventName, paramList)
+            else:
+                messenger.send(eventName)
+
+            # Also send the event down into C++ land
+            self.eventHandler.dispatchEvent(event)
+            
+        # An unnamed event from C++ is probably a bad thing
+        else:
+            EventManager.notify.warning('unnamed event in processEvent')
+
+    def restart(self):
+        taskMgr.spawnTaskNamed(Task.Task(self.eventLoop), 'eventManager')
+
+    def shutdown(self):
+        taskMgr.removeTasksNamed('eventManager')

+ 3 - 0
direct/src/showbase/EventManagerGlobal.py

@@ -0,0 +1,3 @@
+import EventManager
+
+eventMgr = EventManager.EventManager()

+ 140 - 0
direct/src/showbase/FSM.py

@@ -0,0 +1,140 @@
+"""Finite State Machine module: contains the FSM class"""
+
+from DirectObject import *
+
+class FSM(DirectObject):
+    """FSM class: Finite State Machine class"""
+
+    # create FSM DirectNotify category
+    notify = directNotify.newCategory("FSM")
+
+    # special methods
+    
+    def __init__(self, name, states=[], initialStateName=None,
+                 finalStateName=None):
+        """__init__(self, string, State[], string, string)
+
+        FSM constructor: takes name, list of states, initial state and
+        final state as:
+
+        fsm = FSM.FSM('stopLight',
+          [ State.State('red', enterRed, exitRed, ['green']),
+            State.State('yellow', enterYellow, exitYellow, ['red']),
+            State.State('green', enterGreen, exitGreen, ['yellow']) ],
+          'red',
+          'red')
+
+        """
+
+        self.setName(name)
+        self.setStates(states)
+        self.setInitialState(initialStateName)
+        self.setFinalState(finalStateName)
+
+
+        #enter the initial state
+        self.__currentState = self.__initialState
+        self.__enter(self.__initialState)
+        
+    def __str__(self):
+        """__str__(self)"""
+        return "FSM: name = %s \n states = %s \n initial = %s \n final = %s \n current = %s" % (self.__name, self.__states, self.__initialState, self.__finalState, self.__currentState)
+
+
+    #setters and getters
+    
+    def getName(self):
+        """getName(self)"""
+        return(self.__name)
+
+    def setName(self, name):
+        """setName(self, string)"""
+        self.__name = name
+
+    def getStates(self):
+        """getStates(self)"""
+        return(self.__states)
+
+    def setStates(self, states):
+        """setStates(self, State[])"""
+        self.__states = states
+
+    def getInitialState(self):
+        """getInitialState(self)"""
+        return(self.__initialState)
+
+    def setInitialState(self, initialStateName):
+        """setInitialState(self, string)"""
+        self.__initialState = self.getStateNamed(initialStateName)
+
+    def getFinalState(self):
+        """getFinalState(self)"""
+        return(self.__finalState)
+
+    def setFinalState(self, finalStateName):
+        """setFinalState(self, string)"""
+        self.__finalState = self.getStateNamed(finalStateName)
+        
+    def getCurrentState(self):
+        """getCurrentState(self)"""
+        return(self.__currentState)
+
+
+    # lookup funcs
+    
+    def getStateNamed(self, stateName):
+        """getStateNamed(self, string)
+        Return the state with given name if found, issue warning otherwise"""
+        for state in self.__states:
+            if (state.getName() == stateName):
+                return state
+        FSM.notify.warning("getStateNamed: no such state")
+
+
+    # basic FSM functionality
+    
+    def __exitCurrent(self):
+        """__exitCurrent(self)
+        Exit the current state"""
+        FSM.notify.info("exiting %s" % self.__currentState.getName())
+        self.__currentState.exit()
+        self.__currentState = None
+                    
+    def __enter(self, aState):
+        """__enter(self, State)
+        Enter a given state, if it exists"""
+        if (aState in self.__states):
+            self.__currentState = aState
+            aState.enter()
+            FSM.notify.info("entering %s" % aState.getName())
+        else:
+            FSM.notify.error("enter: no such state")
+
+    def __transition(self, aState):
+        """__transition(self, State)
+        Exit currentState and enter given one"""
+        self.__exitCurrent()
+        self.__enter(aState)
+        
+    def request(self, aStateName):
+        """request(self, string)
+        Attempt transition from currentState to given one.
+        Return true is transition exists to given state,
+        false otherwise"""
+        if (aStateName in self.__currentState.getTransitions()):
+            self.__transition(self.getStateNamed(aStateName))
+            return 1
+        else:
+            FSM.notify.info("no transition exists to %s" % aStateName)
+            return 0
+
+
+
+
+
+
+
+
+
+
+

+ 14 - 0
direct/src/showbase/LerpBlendHelpers.py

@@ -0,0 +1,14 @@
+"""LerpBlendHelpers module: contains LerpBlendHelpers class"""
+
+from PandaModules import *
+class LerpBlendHelpers:
+
+    """global lerp blend types for lerp function"""
+    
+    easeIn = EaseInBlendType()
+
+    easeOut = EaseOutBlendType()
+
+    easeInOut = EaseInOutBlendType()
+
+    noBlend = NoBlendType()

+ 96 - 0
direct/src/showbase/Loader.py

@@ -0,0 +1,96 @@
+"""Loader module: contains the Loader class"""
+
+from PandaModules import *
+from DirectNotifyGlobal import *
+
+
+class Loader:
+
+    """Loader class: contains method to load models, sounds and code"""
+
+    notify = directNotify.newCategory("Loader")
+    
+    # special methods
+    def __init__(self, base):
+        """__init__(self)
+        Loader constructor"""
+        self.__base = base
+        self.__loader = PandaLoader()
+        self.__texturePool = TexturePool()
+        self.__modelPool = ModelPool()
+        self.__audioPool = AudioPool()
+        
+    # model loading funcs
+    def loadModel(self, modelPath):
+        """loadModel(self, string)
+        Attempt to load a model from given file path, return
+        a nodepath to the model if successful or None otherwise."""
+        Loader.notify.info("Loading model: %s" % (modelPath) )
+        node = self.__loader.loadSync(Filename(modelPath))
+        if (node != None):
+            nodePath = self.__base.hidden.attachNewNode(node)
+        else:
+            nodePath = None
+        return nodePath
+    
+    def loadModelOnce(self, modelPath):
+        """loadModelOnce(self, string)
+        Attempt to load a model from modelPool, if not present
+        then attempt to load it from disk. Return a nodepath to
+        the model if successful or None otherwise"""
+        Loader.notify.info("Loading model once: %s" % (modelPath))
+        node = self.__modelPool.loadModel(modelPath)
+        if (node != None):
+            nodePath = self.__base.hidden.attachNewNode(node)
+        else:
+            nodePath = None
+        return nodePath
+    
+    def loadModelCopy(self, modelPath):
+        """loadModelCopy(self, string)
+        Attempt to load a model from modelPool, if not present
+        then attempt to load it from disk. Return a nodepath to
+        a copy of the model if successful or None otherwise"""
+        Loader.notify.info("Loading model copy: %s" % (modelPath))
+        # utilize load once goodness
+        nodePath = self.loadModelOnce(modelPath)
+        if (nodePath != None):
+            return (nodePath.copyTo(self.__base.hidden))
+        else:
+            return None
+            
+    # texture loading funcs
+    def loadTexture(self, texturePath):
+        """loadTexture(self, string)
+        Attempt to load a texture from the given file path using
+        TexturePool class. Returns None if not found"""
+        Loader.notify.info("Loading texture: %s" % (texturePath) )
+        texture = self.__texturePool.loadTexture(Filename(texturePath))
+        return texture
+
+    # sound loading funcs
+    def loadSample(self, samplePath):
+        """loadSample(self, string)
+        Attempt to load a sound from the given file path using
+        Cary's sound class. Returns None if not found"""
+        Loader.notify.info("Loading sound: %s" % (samplePath) )
+        sound = self.__audioPool.loadSample(samplePath)
+        return sound
+
+    def loadMusic(self, musicPath):
+        """loadMusic(self, string)
+        Attempt to load music from the given file path using
+        Cary's sound class. Returns None if not found"""
+        Loader.notify.info("Loading music: %s" % (musicPath) )
+        music = self.__audioPool.loadMusic(musicPath)
+        return music
+
+
+
+
+
+
+
+
+
+

+ 93 - 0
direct/src/showbase/Logger.py

@@ -0,0 +1,93 @@
+"""Logger module: contains the logger class which creates and writes
+   data to log files on disk"""
+
+import sys
+import time
+import math
+
+class Logger:
+
+    """Logger class: """
+    
+    # built-ins
+    
+    def __init__(self, fileName="log"):
+        """__init__(self)
+        Logger constructor"""    
+        self.__timeStamp = 1
+        self.__startTime = 0.0
+        self.__logFile = None
+        self.__logFileName = fileName
+
+    
+    # setters and getters
+    
+    def setTimeStamp(self, bool):
+        """setTimeStamp(self, int)
+        Toggle time stamp printing with log entries on and off"""
+        self.__timeStamp = bool
+
+    def getTimeStamp(self):
+        """getTimeStamp(self)
+        Return whether or not we are printing time stamps with log entries"""
+        return(self.__timeStamp)
+
+
+    # logging control
+   
+    def resetStartTime(self):
+        """resetStartTime()
+        Reset the start time of the log file for time stamps"""
+        self.__startTime = time.time()
+
+    def log(self, entryString):
+        """log(self, string)
+        Print the given string to the log file"""
+        if (self.__logFile == None):
+            self.__openLogFile()
+        if (self.__timeStamp):
+            self.__logFile.write(self.__getTimeStamp())
+        self.__logFile.write(entryString + '\n')
+
+ 
+    # logging functions
+    
+    def __openLogFile(self):
+        """__openLogFile(self)
+        Open a file for logging error/warning messages"""
+        self.resetStartTime()
+        t = time.localtime(self.__startTime)
+        st = time.strftime("%m.%d.%Y-%H:%M:%S", t)
+        logFileName = self.__logFileName + "." + st
+        self.__logFile = open(logFileName, "w")
+
+    def __closeLogFile(self):
+        """__closeLogFile(self)
+        Close the error/warning output file"""
+        if (self.__logFile != None):
+            self.__logFile.close()
+
+    def __getTimeStamp(self):
+        """__getTimeStamp(self)
+        Return the offset between current time and log file startTime"""
+        t = time.time()
+        dt = t - self.__startTime
+        if (dt >= 86400):
+            days = int(math.floor(dt/86400))
+            dt = dt%86400
+        else:
+            days = 0
+        if (dt >= 3600):
+            hours = int(math.floor(dt/3600))
+            dt = dt%3600
+        else:
+            hours = 0
+        if (dt >= 60):
+            minutes = int(math.floor(dt/60))
+            dt = dt%60
+        else:
+            minutes = 0
+        seconds = int(math.ceil(dt))
+        return("%02d:%02d:%02d:%02d: " % (days, hours, minutes, seconds) )
+
+

+ 5 - 0
direct/src/showbase/LoggerGlobal.py

@@ -0,0 +1,5 @@
+"""instantiate global Logger object"""
+
+import Logger
+
+defaultLogger = Logger.Logger()

+ 126 - 0
direct/src/showbase/Messenger.py

@@ -0,0 +1,126 @@
+
+from PythonUtil import *
+from DirectNotifyGlobal import *
+
+
+class Messenger:
+
+    notify = None
+    
+    def __init__(self):
+        """ __init__(self)
+        One dictionary does it all. It has the following structure:
+            {event1 : {object1: [method, extraArgs, persistent],
+                       object2: [method, extraArgs, persistent]},
+             event2 : {object1: [method, extraArgs, persistent],
+                       object2: [method, extraArgs, persistent]}}
+
+        Or, for an example with more real data:
+            {'mouseDown' : {avatar : [avatar.jump, (2.0), 1]}}
+        """
+        self.dict = {}
+
+        if (Messenger.notify == None):
+            Messenger.notify = directNotify.newCategory("Messenger")
+        
+    def accept(self, event, object, method, extraArgs=[], persistent=1):
+        """ accept(self, string, DirectObject, Function, List, Boolean)
+        
+        Make this object accept this event. When the event is
+        sent (using Messenger.send or from C++), method will be executed,
+        optionally passing in extraArgs.
+        
+        If the persistent flag is set, it will continue to respond
+        to this event, otherwise it will respond only once.
+        """
+
+        Messenger.notify.debug('object: ' + `object`
+                  + '\n accept: ' + `event`
+                  + '\n method: ' + `method`
+                  + '\n extraArgs: ' + `extraArgs`
+                  + '\n persistent: ' + `persistent`)
+            
+        acceptorDict = ifAbsentPut(self.dict, event, {})
+        acceptorDict[object] = [method, extraArgs, persistent]
+
+    def ignore(self, event, object):
+        """ ignore(self, string, DirectObject)
+        Make this object no longer respond to this event.
+        It is safe to call even if it was not alread
+        """
+
+        Messenger.notify.debug(`object` + '\n ignore: ' + `event`)
+            
+        if self.dict.has_key(event):
+            # Find the dictionary of all the objects accepting this event
+            acceptorDict = self.dict[event]
+            # If this object is there, delete it from the dictionary
+            if acceptorDict.has_key(object):
+                del acceptorDict[object]
+            # If this dictionary is now empty, remove the event
+            # entry from the Messenger alltogether
+            if (len(acceptorDict) == 0):
+                del self.dict[event]
+
+    def isAccepting(self, event, object):
+        """ isAccepting(self, string, DirectOject)        
+        Is this object accepting this event?
+        """
+        if self.dict.has_key(event):
+            if self.dict[event].has_key(object):
+                # Found it, return true
+                return 1
+                
+        # If we looked in both dictionaries and made it here
+        # that object must not be accepting that event.
+        return 0
+        
+    def isIgnoring(self, event, object):
+        """ isIgnorning(self, string, DirectObject)
+        Is this object ignoring this event?
+        """
+        return (not self.isAccepting(event, object))
+
+    def send(self, event, sentArgs=[]):
+        """ send(self, string, [arg1, arg2,...])
+        Send this event, optionally passing in arguments
+        """
+        
+        Messenger.notify.debug('sent event: ' + event + ' sentArgs: ' + `sentArgs`)
+
+        if self.dict.has_key(event):
+            acceptorDict = self.dict[event]
+            for object in acceptorDict.keys():
+                method, extraArgs, persistent = acceptorDict[object]
+                apply(method, (extraArgs + sentArgs))
+                # If this object was only accepting this event once,
+                # remove it from the dictionary
+                if not persistent:
+                    del acceptorDict[object]
+                    # If this dictionary is now empty, remove the event
+                    # entry from the Messenger alltogether
+                    if (len(acceptorDict) == 0):
+                        del self.dict[event]
+
+    def clear(self):
+        """clear(self)
+        Start fresh with a clear dict
+        """
+        self.dict.clear()
+
+    def __repr__(self):
+        """__repr__(self)
+        Print out the table in a readable format
+        """
+        str = 'Messenger\n'
+        str = str + '='*50 + '\n'
+        for event in self.dict.keys():
+            acceptorDict = self.dict[event]
+            str = str + event + '\n'
+            for object in acceptorDict.keys():
+                method, extraArgs, persistent = acceptorDict[object]
+                str = str + '\t' + `object` + '\n\t' + `method` + '\n\t' + `extraArgs` + ' ' + `persistent` + '\n'
+        str = str + '='*50 + '\n'
+        return str
+
+

+ 5 - 0
direct/src/showbase/MessengerGlobal.py

@@ -0,0 +1,5 @@
+"""instantiate global Messenger object"""
+
+import Messenger
+
+messenger = Messenger.Messenger()

+ 127 - 0
direct/src/showbase/Notifier.py

@@ -0,0 +1,127 @@
+"""Notifier module: contains methods for handling information output
+   for the programmer/user"""
+
+from LoggerGlobal import *
+
+class Notifier:
+
+    def __init__(self, name, logger=None):
+        """__init__(self, string, Logger=None)
+        Create a new instance of the Notifier class with a given name
+        and an optional Logger class for piping output to. If no logger
+        specified, use the global default"""
+        self.__name = name
+
+        if (logger==None):
+            self.__logger = defaultLogger
+        else:
+            self.__logger = logger
+
+        self.__verbose = 0
+        self.__warning = 1
+        self.__debug = 0
+        self.__logging = 0
+
+    def __str__(self):
+        """__str__(self)
+        Print handling routine"""
+        return "%s: verbose = %d, warning = %d, debug = %d, logging = %d" % \
+               (self.__name, self.__verbose, self.__warning, self.__debug, self.__logging)
+    
+        
+    # error funcs
+    def error(self, errorString, exception=StandardError):
+        """error(self, string, Exception=StandardError)
+        Raise an exception with given string and optional type:
+        Exception: error"""
+        self.__log(str(exception) + ": " + self.__name + ": " + errorString)
+        raise exception(errorString)
+
+
+    # warning funcs
+    def warning(self, warningString):
+        """warning(self, string)
+        Issue the warning message if warn flag is on"""
+        if (self.__warning):
+            str = "Warning: " + self.__name + ": " + warningString
+            self.__log(str)
+            print(str)
+
+    def setWarning(self, bool):
+        """setWarning(self, int)
+        Enable/Disable the printing of warning messages"""
+        self.__warning = bool
+
+    def getWarning(self):
+        """getWarning(self)
+        Return whether the printing of warning messages is on or off"""
+        return(self.__warning)
+
+
+
+    # debug funcs
+    def debug(self, debugString):
+        """debug(self, string)
+        Issue the debug message if debug flag is on"""
+        if (self.__debug):
+            str = "Debug: " + self.__name + ": " + debugString
+            self.__log(str)
+            print(str)
+
+    def setDebug(self, bool):
+        """setDebug(self, int)
+        Enable/Disable the printing of debug messages"""
+        self.__debug = bool
+
+    def getDebug(self):
+        """getDebug(self)
+        Return whether the printing of debug messages is on or off"""
+        return(self.__debug)
+
+
+
+    # info funcs
+    def info(self, infoString):
+        """info(self, string)
+        Print the given informational string, if verbose flag is on"""
+        if (self.__verbose):
+            str = "Info: " + self.__name + ": " + infoString
+            self.__log(str)
+            print(str)
+
+    def getVerbose(self):
+        """getVerbose(self)
+        Return whether the printing of info messages is on or off"""
+        return(self.__verbose)
+
+    def setVerbose(self, bool):
+        """setVerbose(self, int)
+        Enable/Disable informational message  printing"""
+        self.__verbose = bool
+
+
+    # log funcs
+    def __log(self, logEntry):
+        """__log(self, string)
+        Determine whether to send informational message to the logger"""
+        if (self.__logging):
+            self.__logger.log(logEntry)
+
+    def getLogging(self):
+        """getLogging(self)
+        Return 1 if logging enabled, 0 otherwise"""
+        return (self.__logging)
+
+    def setLogging(self, bool):
+        """setLogging(self, int)
+        Set the logging flag to int (1=on, 0=off)"""
+        self.__logging = bool
+
+
+
+
+
+
+
+
+

+ 55 - 0
direct/src/showbase/OnscreenText.py

@@ -0,0 +1,55 @@
+"""OnscreenText module: contains the OnscreenText class"""
+
+from PandaObject import *
+
+
+
+class OnscreenText(PandaObject, ShowBase.NodePath):
+
+    Font = loader.loadModelOnce("fonts/ttf-comic").node()
+
+    def __init__(self, string, x=0.0, y=0.0):
+        """__init__(self, string, float=0.0, float=0.0)
+        Make a text node from string, put it into the 2d sg and place
+        it at x, y in screen space
+        """
+        # become one with our NodePath-ness
+        NodePath.__init__(self)
+
+        # make a text node
+        textNode = ShowBase.TextNode()
+        textNode.setBillboard(0)
+        textNode.setTextColor(0.0, 0.0, 0.0, 1.0)
+        textNode.setCardColor(1.0, 1.0, 1.0, 1.0)
+        textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
+        textNode.setFrameColor(0.0, 0.0, 0.0, 1.0)
+        textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1)
+        textNode.setFont(OnscreenText.Font)
+        textNode.setText(string)
+
+        # put the text node into the 2d scene graph
+        textNodePath = render2d.attachNewNode(textNode)
+
+        # we ARE this node path
+        self.assign(textNodePath)
+
+        # position ourselves
+        self.setXY(x, y)
+
+        # assume 4:3 aspect ratio
+        self.setScale( 0.069, 1.0, 0.069)
+        
+ 
+    def setText(self, string):
+        """setText(self, string)
+        Set the text of the onscreen text
+        """
+        self.node().setText(string)
+
+
+    def setXY(self, x, y):
+        """setPos(self, float, float)
+        Position the onscreen text in 2d screen space
+        """
+        # render2d has x across and z up
+        self.setPos(x, 0.0, y)

+ 10 - 0
direct/src/showbase/PandaObject.py

@@ -0,0 +1,10 @@
+
+from DirectObject import *
+from PandaModules import *
+from ShowBaseGlobal import *
+
+class PandaObject(DirectObject):
+    """
+    This is the class that all Panda/Show classes should inherit from
+    """
+    pass

+ 22 - 0
direct/src/showbase/PythonUtil.py

@@ -0,0 +1,22 @@
+
+def ifAbsentPut(dict, key, newValue):
+    """
+    If dict has key, return the value, otherwise insert the newValue and return it
+    """
+    if dict.has_key(key):
+        return dict[key]
+    else:
+        dict[key] = newValue
+        return newValue
+
+
+def indent(stream, numIndents, str):
+    """
+    Write str to stream with numIndents in front it it
+    """
+    #indentString = '\t'
+    # To match emacs, instead of a tab character we will use 4 spaces
+    indentString = '    '
+    stream.write(indentString * numIndents + str)
+
+

+ 140 - 0
direct/src/showbase/ShowBase.py

@@ -0,0 +1,140 @@
+
+from PandaModules import *
+from DirectNotifyGlobal import *
+from MessengerGlobal import *
+from TaskManagerGlobal import *
+from EventManagerGlobal import *
+from AudioManagerGlobal import *
+# This should be on a dconfig variable
+from TkGlobal import *
+import Task
+import EventManager
+
+class ShowBase:
+
+    notify = None
+
+    def __init__(self):
+        
+        import Loader
+
+        self.initialState = NodeAttributes()
+        self.renderTop = NodePath(NamedNode('renderTop'))
+        self.render = self.renderTop.attachNewNode('render')
+        self.hidden = NodePath(NamedNode('hidden'))
+        self.camera = self.render.attachNewNode('camera')
+        self.dataRoot = NodePath(NamedNode('dataRoot'), DataRelation.getClassType())
+        self.dataUnused = NodePath(NamedNode('dataUnused'), DataRelation.getClassType())
+
+        self.pipe = makeGraphicsPipe()
+        self.win = self.pipe.makeGraphicsWindow(self.renderTop.node(),
+                                                self.camera.node(),
+                                                self.dataRoot.node(),
+                                                self.initialState)
+
+        self.render2d = NodePath(self.win.setupPanda2d())
+        self.cam = self.camera.find('**/+Camera')
+        self.mak = self.dataRoot.attachNewNode(MouseAndKeyboard(self.win, 0, 'mak'))
+        self.trackball = self.dataUnused.attachNewNode(Trackball('trackball'))
+        self.drive = self.dataUnused.attachNewNode(DriveInterface('drive'))
+        self.mouse2cam = self.dataUnused.attachNewNode(Transform2SG('mouse2cam'))
+        self.mouse2cam.node().setArc(self.camera.getBottomArc())
+        self.useDrive()
+
+        self.buttonThrower = self.mak.attachNewNode(ButtonThrower())
+
+        if (ShowBase.notify == None):
+            ShowBase.notify = directNotify.newCategory("ShowBase")
+        
+        self.loader = Loader.Loader(self)
+
+        self.eventMgr = eventMgr
+
+        self.messenger = messenger
+
+        self.taskMgr = taskMgr
+
+        self.audioMgr = audioMgr
+
+        self.wantTk = 1
+        self.createRootPanel()
+        
+        self.restart()
+
+        self.wantSound = 1
+
+        self.wantMusic = 1
+
+    def createRootPanel(self):
+        if self.wantTk:
+            self.tkroot = Pmw.initialise()
+        else:
+            self.tkroot = None
+
+    def igloop(self, state):
+        self.win.update()
+        if (self.wantSound):
+            self.audioMgr.update()
+        return Task.cont
+    
+    def restart(self):
+        #self.shutdown()
+        self.taskMgr.spawnTaskNamed(Task.Task(self.igloop), 'igloop')
+        self.eventMgr.restart()
+
+    def shutdown(self):
+        self.taskMgr.removeTasksNamed('igloop')
+        self.eventMgr.shutdown()
+
+    def toggleBackface(self):
+        self.initialState.toggleBackface()
+
+    def toggleTexture(self):
+        self.initialState.toggleTexture()
+
+    def toggleWireframe(self):
+        self.initialState.toggleWireframe()
+
+    def disableMouse(self):
+        self.drive.reparentTo(self.dataUnused)
+        self.trackball.reparentTo(self.dataUnused)
+        self.mouse2cam.reparentTo(self.dataUnused)
+        self.mouseInterface = None
+
+    def setMouseOnArc(self, newArc):
+        self.mouse2cam.node().setArc(newArc)
+
+    def useDrive(self):
+        """
+        Toggle mouse action to drive mode
+        """
+        # Get rid of the trackball
+        self.trackball.reparentTo(self.dataUnused)
+        # Update the mouseInterface to point to the drive
+        self.mouseInterface = self.drive
+        self.drive.node().reset()
+        self.drive.reparentTo(self.mak)
+        # Hookup the drive to the camera
+        self.mouse2cam.reparentTo(self.drive)
+        # Set the height to a good eyeheight
+        self.drive.node().setZ(4.0)
+
+    def useTrackball(self):
+        """
+        Toggle mouse action to trackball mode
+        """
+        # Get rid of the drive
+        self.drive.reparentTo(self.dataUnused)
+        # Update the mouseInterface to point to the trackball
+        self.mouseInterface = self.trackball
+        # Hookup the trackball to the camera
+        self.trackball.reparentTo(self.mak)
+        self.mouse2cam.reparentTo(self.trackball)
+        
+    def run(self):
+        self.taskMgr.run()
+
+if __name__ == '__main__':
+    base = ShowBase()
+    base.run()
+

+ 16 - 0
direct/src/showbase/ShowBaseGlobal.py

@@ -0,0 +1,16 @@
+"""instantiate global ShowBase object"""
+
+import ShowBase
+
+base = ShowBase.ShowBase()
+
+# Make some global aliases for convenience
+render2d = base.render2d
+render = base.render
+hidden = base.hidden
+camera = base.camera
+loader = base.loader
+messenger = base.messenger
+taskMgr = base.taskMgr
+run = base.run
+tkroot = base.tkroot

+ 14 - 0
direct/src/showbase/Sources.pp

@@ -0,0 +1,14 @@
+#begin lib_target
+  #define TARGET showbase
+  #define LOCAL_LIBS \
+    pandatoolbase
+  #define OTHER_LIBS \
+    linmath:c putil:c express:c panda:m dtool
+
+  #define SOURCES \
+    showBase.cxx showBase.h
+
+  #define IGATESCAN all
+#end lib_target
+
+#define INSTALL_SCRIPTS ppython

+ 136 - 0
direct/src/showbase/State.py

@@ -0,0 +1,136 @@
+
+"""State module: contains State class"""
+
+from DirectObject import *
+
+
+class State(DirectObject):
+
+    """State class: """
+
+    def __init__(self, name, enterFunc=None, exitFunc=None, transitions=[]):
+        """__init__(self, string, func, func, string[])
+        State constructor: takes name, enter func, exit func, and
+        a list of states it can transition to."""
+        self.setName(name)
+        self.setEnterFunc(enterFunc)
+        self.setExitFunc(exitFunc)
+        self.setTransitions(transitions)
+        self.__FSMList = None
+
+
+    # setters and getters
+    
+    def getName(self):
+        """getName(self)"""
+        return(self.__name)
+
+    def setName(self, stateName):
+        """setName(self, string)"""
+        self.__name = stateName
+
+    def getEnterFunc(self):
+        """getEnterFunc(self)"""
+        return(self.__enterFunc)
+
+    def setEnterFunc(self, stateEnterFunc):
+        """setEnterFunc(self, func)"""
+        self.__enterFunc = stateEnterFunc
+
+    def getExitFunc(self):
+        """getExitFunc(self)"""
+        return(self.__exitFunc)
+
+    def setExitFunc(self, stateExitFunc):
+        """setExitFunc(self, func)"""
+        self.__exitFunc = stateExitFunc
+
+    def getTransitions(self):
+        """getTransitions(self)"""
+        return(self.__transitions)
+
+    def setTransitions(self, stateTransitions):
+        """setTransitions(self, string[])"""
+        self.__transitions = stateTransitions
+
+
+    # support for HFSMs
+    
+    def getChildren(self):
+        """getChildren(self)
+        Return the list of child FSMs"""
+        return(self.__FSMList)
+
+    def setChildren(self, FSMList):
+        """setChildren(self, FSM[])
+        Set the children to given list of FSMs"""
+        self.__FSMList = FSMList
+
+    def addChild(self, FSM):
+        """addChild(self, FSM)
+        Add the given FSM to list of child FSMs"""
+        if (self.__FSMList == None):
+            self.__FSMList = [FSM]
+        else:
+            self.__FSMList.append(FSM)
+
+    def hasChildren(self):
+        """hasChildren(self)
+        Return true if state has child FSMs"""
+        return(self.__FSMList != None)
+
+    def __enterChildren(self):
+        """__enterChildren(self)
+        Enter all child FSMs"""
+        if self.hasChildren():
+            for fsm in self.__FSMList:
+                fsm.request((fsm.getInitialState()).getName())
+
+    def __exitChildren(self):
+        """__exitChildren(self)
+        Exit all child FSMs"""
+        if self.hasChildren():
+            for fsm in self.__FSMList:
+                fsm.request((fsm.getFinalState()).getName())
+
+
+    # basic State functionality
+    
+    def enter(self):
+        """enter(self)
+        Call the enter function for this state"""
+        if (self.__enterFunc != None):
+            apply(self.__enterFunc)
+        
+        #enter child FSMs
+        self.__enterChildren()
+        
+    def exit(self):
+        """exit(self)
+        Call the exit function for this state"""
+        #first exit child FSMs
+        self.__exitChildren()
+
+        #call exit function if it exists
+        if (self.__exitFunc != None):
+            apply(self.__exitFunc)
+        
+    def __str__(self):
+        """__str__(self)"""
+        return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\
+               (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList)
+
+
+
+
+            
+        
+
+    
+
+
+
+
+
+
+

+ 335 - 0
direct/src/showbase/Task.py

@@ -0,0 +1,335 @@
+
+from PandaModules import *
+from DirectNotify import *
+from PythonUtil import *
+
+
+exit = -1
+done = 0
+cont = 1
+
+
+def getTimeFrame():
+    # WARNING: If you are testing tasks without an igloop,
+    # you must manually tick the clock
+
+    # Ask for the time last frame
+    t = ClockObject.getGlobalClock().getTime()
+
+    # Set the clock to have last frame's time in case we were
+    # Paused at the prompt for a long time
+    ClockObject.getGlobalClock().setTime(t)
+    
+    # Get the new frame count
+    f = ClockObject.getGlobalClock().getFrameCount()
+
+    return t, f
+
+
+class Task:
+    def __init__(self, callback):
+        self.__call__ = callback
+        
+    def setStartTimeFrame(self, startTime, startFrame):
+        self.starttime = startTime
+        self.startframe = startFrame
+        
+    def setCurrentTimeFrame(self, currentTime, currentFrame):
+        # Calculate and store this task's time (relative to when it started)
+        self.time = currentTime - self.starttime
+        self.frame = currentFrame - self.startframe
+
+def doLater(delayTime, task, taskName):
+    task.name = taskName
+    # make a sequence out of the delay and the task
+    seq = sequence(pause(delayTime), task)
+    return seq
+    
+def pause(delayTime):
+    def func(self):
+        if (self.time < self.delayTime):
+            return cont
+        else:
+            # Time is up, return done
+            TaskManager.notify.debug('pause done: ' + self.name)
+            return done
+    task = Task(func)
+    task.name = 'pause'
+    task.delayTime = delayTime
+    return task
+
+
+def release():
+    def func(self):
+        # A release is immediately done
+        TaskManager.notify.debug('release done: ' + self.name)
+        return done
+    task = Task(func)
+    task.name = 'release'
+    return task
+
+
+def sequence(*taskList):
+    return make_sequence(taskList)
+
+
+def make_sequence(taskList):
+    def func(self):
+        # If we got to the end of the list, this sequence is done
+        if (self.index >= len(self.taskList)):
+            TaskManager.notify.debug('sequence done: ' + self.name)
+            return done
+        else:
+            task = self.taskList[self.index]
+            # If this is a new task, set it's start time and frame
+            if (self.index > self.prevIndex):
+                task.setStartTimeFrame(self.time, self.frame)
+            self.prevIndex = self.index
+
+            # Calculate this task's time since it started
+            task.setCurrentTimeFrame(self.time, self.frame)
+            
+            # Execute the current task
+            ret = task(task)
+
+            # Check the return value from the task
+            # If this current task wants to continue,
+            # come back to it next frame
+            if (ret == cont):
+                return cont
+            
+            # If this task is done, increment the index so that next frame
+            # we will start executing the next task on the list
+            elif (ret == done):
+                self.index = self.index + 1
+                return cont
+
+            # If this task wants to exit, the sequence exits
+            elif (ret == exit):
+                return exit
+
+    task = Task(func)
+    task.name = 'sequence'
+    task.taskList = taskList
+    task.prevIndex = -1
+    task.index = 0
+    return task
+
+def makeSpawner(task, taskName, taskMgr):
+    def func(self):
+        self.taskMgr.spawnTaskNamed(self.task, self.taskName)
+        return done
+    newTask = Task(func)
+    newTask.name = taskName + "-spawner"
+    newTask.task = task
+    newTask.taskName = taskName
+    newTask.taskMgr = taskMgr
+    return newTask
+
+
+def makeSequenceFromTimeline(timelineList, taskMgr):
+    timeline = []
+    lastPause = 0
+    sortedList = list(timelineList)
+    sortedList.sort()
+    for triple in sortedList:
+        t = triple[0] - lastPause
+        lastPause = triple[0]
+        task = triple[1]
+        taskName = triple[2]
+        timeline.append(pause(t))
+        timeline.append(makeSpawner(task, taskName, taskMgr))
+    return make_sequence(timeline)
+
+
+def timeline(*timelineList):
+    def func(self):
+        # Step our sub task manager (returns the number of tasks remaining)
+        lenTaskList = self.taskMgr.step()
+        
+        # The sequence start time is the same as our start time
+        self.sequence.time = self.time
+        self.sequence.frame = self.frame
+        
+        if (not self.sequenceDone):
+            # Execute the sequence for this frame
+            seqRet = self.sequence(self.sequence)
+            # See if sequence is done
+            if (seqRet == done):
+                self.sequenceDone = 1
+                # See if the timeline is done
+                if (lenTaskList == 0):
+                    TaskManager.notify.debug('timeline done: ' + self.name)
+                    return done
+                else:
+                    return cont
+            else:
+                return cont
+        else:
+            return cont
+    task = Task(func)
+    task.name = 'timeline'
+    task.taskMgr = TaskManager()
+    task.sequence = makeSequenceFromTimeline(timelineList, task.taskMgr)
+    task.sequenceDone = 0
+    return task
+
+
+class TaskManager:
+
+    notify = None
+    
+    def __init__(self):
+        self.running = 0
+        self.taskList = []
+        self.currentTime, self.currentFrame = getTimeFrame()
+        if (TaskManager.notify == None):
+            TaskManager.notify = directNotify.newCategory("TaskManager")
+        #TaskManager.notify.setDebug(1)
+
+
+    def spawnMethodNamed(self, func, name):
+        task = Task(func)
+        self.spawnTaskNamed(task, name)
+        
+    def spawnTaskNamed(self, task, name):
+        TaskManager.notify.debug('spawning task named: ' + name)
+        task.name = name
+        task.setStartTimeFrame(self.currentTime, self.currentFrame)
+        self.taskList.append(task)
+        return task
+
+    def removeAllTasks(self):
+        # Make a shallow copy so we do not modify the list in place
+        taskListCopy = self.taskList[:]
+        for task in taskListCopy:
+            self.removeTask(task)
+
+    def removeTask(self, task):
+        TaskManager.notify.debug('removing task: ' + `task`)
+        try:
+            self.taskList.remove(task)
+        except:
+            pass
+        # TODO: upon death
+
+    def removeTasksNamed(self, taskName):
+        removedTasks = []
+        TaskManager.notify.debug('removing tasks named: ' + taskName)
+        
+        # Find the tasks that match by name and make a list of them
+        for task in self.taskList:
+            if (task.name == taskName):
+                removedTasks.append(task)
+
+        # Now iterate through the tasks we need to remove and remove them
+        for task in removedTasks:
+            self.removeTask(task)
+
+        # Return the number of tasks removed
+        return len(removedTasks)
+
+    def step(self):
+        self.currentTime, self.currentFrame = getTimeFrame()
+        for task in self.taskList:
+            task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
+            # Run the task and check the return value
+            ret = task(task)
+            if (ret == cont):
+                continue
+            elif (ret == done):
+                self.removeTask(task)
+            elif (ret == exit):
+                self.removeTask(task)
+            else:
+                raise 'invalid task state'
+        return len(self.taskList)
+
+    def run(self):
+        self.running = 1
+        while self.running:
+            try:
+                self.step()
+            except KeyboardInterrupt:
+                self.stop()
+
+    def stop(self):
+        # Set a flag so we will stop before beginning next frame
+        self.running = 0
+
+    def __repr__(self):
+        str = ''
+        str = str + 'taskList\n'
+        str = str + '--------------------\n'
+        for task in self.taskList:
+            str = str + ' ' + task.name + '\n'
+        return str
+
+
+"""
+import Task
+from ShowBaseGlobal import * # to get taskMgr, and run()
+
+# sequence
+
+def seq1(self):
+    print 'seq1'
+    return Task.done
+def seq2(self):
+    print 'seq2'
+    return Task.done
+
+t = Task.sequence(Task.pause(1.0), Task.Task(seq1), Task.release(),
+                  Task.pause(3.0), Task.Task(seq2))
+taskMgr.spawnTaskNamed(t, 'sequence')
+run()
+
+# timeline
+
+def keyframe1(self):
+    print 'keyframe1'
+    return Task.done
+def keyframe2(self):
+    print 'keyframe2'
+    return Task.done
+def keyframe3(self):
+    print 'keyframe3'
+    return Task.done
+    
+testtl = Task.timeline(
+    (0.5, Task.Task(keyframe1), 'key1'),
+    (0.6, Task.Task(keyframe2), 'key2'),
+    (0.7, Task.Task(keyframe3), 'key3')
+    )
+
+t = taskMgr.spawnTaskNamed(testtl, 'timeline')
+run()
+
+# do later - returns a sequence
+
+def foo(self):
+    print 'later...'
+    return Task.done
+
+seq = Task.doLater(3.0, Task.Task(foo), 'fooLater')
+t = taskMgr.spawnTaskNamed(seq, 'doLater-fooLater')
+run()
+
+# tasks with state
+
+someValue = 1
+
+def func(self):
+    if (self.someValue > 10):
+        print 'true!'
+        return Task.done
+    else:
+        self.someValue = self.someValue + 1
+        return Task.cont
+
+task = Task.Task(func)
+# set task state here
+task.someValue = someValue
+t = taskMgr.spawnTaskNamed(task, 'funcTask')
+run()
+"""

+ 4 - 0
direct/src/showbase/TaskManagerGlobal.py

@@ -0,0 +1,4 @@
+"""TaskManagerGlobal module: contains the global task manager"""
+import Task
+
+taskMgr = Task.TaskManager()

+ 21 - 0
direct/src/showbase/TkGlobal.py

@@ -0,0 +1,21 @@
+
+from Tkinter import *
+import Pmw
+
+import Task
+
+def tkloop(self):
+    # Do all the tkinter events waiting on this frame
+    # dooneevent will return 0 if there are no more events
+    # waiting or 1 if there are still more.
+    # DONT_WAIT tells tkinter not to block waiting for events
+    while tkinter.dooneevent(tkinter.DONT_WAIT):
+        pass
+    # Run forever
+    return Task.cont
+
+# Get the taskMgr
+from TaskManagerGlobal import *
+
+# Spawn this task
+taskMgr.spawnTaskNamed(Task.Task(tkloop), "tkloop")

+ 36 - 0
direct/src/showbase/ppython

@@ -0,0 +1,36 @@
+#! /bin/sh
+
+if [ -z "$CYGWIN_ROOT" ]; then
+  # Add our LD_LIBRARY_PATH to the python path so we can pull in panda modules
+  PYTHONPATH="$DIRECT"/src/all/python:"$DIRECT"/lib/py:"$LD_LIBRARY_PATH"
+  export PYTHONPATH
+
+  # Run unbuffered output, with any options passed in
+  exec python -u $*
+  exit 1
+fi
+
+# Under Windows/Cygwin, we have to do this whole thing a little differently.
+
+# First, we have to mangle the LD_LIBRARY_PATH into a series of Windows
+# paths separated by semicolons.
+PYTHONPATH=
+for path in `echo $LD_LIBRARY_PATH | sed 'y/:/ /'`; do
+  PYTHONPATH=$PYTHONPATH\;`cygpath -w $path`
+done
+
+# And now prefix that with the python source directories.
+PYTHONPATH=`cygpath -w $DIRECT/src/all/python`\;`cygpath -w $DIRECT/lib/py`$PYTHONPATH
+export PYTHONPATH
+
+# We can't run with -u under windows for some reason.  But we do need to
+# make a distinction between python_d and python.  We'll accept the user
+# parameter -d to indicate we should run python_d.
+
+if [ x"$1" = x"-d" ]; then
+  shift 1
+  exec python_d $*
+else
+  exec python $*
+fi
+

+ 262 - 0
direct/src/showbase/showBase.cxx

@@ -0,0 +1,262 @@
+// Filename: showBase.cxx
+// Created by:  shochet (02Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "showBase.h"
+
+#include <throw_event.h>
+#include <camera.h>
+#include <renderRelation.h>
+#include <namedNode.h>
+#include <renderModeTransition.h>
+#include <renderModeAttribute.h>
+#include <textureTransition.h>
+#include <textureAttribute.h>
+#include <interactiveGraphicsPipe.h>
+#include <graphicsWindow.h>
+#include <chancfg.h>
+#include <cullFaceTransition.h>
+#include <cullFaceAttribute.h>
+#include <dftraverser.h>
+#include <renderBuffer.h>
+#include <clockObject.h>
+#include <animControl.h>
+#include <nodeRelation.h>
+#include <dataGraphTraversal.h>
+#include <depthTestTransition.h>
+#include <depthTestAttribute.h>
+#include <depthWriteTransition.h>
+#include <depthWriteAttribute.h>
+#include <lightTransition.h>
+#include <materialTransition.h>
+#include <camera.h>
+#include <frustum.h>
+#include <orthoProjection.h>
+#include <appTraverser.h>
+#include <collisionTraverser.h>
+
+static CollisionTraverser *collision_traverser = NULL;
+
+void render_frame(GraphicsPipe *pipe,
+		  NodeAttributes &initial_state) {
+  int num_windows = pipe->get_num_windows();
+  for (int w = 0; w < num_windows; w++) {
+    GraphicsWindow *win = pipe->get_window(w);
+    win->get_gsg()->render_frame(initial_state);
+  }
+  ClockObject::get_global_clock()->tick();
+  throw_event("NewFrame");
+}
+
+// to be used with new display callback system
+class DisplayCallback : public GraphicsWindow::Callback {
+public:
+  DisplayCallback(GraphicsPipe *pipe, Node *render, NodeAttributes *initial_state) :
+    _pipe(pipe),
+    _render(render),
+    _initial_state(initial_state),
+    _app_traverser(RenderRelation::get_class_type()) { }
+  
+  virtual void draw(bool) {
+    _app_traverser.traverse(_render);
+    render_frame(_pipe, *_initial_state);
+  }
+  
+  PT(GraphicsPipe) _pipe;
+  PT(Node) _render;
+  NodeAttributes *_initial_state;
+  AppTraverser _app_traverser;
+};
+
+
+
+// to be used with new display callback system
+class IdleCallback : public GraphicsWindow::Callback {
+public:
+  IdleCallback(Node *render, Node *data_root) {
+    _render = render;
+    _data_root = data_root;
+  }
+  
+  virtual void idle(void) {
+    // Initiate the data traversal, to send device data down its
+    // respective pipelines.
+    traverse_data_graph(_data_root);
+    if (collision_traverser != (CollisionTraverser *)NULL) {
+      collision_traverser->traverse(_render);
+    }
+  }
+  
+  PT(Node) _render;
+  PT(Node) _data_root;
+};
+
+
+PT(GraphicsPipe) make_graphics_pipe() {
+  PT(GraphicsPipe) main_pipe;
+  
+  // load display modules
+  GraphicsPipe::resolve_modules();
+
+  nout << "Known pipe types:" << endl;
+  GraphicsPipe::_factory.write_types(nout, 2);
+
+  // Create a window
+  main_pipe = GraphicsPipe::_factory.
+    make_instance(InteractiveGraphicsPipe::get_class_type());
+
+  if (main_pipe == (GraphicsPipe*)0L) {
+    nout << "No interactive pipe is available!  Check your Configrc!\n";
+    return NULL;
+  }
+
+  nout << "Opened a '" << main_pipe->get_type().get_name()
+       << "' interactive graphics pipe." << endl;
+  return main_pipe;
+}
+
+PT(GraphicsWindow) make_graphics_window(GraphicsPipe *pipe, 
+					NamedNode *render,
+					NamedNode *camera,
+					NamedNode *data_root,
+					NodeAttributes &initial_state) {
+  PT(GraphicsWindow) main_win;
+  ChanCfgOverrides override;
+
+  // Turn on backface culling
+  CullFaceAttribute *cfa = new CullFaceAttribute;
+  cfa->set_mode(CullFaceProperty::M_cull_clockwise);
+  initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa);
+  DepthTestAttribute *dta = new DepthTestAttribute;
+  initial_state.set_attribute(DepthTestTransition::get_class_type(), dta);
+  DepthWriteAttribute *dwa = new DepthWriteAttribute;
+  initial_state.set_attribute(DepthWriteTransition::get_class_type(), dwa);
+
+  override.setField(ChanCfgOverrides::Mask,
+		    ((unsigned int)(W_DOUBLE|W_DEPTH|W_MULTISAMPLE)));
+
+  main_win = ChanConfig(pipe, "single", camera, render, override);
+  assert(main_win != (GraphicsWindow*)0L);
+
+  DisplayCallback *dcb = new DisplayCallback(pipe, render, &initial_state);
+  IdleCallback *icb = new IdleCallback(render, data_root);
+
+  // Set draw and idle callbacks
+  main_win->set_draw_callback(dcb);
+  main_win->set_idle_callback(icb);
+
+  return main_win;
+}
+
+// Create a scene graph, associated with the indicated window, that
+// can contain 2-d geometry and will be rendered on top of the
+// existing 3-d window.  Returns the top node of the scene graph.
+NodePath
+setup_panda_2d(PT(GraphicsWindow) win) {
+  PT(Node) render2d_top;
+  
+  render2d_top = new NamedNode("render2d_top");
+  Node *render2d = new NamedNode("render2d");
+  RenderRelation *render2d_arc = new RenderRelation(render2d_top, render2d);
+
+  // Set up some overrides to turn off certain properties which we
+  // probably won't need for 2-d objects.
+  render2d_arc->set_transition(new DepthTestTransition(DepthTestProperty::M_none), 1);
+  render2d_arc->set_transition(new DepthWriteTransition(DepthWriteTransition::off()), 1);
+  render2d_arc->set_transition(new LightTransition(LightTransition::all_off()), 1);
+  render2d_arc->set_transition(new MaterialTransition(MaterialTransition::off()), 1);
+  render2d_arc->set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none), 1);
+
+  // Create a 2-d camera.
+  Camera *cam2d = new Camera("cam2d");
+  new RenderRelation(render2d, cam2d);
+  cam2d->set_scene(render2d_top);
+
+  Frustumf frustum2d;
+  frustum2d.make_ortho(-1000,1000);
+  cam2d->set_projection(OrthoProjection(frustum2d));
+
+  // Now create a new layer.
+  GraphicsChannel *chan = win->get_channel(0);
+  nassertr(chan != (GraphicsChannel *)NULL, NodePath());
+
+  GraphicsLayer *layer = chan->make_layer();
+  nassertr(layer != (GraphicsLayer *)NULL, NodePath());
+
+  DisplayRegion *dr = layer->make_display_region();
+  nassertr(dr != (DisplayRegion *)NULL, NodePath());
+  dr->set_camera(cam2d);
+
+  return NodePath(render2d_arc);
+}
+
+// Enable the collision traversal using a particular traverser.
+void set_collision_traverser(CollisionTraverser *traverser) {
+  collision_traverser = traverser;
+}
+// Stop the collision traversal.
+void clear_collision_traverser() {
+  collision_traverser = NULL;
+}
+
+
+void toggle_wireframe(NodeAttributes &initial_state) {
+  static bool wireframe_mode = false;
+
+  wireframe_mode = !wireframe_mode;
+  if (!wireframe_mode) {
+    // Set the normal, filled mode on the render arc.
+    RenderModeAttribute *rma = new RenderModeAttribute;
+    rma->set_mode(RenderModeProperty::M_filled);
+    CullFaceAttribute *cfa = new CullFaceAttribute;
+    cfa->set_mode(CullFaceProperty::M_cull_clockwise);
+    initial_state.set_attribute(RenderModeTransition::get_class_type(), rma);
+    initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa);
+
+  } else {
+    // Set the initial state up for wireframe mode. 
+    RenderModeAttribute *rma = new RenderModeAttribute;
+    rma->set_mode(RenderModeProperty::M_wireframe);
+    CullFaceAttribute *cfa = new CullFaceAttribute;
+    cfa->set_mode(CullFaceProperty::M_cull_none);
+    initial_state.set_attribute(RenderModeTransition::get_class_type(), rma);
+    initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa);
+  }
+}
+
+
+void toggle_backface(NodeAttributes &initial_state) {
+  static bool backface_mode = false;
+
+  // Toggle the state variable
+  backface_mode = !backface_mode;
+
+  if (backface_mode) {
+    // Turn backface culling off
+    CullFaceAttribute *cfa = new CullFaceAttribute;
+    cfa->set_mode(CullFaceProperty::M_cull_none);
+    initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa);
+  } else {
+    // Turn backface culling on
+    CullFaceAttribute *cfa = new CullFaceAttribute;
+    cfa->set_mode(CullFaceProperty::M_cull_clockwise);
+    initial_state.set_attribute(CullFaceTransition::get_class_type(), cfa);
+  }
+}
+
+
+void toggle_texture(NodeAttributes &initial_state) {
+  static bool textures_enabled = true;
+
+  textures_enabled = !textures_enabled;
+  if (textures_enabled) {
+    // Remove the override from the initial state.
+    initial_state.clear_attribute(TextureTransition::get_class_type());
+  } else {
+    // Set an override on the initial state to disable texturing.
+    TextureAttribute *ta = new TextureAttribute;
+    ta->set_priority(100);
+    initial_state.set_attribute(TextureTransition::get_class_type(), ta);
+  }
+}

+ 39 - 0
direct/src/showbase/showBase.h

@@ -0,0 +1,39 @@
+// Filename: showBase.h
+// Created by:  shochet (02Feb00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef SHOWBASE_H
+#define SHOWBASE_H
+
+#include <directbase.h>
+
+#include <eventHandler.h>
+#include <graphicsPipe.h>
+#include <graphicsWindow.h>
+#include <animControl.h>
+#include <nodeRelation.h>
+#include <pointerTo.h>
+#include <nodePath.h>
+
+class CollisionTraverser;
+
+EXPCL_DIRECT PT(GraphicsPipe) make_graphics_pipe();
+EXPCL_DIRECT PT(GraphicsWindow) 
+  make_graphics_window(GraphicsPipe *pipe, 
+		       NamedNode *render,
+		       NamedNode *camera,
+		       NamedNode *data_root,
+		       NodeAttributes &initial_state
+		       );	
+
+EXPCL_DIRECT NodePath setup_panda_2d(PT(GraphicsWindow) win);
+
+EXPCL_DIRECT void set_collision_traverser(CollisionTraverser *traverser);
+EXPCL_DIRECT void clear_collision_traverser();
+
+EXPCL_DIRECT void toggle_wireframe(NodeAttributes &initial_state);
+EXPCL_DIRECT void toggle_texture(NodeAttributes &initial_state);
+EXPCL_DIRECT void toggle_backface(NodeAttributes &initial_state);
+
+#endif