From bb0fcbdd724b5035427844739351e2e2154afa21 Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Mon, 29 Jan 2001 07:16:14 +0000 Subject: [PATCH] *** empty log message *** --- .../particles/{Forces.py => ForceGroup.py} | 19 +- direct/src/particles/ParticleEffect.py | 49 +- direct/src/particles/ParticleTest.py | 10 +- direct/src/particles/Particles.py | 6 + direct/src/showbase/ShowBase.py | 12 + direct/src/tkpanels/ParticlePanel.py | 736 ++++++++++++++---- 6 files changed, 636 insertions(+), 196 deletions(-) rename direct/src/particles/{Forces.py => ForceGroup.py} (79%) diff --git a/direct/src/particles/Forces.py b/direct/src/particles/ForceGroup.py similarity index 79% rename from direct/src/particles/Forces.py rename to direct/src/particles/ForceGroup.py index b34420236f..0231fad3be 100644 --- a/direct/src/particles/Forces.py +++ b/direct/src/particles/ForceGroup.py @@ -4,7 +4,7 @@ from PhysicsManagerGlobal import * import ForceNode -class Forces(DirectObject): +class ForceGroup(DirectObject): forceNum = 1 @@ -12,13 +12,14 @@ class Forces(DirectObject): """__init__(self)""" if (name == None): - self.name = 'Forces-%d' % self.forceNum + self.name = 'ForceGroup-%d' % self.forceNum self.forceNum = self.forceNum + 1 else: self.name = name self.node = ForceNode.ForceNode(self.name) self.nodePath = hidden.attachNewNode(self.node) + self.fEnabled = 0 def enable(self): """enable(self)""" @@ -28,6 +29,7 @@ class Forces(DirectObject): physicsMgr.addLinearForce(f) else: physicsMgr.addAngularForce(f) + self.fEnabled = 1 def disable(self): """disable(self)""" @@ -37,6 +39,10 @@ class Forces(DirectObject): physicsMgr.removeLinearForce(f) else: physicsMgr.removeAngularForce(f) + self.fEnabled = 0 + + def isEnabled(self): + return self.fEnabled def addForce(self, force): """addForce(self, force)""" @@ -44,6 +50,11 @@ class Forces(DirectObject): # Physics manager will need an angular integrator base.addAngularIntegrator() self.node.addForce(force) + if self.fEnabled: + if (force.isLinear() == 1): + physicsMgr.addLinearForce(force) + else: + physicsMgr.addAngularForce(force) def removeForce(self, force): """removeForce(self, force)""" @@ -61,7 +72,7 @@ class Forces(DirectObject): return self.nodePath # Utility functions - def __getItem__(self, index): + def __getitem__(self, index): """__getItem__(self, index)""" return self.node.getForce(index) @@ -72,7 +83,7 @@ class Forces(DirectObject): def asList(self): """asList(self)""" l = [] - for i in self.node.getNumForces(): + for i in range(self.node.getNumForces()): l.append(self.node.getForce(i)) return l diff --git a/direct/src/particles/ParticleEffect.py b/direct/src/particles/ParticleEffect.py index 3973589a10..6cc4e85f1f 100644 --- a/direct/src/particles/ParticleEffect.py +++ b/direct/src/particles/ParticleEffect.py @@ -2,7 +2,7 @@ from PandaObject import * from DirectObject import * import Particles -import Forces +import ForceGroup class ParticleEffect(NodePath): def __init__(self, name = 'ParticleEffect'): @@ -11,37 +11,48 @@ class ParticleEffect(NodePath): self.assign(hidden.attachNewNode(name)) # Record particle effect name self.name = name - # Dictionary of particles and forces + # Enabled flag + self.fEnabled = 0 + # Dictionary of particles and forceGroups self.particlesDict = {} - self.forcesDict = {} + self.forceGroupDict = {} # The effect's particle system self.addParticles(Particles.Particles()) def enable(self): """enable()""" - for f in self.forcesDict.values(): + for f in self.forceGroupDict.values(): f.enable() for p in self.particlesDict.values(): p.enable() + self.fEnabled = 1 def disable(self): """disable()""" - for f in self.forcesDict.values(): + for f in self.forceGroupDict.values(): f.disable() for p in self.particlesDict.values(): p.disable() + self.fEnabled = 0 - def addForces(self, forces): - """addForces(forces)""" - forces.nodePath.reparentTo(self) - self.forcesDict[forces.getName()] = forces + def isEnabled(self): + """ + isEnabled() + Note: this may be misleading if enable(),disable() not used + """ + return self.fEnabled + + def addForceGroup(self, forceGroup): + """addForceGroup(forceGroup)""" + forceGroup.nodePath.reparentTo(self) + self.forceGroupDict[forceGroup.getName()] = forceGroup def addParticles(self, particles): """addParticles(particles)""" particles.nodePath.reparentTo(self) self.particlesDict[particles.getName()] = particles - def getParticles(self): + def getParticlesList(self): """getParticles()""" return self.particlesDict.values() @@ -53,15 +64,15 @@ class ParticleEffect(NodePath): """getParticlesDict()""" return self.particlesDict - def getForces(self): - """getForces()""" - return self.forcesDict.values() + def getForceGroupList(self): + """getForceGroup()""" + return self.forceGroupDict.values() - def getForcesNamed(self, name): - """getForcesNamed(name)""" - return self.forcesDict.get(name, None) + def getForceGroupNamed(self, name): + """getForceGroupNamed(name)""" + return self.forceGroupDict.get(name, None) - def getForcesDict(self): - """getForces()""" - return self.forcesDict + def getForceGroupDict(self): + """getForceGroup()""" + return self.forceGroupDict diff --git a/direct/src/particles/ParticleTest.py b/direct/src/particles/ParticleTest.py index 3f0dbb9844..e15c00827b 100644 --- a/direct/src/particles/ParticleTest.py +++ b/direct/src/particles/ParticleTest.py @@ -2,21 +2,21 @@ from DirectSessionGlobal import * import ParticleEffect import ParticlePanel -import Forces +import ForceGroup # Showbase base.enableParticles() -# Forces +# ForceGroup gravity = LinearVectorForce(Vec3(0.0, 0.0, -10.0)) -f = Forces.Forces() -f.addForce(gravity) +fg = ForceGroup.ForceGroup() +fg.addForce(gravity) # Particle effect pe = ParticleEffect.ParticleEffect('particle-fx') pe.reparentTo(render) pe.setPos(0.0, 5.0, 4.0) -pe.addForces(f) +pe.addForceGroup(fg) pe.enable() # Particle Panel diff --git a/direct/src/particles/Particles.py b/direct/src/particles/Particles.py index c45fee1ae6..2459a4bd98 100644 --- a/direct/src/particles/Particles.py +++ b/direct/src/particles/Particles.py @@ -62,16 +62,22 @@ class Particles(ParticleSystem.ParticleSystem): self.emitter = None self.emitterType = "undefined" self.setEmitter("SphereVolumeEmitter") + self.fEnabled = 0 def enable(self): """enable()""" physicsMgr.attachPhysical(self) particleMgr.attachParticlesystem(self) + self.fEnabled = 1 def disable(self): """disable()""" physicsMgr.removePhysical(self) particleMgr.removeParticlesystem(self) + self.fEnabled = 0 + + def isEnabled(self): + return self.fEnabled def setFactory(self, type): """setFactory(type)""" diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 4713ad84da..b1b867feac 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -150,6 +150,18 @@ class ShowBase: self.physicsMgrEnabled = 0 self.taskMgr.removeTasksNamed('manager-update') + def toggleParticles(self): + if self.particleMgrEnabled == 0: + self.enableParticles() + else: + self.disableParticles() + + def isParticleMgrEnabled(self): + return self.particleMgrEnabled + + def isPhysicsMgrEnabled(self): + return self.physicsMgrEnabled + def __updateManagers(self, state): """__updateManagers(self)""" dt = min(globalClock.getDt(), 0.1) diff --git a/direct/src/tkpanels/ParticlePanel.py b/direct/src/tkpanels/ParticlePanel.py index 94c90b1b80..6635dbdc6e 100644 --- a/direct/src/tkpanels/ParticlePanel.py +++ b/direct/src/tkpanels/ParticlePanel.py @@ -3,24 +3,28 @@ # Import Tkinter, Pmw, and the floater code from this directory tree. from AppShell import * from Tkinter import * +from tkFileDialog import * +from tkSimpleDialog import askstring +import os import Pmw import Dial import Floater import EntryScale import VectorWidgets import Placer +import ForceGroup import Particles import ParticleEffect class ParticlePanel(AppShell): # Override class variables appname = 'Particle Panel' - frameWidth = 400 - frameHeight = 660 + frameWidth = 375 + frameHeight = 775 usecommandarea = 0 usestatusarea = 0 balloonState = 'both' - + def __init__(self, particleEffect = None, **kw): INITOPT = Pmw.INITOPT optiondefs = ( @@ -33,7 +37,7 @@ class ParticlePanel(AppShell): self.particleEffect = particleEffect else: # Or create a new one if none given - pe = ParticleEffect.ParticleEffect('particle-fx') + pe = ParticleEffect.ParticleEffect('effect-1') self.particleEffect = pe pe.reparentTo(render) pe.enable() @@ -45,42 +49,156 @@ class ParticlePanel(AppShell): self.initialiseoptions(ParticlePanel) # Update panel values to reflect particle effect's state - self.selectEffectNamed(self.effectDict.keys()[0]) + self.selectEffectNamed(self.effectsDict.keys()[0]) + # Make sure labels/menus reflect current state + self.updateMenusAndLabels() + # Make sure there is a page for each forceGroup objects + for forceGroup in self.particleEffect.getForceGroupList(): + self.addForceGroupNotebookPage(self.particleEffect, forceGroup) def appInit(self): + # Create dictionaries to keep track of panel objects self.widgetDict = {} self.variableDict = {} - self.effectDict = {} - self.effectDict[self.particleEffect.getName()] = ( + self.effectsDict = {} + self.effectsDict[self.particleEffect.getName()] = ( self.particleEffect) + self.forcePagesDict = {} + # Make sure particles are enabled + base.enableParticles() def createInterface(self): # Handle to the toplevels hull interior = self.interior() - self.menuBar.addmenu('Particles', 'Particle Panel Operations') - self.menuBar.addmenuitem( - 'Particles', 'command', - 'Print Particle System Parameters', + # Create particle panel menu items + + ## MENUBAR ENTRIES ## + # FILE MENU + # Get a handle on the file menu so commands can be inserted + # before quit item + fileMenu = self.menuBar.component('File-menu') + # MRM: Need to add load and save effects methods + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Load Params', + command = self.loadParticleEffectFromFile) + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Save Params', + command = self.saveParticleEffectToFile) + fileMenu.insert_command( + fileMenu.index('Quit'), label = 'Print Params', command = lambda s = self: s.particles.printParams()) - # Combo box to switch between particle systems - self.effectSelector = Pmw.ComboBox(self.menuFrame, - labelpos = W, - label_text = 'Particle System', - entry_width = 16, - selectioncommand = self.selectEffectNamed, - scrolledlist_items = ('system 0',)) - self.effectSelector.selectitem('system 0') - self.effectSelector.pack(side = 'left', expand = 0) + # EFFECTS MENU + self.menuBar.addmenu('Effects', 'Particle Effects Operations') + # Get a handle on this menu + effectsMenu = self.menuBar.component('Effects-menu') + # Create new particle effect + self.menuBar.addmenuitem( + 'Effects', 'command', + 'Create New Particle Effect', + label = 'Create New Effect', + command = self.createNewEffect) + # Add cascade menus to view and enable, saving a handle + effectsConfigureMenu = Menu(effectsMenu, tearoff = 0) + effectsMenu.add_cascade(label = 'Configure', + menu = effectsConfigureMenu) + self.effectsEnableMenu = Menu(effectsMenu, tearoff = 0) + effectsMenu.add_cascade(label = 'Enable/Disable', + menu = self.effectsEnableMenu) - self.createCheckbutton( - self.menuFrame, 'System', 'Active', - 'Turn particle systems on/off', - self.toggleParticleSystem, 1) + # PARTICLES MENU + self.menuBar.addmenu('Particles', 'Particles Operations') + # Get a handle on this menu + particlesMenu = self.menuBar.component('Particles-menu') + # Create new particles object + self.menuBar.addmenuitem( + 'Particles', 'command', + 'Create New Particles Object', + label = 'Create New Particles', + command = self.createNewParticles) + # Add cascade menus to view and enable, saving a handle + particlesConfigureMenu = Menu(particlesMenu, tearoff = 0) + particlesMenu.add_cascade(label = 'Configure', + menu = particlesConfigureMenu) + self.particlesEnableMenu = Menu(particlesMenu, tearoff = 0) + particlesMenu.add_cascade(label = 'Enable/Disable', + menu = self.particlesEnableMenu) - # Create the notebook pages + # FORCE GROUP MENU + self.menuBar.addmenu('ForceGroup', 'ForceGroup Operations') + # Get a handle on this menu + forceGroupMenu = self.menuBar.component('ForceGroup-menu') + # Create new particle effect + self.menuBar.addmenuitem( + 'ForceGroup', 'command', + 'Create New ForceGroup Object', + label = 'Create New ForceGroup', + command = self.createNewForceGroup) + # Add cascade menus to view and enable, saving a handle + forceGroupConfigureMenu = Menu(forceGroupMenu, tearoff = 0) + forceGroupMenu.add_cascade(label = 'Configure', + menu = forceGroupConfigureMenu) + self.forceGroupEnableMenu = Menu(forceGroupMenu, tearoff = 0) + forceGroupMenu.add_cascade(label = 'Enable/Disable', + menu = self.forceGroupEnableMenu) + + # PARTICLE MANAGER MENU + self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations') + self.particleMgrActive = IntVar() + self.particleMgrActive.set(base.isParticleMgrEnabled()) + self.menuBar.addmenuitem( + 'ParticleMgr', 'checkbutton', + 'Enable/Disable ParticleMgr', + label = 'Active', + variable = self.particleMgrActive, + command = self.toggleParticleMgr) + + ## MENUBUTTON LABELS ## + # Menubutton/label to identify the current objects being configured + labelFrame = Frame(interior) + # Current effect + self.effectsLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 14, 'bold'), + activebackground = '#909090') + effectsConfigureLabel = Menu(self.effectsLabel, tearoff = 0) + self.effectsLabel['menu'] = effectsConfigureLabel + self.effectsLabel.pack(side = LEFT, fill = 'x', expand = 0) + # Current particles + self.particlesLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 14, 'bold'), + activebackground = '#909090') + particlesConfigureLabel = Menu(self.particlesLabel, tearoff = 0) + self.particlesLabel['menu'] = particlesConfigureLabel + self.particlesLabel.pack(side = LEFT, fill = 'x', expand = 0) + # Current force + self.forceGroupLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 14, 'bold'), + activebackground = '#909090') + forceGroupConfigureLabel = Menu(self.forceGroupLabel, tearoff = 0) + self.forceGroupLabel['menu'] = forceGroupConfigureLabel + self.forceGroupLabel.pack(side = LEFT, fill = 'x', expand = 0) + # Pack labels + labelFrame.pack(fill = 'x', expand = 0) + + # These get updated together + self.effectsConfigureMenus = [effectsConfigureMenu, + effectsConfigureLabel] + self.particlesConfigureMenus = [particlesConfigureMenu, + particlesConfigureLabel] + self.forceGroupConfigureMenus = [forceGroupConfigureMenu, + forceGroupConfigureLabel] + + # Create the toplevel notebook pages self.mainNotebook = Pmw.NoteBook(interior) self.mainNotebook.pack(fill = BOTH, expand = 1) systemPage = self.mainNotebook.add('System') @@ -91,7 +209,7 @@ class ParticlePanel(AppShell): # Put this here so it isn't called right away self.mainNotebook['raisecommand'] = self.updateInfo - ## SYSTEM PAGE ## + ## SYSTEM PAGE WIDGETS ## # Create system floaters systemFloaterDefs = ( ('System', 'Pool Size', @@ -116,17 +234,18 @@ class ParticlePanel(AppShell): 0.0, None) ) self.createFloaters(systemPage, systemFloaterDefs) + # Checkboxes - # Note: Sense is reversed on this one self.createCheckbutton( - systemPage, 'System', 'Render Relative Velocities', + systemPage, 'System', 'Render Space Velocities', ('On: velocities are in render space; ' + 'Off: velocities are in particle local space'), self.toggleSystemLocalVelocity, 0) self.createCheckbutton( - systemPage, 'System', 'Grows Older', + systemPage, 'System', 'System Grows Older', 'On: system has a lifespan', self.toggleSystemGrowsOlder, 0) + # Vector widgets pos = self.createVector3Entry(systemPage, 'System', 'Pos', 'Particle system position', @@ -138,7 +257,7 @@ class ParticlePanel(AppShell): command = self.setSystemHpr) hpr.addMenuItem('Popup Placer Panel', Placer.Placer) - ## FACTORY PAGE ## + ## FACTORY PAGE WIDGETS ## self.createOptionMenu( factoryPage, 'Factory', 'Factory Type', @@ -200,7 +319,7 @@ class ParticlePanel(AppShell): fill = BOTH) self.factoryNotebook.pack(expand = 1, fill = BOTH) - ## EMITTER PAGE ## + ## EMITTER PAGE WIDGETS ## self.createOptionMenu( emitterPage, 'Emitter', 'Emitter Type', @@ -354,7 +473,7 @@ class ParticlePanel(AppShell): min = 0.01) self.emitterNotebook.pack(fill = X) - ## RENDERER PAGE ## + ## RENDERER PAGE WIDGETS ## self.createOptionMenu( rendererPage, 'Renderer', 'Renderer Type', 'Select type of particle renderer', @@ -503,77 +622,46 @@ class ParticlePanel(AppShell): self.toggleRendererSpriteAlphaDisable, 0) self.rendererNotebook.pack(fill = X) - ## FORCE PAGE ## - # 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', + ## FORCE PAGE WIDGETS ## + self.addForceButton = Menubutton(forcePage, text = 'Add Force', + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 14, 'bold'), + activebackground = '#909090') + forceMenu = Menu(self.addForceButton) + self.addForceButton['menu'] = forceMenu + forceMenu.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.createCheckbutton( - forceMenuFrame, - 'Force', 'On/Off', - 'Turn selected force on/off', - self.toggleActiveForce, 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) + forceMenu.add_command(label = 'Add Linear Friction Force', + command = self.addLinearFrictionForce) + """ + forceMenu.add_command(label = 'Add Linear Cylinder Vortex Force', + command = self.addLinearCylinderVortexForce) + forceMenu.add_command(label = 'Add Linear Distance Force', + command = self.addLinearDistanceForce) + forceMenu.add_command(label = 'Add Linear Jitter Force', + command = self.addLinearJitterForce) + forceMenu.add_command(label = 'Add Linear Noise Force', + command = self.addLinearNoiseForce) + forceMenu.add_command(label = 'Add Linear Random Force', + command = self.addLinearRandomForce) + forceMenu.add_command(label = 'Add Linear Sink Force', + command = self.addLinearSinkForce) + forceMenu.add_command(label = 'Add Linear Source Force', + command = self.addLinearSourceForce) + forceMenu.add_command(label = 'Add Linear User Defined Force', + command = self.addLinearUserDefinedForce) + """ + self.addForceButton.pack(expand = 0) + # Notebook to hold force widgets as the are added + self.forceGroupNotebook = Pmw.NoteBook(forcePage, tabpos = None) + self.forceGroupNotebook.pack(fill = X) self.factoryNotebook.setnaturalsize() self.emitterNotebook.setnaturalsize() self.rendererNotebook.setnaturalsize() + self.forceGroupNotebook.setnaturalsize() self.mainNotebook.setnaturalsize() # Make sure input variables processed @@ -715,28 +803,232 @@ class ParticlePanel(AppShell): return optionVar def createComboBox(self, parent, category, text, balloonHelp, - items, command): + items, command, history = 0): widget = Pmw.ComboBox(parent, labelpos = W, label_text = text, + label_anchor = 'w', + label_width = 12, entry_width = 16, + history = history, scrolledlist_items = items) + # Don't allow user to edit entryfield + widget.configure(entryfield_entry_state = 'disabled') + # Select first item if it exists if len(items) > 0: widget.selectitem(items[0]) - widget['command'] = command + # Bind selection command + widget['selectioncommand'] = command widget.pack(side = 'left', expand = 0) - self.bind(widget.component('menubutton'), balloonHelp) - self.widgetDict[category + '-' + text] = optionVar + # Bind help + self.bind(widget, balloonHelp) + # Record widget + self.widgetDict[category + '-' + text] = widget return widget + def updateMenusAndLabels(self): + self.updateMenus() + self.updateLabels() + + def updateLabels(self): + self.effectsLabel['text'] = self.particleEffect.getName() + self.particlesLabel['text'] = self.particles.getName() + if self.forceGroup != None: + self.forceGroupLabel['text'] = self.forceGroup.getName() + else: + self.forceGroupLabel['text'] = '' + + def updateMenus(self): + self.updateEffectsMenus() + self.updateParticlesMenus() + self.updateForceGroupMenus() + + def updateEffectsMenus(self): + # Get rid of old effects entries if any + self.effectsEnableMenu.delete(0, 'end') + for menu in self.effectsConfigureMenus: + menu.delete(0, 'end') + # Add in a checkbutton for each effect (to toggle on/off) + keys = self.effectsDict.keys() + keys.sort() + for name in keys: + effect = self.effectsDict[name] + for menu in self.effectsConfigureMenus: + menu.add_command( + label = effect.getName(), + command = (lambda s = self, + e = effect: s.selectEffectNamed(e.getName())) + ) + effectActive = IntVar() + effectActive.set(effect.isEnabled()) + self.effectsEnableMenu.add_checkbutton( + label = effect.getName(), + variable = effectActive, + command = (lambda s = self, + e = effect, + v = effectActive: s.toggleEffect(e, v))) + + def updateParticlesMenus(self): + # Get rid of old particles entries if any + self.particlesEnableMenu.delete(0, 'end') + for menu in self.particlesConfigureMenus: + menu.delete(0, 'end') + # Add in a checkbutton for each effect (to toggle on/off) + particles = self.particleEffect.getParticlesList() + names = map(lambda x: x.getName(), particles) + names.sort() + for name in names: + particle = self.particleEffect.getParticlesNamed(name) + for menu in self.particlesConfigureMenus: + menu.add_command( + label = name, + command = (lambda s = self, + n = name: s.selectParticlesNamed(n)) + ) + particleActive = IntVar() + particleActive.set(particle.isEnabled()) + self.particlesEnableMenu.add_checkbutton( + label = name, + variable = particleActive, + command = (lambda s = self, + p = particle, + v = particleActive: s.toggleParticles(p, v))) + + def updateForceGroupMenus(self): + # Get rid of old forceGroup entries if any + self.forceGroupEnableMenu.delete(0, 'end') + for menu in self.forceGroupConfigureMenus: + menu.delete(0, 'end') + # Add in a checkbutton for each effect (to toggle on/off) + forceGroupList = self.particleEffect.getForceGroupList() + names = map(lambda x: x.getName(), forceGroupList) + names.sort() + for name in names: + force = self.particleEffect.getForceGroupNamed(name) + for menu in self.forceGroupConfigureMenus: + menu.add_command( + label = name, + command = (lambda s = self, + n = name: s.selectForceGroupNamed(n)) + ) + forceActive = IntVar() + forceActive.set(force.isEnabled()) + self.forceGroupEnableMenu.add_checkbutton( + label = name, + variable = forceActive, + command = (lambda s = self, + f = force, + v = forceActive: s.toggleForceGroup(f, v))) + + def selectEffectNamed(self, name): + effect = self.effectsDict.get(name, None) + if effect != None: + self.particleEffect = effect + # Default to first particle in particlesDict + self.particles = self.particleEffect.getParticlesList()[0] + # See if particle effect has any forceGroup + forceGroupList = self.particleEffect.getForceGroupList() + if len(forceGroupList) > 0: + self.forceGroup = forceGroupList[0] + else: + self.forceGroup = None + self.mainNotebook.selectpage('System') + self.updateInfo('System') + else: + print 'ParticlePanel: No effect named ' + name + + def toggleEffect(self, effect, var): + if var.get(): + effect.enable() + else: + effect.disable() + + def selectParticlesNamed(self, name): + particles = self.particleEffect.getParticlesNamed(name) + if particles != None: + self.particles = particles + self.updateInfo() + + def toggleParticles(self, particles, var): + if var.get(): + particles.enable() + else: + particles.disable() + + def selectForceGroupNamed(self, name): + forceGroup = self.particleEffect.getForceGroupNamed(name) + if forceGroup != None: + self.forceGroup = forceGroup + self.updateInfo('Force') + + def toggleForceGroup(self, forceGroup, var): + if var.get(): + forceGroup.enable() + else: + forceGroup.disable() + + def toggleForce(self, force, pagename, variableName): + v = self.getVariable(pagename, variableName) + if v.get(): + if (force.isLinear() == 1): + physicsMgr.addLinearForce(force) + else: + physicsMgr.addAngularForce(force) + else: + if (force.isLinear() == 1): + physicsMgr.removeLinearForce(force) + else: + physicsMgr.removeAngularForce(force) + + def getWidget(self, category, text): return self.widgetDict[category + '-' + text] def getVariable(self, category, text): return self.variableDict[category + '-' + text] + + def loadParticleEffectFromFile(self): + # Find path to particle directory + path = getParticlePath().getDirectory(0).toOsSpecific() + if not os.path.isdir(path): + print 'LevelEditor Warning: Invalid default DNA directory!' + print 'Using: C:\\' + path = 'C:\\' + particleFilename = askopenfilename( + defaultextension = '.ptf', + filetypes = (('Particle Files', '*.ptf'),('All files', '*')), + initialdir = path, + title = 'Load Particle Effect', + parent = self.parent) + if particleFilename: + self.particles.loadConfig(Filename(particleFilename)) + + def saveParticleEffectToFile(self): + # Find path to particle directory + path = getParticlePath().getDirectory(0).toOsSpecific() + if not os.path.isdir(path): + print 'LevelEditor Warning: Invalid default DNA directory!' + print 'Using: C:\\' + path = 'C:\\' + particleFilename = asksaveasfilename( + defaultextension = '.ptf', + filetypes = (('Particle Files', '*.ptf'),('All files', '*')), + initialdir = path, + title = 'Save Particle Effect as', + parent = self.parent) + if particleFilename: + self.particles.saveConfig(Filename(particleFilename)) + + ### PARTICLE EFFECTS COMMANDS ### + def toggleParticleMgr(self): + if self.particleMgrActive.get(): + base.enableParticles() + else: + base.disableParticles() ### PARTICLE SYSTEM COMMANDS ### def updateInfo(self, page = 'System'): + self.updateMenusAndLabels() if page == 'System': self.updateSystemWidgets() elif page == 'Factory': @@ -749,15 +1041,13 @@ class ParticlePanel(AppShell): self.selectRendererPage() self.updateRendererWidgets() elif page == 'Force': - self.selectForcePage() self.updateForceWidgets() - def toggleParticleSystem(self): - if self.getWidget('System', 'Active').get(): + def toggleParticleEffect(self): + if self.getVariable('Effect', 'Active').get(): self.particleEffect.enable() else: self.particleEffect.disable() - return None ## SYSTEM PAGE ## def updateSystemWidgets(self): @@ -775,9 +1065,9 @@ class ParticlePanel(AppShell): self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0) hpr = self.particles.nodePath.getHpr() self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0) - self.getVariable('System', 'Render Relative Velocities').set( + self.getVariable('System', 'Render Space Velocities').set( self.particles.getLocalVelocityFlag()) - self.getVariable('System', 'Grows Older').set( + self.getVariable('System', 'System Grows Older').set( self.particles.getSystemGrowsOlderFlag()) def setSystemPoolSize(self, value): self.particles.setPoolSize(int(value)) @@ -791,10 +1081,10 @@ class ParticlePanel(AppShell): self.particles.setSystemLifespan(value) def toggleSystemLocalVelocity(self): self.particles.setLocalVelocityFlag( - self.getVariable('System', 'Render Relative Velocities').get()) + self.getVariable('System', 'Render Space Velocities').get()) def toggleSystemGrowsOlder(self): self.particles.setSystemGrowsOlderFlag( - self.getVariable('System', 'Grows Older').get()) + self.getVariable('System', 'System Grows Older').get()) def setSystemPos(self, pos): self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2])) def setSystemHpr(self, pos): @@ -1008,7 +1298,7 @@ class ParticlePanel(AppShell): self.particles.emitter.setOuterMagnitude(velocity) def toggleEmitterDiscCubicLerping(self): self.particles.emitter.setCubicLerping( - self.emitterDiscCubicLerping.get()) + self.getVariable('Disc Emitter', 'Cubic Lerping').get()) # Line # def setEmitterLinePoint1(self, point): self.particles.emitter.setEndpoint1(Point3(point[0], @@ -1272,79 +1562,189 @@ class ParticlePanel(AppShell): self.particles.renderer.setAlphaDisable( self.getVariable('Sprite Renderer', 'Alpha Disable').get()) - ## FORCES COMMANDS ## - def selectForceNamed(self, name): - pass - 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 toggleActiveForce(self): - pass - + ## FORCEGROUP COMMANDS ## def updateForceWidgets(self): - pass + # Select appropriate notebook page + if self.forceGroup != None: + self.forceGroupNotebook.pack(fill = X) + self.forcePageName = (self.particleEffect.getName() + '-' + + self.forceGroup.getName()) + self.forcePage = self.forcePagesDict[self.forcePageName] + self.forceGroupNotebook.selectpage(self.forcePageName) + else: + self.forceGroupNotebook.pack_forget() def addLinearCylinderVortexForce(self): - f = LinearCylinderVortexForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearCylinderVortexForce()) def addLinearDistanceForce(self): - f = LinearDistanceForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearDistanceForce()) def addLinearFrictionForce(self): - f = LinearFrictionForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearFrictionForce()) def addLinearJitterForce(self): - f = LinearJitterForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearJitterForce()) def addLinearNoiseForce(self): - f = LinearNoiseForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearNoiseForce()) def addLinearRandomForce(self): - f = LinearRandomForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearRandomForce()) def addLinearSinkForce(self): - f = LinearSingForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearSingForce()) def addLinearSourceForce(self): - f = LinearSourceForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearSourceForce()) def addLinearUserDefinedForce(self): - f = LinearUserDefinedForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearUserDefinedForce()) def addLinearVectorForce(self): - f = LinearVectorForce() - self.activeForce.addLinearForce(f) + self.addForce(LinearVectorForce()) + + def addForce(self, f): + if self.forceGroup == None: + self.createNewForceGroup() + self.forceGroup.addForce(f) + self.addForceWidget(self.forceGroup,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.updateInfo('System') - else: - print 'ParticlePanel: No effect named ' + name + def createNewEffect(self): + name = askstring('Particle Panel', 'Effect Name:', + parent = self.parent) + if name: + effect = ParticleEffect.ParticleEffect(name) + self.effectsDict[name] = effect + self.updateMenusAndLabels() + self.selectEffectNamed(name) + effect.reparentTo(render) + effect.enable() - def createEffectNamed(self, name): - effect = ParticleEffect.ParticleEffect(name) - self.effectDict[name] = effect - self.selectEffectNamed(name) - + def createNewParticles(self): + name = askstring('Particle Panel', 'Particles Name:', + parent = self.parent) + if name: + p = Particles.Particles(name) + self.particleEffect.addParticles(p) + self.updateParticlesMenus() + self.selectParticlesNamed(name) + p.enable() + + def createNewForceGroup(self): + name = askstring('Particle Panel', 'ForceGroup Name:', + parent = self.parent) + if name: + forceGroup = ForceGroup.ForceGroup(name) + self.particleEffect.addForceGroup(forceGroup) + self.updateForceGroupMenus() + self.addForceGroupNotebookPage(self.particleEffect, forceGroup) + self.selectForceGroupNamed(name) + forceGroup.enable() + + def addForceGroupNotebookPage(self, particleEffect, forceGroup): + self.forcePageName = (particleEffect.getName() + '-' + + forceGroup.getName()) + self.forcePage = self.forceGroupNotebook.add(self.forcePageName) + self.forcePagesDict[self.forcePageName] = self.forcePage + for force in forceGroup.asList(): + self.addForceWidget(forceGroup, force) + + def addForceWidget(self, forceGroup, force): + forcePage = self.forcePage + pageName = self.forcePageName + # How many forces of the same type in the force group object + count = 0 + for f in forceGroup.asList(): + if f.getClassType().eq(force.getClassType()): + count += 1 + if isinstance(force, LinearVectorForce): + # Vector + self.createLinearVectorForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearFrictionForce): + # Coef + self.createLinearFrictionForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearCylinderVortexForce): + # Coef, length, radius + self.createLinearCylinderVortexForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearDistanceForce): + # No constructor + # Falloff type, force center, radius + pass + elif isinstance(force, LinearJitterForce): + # Nothing + pass + elif isinstance(force, LinearNoiseForce): + # Nothing + pass + elif isinstance(force, LinearRandomForce): + # Nothing + pass + elif isinstance(force, LinearSinkForce): + # Nothing + pass + elif isinstance(force, LinearSourceForce): + # Nothing + pass + elif isinstance(force, LinearUserDefinedForce): + # Nothing + pass + self.forceGroupNotebook.setnaturalsize() + + def createLinearVectorForceWidget(self, forcePage, pageName, + count, force): + name = 'Vector Force-' + `count` + cbName = name + ' Active' + def setVec(vec, f = force): + f.setVector(vec[0], vec[1], vec[2]) + def toggle(s = self, f = force, p = pageName, n = cbName): + s.toggleForce(f, p, n) + frame = Frame(forcePage, relief = RAISED, borderwidth = 2) + self.createVector3Entry(frame, pageName, name, + 'Set force direction and magnitude', + command = setVec) + self.createCheckbutton(frame, pageName, cbName, + 'On: force is enabled; Off: force is disabled', + toggle, 1) + frame.pack(fill = 'x', expand =0) + + def createLinearFrictionForceWidget(self, forcePage, pageName, + count, force): + name = 'Friction Force-' + `count` + cbName = name + ' Active' + def setCoef(coef, f = force): + f.setCoef(coef) + def toggle(s = self, f = force, p = pageName, n = cbName): + s.toggleForce(f, p, n) + frame = Frame(forcePage, relief = RAISED, borderwidth = 2) + self.createFloater(frame, pageName, name, + 'Set linear friction force', + command = setCoef, min = None) + self.createCheckbutton(frame, pageName, cbName, + 'On: force is enabled; Off: force is disabled', + toggle, 1) + frame.pack(fill = 'x', expand =0) + + def createLinearCylinderVortexForceWidget(self, forcePage, pageName, + count, force): + cbName = 'Vortex Force-' + `count` + ' Active' + def setCoef(coef, f = force): + f.setCoef(coef) + def setLength(length, f = force): + f.setLength(length) + def setRadius(radius, f = force): + f.setRadius(radius) + def toggle(s = self, f = force, p = pageName, n = cbName): + s.toggleForce(f, p, n) + frame = Frame(forcePage, relief = RAISED, borderwidth = 2) + self.createFloater(frame, pageName, 'Coefficient', + 'Set linear cylinder vortex coefficient', + command = setCoef) + self.createFloater(frame, pageName, 'Length', + 'Set linear cylinder vortex length', + command = setLength) + self.createFloater(frame, pageName, 'Radius', + 'Set linear cylinder vortex radius', + command = setRadius) + self.createCheckbutton(frame, pageName, cbName, + 'On: force is enabled; Off: force is disabled', + toggle, 1) + frame.pack(fill = 'x', expand =0) ######################################################################