Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
f97db9ecea

+ 26 - 13
direct/src/particles/Forces.py

@@ -20,17 +20,6 @@ class Forces(DirectObject):
  	self.node = ForceNode.ForceNode(self.name)
  	self.node = ForceNode.ForceNode(self.name)
 	self.nodePath = hidden.attachNewNode(self.node)
 	self.nodePath = hidden.attachNewNode(self.node)
 
 
-    def addForce(self, force):
-	"""addForce(self, force)"""
-	if (force.isLinear() == 0):
-	    # Physics manager will need an angular integrator
-	    base.addAngularIntegrator()
-	self.node.addForce(force)
-
-    def removeForce(self, force):
-	"""removeForce(self, force)"""
-	self.node.removeForce(force)
-
     def enable(self):
     def enable(self):
 	"""enable(self)"""
 	"""enable(self)"""
 	for i in range(self.node.getNumForces()):
 	for i in range(self.node.getNumForces()):
@@ -49,6 +38,29 @@ class Forces(DirectObject):
 	    else:
 	    else:
 		physicsMgr.removeAngularForce(f)
 		physicsMgr.removeAngularForce(f)
 
 
+    def addForce(self, force):
+	"""addForce(self, force)"""
+	if (force.isLinear() == 0):
+	    # Physics manager will need an angular integrator
+	    base.addAngularIntegrator()
+	self.node.addForce(force)
+
+    def removeForce(self, force):
+	"""removeForce(self, force)"""
+	self.node.removeForce(force)
+
+    # Get/set
+    def getName(self):
+        """getName(self)"""
+        return self.name
+    def getNode(self):
+        """getNode(self)"""
+        return self.node
+    def getNodePath(self):
+        """getNodePath(self)"""
+        return self.nodePath
+
+    # Utility functions 
     def __getItem__(self, index):
     def __getItem__(self, index):
 	"""__getItem__(self, index)"""
 	"""__getItem__(self, index)"""
 	return self.node.getForce(index)
 	return self.node.getForce(index)
@@ -57,9 +69,10 @@ class Forces(DirectObject):
 	"""__len__(self)"""
 	"""__len__(self)"""
 	return self.node.getNumForces()
 	return self.node.getNumForces()
 
 
-    def __asList__(self):
-	"""__asList__(self)"""
+    def asList(self):
+	"""asList(self)"""
 	l = []
 	l = []
 	for i in self.node.getNumForces():
 	for i in self.node.getNumForces():
 	    l.append(self.node.getForce(i))
 	    l.append(self.node.getForce(i))
 	return l
 	return l
+

+ 47 - 21
direct/src/particles/ParticleEffect.py

@@ -6,36 +6,62 @@ import Forces
 
 
 class ParticleEffect(NodePath):
 class ParticleEffect(NodePath):
     def __init__(self, name = 'ParticleEffect'):
     def __init__(self, name = 'ParticleEffect'):
-	"""__init__(self)"""
+	"""__init__()"""
 	NodePath.__init__(self)
 	NodePath.__init__(self)
         self.assign(hidden.attachNewNode(name))
         self.assign(hidden.attachNewNode(name))
+        # Record particle effect name
 	self.name = name
 	self.name = name
-
-	self.forces = []
-
-	self.particles = []
+        # Dictionary of particles and forces
+	self.particlesDict = {}
+	self.forcesDict = {}
+        # The effect's particle system
 	self.addParticles(Particles.Particles())
 	self.addParticles(Particles.Particles())
 
 
-    def addForces(self, forces):
-	"""addForces(self, forces)"""
-	forces.nodePath.reparentTo(self)
-	self.forces.append(forces)
-
-    def addParticles(self, part):
-	"""addParticles(self, part)"""
-	part.nodePath.reparentTo(self)
-	self.particles.append(part)
-
     def enable(self):
     def enable(self):
-	"""enable(self)"""
-	for f in self.forces:
+	"""enable()"""
+	for f in self.forcesDict.values():
 	    f.enable()
 	    f.enable()
-	for p in self.particles:
+	for p in self.particlesDict.value():
 	    p.enable()
 	    p.enable()
 
 
     def disable(self):
     def disable(self):
-	"""disable(self)"""
-	for f in self.forces:
+	"""disable()"""
+	for f in self.forcesDict.values():
 	    f.disable()
 	    f.disable()
-	for p in self.particles:
+	for p in self.particlesDict.values():
 	    p.disable()
 	    p.disable()
+
+    def addForces(self, forces):
+	"""addForces(forces)"""
+	forces.nodePath.reparentTo(self)
+	self.forcesDict[forces.getName()] = forces
+
+    def addParticles(self, particles):
+	"""addParticles(particles)"""
+	particles.nodePath.reparentTo(self)
+	self.particlesDict[particles.getName()] = particles
+
+    def getParticles(self):
+        """getParticles()"""
+        return self.particlesDict.values()
+    
+    def getParticlesNamed(self, name):
+        """getParticlesNamed(name)"""
+        return self.particlesDict.get(name, None)
+
+    def getParticlesDict(self):
+        """getParticlesDict()"""
+        return self.particlesDict
+
+    def getForces(self):
+        """getForces()"""
+        return self.forcesDict.values()
+
+    def getForcesNamed(self, name):
+        """getForcesNamed(name)"""
+        return self.forcesDict.get(name, None)
+
+    def getForcesDict(self):
+        """getForces()"""
+        return self.forcesDict
+

+ 1 - 2
direct/src/particles/ParticleTest.py

@@ -20,5 +20,4 @@ pe.addForces(f)
 pe.enable()
 pe.enable()
 
 
 # Particle Panel
 # Particle Panel
-p = pe.particles[0]
-ParticlePanel.ParticlePanel(pe, p)
+ParticlePanel.ParticlePanel(pe)

+ 24 - 10
direct/src/particles/Particles.py

@@ -35,7 +35,7 @@ class Particles(ParticleSystem.ParticleSystem):
     particleNum = 1
     particleNum = 1
 
 
     def __init__(self, name = None, poolSize = 1024):
     def __init__(self, name = None, poolSize = 1024):
-	"""__init__(self, name, poolSize)"""
+	"""__init__(name, poolSize)"""
 
 
 	if (name == None):
 	if (name == None):
 	    self.name = 'particles-%d' % self.particleNum
 	    self.name = 'particles-%d' % self.particleNum
@@ -64,17 +64,17 @@ class Particles(ParticleSystem.ParticleSystem):
 	self.setEmitter("SphereVolumeEmitter")
 	self.setEmitter("SphereVolumeEmitter")
 
 
     def enable(self):
     def enable(self):
-	"""enable(self)"""
+	"""enable()"""
 	physicsMgr.attachPhysical(self)
 	physicsMgr.attachPhysical(self)
 	particleMgr.attachParticlesystem(self)
 	particleMgr.attachParticlesystem(self)
 
 
     def disable(self):
     def disable(self):
-	"""disable(self)"""
+	"""disable()"""
 	physicsMgr.removePhysical(self)
 	physicsMgr.removePhysical(self)
 	particleMgr.removeParticlesystem(self)
 	particleMgr.removeParticlesystem(self)
 
 
     def setFactory(self, type):
     def setFactory(self, type):
-	"""setFactory(self, type)"""
+	"""setFactory(type)"""
 	if (self.factoryType == type):
 	if (self.factoryType == type):
 	    return None
 	    return None
 	if (self.factory):
 	if (self.factory):
@@ -93,7 +93,7 @@ class Particles(ParticleSystem.ParticleSystem):
 	ParticleSystem.ParticleSystem.setFactory(self, self.factory)
 	ParticleSystem.ParticleSystem.setFactory(self, self.factory)
 
 
     def setRenderer(self, type):
     def setRenderer(self, type):
-	"""setRenderer(self, type)"""
+	"""setRenderer(type)"""
 	if (self.rendererType == type):
 	if (self.rendererType == type):
 	    return None
 	    return None
 	if (self.renderer):
 	if (self.renderer):
@@ -124,7 +124,7 @@ class Particles(ParticleSystem.ParticleSystem):
 	ParticleSystem.ParticleSystem.setRenderer(self, self.renderer)
 	ParticleSystem.ParticleSystem.setRenderer(self, self.renderer)
 
 
     def setEmitter(self, type):
     def setEmitter(self, type):
-	"""setEmitter(self, type)"""
+	"""setEmitter(type)"""
 	if (self.emitterType == type):
 	if (self.emitterType == type):
 	    return None
 	    return None
 	if (self.emitter):
 	if (self.emitter):
@@ -154,8 +154,22 @@ class Particles(ParticleSystem.ParticleSystem):
 	    return None
 	    return None
 	ParticleSystem.ParticleSystem.setEmitter(self, self.emitter)
 	ParticleSystem.ParticleSystem.setEmitter(self, self.emitter)
 
 
+    ## Getters ##
+    def getName(self):
+        """getName()"""
+        return self.name
+    def getFactory(self):
+        """getFactory()"""
+        return self.factory
+    def getEmitter(self):
+        """getEmitter()"""
+        return self.emitter
+    def getRenderer(self):
+        """getRenderer()"""
+        return self.renderer
+
     def bakeConfig(self, filename):
     def bakeConfig(self, filename):
-	"""saveFileData(self, filename)"""
+	"""bakeConfig(filename)"""
 	#fname = Filename(filename)
 	#fname = Filename(filename)
 	#fname.resolveFilename(getParticlePath())
 	#fname.resolveFilename(getParticlePath())
 	#fname.resolveFilename(getModelPath())
 	#fname.resolveFilename(getModelPath())
@@ -168,7 +182,7 @@ class Particles(ParticleSystem.ParticleSystem):
         f.close()
         f.close()
 
 
     def saveConfig(self, filename):
     def saveConfig(self, filename):
-	"""saveFileData(self, filename)"""
+	"""saveFileData(filename)"""
 	#fname = Filename(filename)
 	#fname = Filename(filename)
 	#fname.resolveFilename(getParticlePath())
 	#fname.resolveFilename(getParticlePath())
 	#fname.resolveFilename(getModelPath())
 	#fname.resolveFilename(getModelPath())
@@ -181,7 +195,7 @@ class Particles(ParticleSystem.ParticleSystem):
         f.close()
         f.close()
 
 
     def loadConfig(self, filename):
     def loadConfig(self, filename):
-	"""getFileData(self, filename)
+	"""getFileData(filename)
         Open the specified file and strip out unwanted whitespace and
         Open the specified file and strip out unwanted whitespace and
         empty lines.  Return file as list, one file line per element.
         empty lines.  Return file as list, one file line per element.
         """
         """
@@ -191,7 +205,7 @@ class Particles(ParticleSystem.ParticleSystem):
 	execfile(filename.toOsSpecific())
 	execfile(filename.toOsSpecific())
 
 
     def printParams(self, file = sys.stdout, bakeflag = 0):
     def printParams(self, file = sys.stdout, bakeflag = 0):
-	"""printParams(self, file)"""
+	"""printParams(file)"""
 	targ = 'self'
 	targ = 'self'
 	if (bakeflag == 0):
 	if (bakeflag == 0):
 	    targ = 'p'
 	    targ = 'p'

+ 217 - 55
direct/src/tkpanels/ParticlePanel.py

@@ -21,26 +21,35 @@ class ParticlePanel(AppShell):
     usestatusarea  = 0
     usestatusarea  = 0
     balloonState = 'both'
     balloonState = 'both'
     
     
-    def __init__(self, particleEffect, particles, **kw):
+    def __init__(self, particleEffect = None, **kw):
         INITOPT = Pmw.INITOPT
         INITOPT = Pmw.INITOPT
         optiondefs = (
         optiondefs = (
             ('title',     self.appname,       None),
             ('title',     self.appname,       None),
             )
             )
         self.defineoptions(kw, optiondefs)
         self.defineoptions(kw, optiondefs)
 
 
-	self.particleEffect = particleEffect
-	self.particles = particles 
+        # Record particle effect
+        if particleEffect != None:
+            self.particleEffect = particleEffect
+        else:
+            # Or create a new one if none given
+            self.particleEffect = ParticleEffect.ParticleEffect(name)
 
 
+        # Initialize application specific info
         AppShell.__init__(self)
         AppShell.__init__(self)
 
 
+        # Initialize panel Pmw options
         self.initialiseoptions(ParticlePanel)
         self.initialiseoptions(ParticlePanel)
 
 
-        self.updateInfo()
+        # Update panel values to reflect particle effect's state
+        self.selectEffectNamed(self.effectDict.keys()[0])
 
 
     def appInit(self):
     def appInit(self):
         self.widgetDict = {}
         self.widgetDict = {}
-        self.systemDict = {}
-        self.systemDict['system 0'] = self.particles
+        self.variableDict = {}
+        self.effectDict = {}
+        self.effectDict[self.particleEffect.getName()] = (
+            self.particleEffect)
 
 
     def createInterface(self):
     def createInterface(self):
         # Handle to the toplevels hull
         # Handle to the toplevels hull
@@ -54,16 +63,16 @@ class ParticlePanel(AppShell):
             command = lambda s = self: s.particles.printParams())
             command = lambda s = self: s.particles.printParams())
 
 
         # Combo box to switch between particle systems
         # Combo box to switch between particle systems
-        self.systemSelector = Pmw.ComboBox(self.menuFrame,
+        self.effectSelector = Pmw.ComboBox(self.menuFrame,
                                      labelpos = W,
                                      labelpos = W,
                                      label_text = 'Particle System',
                                      label_text = 'Particle System',
                                      entry_width = 16,
                                      entry_width = 16,
-                                     selectioncommand = self.selectSystemNamed,
+                                     selectioncommand = self.selectEffectNamed,
                                      scrolledlist_items = ('system 0',))
                                      scrolledlist_items = ('system 0',))
-        self.systemSelector.selectitem('system 0')
-        self.systemSelector.pack(side = 'left', expand = 0)
+        self.effectSelector.selectitem('system 0')
+        self.effectSelector.pack(side = 'left', expand = 0)
 
 
-        self.systemActive = self.createCheckbutton(
+        self.createCheckbutton(
             self.menuFrame, 'System', 'Active',
             self.menuFrame, 'System', 'Active',
             'Turn particle systems on/off',
             'Turn particle systems on/off',
             self.toggleParticleSystem, 1)
             self.toggleParticleSystem, 1)
@@ -106,12 +115,12 @@ class ParticlePanel(AppShell):
         self.createFloaters(systemPage, systemFloaterDefs)
         self.createFloaters(systemPage, systemFloaterDefs)
         # Checkboxes
         # Checkboxes
         # Note: Sense is reversed on this one
         # Note: Sense is reversed on this one
-        self.systemLocalVelocity = self.createCheckbutton(
+        self.createCheckbutton(
             systemPage, 'System', 'Render Relative Velocities',
             systemPage, 'System', 'Render Relative Velocities',
             ('On: velocities are in render space; ' +
             ('On: velocities are in render space; ' +
              'Off: velocities are in particle local space'),
              'Off: velocities are in particle local space'),
             self.toggleSystemLocalVelocity, 0)
             self.toggleSystemLocalVelocity, 0)
-        self.systemGrowsOlder = self.createCheckbutton(
+        self.createCheckbutton(
             systemPage, 'System', 'Grows Older',
             systemPage, 'System', 'Grows Older',
             'On: system has a lifespan',
             'On: system has a lifespan',
             self.toggleSystemGrowsOlder, 0)
             self.toggleSystemGrowsOlder, 0)
@@ -127,7 +136,7 @@ class ParticlePanel(AppShell):
         hpr.addMenuItem('Popup Placer Panel', Placer.Placer)
         hpr.addMenuItem('Popup Placer Panel', Placer.Placer)
 
 
         ## FACTORY PAGE ##
         ## FACTORY PAGE ##
-        self.factoryTypeMenu = self.createOptionMenu(
+        self.createOptionMenu(
             factoryPage,
             factoryPage,
             'Factory', 'Factory Type',
             'Factory', 'Factory Type',
             'Select type of particle factory',
             'Select type of particle factory',
@@ -189,9 +198,9 @@ class ParticlePanel(AppShell):
         self.factoryNotebook.pack(expand = 1, fill = BOTH)
         self.factoryNotebook.pack(expand = 1, fill = BOTH)
 
 
         ## EMITTER PAGE ##
         ## EMITTER PAGE ##
-        self.emitterTypeMenu = self.createOptionMenu(
+        self.createOptionMenu(
             emitterPage, 'Emitter',
             emitterPage, 'Emitter',
-            'Emitter type',
+            'Emitter Type',
             'Select type of particle emitter',
             'Select type of particle emitter',
             ('BoxEmitter', 'DiscEmitter', 'LineEmitter', 'PointEmitter',
             ('BoxEmitter', 'DiscEmitter', 'LineEmitter', 'PointEmitter',
              'RectangleEmitter', 'RingEmitter', 'SphereVolumeEmitter',
              'RectangleEmitter', 'RingEmitter', 'SphereVolumeEmitter',
@@ -279,7 +288,7 @@ class ParticlePanel(AppShell):
         self.createFloater(customPage, 'Disc Emitter', 'Outer Velocity',
         self.createFloater(customPage, 'Disc Emitter', 'Outer Velocity',
                            'Launch velocity multiplier at edge of disc',
                            'Launch velocity multiplier at edge of disc',
                            command = self.setEmitterDiscOuterVelocity)
                            command = self.setEmitterDiscOuterVelocity)
-        self.emitterDiscCubicLerping = self.createCheckbutton(
+        self.createCheckbutton(
             customPage, 'Disc Emitter', 'Cubic Lerping',
             customPage, 'Disc Emitter', 'Cubic Lerping',
             'On: magnitude/angle interpolation from center',
             'On: magnitude/angle interpolation from center',
             self.toggleEmitterDiscCubicLerping, 0)
             self.toggleEmitterDiscCubicLerping, 0)
@@ -343,8 +352,8 @@ class ParticlePanel(AppShell):
         self.emitterNotebook.pack(fill = X)
         self.emitterNotebook.pack(fill = X)
 
 
         ## RENDERER PAGE ##
         ## RENDERER PAGE ##
-        self.rendererTypeMenu = self.createOptionMenu(
-            rendererPage, 'Renderer', 'Renderer type',
+        self.createOptionMenu(
+            rendererPage, 'Renderer', 'Renderer Type',
             'Select type of particle renderer',
             'Select type of particle renderer',
             ('LineParticleRenderer', 'GeomParticleRenderer',
             ('LineParticleRenderer', 'GeomParticleRenderer',
              'PointParticleRenderer', 'SparkleParticleRenderer',
              'PointParticleRenderer', 'SparkleParticleRenderer',
@@ -444,17 +453,17 @@ class ParticlePanel(AppShell):
             '<Return>', self.setRendererSpriteTexture)
             '<Return>', self.setRendererSpriteTexture)
         self.rendererSpriteTextureEntry.pack(
         self.rendererSpriteTextureEntry.pack(
             side = LEFT, expand = 1, fill = X)
             side = LEFT, expand = 1, fill = X)
-        self.rendererSpriteXScale = self.createCheckbutton(
+        self.createCheckbutton(
             spritePage, 'Sprite Renderer', 'X Scale',
             spritePage, 'Sprite Renderer', 'X Scale',
             ("On: x scale is interpolated over particle's life; " +
             ("On: x scale is interpolated over particle's life; " +
              "Off: stays as start_X_Scale"),
              "Off: stays as start_X_Scale"),
             self.toggleRendererSpriteXScale, 0)
             self.toggleRendererSpriteXScale, 0)
-        self.rendererSpriteYScale = self.createCheckbutton(
+        self.createCheckbutton(
             spritePage, 'Sprite Renderer', 'Y Scale',
             spritePage, 'Sprite Renderer', 'Y Scale',
             ("On: y scale is interpolated over particle's life; " +
             ("On: y scale is interpolated over particle's life; " +
              "Off: stays as start_Y_Scale"),
              "Off: stays as start_Y_Scale"),
             self.toggleRendererSpriteYScale, 0)
             self.toggleRendererSpriteYScale, 0)
-        self.rendererSpriteAnimAngle = self.createCheckbutton(
+        self.createCheckbutton(
             spritePage, 'Sprite Renderer', 'Anim Angle',
             spritePage, 'Sprite Renderer', 'Anim Angle',
             ("On: particles that are set to spin on the Z axis will " +
             ("On: particles that are set to spin on the Z axis will " +
              "spin appropriately"),
              "spin appropriately"),
@@ -485,19 +494,81 @@ class ParticlePanel(AppShell):
                               'Interpolation blend type for X and Y scaling',
                               'Interpolation blend type for X and Y scaling',
                               ('PP_NO_BLEND', 'PP_LINEAR', 'PP_CUBIC'),
                               ('PP_NO_BLEND', 'PP_LINEAR', 'PP_CUBIC'),
                               self.setRendererSpriteBlendMethod)
                               self.setRendererSpriteBlendMethod)
-        self.rendererSpriteAlphaDisable = self.createCheckbutton(
+        self.createCheckbutton(
             spritePage, 'Sprite Renderer', 'Alpha Disable',
             spritePage, 'Sprite Renderer', 'Alpha Disable',
             'On: alpha blending is disabled',
             'On: alpha blending is disabled',
             self.toggleRendererSpriteAlphaDisable, 0)
             self.toggleRendererSpriteAlphaDisable, 0)
         self.rendererNotebook.pack(fill = X)
         self.rendererNotebook.pack(fill = X)
 
 
         ## FORCE PAGE ##
         ## FORCE PAGE ##
-        # fn = ForceNode()
-        # lvf = LinearVectorForce()
-        # lvf.setVector(0,0,-4)
-        # fn.addForce(lvf)
-        # physicsMgr.addLinearForce(lvf)
-        # render.attachNewNode(fn)
+        # Menu to select between differnt Forces
+        self.createOptionMenu(
+            forcePage, 'Force',
+            'Active Force',
+            'Select force component', [],
+            self.selectForceType)
+        
+        forceFrame = Frame(forcePage, borderwidth = 2, relief = 'sunken')
+        self.forcesButton = Menubutton(forceFrame, text = 'Forces',
+                                       font=('MSSansSerif', 14, 'bold'),
+                                       activebackground = '#909090')
+        forcesMenu = Menu(self.forcesButton)
+        forcesMenu.add_command(label = 'Add Linear Cylinder Vortex Force',
+                            command = self.addLinearCylinderVortexForce)
+        forcesMenu.add_command(label = 'Add Linear Distance Force',
+                            command = self.addLinearDistanceForce)
+        forcesMenu.add_command(label = 'Add Linear Friction Force',
+                            command = self.addLinearFrictionForce)
+        forcesMenu.add_command(label = 'Add Linear Jitter Force',
+                            command = self.addLinearJitterForce)
+        forcesMenu.add_command(label = 'Add Linear Noise Force',
+                            command = self.addLinearNoiseForce)
+        forcesMenu.add_command(label = 'Add Linear Random Force',
+                            command = self.addLinearRandomForce)
+        forcesMenu.add_command(label = 'Add Linear Sink Force',
+                            command = self.addLinearSinkForce)
+        forcesMenu.add_command(label = 'Add Linear Source Force',
+                            command = self.addLinearSourceForce)
+        forcesMenu.add_command(label = 'Add Linear User Defined Force',
+                            command = self.addLinearUserDefinedForce)
+        forcesMenu.add_command(label = 'Add Linear Vector Force',
+                            command = self.addLinearVectorForce)
+        self.forcesButton.pack(expand = 0)
+        self.forcesButton['menu'] = forcesMenu
+        
+        # Notebook pages for force specific controls
+        self.forceNotebook = Pmw.NoteBook(forceFrame, tabpos = None,
+                                          borderwidth = 0)
+        # Put this here so it isn't called right away
+        self.forceNotebook['raisecommand'] = self.updateForceWidgets
+
+        # Widget to select a force to configure
+        nameList = map(lambda x: x.getName(), self.particleEffect.getForces())
+        forceMenuFrame = Frame(forceFrame)
+        self.forceMenu = Pmw.ComboBox(
+            forceMenuFrame, labelpos = W, label_text = 'Force:',
+            entry_width = 20,
+            selectioncommand = self.selectForceNamed,
+            scrolledlist_items = nameList)
+        self.forceMenu.pack(side = 'left', fill = 'x', expand = 0)
+        self.bind(self.forceMenu, 'Select force to configure')
+        
+        self.forceActive = BooleanVar()
+        self.forceActiveButton = Checkbutton(
+            forceMenuFrame,
+            text = 'On/Off',
+            variable = self.forceActive,
+            command = self.toggleActiveForce)
+        self.forceActiveButton.pack(side = 'left', fill = 'x', expand = 0)
+
+        # Pack force menu
+        forceMenuFrame.pack(fill = 'x', expand = 0, padx = 2)
+
+        self.forceNotebook.setnaturalsize()
+        self.forceNotebook.pack(expand = 1, fill = BOTH)
+        
+        forceFrame.pack(expand = 1, fill = BOTH)
+
         
         
         self.factoryNotebook.setnaturalsize()
         self.factoryNotebook.setnaturalsize()
         self.emitterNotebook.setnaturalsize()
         self.emitterNotebook.setnaturalsize()
@@ -519,7 +590,8 @@ class ParticlePanel(AppShell):
         widget.pack(fill = X)
         widget.pack(fill = X)
         self.bind(widget, balloonHelp)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         self.widgetDict[category + '-' + text] = widget
-        return bool
+        self.variableDict[category + '-' + text] = bool
+        return widget
         
         
     def createRadiobutton(self, parent, side, category, text,
     def createRadiobutton(self, parent, side, category, text,
                           balloonHelp, variable, value,
                           balloonHelp, variable, value,
@@ -627,7 +699,8 @@ class ParticlePanel(AppShell):
     def createOptionMenu(self, parent, category, text, balloonHelp,
     def createOptionMenu(self, parent, category, text, balloonHelp,
                          items, command):
                          items, command):
         optionVar = StringVar()
         optionVar = StringVar()
-        optionVar.set(items[0])
+        if len(items) > 0:
+            optionVar.set(items[0])
         widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text,
         widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text,
                                 label_width = 12, menu_tearoff = 1,
                                 label_width = 12, menu_tearoff = 1,
                                 menubutton_textvariable = optionVar,
                                 menubutton_textvariable = optionVar,
@@ -636,12 +709,31 @@ class ParticlePanel(AppShell):
         widget['command'] = command
         widget['command'] = command
         widget.pack(fill = X)
         widget.pack(fill = X)
         self.bind(widget.component('menubutton'), balloonHelp)
         self.bind(widget.component('menubutton'), balloonHelp)
-        self.widgetDict[category + '-' + text] = optionVar
+        self.widgetDict[category + '-' + text] = widget
+        self.variableDict[category + '-' + text] = optionVar
         return optionVar
         return optionVar
 
 
+    def createComboBox(self, parent, category, text, balloonHelp,
+                         items, command):
+        widget = Pmw.ComboBox(parent
+                              labelpos = W,
+                              label_text = text,
+                              entry_width = 16,
+                              scrolledlist_items = items)
+        if len(items) > 0:
+            widget.selectitem(items[0])
+        widget['command'] = command
+        widget.pack(side = 'left', expand = 0)
+        self.bind(widget.component('menubutton'), balloonHelp)
+        self.widgetDict[category + '-' + text] = optionVar
+        return widget
+
     def getWidget(self, category, text):
     def getWidget(self, category, text):
         return self.widgetDict[category + '-' + text]
         return self.widgetDict[category + '-' + text]
 
 
+    def getVariable(self, category, text):
+        return self.variableDict[category + '-' + text]
+
     ### PARTICLE SYSTEM COMMANDS ###
     ### PARTICLE SYSTEM COMMANDS ###
     def updateInfo(self, page = 'System'):
     def updateInfo(self, page = 'System'):
         if page == 'System':
         if page == 'System':
@@ -655,9 +747,12 @@ class ParticlePanel(AppShell):
         elif page == 'Renderer':
         elif page == 'Renderer':
             self.selectRendererPage()
             self.selectRendererPage()
             self.updateRendererWidgets()
             self.updateRendererWidgets()
+        elif page == 'Force':
+            self.selectForcePage()
+            self.updateForceWidgets()
 
 
     def toggleParticleSystem(self):
     def toggleParticleSystem(self):
-        if self.systemActive.get():
+        if self.getWidget('System', 'Active').get():
             self.particleEffect.enable()
             self.particleEffect.enable()
         else:
         else:
             self.particleEffect.disable()
             self.particleEffect.disable()
@@ -679,8 +774,10 @@ class ParticlePanel(AppShell):
         self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0)
         self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0)
         hpr = self.particles.nodePath.getHpr()
         hpr = self.particles.nodePath.getHpr()
         self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0)
         self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0)
-        self.systemLocalVelocity.set(self.particles.getLocalVelocityFlag())
-        self.systemGrowsOlder.set(self.particles.getSystemGrowsOlderFlag())
+        self.getWidget('System', 'Render Relative Velocities').set(
+            self.particles.getLocalVelocityFlag())
+        self.getWidget('System', 'Grows Older').set(
+            self.particles.getSystemGrowsOlderFlag())
     def setSystemPoolSize(self, value):
     def setSystemPoolSize(self, value):
 	self.particles.setPoolSize(int(value))
 	self.particles.setPoolSize(int(value))
     def setSystemBirthRate(self, value):
     def setSystemBirthRate(self, value):
@@ -692,9 +789,11 @@ class ParticlePanel(AppShell):
     def setSystemLifespan(self, value):
     def setSystemLifespan(self, value):
 	self.particles.setSystemLifespan(value)
 	self.particles.setSystemLifespan(value)
     def toggleSystemLocalVelocity(self):
     def toggleSystemLocalVelocity(self):
-	self.particles.setLocalVelocityFlag(self.systemLocalVelocity.get())
+	self.particles.setLocalVelocityFlag(
+            self.getWidget('System', 'Render Relative Velocities').get())
     def toggleSystemGrowsOlder(self):
     def toggleSystemGrowsOlder(self):
-	self.particles.setSystemGrowsOlderFlag(self.systemGrowsOlder.get())
+	self.particles.setSystemGrowsOlderFlag(
+            self.getWidget('System', 'Grows Older').get())
     def setSystemPos(self, pos):
     def setSystemPos(self, pos):
 	self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2]))
 	self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2]))
     def setSystemHpr(self, pos):
     def setSystemHpr(self, pos):
@@ -757,7 +856,7 @@ class ParticlePanel(AppShell):
     def selectEmitterPage(self):
     def selectEmitterPage(self):
         type = self.particles.emitter.__class__.__name__
         type = self.particles.emitter.__class__.__name__
         self.emitterNotebook.selectpage(type)
         self.emitterNotebook.selectpage(type)
-        self.emitterTypeMenu.set(type)
+        self.getVariable('Emitter', 'Emitter Type').set(type)
         
         
     def updateEmitterWidgets(self):
     def updateEmitterWidgets(self):
         emitter = self.particles.emitter
         emitter = self.particles.emitter
@@ -796,7 +895,7 @@ class ParticlePanel(AppShell):
             self.getWidget('Disc Emitter', 'Inner Velocity').set(
             self.getWidget('Disc Emitter', 'Inner Velocity').set(
                 outerMagnitude, 0)
                 outerMagnitude, 0)
             cubicLerping = emitter.getCubicLerping()
             cubicLerping = emitter.getCubicLerping()
-            self.emitterDiscCubicLerping.set(cubicLerping)
+            self.getWidget('Disc Emitter', 'Cubic Lerping').set(cubicLerping)
         elif isinstance(emitter, LineEmitter):
         elif isinstance(emitter, LineEmitter):
             min = emitter.getEndpoint1()
             min = emitter.getEndpoint1()
             self.getWidget('Line Emitter', 'Min').set(
             self.getWidget('Line Emitter', 'Min').set(
@@ -1018,9 +1117,12 @@ class ParticlePanel(AppShell):
             texture = renderer.getTexture()
             texture = renderer.getTexture()
 	    if (texture != None):
 	    if (texture != None):
 		self.rendererSpriteTexture = texture.getName()
 		self.rendererSpriteTexture = texture.getName()
-            self.rendererSpriteXScale.set(renderer.getXScaleFlag())
-            self.rendererSpriteYScale.set(renderer.getYScaleFlag())
-            self.rendererSpriteAnimAngle.set(renderer.getAnimAngleFlag())
+            self.getWidget('Sprite Renderer', 'X Scale').set(
+                renderer.getXScaleFlag())
+            self.getWidget('Sprite Renderer', 'Y Scale').set(
+                renderer.getYScaleFlag())
+            self.getWidget('Sprite Renderer', 'Anim Angle').set(
+                renderer.getAnimAngleFlag())
             initialXScale = renderer.getInitialXScale()
             initialXScale = renderer.getInitialXScale()
             self.getWidget('Sprite Renderer', 'Initial X Scale').set(
             self.getWidget('Sprite Renderer', 'Initial X Scale').set(
                 initialXScale)
                 initialXScale)
@@ -1044,12 +1146,13 @@ class ParticlePanel(AppShell):
 		bMethod = "PP_BLEND_LINEAR"
 		bMethod = "PP_BLEND_LINEAR"
 	    elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC):
 	    elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC):
 		bMethod = "PP_BLEND_CUBIC"
 		bMethod = "PP_BLEND_CUBIC"
