Initial revision
This commit is contained in:
parent
61e5654fae
commit
c07eb09341
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// This is a group directory: a directory level above a number of
|
||||
// source subdirectories.
|
||||
|
||||
#define DIR_TYPE group
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// For now, since we are not installing Python files, this file can
|
||||
// remain empty.
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
model-path /beta/direct/install
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#define INSTALL_DATA \
|
||||
direct.init Configrc
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
ATTACH panda
|
||||
MODREL ETC_PATH etc
|
||||
SEP PYTHONPATH :
|
||||
MODREL PYTHONPATH lib
|
||||
MODREL PYTHONPATH lib/py
|
||||
MODREL PYTHONPATH src/all/python
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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 = ∈
|
||||
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];
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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:
|
||||
;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Filename: directbase.cc
|
||||
// Created by: drose (15Sep00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "directbase.h"
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Filename: directbase.h
|
||||
* Created by: drose (12Sep00)
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file is included at the beginning of every header file and/or
|
||||
C or C++ file. It must be compilable for C as well as C++ files,
|
||||
so no C++-specific code or syntax can be put here. */
|
||||
|
||||
#ifndef DIRECTBASE_H
|
||||
#define DIRECTBASE_H
|
||||
|
||||
#include <pandabase.h>
|
||||
#include "directsymbols.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
"""DistributedActor module: contains the DistributedActor class"""
|
||||
|
||||
from DistributedNode import *
|
||||
import Actor
|
||||
|
||||
class DistributedActor(DistributedNode, Actor.Actor):
|
||||
"""Distributed Actor class:"""
|
||||
pass
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
"""DistributedNode module: contains the DistributedNode class"""
|
||||
|
||||
from DistributedObject import *
|
||||
|
||||
class DistributedNode(DistributedObject, ShowBase.NodePath):
|
||||
"""Distributed Node class:"""
|
||||
pass
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
"""DistributedObject module: contains the DistributedObject class"""
|
||||
|
||||
from PandaObject import *
|
||||
|
||||
class DistributedObject(PandaObject):
|
||||
"""Distributed Object class:"""
|
||||
pass
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// For now, since we are not installing Python files, this file can
|
||||
// remain empty.
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// For now, since we are not installing Python files, this file can
|
||||
// remain empty.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// For now, since we are not installing Python files, this file can
|
||||
// remain empty.
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
"""AudioManagerGlobal module: contains the global audio manager"""
|
||||
import AudioManager
|
||||
|
||||
audioMgr = AudioManager.AudioManager()
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
"""instantiate global DirectNotfiy used in Direct"""
|
||||
|
||||
import DirectNotify
|
||||
|
||||
directNotify = DirectNotify.DirectNotify()
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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')
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import EventManager
|
||||
|
||||
eventMgr = EventManager.EventManager()
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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) )
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
"""instantiate global Logger object"""
|
||||
|
||||
import Logger
|
||||
|
||||
defaultLogger = Logger.Logger()
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
"""instantiate global Messenger object"""
|
||||
|
||||
import Messenger
|
||||
|
||||
messenger = Messenger.Messenger()
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
"""
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
"""TaskManagerGlobal module: contains the global task manager"""
|
||||
import Task
|
||||
|
||||
taskMgr = Task.TaskManager()
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue