Initial revision

This commit is contained in:
David Rose 2000-10-11 20:04:08 +00:00
parent 61e5654fae
commit c07eb09341
85 changed files with 8382 additions and 0 deletions

18
direct/Config.pp Normal file
View File

@ -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
direct/Package.pp Normal file
View File

@ -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
direct/Sources.pp Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
direct/src/Sources.pp Normal file
View File

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

580
direct/src/actor/Actor.py Normal file
View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 $@.h 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

View File

@ -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
direct/src/dcparse/dc.dsp Normal file
View File

@ -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
direct/src/dcparse/dc.dsw Normal file
View File

@ -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>
{{{
}}}
###############################################################################

View File

@ -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";
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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];
}

View File

@ -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

View File

@ -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";
}

View File

@ -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

View File

@ -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:
;

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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)

View File

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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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
direct/src/ffi/FFIRename.py Normal file
View File

@ -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
direct/src/ffi/FFISpecs.py Normal file
View File

@ -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
direct/src/ffi/FFITypes.py Normal file
View File

@ -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)

View File

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

View File

@ -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)

View File

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

View File

@ -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()

View File

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

View File

@ -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)

View File

@ -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')

View File

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

140
direct/src/showbase/FSM.py Normal file
View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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) )

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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
direct/src/showbase/Task.py Normal file
View File

@ -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()
"""

View File

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

View File

@ -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
direct/src/showbase/ppython Executable file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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