-            self.rendererSpriteAlphaDisable.set(renderer.getAlphaDisable())
+            self.getWidget('Sprite Renderer', 'Alpha Disable').set(
+                renderer.getAlphaDisable())
 
 
     def selectRendererPage(self):
     def selectRendererPage(self):
         type = self.particles.renderer.__class__.__name__
         type = self.particles.renderer.__class__.__name__
         self.rendererNotebook.selectpage(type)
         self.rendererNotebook.selectpage(type)
-        self.rendererTypeMenu.set(type)
+        self.getVariable('Renderer', 'Renderer Type').set(type)
 
 
     # All #
     # All #
     def setRendererAlphaMode(self, alphaMode):
     def setRendererAlphaMode(self, alphaMode):
@@ -1136,13 +1239,13 @@ class ParticlePanel(AppShell):
 	    print "Couldn't find rendererSpriteTexture"
 	    print "Couldn't find rendererSpriteTexture"
     def toggleRendererSpriteXScale(self):
     def toggleRendererSpriteXScale(self):
 	self.particles.renderer.setXScaleFlag(
 	self.particles.renderer.setXScaleFlag(
-            self.rendererSpriteXScale.get())
+            self.getWidget('Sprite Renderer', 'X Scale').get())
     def toggleRendererSpriteYScale(self):
     def toggleRendererSpriteYScale(self):
 	self.particles.renderer.setYScaleFlag(
 	self.particles.renderer.setYScaleFlag(
-            self.rendererSpriteYScale.get())
+            self.getWidget('Sprite Renderer', 'Y Scale').get())
     def toggleRendererSpriteAnimAngle(self):
     def toggleRendererSpriteAnimAngle(self):
 	self.particles.renderer.setAnimAngleFlag(
 	self.particles.renderer.setAnimAngleFlag(
-            self.rendererSpriteAnimAngle.get())
+            self.getWidget('Sprite Renderer', 'Anim Angle').get())
     def setRendererSpriteInitialXScale(self, xScale):
     def setRendererSpriteInitialXScale(self, xScale):
 	self.particles.renderer.setInitialXScale(xScale)
 	self.particles.renderer.setInitialXScale(xScale)
     def setRendererSpriteFinalXScale(self, xScale):
     def setRendererSpriteFinalXScale(self, xScale):
@@ -1166,17 +1269,76 @@ class ParticlePanel(AppShell):
 	self.particles.renderer.setAlphaBlendMethod(bMethod)
 	self.particles.renderer.setAlphaBlendMethod(bMethod)
     def toggleRendererSpriteAlphaDisable(self):
     def toggleRendererSpriteAlphaDisable(self):
 	self.particles.renderer.setAlphaDisable(
 	self.particles.renderer.setAlphaDisable(
-            self.rendererSpriteAlphaDisable.get())
+            self.getWidget('Sprite Renderer', 'Alpha Disable').get())
+        
+    ## FORCES COMMANDS ##
+    def selectForceType(self, type):
+        self.forceNotebook.selectpage(type)
+        self.updateForceWidgets()
+
+    def selectForcePage(self):
+        if self.forces:
+            type = self.forces.__class__.__name__
+            self.forceNotebook.selectpage(type)
+            self.getVariable('Emitter', 'Emitter Type').set(type)
         
         
-    def selectSystemNamed(self, name):
-        system = self.systemDict.get(name, None)
-        if system == None:
-            system = Particles.Particles('new-particles')
-            self.systemDict[name] = system
-        if system:
-            self.particles = system
+    def updateForceWidgets(self):
+        pass
+
+    def addLinearCylinderVortexForce(self):
+        f = LinearCylinderVortexForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearDistanceForce(self):
+        f = LinearDistanceForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearFrictionForce(self):
+        f = LinearFrictionForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearJitterForce(self):
+        f = LinearJitterForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearNoiseForce(self):
+        f = LinearNoiseForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearRandomForce(self):
+        f = LinearRandomForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearSinkForce(self):
+        f = LinearSingForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearSourceForce(self):
+        f = LinearSourceForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearUserDefinedForce(self):
+        f = LinearUserDefinedForce()
+        self.activeForce.addLinearForce(f)
+    def addLinearVectorForce(self):
+        f = LinearVectorForce()
+        self.activeForce.addLinearForce(f)
+
+    ## SYSTEM COMMANDS ##
+    def selectEffectNamed(self, name):
+        effect = self.effectDict.get(name, None)
+        if effect:
+            self.particleEffect = effect
+            # Default to first particle in particlesDict
+            self.particles = self.particleEffect.getParticles()[0]
+            # See if particle effect has any forces
+            forcesList = self.particleEffect.getForces()
+            if len(forcesList) > 0:
+                self.forces = forcesList[0]
+            else:
+                self.forces = None
             self.mainNotebook.selectpage('System')
             self.mainNotebook.selectpage('System')
             self.updateInfo('System')
             self.updateInfo('System')
+        else:
+            print 'ParticlePanel: No effect named ' + name
+
+    def createEffectNamed(self, name):
+        effect = ParticleEffect.ParticleEffect(name)
+        self.effectDict[name] = effect
+        self.selectEffectNamed(name)
+            
 
 
 ######################################################################
 ######################################################################