seParticlePanel.py 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976
  1. """PANDA3D Particle Panel"""
  2. # Import Tkinter, Pmw, and the floater code from this directory tree.
  3. from direct.tkwidgets.AppShell import AppShell
  4. from tkFileDialog import *
  5. from tkSimpleDialog import askstring
  6. import os, Pmw, Tkinter
  7. from direct.tkwidgets.Dial import AngleDial
  8. from direct.tkwidgets.Floater import Floater
  9. from direct.tkwidgets.Slider import Slider
  10. from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
  11. from direct.tkwidgets.VectorWidgets import ColorEntry
  12. import sePlacer
  13. import seForceGroup
  14. import seParticles
  15. import seParticleEffect
  16. class ParticlePanel(AppShell):
  17. # Override class variables
  18. appname = 'Particle Panel'
  19. frameWidth = 375
  20. frameHeight = 575
  21. usecommandarea = 0
  22. usestatusarea = 0
  23. balloonState = 'both'
  24. effectsDict={}
  25. def __init__(self, particleEffect = None, effectsDict={}, **kw):
  26. INITOPT = Pmw.INITOPT
  27. optiondefs = (
  28. ('title', self.appname, None),
  29. )
  30. self.defineoptions(kw, optiondefs)
  31. # Record particle effect
  32. if particleEffect != None:
  33. self.particleEffect = particleEffect
  34. self.effectsDict = effectsDict
  35. else:
  36. # Or create a new one if none given
  37. particles = seParticles.Particles()
  38. particles.setBirthRate(0.02)
  39. particles.setLitterSize(10)
  40. particles.setLitterSpread(0)
  41. particles.setFactory("PointParticleFactory")
  42. particles.setRenderer("PointParticleRenderer")
  43. particles.setEmitter("SphereVolumeEmitter")
  44. particles.enable()
  45. pe = seParticleEffect.ParticleEffect('effect1', particles)
  46. self.particleEffect = pe
  47. self.emitter=loader.loadModel("sphere")
  48. pe.reparentTo(self.emitter)
  49. self.emitter.setName("effect1")
  50. self.emitter.reparentTo(render)
  51. pe.enable()
  52. messenger.send('ParticlePanel_Added_Effect',['effect1',pe,self.emitter])
  53. self.effectsDict[self.particleEffect.getName()]=self.particleEffect
  54. messenger.send('SGE_Update Explorer',[render])
  55. # Initialize application specific info
  56. AppShell.__init__(self)
  57. # Initialize panel Pmw options
  58. self.initialiseoptions(ParticlePanel)
  59. # Update panel values to reflect particle effect's state
  60. self.selectEffectNamed(self.effectsDict.keys()[0])
  61. # Make sure labels/menus reflect current state
  62. self.updateMenusAndLabels()
  63. # Make sure there is a page for each forceGroup objects
  64. for forceGroup in self.particleEffect.getForceGroupList():
  65. self.addForceGroupNotebookPage(self.particleEffect, forceGroup)
  66. def appInit(self):
  67. # Create dictionaries to keep track of panel objects
  68. self.widgetDict = {}
  69. self.variableDict = {}
  70. self.forcePagesDict = {}
  71. # Make sure particles are enabled
  72. base.enableParticles()
  73. def onDestroy(self, event):
  74. messenger.send('ParticlePanle_close')
  75. return
  76. def createInterface(self):
  77. # Handle to the toplevels hull
  78. interior = self.interior()
  79. # Create particle panel menu items
  80. ## MENUBAR ENTRIES ##
  81. # FILE MENU
  82. # Get a handle on the file menu so commands can be inserted
  83. # before quit item
  84. fileMenu = self.menuBar.component('File-menu')
  85. # MRM: Need to add load and save effects methods
  86. fileMenu.insert_command(
  87. fileMenu.index('Quit'),
  88. label = 'Load Params',
  89. command = self.loadParticleEffectFromFile)
  90. fileMenu.insert_command(
  91. fileMenu.index('Quit'),
  92. label = 'Save Params',
  93. command = self.saveParticleEffectToFile)
  94. fileMenu.insert_command(
  95. fileMenu.index('Quit'),
  96. label = 'Print Params',
  97. command = lambda s = self: s.particles.printParams())
  98. # PARTICLE MANAGER MENU
  99. self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')
  100. self.particleMgrActive = IntVar()
  101. self.particleMgrActive.set(base.isParticleMgrEnabled())
  102. self.menuBar.addmenuitem(
  103. 'ParticleMgr', 'checkbutton',
  104. 'Enable/Disable ParticleMgr',
  105. label = 'Active',
  106. variable = self.particleMgrActive,
  107. command = self.toggleParticleMgr)
  108. ## MENUBUTTON LABELS ##
  109. # Menubutton/label to identify the current objects being configured
  110. labelFrame = Frame(interior)
  111. # Current effect
  112. self.effectsLabel = Menubutton(labelFrame, width = 10,
  113. relief = RAISED,
  114. borderwidth = 2,
  115. font=('MSSansSerif', 12, 'bold'),
  116. activebackground = '#909090')
  117. self.effectsLabelMenu = Menu(self.effectsLabel, tearoff = 0)
  118. self.effectsLabel['menu'] = self.effectsLabelMenu
  119. self.effectsLabel.pack(side = LEFT, fill = 'x', expand = 1)
  120. self.bind(self.effectsLabel,
  121. 'Select effect to configure or create new effect')
  122. self.effectsLabelMenu.add_command(label = 'Create New Effect',
  123. command = self.createNewEffect)
  124. self.effectsLabelMenu.add_command(
  125. label = 'Select Particle Effect',
  126. command = lambda s = self: SEditor.select(s.particleEffect))
  127. self.effectsLabelMenu.add_command(
  128. label = 'Place Particle Effect',
  129. command = lambda s = self: sePlacer.place(s.particleEffect))
  130. def togglePEVis(s = self):
  131. if s.particleEffect.isHidden():
  132. s.particleEffect.show()
  133. else:
  134. s.particleEffect.hide()
  135. self.effectsLabelMenu.add_command(
  136. label = 'Toggle Effect Vis',
  137. command = togglePEVis)
  138. self.effectsEnableMenu = Menu(self.effectsLabelMenu, tearoff = 0)
  139. self.effectsLabelMenu.add_cascade(label = 'Enable/Disable',
  140. menu = self.effectsEnableMenu)
  141. self.effectsLabelMenu.add_separator()
  142. # Current particles
  143. self.particlesLabel = Menubutton(labelFrame, width = 10,
  144. relief = RAISED,
  145. borderwidth = 2,
  146. font=('MSSansSerif', 12, 'bold'),
  147. activebackground = '#909090')
  148. self.particlesLabelMenu = Menu(self.particlesLabel, tearoff = 0)
  149. self.particlesLabel['menu'] = self.particlesLabelMenu
  150. self.particlesLabel.pack(side = LEFT, fill = 'x', expand = 1)
  151. self.bind(self.particlesLabel,
  152. ('Select particles object to configure ' +
  153. 'or add new particles object to current effect' ))
  154. self.particlesLabelMenu.add_command(label = 'Create New Particles',
  155. command = self.createNewParticles)
  156. self.particlesEnableMenu = Menu(self.particlesLabelMenu, tearoff = 0)
  157. self.particlesLabelMenu.add_cascade(label = 'Enable/Disable',
  158. menu = self.particlesEnableMenu)
  159. self.particlesLabelMenu.add_separator()
  160. # Current force
  161. self.forceGroupLabel = Menubutton(labelFrame, width = 10,
  162. relief = RAISED,
  163. borderwidth = 2,
  164. font=('MSSansSerif', 12, 'bold'),
  165. activebackground = '#909090')
  166. self.forceGroupLabelMenu = Menu(self.forceGroupLabel, tearoff = 0)
  167. self.forceGroupLabel['menu'] = self.forceGroupLabelMenu
  168. self.forceGroupLabel.pack(side = LEFT, fill = 'x', expand = 1)
  169. self.bind(self.forceGroupLabel,
  170. ('Select force group to configure ' +
  171. 'or add a new force group to current effect'))
  172. self.forceGroupLabelMenu.add_command(
  173. label = 'Create New ForceGroup',
  174. command = self.createNewForceGroup)
  175. self.forceGroupEnableMenu = Menu(self.forceGroupLabelMenu, tearoff = 0)
  176. self.forceGroupLabelMenu.add_cascade(label = 'Enable/Disable',
  177. menu = self.forceGroupEnableMenu)
  178. self.forceGroupLabelMenu.add_separator()
  179. # Pack labels
  180. labelFrame.pack(fill = 'x', expand = 0)
  181. # Create the toplevel notebook pages
  182. self.mainNotebook = Pmw.NoteBook(interior)
  183. self.mainNotebook.pack(fill = BOTH, expand = 1)
  184. systemPage = self.mainNotebook.add('System')
  185. factoryPage = self.mainNotebook.add('Factory')
  186. emitterPage = self.mainNotebook.add('Emitter')
  187. rendererPage = self.mainNotebook.add('Renderer')
  188. forcePage = self.mainNotebook.add('Force')
  189. # Put this here so it isn't called right away
  190. self.mainNotebook['raisecommand'] = self.updateInfo
  191. ## SYSTEM PAGE WIDGETS ##
  192. # Create system floaters
  193. systemFloaterDefs = (
  194. ('System', 'Pool Size',
  195. 'Max number of simultaneous particles',
  196. self.setSystemPoolSize,
  197. 1.0, 1.0),
  198. ('System', 'Birth Rate',
  199. 'Seconds between particle births',
  200. self.setSystemBirthRate,
  201. 0.0, None),
  202. ('System', 'Litter Size',
  203. 'Number of particle created at each birth',
  204. self.setSystemLitterSize,
  205. 1.0, 1.0),
  206. ('System', 'Litter Spread',
  207. 'Variation in litter size',
  208. self.setSystemLitterSpread,
  209. 0.0, 1.0),
  210. ('System', 'Lifespan',
  211. 'Age in seconds at which the system (vs. particles) should die',
  212. self.setSystemLifespan,
  213. 0.0, None)
  214. )
  215. self.createFloaters(systemPage, systemFloaterDefs)
  216. # Checkboxes
  217. self.createCheckbutton(
  218. systemPage, 'System', 'Render Space Velocities',
  219. ('On: velocities are in render space; ' +
  220. 'Off: velocities are in particle local space'),
  221. self.toggleSystemLocalVelocity, 0)
  222. self.createCheckbutton(
  223. systemPage, 'System', 'System Grows Older',
  224. 'On: system has a lifespan',
  225. self.toggleSystemGrowsOlder, 0)
  226. # Vector widgets
  227. pos = self.createVector3Entry(systemPage, 'System', 'Pos',
  228. 'Particle system position',
  229. command = self.setSystemPos)
  230. pos.addMenuItem('Popup Placer Panel', sePlacer.Placer)
  231. hpr = self.createVector3Entry(systemPage, 'System', 'Hpr',
  232. 'Particle system orientation',
  233. fGroup_labels = ('H', 'P', 'R'),
  234. command = self.setSystemHpr)
  235. hpr.addMenuItem('Popup Placer Panel', sePlacer.Placer)
  236. ## FACTORY PAGE WIDGETS ##
  237. self.createOptionMenu(
  238. factoryPage,
  239. 'Factory', 'Factory Type',
  240. 'Select type of particle factory',
  241. ('PointParticleFactory', 'ZSpinParticleFactory',
  242. 'OrientedParticleFactory'),
  243. self.selectFactoryType)
  244. factoryWidgets = (
  245. ('Factory', 'Life Span',
  246. 'Average particle lifespan in seconds',
  247. self.setFactoryLifeSpan,
  248. 0.0, None),
  249. ('Factory', 'Life Span Spread',
  250. 'Variation in lifespan',
  251. self.setFactoryLifeSpanSpread,
  252. 0.0, None),
  253. ('Factory', 'Mass',
  254. 'Average particle mass',
  255. self.setFactoryParticleMass,
  256. 0.001, None),
  257. ('Factory', 'Mass Spread',
  258. 'Variation in particle mass',
  259. self.setFactoryParticleMassSpread,
  260. 0.0, None),
  261. ('Factory', 'Terminal Velocity',
  262. 'Cap on average particle velocity',
  263. self.setFactoryTerminalVelocity,
  264. 0.0, None),
  265. ('Factory', 'Terminal Vel. Spread',
  266. 'Variation in terminal velocity',
  267. self.setFactoryTerminalVelocitySpread,
  268. 0.0, None),
  269. )
  270. self.createFloaters(factoryPage, factoryWidgets)
  271. self.factoryNotebook = Pmw.NoteBook(factoryPage, tabpos = None)
  272. # Point page #
  273. factoryPointPage = self.factoryNotebook.add('PointParticleFactory')
  274. # Z spin page #
  275. zSpinPage = self.factoryNotebook.add('ZSpinParticleFactory')
  276. self.createCheckbutton(
  277. zSpinPage, 'Z Spin Factory', 'Enable Angular Velocity',
  278. ("On: angular velocity is used; " +
  279. "Off: final angle is used"),
  280. self.toggleAngularVelocity, 0, side = TOP),
  281. self.createFloater(
  282. zSpinPage, 'Z Spin Factory', 'Angular Velocity',
  283. 'How fast sprites rotate',
  284. command = self.setFactoryZSpinAngularVelocity)
  285. self.createFloater(
  286. zSpinPage, 'Z Spin Factory', 'Angular Velocity Spread',
  287. 'Variation in how fast sprites rotate',
  288. command = self.setFactoryZSpinAngularVelocitySpread)
  289. self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Initial Angle',
  290. 'Starting angle in degrees',
  291. fRollover = 1,
  292. command = self.setFactoryZSpinInitialAngle)
  293. self.createAngleDial(
  294. zSpinPage, 'Z Spin Factory',
  295. 'Initial Angle Spread',
  296. 'Spread of the initial angle',
  297. fRollover = 1,
  298. command = self.setFactoryZSpinInitialAngleSpread)
  299. self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Final Angle',
  300. 'Final angle in degrees',
  301. fRollover = 1,
  302. command = self.setFactoryZSpinFinalAngle)
  303. self.createAngleDial(
  304. zSpinPage, 'Z Spin Factory',
  305. 'Final Angle Spread',
  306. 'Spread of the final angle',
  307. fRollover = 1,
  308. command = self.setFactoryZSpinFinalAngleSpread)
  309. # Oriented page #
  310. orientedPage = self.factoryNotebook.add('OrientedParticleFactory')
  311. Label(orientedPage, text = 'Not implemented').pack(expand = 1,
  312. fill = BOTH)
  313. self.factoryNotebook.pack(expand = 1, fill = BOTH)
  314. ## EMITTER PAGE WIDGETS ##
  315. self.createOptionMenu(
  316. emitterPage, 'Emitter',
  317. 'Emitter Type',
  318. 'Select type of particle emitter',
  319. ('BoxEmitter', 'DiscEmitter', 'LineEmitter', 'PointEmitter',
  320. 'RectangleEmitter', 'RingEmitter', 'SphereVolumeEmitter',
  321. 'SphereSurfaceEmitter', 'TangentRingEmitter'),
  322. self.selectEmitterType)
  323. # Emitter modes
  324. self.emissionType = IntVar()
  325. self.emissionType.set(BaseParticleEmitter.ETRADIATE)
  326. emissionFrame = Frame(emitterPage)
  327. self.createRadiobutton(
  328. emissionFrame, 'left',
  329. 'Emitter', 'Explicit Emission',
  330. ('particles are all emitted in parallel, direction is based ' +
  331. 'on explicit velocity vector'),
  332. self.emissionType, BaseParticleEmitter.ETEXPLICIT,
  333. self.setEmissionType)
  334. self.createRadiobutton(
  335. emissionFrame, 'left',
  336. 'Emitter', 'Radiate Emission',
  337. 'particles are emitted away from a specific point',
  338. self.emissionType, BaseParticleEmitter.ETRADIATE,
  339. self.setEmissionType)
  340. self.createRadiobutton(
  341. emissionFrame, 'left',
  342. 'Emitter', 'Custom Emission',
  343. ('particles are emitted with a velocity that ' +
  344. 'is determined by the particular emitter'),
  345. self.emissionType, BaseParticleEmitter.ETCUSTOM,
  346. self.setEmissionType)
  347. emissionFrame.pack(fill = 'x', expand = 0)
  348. self.createFloater(
  349. emitterPage, 'Emitter', 'Velocity Multiplier',
  350. 'launch velocity multiplier (all emission modes)',
  351. command = self.setEmitterAmplitude,
  352. min = None)
  353. self.createFloater(
  354. emitterPage, 'Emitter', 'Velocity Multiplier Spread',
  355. 'spread for launch velocity multiplier (all emission modes)',
  356. command = self.setEmitterAmplitudeSpread)
  357. self.createVector3Entry(
  358. emitterPage, 'Emitter', 'Offset Velocity',
  359. 'Velocity vector applied to all particles',
  360. command = self.setEmitterOffsetForce)
  361. self.createVector3Entry(
  362. emitterPage, 'Emitter', 'Explicit Velocity',
  363. 'all particles launch with this velocity in Explicit mode',
  364. command = self.setEmitterExplicitLaunchVector)
  365. self.createVector3Entry(
  366. emitterPage, 'Emitter', 'Radiate Origin',
  367. 'particles launch away from this point in Radiate mode',
  368. command = self.setEmitterRadiateOrigin)
  369. self.emitterNotebook = Pmw.NoteBook(emitterPage, tabpos = None)
  370. # Box page #
  371. boxPage = self.emitterNotebook.add('BoxEmitter')
  372. self.createVector3Entry(boxPage, 'Box Emitter', 'Min',
  373. 'Min point defining emitter box',
  374. command = self.setEmitterBoxPoint1)
  375. self.createVector3Entry(boxPage, 'Box Emitter', 'Max',
  376. 'Max point defining emitter box',
  377. command = self.setEmitterBoxPoint2,
  378. value = (1.0, 1.0, 1.0))
  379. # Disc page #
  380. discPage = self.emitterNotebook.add('DiscEmitter')
  381. self.createFloater(discPage, 'Disc Emitter', 'Radius',
  382. 'Radius of disc',
  383. command = self.setEmitterDiscRadius,
  384. min = 0.01)
  385. customPage = self.discCustomFrame = Frame(discPage)
  386. self.createAngleDial(customPage, 'Disc Emitter', 'Inner Angle',
  387. 'Particle launch angle at center of disc',
  388. command = self.setEmitterDiscInnerAngle)
  389. self.createFloater(customPage, 'Disc Emitter', 'Inner Velocity',
  390. 'Launch velocity multiplier at center of disc',
  391. command = self.setEmitterDiscInnerVelocity)
  392. self.createAngleDial(customPage, 'Disc Emitter', 'Outer Angle',
  393. 'Particle launch angle at outer edge of disc',
  394. command = self.setEmitterDiscOuterAngle)
  395. self.createFloater(customPage, 'Disc Emitter', 'Outer Velocity',
  396. 'Launch velocity multiplier at edge of disc',
  397. command = self.setEmitterDiscOuterVelocity)
  398. self.createCheckbutton(
  399. customPage, 'Disc Emitter', 'Cubic Lerping',
  400. 'On: magnitude/angle interpolation from center',
  401. self.toggleEmitterDiscCubicLerping, 0)
  402. customPage.pack(fill = BOTH, expand = 1)
  403. # Line page #
  404. linePage = self.emitterNotebook.add('LineEmitter')
  405. self.createVector3Entry(linePage, 'Line Emitter', 'Min',
  406. 'Min point defining emitter line',
  407. command = self.setEmitterLinePoint1)
  408. self.createVector3Entry(linePage, 'Line Emitter', 'Max',
  409. 'Max point defining emitter line',
  410. command = self.setEmitterLinePoint2,
  411. value = (1.0, 0.0, 0.0))
  412. # Point page #
  413. emitterPointPage = self.emitterNotebook.add('PointEmitter')
  414. self.createVector3Entry(emitterPointPage, 'Point Emitter', 'Position',
  415. 'Position of emitter point',
  416. command = self.setEmitterPointPosition)
  417. # Rectangle #
  418. rectanglePage = self.emitterNotebook.add('RectangleEmitter')
  419. self.createVector2Entry(rectanglePage,
  420. 'Rectangle Emitter', 'Min',
  421. 'Point defining rectangle',
  422. command = self.setEmitterRectanglePoint1)
  423. self.createVector2Entry(rectanglePage,
  424. 'Rectangle Emitter', 'Max',
  425. 'Point defining rectangle',
  426. command = self.setEmitterRectanglePoint2)
  427. # Ring #
  428. ringPage = self.emitterNotebook.add('RingEmitter')
  429. self.createFloater(ringPage, 'Ring Emitter', 'Radius',
  430. 'Radius of ring',
  431. command = self.setEmitterRingRadius,
  432. min = 0.01)
  433. self.ringCustomFrame = Frame(ringPage)
  434. self.createAngleDial(self.ringCustomFrame, 'Ring Emitter', 'Angle',
  435. 'Particle launch angle',
  436. command = self.setEmitterRingLaunchAngle)
  437. self.ringCustomFrame.pack(fill = BOTH, expand = 1)
  438. # Sphere volume #
  439. sphereVolumePage = self.emitterNotebook.add('SphereVolumeEmitter')
  440. self.createFloater(sphereVolumePage, 'Sphere Volume Emitter', 'Radius',
  441. 'Radius of sphere',
  442. command = self.setEmitterSphereVolumeRadius,
  443. min = 0.01)
  444. # Sphere surface #
  445. sphereSurfacePage = self.emitterNotebook.add('SphereSurfaceEmitter')
  446. self.createFloater(sphereSurfacePage, 'Sphere Surface Emitter',
  447. 'Radius',
  448. 'Radius of sphere',
  449. command = self.setEmitterSphereSurfaceRadius,
  450. min = 0.01)
  451. # Tangent ring #
  452. tangentRingPage = self.emitterNotebook.add('TangentRingEmitter')
  453. self.createFloater(tangentRingPage, 'Tangent Ring Emitter', 'Radius',
  454. 'Radius of ring',
  455. command = self.setEmitterTangentRingRadius,
  456. min = 0.01)
  457. self.emitterNotebook.pack(fill = X)
  458. ## RENDERER PAGE WIDGETS ##
  459. self.createOptionMenu(
  460. rendererPage, 'Renderer', 'Renderer Type',
  461. 'Select type of particle renderer',
  462. ('LineParticleRenderer', 'GeomParticleRenderer',
  463. 'PointParticleRenderer', 'SparkleParticleRenderer',
  464. 'SpriteParticleRenderer'),
  465. self.selectRendererType)
  466. self.createOptionMenu(rendererPage,
  467. 'Renderer', 'Alpha Mode',
  468. "alpha setting over particles' lifetime",
  469. ('NO_ALPHA','ALPHA_OUT','ALPHA_IN','ALPHA_USER'),
  470. self.setRendererAlphaMode)
  471. self.createSlider(
  472. rendererPage, 'Renderer', 'User Alpha',
  473. 'alpha value for ALPHA_USER alpha mode',
  474. command = self.setRendererUserAlpha)
  475. self.rendererNotebook = Pmw.NoteBook(rendererPage, tabpos = None)
  476. # Line page #
  477. linePage = self.rendererNotebook.add('LineParticleRenderer')
  478. self.createColorEntry(linePage, 'Line Renderer', 'Head Color',
  479. 'Head color of line',
  480. command = self.setRendererLineHeadColor)
  481. self.createColorEntry(linePage, 'Line Renderer', 'Tail Color',
  482. 'Tail color of line',
  483. command = self.setRendererLineTailColor)
  484. # Geom page #
  485. geomPage = self.rendererNotebook.add('GeomParticleRenderer')
  486. f = Frame(geomPage)
  487. f.pack(fill = X)
  488. Label(f, width = 12, text = 'Geom Node').pack(side = LEFT)
  489. self.rendererGeomNode = StringVar()
  490. self.rendererGeomNodeEntry = Entry(
  491. f, width = 12,
  492. textvariable = self.rendererGeomNode)
  493. self.rendererGeomNodeEntry.bind('<Return>', self.setRendererGeomNode)
  494. self.rendererGeomNodeEntry.pack(side = LEFT, expand = 1, fill = X)
  495. # Point #
  496. rendererPointPage = self.rendererNotebook.add('PointParticleRenderer')
  497. self.createFloater(rendererPointPage, 'Point Renderer', 'Point Size',
  498. 'Width and height of points in pixels',
  499. command = self.setRendererPointSize)
  500. self.createColorEntry(rendererPointPage, 'Point Renderer',
  501. 'Start Color',
  502. 'Starting color of point',
  503. command = self.setRendererPointStartColor)
  504. self.createColorEntry(rendererPointPage, 'Point Renderer',
  505. 'End Color',
  506. 'Ending color of point',
  507. command = self.setRendererPointEndColor)
  508. self.createOptionMenu(rendererPointPage, 'Point Renderer',
  509. 'Blend Type',
  510. 'Type of color blending used for particle',
  511. ('PP_ONE_COLOR', 'PP_BLEND_LIFE',
  512. 'PP_BLEND_VEL'),
  513. self.rendererPointSelectBlendType)
  514. self.createOptionMenu(rendererPointPage, 'Point Renderer',
  515. 'Blend Method',
  516. 'Interpolation method between colors',
  517. ('PP_NO_BLEND', 'PP_BLEND_LINEAR',
  518. 'PP_BLEND_CUBIC'),
  519. self.rendererPointSelectBlendMethod)
  520. # Sparkle #
  521. sparklePage = self.rendererNotebook.add('SparkleParticleRenderer')
  522. self.createColorEntry(sparklePage, 'Sparkle Renderer',
  523. 'Center Color',
  524. 'Color of sparkle center',
  525. command = self.setRendererSparkleCenterColor)
  526. self.createColorEntry(sparklePage, 'Sparkle Renderer',
  527. 'Edge Color',
  528. 'Color of sparkle line endpoints',
  529. command = self.setRendererSparkleEdgeColor)
  530. self.createFloater(sparklePage, 'Sparkle Renderer',
  531. 'Birth Radius',
  532. 'Initial sparkle radius',
  533. command = self.setRendererSparkleBirthRadius)
  534. self.createFloater(sparklePage, 'Sparkle Renderer',
  535. 'Death Radius',
  536. 'Final sparkle radius',
  537. command = self.setRendererSparkleDeathRadius)
  538. self.createOptionMenu(sparklePage,
  539. 'Sparkle Renderer', 'Life Scale',
  540. 'Does particle scale over its lifetime?',
  541. ('SP_NO_SCALE', 'SP_SCALE'),
  542. self.setRendererSparkleLifeScale)
  543. # Sprite #
  544. spritePage = self.rendererNotebook.add('SpriteParticleRenderer')
  545. f = Frame(spritePage)
  546. Label(f, width = 12, text = 'Texture Type:').pack(side = LEFT)
  547. self.rendererSpriteSourceType = IntVar()
  548. self.rendererSpriteSourceType.set(SpriteParticleRenderer.STTexture)
  549. self.rendererSpriteSTTexture = self.createRadiobutton(
  550. f, 'left',
  551. 'Sprite Renderer', 'Texture Type',
  552. 'Sprite particle renderer created from texture file',
  553. self.rendererSpriteSourceType, SpriteParticleRenderer.STTexture,
  554. self.setSpriteSourceType)
  555. self.rendererSpriteSTTexture = self.createRadiobutton(
  556. f, 'left',
  557. 'Sprite Renderer', 'NodePath Type',
  558. 'Sprite particle renderer created from node path',
  559. self.rendererSpriteSourceType, SpriteParticleRenderer.STFromNode,
  560. self.setSpriteSourceType)
  561. f.pack(fill = X)
  562. f = Frame(spritePage)
  563. Label(f, width = 6, text = 'Texture:').pack(side = LEFT)
  564. self.rendererSpriteTexture = StringVar()
  565. self.rendererSpriteTexture.set(SpriteParticleRenderer.sourceTextureName)
  566. self.rendererSpriteTextureEntry = Entry(
  567. f, width = 12,
  568. textvariable = self.rendererSpriteTexture)
  569. self.rendererSpriteTextureEntry.pack(side = LEFT, expand = 1, fill = X)
  570. f.pack(fill = X)
  571. f = Frame(spritePage)
  572. Label(f, width = 6, text = 'File:').pack(side = LEFT)
  573. self.rendererSpriteFile = StringVar()
  574. self.rendererSpriteFile.set(SpriteParticleRenderer.sourceFileName)
  575. self.rendererSpriteFileEntry = Entry(
  576. f, width = 12,
  577. textvariable = self.rendererSpriteFile)
  578. self.rendererSpriteFileEntry.pack(side = LEFT, expand = 1, fill = X)
  579. Label(f, width = 6, text = 'Node:').pack(side = LEFT)
  580. self.rendererSpriteNode = StringVar()
  581. self.rendererSpriteNode.set(SpriteParticleRenderer.sourceNodeName)
  582. self.rendererSpriteNodeEntry = Entry(
  583. f, width = 6,
  584. textvariable = self.rendererSpriteNode)
  585. self.rendererSpriteNodeEntry.pack(side = LEFT, expand = 1, fill = X)
  586. f.pack(fill = X)
  587. # Init entries
  588. self.setSpriteSourceType()
  589. self.setTextureButton = Button(spritePage, text = 'Set Texture',
  590. command = self.setRendererSpriteTexture)
  591. self.setTextureButton.pack(fill = X)
  592. f = Frame(spritePage)
  593. self.createCheckbutton(
  594. f, 'Sprite Renderer', 'X Scale',
  595. ("On: x scale is interpolated over particle's life; " +
  596. "Off: stays as start_X_Scale"),
  597. self.toggleRendererSpriteXScale, 0, side = LEFT)
  598. self.createCheckbutton(
  599. f, 'Sprite Renderer', 'Y Scale',
  600. ("On: y scale is interpolated over particle's life; " +
  601. "Off: stays as start_Y_Scale"),
  602. self.toggleRendererSpriteYScale, 0, side = LEFT)
  603. self.createCheckbutton(
  604. f, 'Sprite Renderer', 'Anim Angle',
  605. ("On: particles that are set to spin on the Z axis will " +
  606. "spin appropriately"),
  607. self.toggleRendererSpriteAnimAngle, 0, side = LEFT)
  608. f.pack(fill = X)
  609. self.createFloater(spritePage, 'Sprite Renderer',
  610. 'Initial X Scale',
  611. 'Initial X scaling factor',
  612. command = self.setRendererSpriteInitialXScale)
  613. self.createFloater(spritePage, 'Sprite Renderer',
  614. 'Final X Scale',
  615. 'Final X scaling factor, if xScale enabled',
  616. command = self.setRendererSpriteFinalXScale)
  617. self.createFloater(spritePage, 'Sprite Renderer',
  618. 'Initial Y Scale',
  619. 'Initial Y scaling factor',
  620. command = self.setRendererSpriteInitialYScale)
  621. self.createFloater(spritePage, 'Sprite Renderer',
  622. 'Final Y Scale',
  623. 'Final Y scaling factor, if yScale enabled',
  624. command = self.setRendererSpriteFinalYScale)
  625. self.createAngleDial(spritePage, 'Sprite Renderer',
  626. 'Non Animated Theta',
  627. ('If animAngle is false: counter clockwise ' +
  628. 'Z rotation of all sprites'),
  629. command = self.setRendererSpriteNonAnimatedTheta)
  630. self.createOptionMenu(spritePage, 'Sprite Renderer',
  631. 'Blend Type',
  632. 'Interpolation blend type for X and Y scaling',
  633. ('PP_NO_BLEND', 'PP_LINEAR', 'PP_CUBIC'),
  634. self.setRendererSpriteBlendMethod)
  635. self.createCheckbutton(
  636. spritePage, 'Sprite Renderer', 'Alpha Disable',
  637. 'On: alpha blending is disabled',
  638. self.toggleRendererSpriteAlphaDisable, 0)
  639. self.rendererNotebook.pack(fill = X)
  640. ## FORCE PAGE WIDGETS ##
  641. self.addForceButton = Menubutton(forcePage, text = 'Add Force',
  642. relief = RAISED,
  643. borderwidth = 2,
  644. font=('MSSansSerif', 14, 'bold'),
  645. activebackground = '#909090')
  646. forceMenu = Menu(self.addForceButton)
  647. self.addForceButton['menu'] = forceMenu
  648. # DERIVED FROM LINEAR FORCE
  649. # This also has: setVector
  650. forceMenu.add_command(label = 'Add Linear Vector Force',
  651. command = self.addLinearVectorForce)
  652. # Parameters: setAmplitude, setMassDependent, setVectorMasks
  653. forceMenu.add_command(label = 'Add Linear Noise Force',
  654. command = self.addLinearNoiseForce)
  655. forceMenu.add_command(label = 'Add Linear Jitter Force',
  656. command = self.addLinearJitterForce)
  657. # This also has setCoef
  658. forceMenu.add_command(label = 'Add Linear Friction Force',
  659. command = self.addLinearFrictionForce)
  660. # This also has: setCoef, setLength, setRadius,
  661. forceMenu.add_command(label = 'Add Linear Cylinder Vortex Force',
  662. command = self.addLinearCylinderVortexForce)
  663. # DERIVED FROM LINEAR DISTANCE FORCE
  664. # Parameters: setFalloffType, setForceCenter, setRadius
  665. forceMenu.add_command(label = 'Add Linear Sink Force',
  666. command = self.addLinearSinkForce)
  667. forceMenu.add_command(label = 'Add Linear Source Force',
  668. command = self.addLinearSourceForce)
  669. """
  670. # Avoid for now
  671. forceMenu.add_command(label = 'Add Linear User Defined Force',
  672. command = self.addLinearUserDefinedForce)
  673. """
  674. self.addForceButton.pack(expand = 0)
  675. # Scrolled frame to hold force widgets
  676. self.sf = Pmw.ScrolledFrame(forcePage, horizflex = 'elastic')
  677. self.sf.pack(fill = 'both', expand = 1)
  678. self.forceFrame = self.sf.interior()
  679. # Notebook to hold force widgets as the are added
  680. self.forceGroupNotebook = Pmw.NoteBook(self.forceFrame, tabpos = None)
  681. self.forceGroupNotebook.pack(fill = X)
  682. self.factoryNotebook.setnaturalsize()
  683. self.emitterNotebook.setnaturalsize()
  684. self.rendererNotebook.setnaturalsize()
  685. self.forceGroupNotebook.setnaturalsize()
  686. self.mainNotebook.setnaturalsize()
  687. # Make sure input variables processed
  688. self.initialiseoptions(ParticlePanel)
  689. ### WIDGET UTILITY FUNCTIONS ###
  690. def createCheckbutton(self, parent, category, text,
  691. balloonHelp, command, initialState, side = 'top'):
  692. bool = BooleanVar()
  693. bool.set(initialState)
  694. widget = Checkbutton(parent, text = text, anchor = W,
  695. variable = bool)
  696. # Do this after the widget so command isn't called on creation
  697. widget['command'] = command
  698. widget.pack(fill = X, side = side)
  699. self.bind(widget, balloonHelp)
  700. self.widgetDict[category + '-' + text] = widget
  701. self.variableDict[category + '-' + text] = bool
  702. return widget
  703. def createRadiobutton(self, parent, side, category, text,
  704. balloonHelp, variable, value,
  705. command):
  706. widget = Radiobutton(parent, text = text, anchor = W,
  707. variable = variable, value = value)
  708. # Do this after the widget so command isn't called on creation
  709. widget['command'] = command
  710. widget.pack(side = side, fill = X)
  711. self.bind(widget, balloonHelp)
  712. self.widgetDict[category + '-' + text] = widget
  713. return widget
  714. def createFloaters(self, parent, widgetDefinitions):
  715. widgets = []
  716. for category, label, balloonHelp, command, min, resolution in widgetDefinitions:
  717. widgets.append(
  718. self.createFloater(parent, category, label, balloonHelp,
  719. command, min, resolution)
  720. )
  721. return widgets
  722. def createFloater(self, parent, category, text, balloonHelp,
  723. command = None, min = 0.0, resolution = None,
  724. numDigits = 3, **kw):
  725. kw['text'] = text
  726. kw['min'] = min
  727. kw['resolution'] = resolution
  728. kw['numDigits'] = numDigits
  729. widget = apply(Floater, (parent,), kw)
  730. # Do this after the widget so command isn't called on creation
  731. widget['command'] = command
  732. widget.pack(fill = X)
  733. self.bind(widget, balloonHelp)
  734. self.widgetDict[category + '-' + text] = widget
  735. return widget
  736. def createAngleDial(self, parent, category, text, balloonHelp,
  737. command = None, **kw):
  738. kw['text'] = text
  739. kw['style'] = 'mini'
  740. widget = apply(AngleDial,(parent,), kw)
  741. # Do this after the widget so command isn't called on creation
  742. widget['command'] = command
  743. widget.pack(fill = X)
  744. self.bind(widget, balloonHelp)
  745. self.widgetDict[category + '-' + text] = widget
  746. return widget
  747. def createSlider(self, parent, category, text, balloonHelp,
  748. command = None, min = 0.0, max = 1.0,
  749. resolution = 0.001, **kw):
  750. kw['text'] = text
  751. kw['min'] = min
  752. kw['max'] = max
  753. kw['resolution'] = resolution
  754. widget = apply(Slider, (parent,), kw)
  755. # Do this after the widget so command isn't called on creation
  756. widget['command'] = command
  757. widget.pack(fill = X)
  758. self.bind(widget, balloonHelp)
  759. self.widgetDict[category + '-' + text] = widget
  760. return widget
  761. def createVector2Entry(self, parent, category, text, balloonHelp,
  762. command = None, **kw):
  763. # Set label's text
  764. kw['text'] = text
  765. widget = apply(Vector2Entry, (parent,), kw)
  766. # Do this after the widget so command isn't called on creation
  767. widget['command'] = command
  768. widget.pack(fill = X)
  769. self.bind(widget, balloonHelp)
  770. self.widgetDict[category + '-' + text] = widget
  771. return widget
  772. def createVector3Entry(self, parent, category, text, balloonHelp,
  773. command = None, **kw):
  774. # Set label's text
  775. kw['text'] = text
  776. widget = apply(Vector3Entry, (parent,), kw)
  777. # Do this after the widget so command isn't called on creation
  778. widget['command'] = command
  779. widget.pack(fill = X)
  780. self.bind(widget, balloonHelp)
  781. self.widgetDict[category + '-' + text] = widget
  782. return widget
  783. def createColorEntry(self, parent, category, text, balloonHelp,
  784. command = None, **kw):
  785. # Set label's text
  786. kw['text'] = text
  787. widget = apply(ColorEntry, (parent,) ,kw)
  788. # Do this after the widget so command isn't called on creation
  789. widget['command'] = command
  790. widget.pack(fill = X)
  791. self.bind(widget, balloonHelp)
  792. self.widgetDict[category + '-' + text] = widget
  793. return widget
  794. def createOptionMenu(self, parent, category, text, balloonHelp,
  795. items, command):
  796. optionVar = StringVar()
  797. if len(items) > 0:
  798. optionVar.set(items[0])
  799. widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text,
  800. label_width = 12, menu_tearoff = 1,
  801. menubutton_textvariable = optionVar,
  802. items = items)
  803. # Do this after the widget so command isn't called on creation
  804. widget['command'] = command
  805. widget.pack(fill = X)
  806. self.bind(widget.component('menubutton'), balloonHelp)
  807. self.widgetDict[category + '-' + text] = widget
  808. self.variableDict[category + '-' + text] = optionVar
  809. return optionVar
  810. def createComboBox(self, parent, category, text, balloonHelp,
  811. items, command, history = 0):
  812. widget = Pmw.ComboBox(parent,
  813. labelpos = W,
  814. label_text = text,
  815. label_anchor = 'w',
  816. label_width = 12,
  817. entry_width = 16,
  818. history = history,
  819. scrolledlist_items = items)
  820. # Don't allow user to edit entryfield
  821. widget.configure(entryfield_entry_state = 'disabled')
  822. # Select first item if it exists
  823. if len(items) > 0:
  824. widget.selectitem(items[0])
  825. # Bind selection command
  826. widget['selectioncommand'] = command
  827. widget.pack(side = 'left', expand = 0)
  828. # Bind help
  829. self.bind(widget, balloonHelp)
  830. # Record widget
  831. self.widgetDict[category + '-' + text] = widget
  832. return widget
  833. def updateMenusAndLabels(self):
  834. self.updateMenus()
  835. self.updateLabels()
  836. def updateLabels(self):
  837. self.effectsLabel['text'] = self.particleEffect.getName()
  838. self.particlesLabel['text'] = self.particles.getName()
  839. if self.forceGroup != None:
  840. self.forceGroupLabel['text'] = self.forceGroup.getName()
  841. else:
  842. self.forceGroupLabel['text'] = 'Force Group'
  843. def updateMenus(self):
  844. self.updateEffectsMenus()
  845. self.updateParticlesMenus()
  846. self.updateForceGroupMenus()
  847. def updateEffectsMenus(self):
  848. # Get rid of old effects entries if any
  849. self.effectsEnableMenu.delete(0, 'end')
  850. self.effectsLabelMenu.delete(5, 'end')
  851. self.effectsLabelMenu.add_separator()
  852. # Add in a checkbutton for each effect (to toggle on/off)
  853. keys = self.effectsDict.keys()
  854. keys.sort()
  855. for name in keys:
  856. effect = self.effectsDict[name]
  857. self.effectsLabelMenu.add_command(
  858. label = effect.getName(),
  859. command = (lambda s = self,
  860. e = effect: s.selectEffectNamed(e.getName()))
  861. )
  862. effectActive = IntVar()
  863. effectActive.set(effect.isEnabled())
  864. self.effectsEnableMenu.add_checkbutton(
  865. label = effect.getName(),
  866. variable = effectActive,
  867. command = (lambda s = self,
  868. e = effect,
  869. v = effectActive: s.toggleEffect(e, v)))
  870. def updateParticlesMenus(self):
  871. # Get rid of old particles entries if any
  872. self.particlesEnableMenu.delete(0, 'end')
  873. self.particlesLabelMenu.delete(2, 'end')
  874. self.particlesLabelMenu.add_separator()
  875. # Add in a checkbutton for each effect (to toggle on/off)
  876. particles = self.particleEffect.getParticlesList()
  877. names = map(lambda x: x.getName(), particles)
  878. names.sort()
  879. for name in names:
  880. particle = self.particleEffect.getParticlesNamed(name)
  881. self.particlesLabelMenu.add_command(
  882. label = name,
  883. command = (lambda s = self,
  884. n = name: s.selectParticlesNamed(n))
  885. )
  886. particleActive = IntVar()
  887. particleActive.set(particle.isEnabled())
  888. self.particlesEnableMenu.add_checkbutton(
  889. label = name,
  890. variable = particleActive,
  891. command = (lambda s = self,
  892. p = particle,
  893. v = particleActive: s.toggleParticles(p, v)))
  894. def updateForceGroupMenus(self):
  895. # Get rid of old forceGroup entries if any
  896. self.forceGroupEnableMenu.delete(0, 'end')
  897. self.forceGroupLabelMenu.delete(2, 'end')
  898. self.forceGroupLabelMenu.add_separator()
  899. # Add in a checkbutton for each effect (to toggle on/off)
  900. forceGroupList = self.particleEffect.getForceGroupList()
  901. names = map(lambda x: x.getName(), forceGroupList)
  902. names.sort()
  903. for name in names:
  904. force = self.particleEffect.getForceGroupNamed(name)
  905. self.forceGroupLabelMenu.add_command(
  906. label = name,
  907. command = (lambda s = self,
  908. n = name: s.selectForceGroupNamed(n))
  909. )
  910. forceActive = IntVar()
  911. forceActive.set(force.isEnabled())
  912. self.forceGroupEnableMenu.add_checkbutton(
  913. label = name,
  914. variable = forceActive,
  915. command = (lambda s = self,
  916. f = force,
  917. v = forceActive: s.toggleForceGroup(f, v)))
  918. def selectEffectNamed(self, name):
  919. effect = self.effectsDict.get(name, None)
  920. if effect != None:
  921. self.particleEffect = effect
  922. # Default to first particle in particlesDict
  923. self.particles = self.particleEffect.getParticlesList()[0]
  924. # See if particle effect has any forceGroup
  925. forceGroupList = self.particleEffect.getForceGroupList()
  926. if len(forceGroupList) > 0:
  927. self.forceGroup = forceGroupList[0]
  928. else:
  929. self.forceGroup = None
  930. self.mainNotebook.selectpage('System')
  931. self.updateInfo('System')
  932. else:
  933. print 'ParticlePanel: No effect named ' + name
  934. def toggleEffect(self, effect, var):
  935. if var.get():
  936. effect.enable()
  937. else:
  938. effect.disable()
  939. def selectParticlesNamed(self, name):
  940. particles = self.particleEffect.getParticlesNamed(name)
  941. if particles != None:
  942. self.particles = particles
  943. self.updateInfo()
  944. def toggleParticles(self, particles, var):
  945. if var.get():
  946. particles.enable()
  947. else:
  948. particles.disable()
  949. def selectForceGroupNamed(self, name):
  950. forceGroup = self.particleEffect.getForceGroupNamed(name)
  951. if forceGroup != None:
  952. self.forceGroup = forceGroup
  953. self.updateInfo('Force')
  954. def toggleForceGroup(self, forceGroup, var):
  955. if var.get():
  956. forceGroup.enable()
  957. else:
  958. forceGroup.disable()
  959. def toggleForce(self, force, pageName, variableName):
  960. v = self.getVariable(pageName, variableName)
  961. if v.get():
  962. force.setActive(1)
  963. else:
  964. force.setActive(0)
  965. def getWidget(self, category, text):
  966. return self.widgetDict[category + '-' + text]
  967. def getVariable(self, category, text):
  968. return self.variableDict[category + '-' + text]
  969. def loadParticleEffectFromFile(self):
  970. # Find path to particle directory
  971. pPath = getParticlePath()
  972. if pPath.getNumDirectories() > 0:
  973. if `pPath.getDirectory(0)` == '.':
  974. path = '.'
  975. else:
  976. path = pPath.getDirectory(0).toOsSpecific()
  977. else:
  978. path = '.'
  979. if not os.path.isdir(path):
  980. print 'ParticlePanel Warning: Invalid default DNA directory!'
  981. print 'Using current directory'
  982. path = '.'
  983. particleFilename = askopenfilename(
  984. defaultextension = '.ptf',
  985. filetypes = (('Particle Files', '*.ptf'),('All files', '*')),
  986. initialdir = path,
  987. title = 'Load Particle Effect',
  988. parent = self.parent)
  989. if particleFilename:
  990. # Delete existing particles and forces
  991. self.particleEffect.loadConfig(
  992. Filename.fromOsSpecific(particleFilename))
  993. self.selectEffectNamed(self.particleEffect.getName())
  994. # Enable effect
  995. self.particleEffect.enable()
  996. messenger.send('SGE_Update Explorer',[render])
  997. def saveParticleEffectToFile(self):
  998. # Find path to particle directory
  999. pPath = getParticlePath()
  1000. if pPath.getNumDirectories() > 0:
  1001. if `pPath.getDirectory(0)` == '.':
  1002. path = '.'
  1003. else:
  1004. path = pPath.getDirectory(0).toOsSpecific()
  1005. else:
  1006. path = '.'
  1007. if not os.path.isdir(path):
  1008. print 'ParticlePanel Warning: Invalid default DNA directory!'
  1009. print 'Using current directory'
  1010. path = '.'
  1011. particleFilename = asksaveasfilename(
  1012. defaultextension = '.ptf',
  1013. filetypes = (('Particle Files', '*.ptf'),('All files', '*')),
  1014. initialdir = path,
  1015. title = 'Save Particle Effect as',
  1016. parent = self.parent)
  1017. if particleFilename:
  1018. self.particleEffect.saveConfig(Filename(particleFilename))
  1019. ### PARTICLE EFFECTS COMMANDS ###
  1020. def toggleParticleMgr(self):
  1021. if self.particleMgrActive.get():
  1022. base.enableParticles()
  1023. else:
  1024. base.disableParticles()
  1025. ### PARTICLE SYSTEM COMMANDS ###
  1026. def updateInfo(self, page = 'System'):
  1027. self.updateMenusAndLabels()
  1028. if page == 'System':
  1029. self.updateSystemWidgets()
  1030. elif page == 'Factory':
  1031. self.selectFactoryPage()
  1032. self.updateFactoryWidgets()
  1033. elif page == 'Emitter':
  1034. self.selectEmitterPage()
  1035. self.updateEmitterWidgets()
  1036. elif page == 'Renderer':
  1037. self.selectRendererPage()
  1038. self.updateRendererWidgets()
  1039. elif page == 'Force':
  1040. self.updateForceWidgets()
  1041. def toggleParticleEffect(self):
  1042. if self.getVariable('Effect', 'Active').get():
  1043. self.particleEffect.enable()
  1044. else:
  1045. self.particleEffect.disable()
  1046. ## SYSTEM PAGE ##
  1047. def updateSystemWidgets(self):
  1048. poolSize = self.particles.getPoolSize()
  1049. self.getWidget('System', 'Pool Size').set(int(poolSize), 0)
  1050. birthRate = self.particles.getBirthRate()
  1051. self.getWidget('System', 'Birth Rate').set(birthRate, 0)
  1052. litterSize = self.particles.getLitterSize()
  1053. self.getWidget('System', 'Litter Size').set(int(litterSize), 0)
  1054. litterSpread = self.particles.getLitterSpread()
  1055. self.getWidget('System', 'Litter Spread').set(litterSpread, 0)
  1056. systemLifespan = self.particles.getSystemLifespan()
  1057. self.getWidget('System', 'Lifespan').set(systemLifespan, 0)
  1058. pos = self.particles.nodePath.getPos()
  1059. self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0)
  1060. hpr = self.particles.nodePath.getHpr()
  1061. self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0)
  1062. self.getVariable('System', 'Render Space Velocities').set(
  1063. self.particles.getLocalVelocityFlag())
  1064. self.getVariable('System', 'System Grows Older').set(
  1065. self.particles.getSystemGrowsOlderFlag())
  1066. def setSystemPoolSize(self, value):
  1067. self.particles.setPoolSize(int(value))
  1068. def setSystemBirthRate(self, value):
  1069. self.particles.setBirthRate(value)
  1070. def setSystemLitterSize(self, value):
  1071. self.particles.setLitterSize(int(value))
  1072. def setSystemLitterSpread(self, value):
  1073. self.particles.setLitterSpread(int(value))
  1074. def setSystemLifespan(self, value):
  1075. self.particles.setSystemLifespan(value)
  1076. def toggleSystemLocalVelocity(self):
  1077. self.particles.setLocalVelocityFlag(
  1078. self.getVariable('System', 'Render Space Velocities').get())
  1079. def toggleSystemGrowsOlder(self):
  1080. self.particles.setSystemGrowsOlderFlag(
  1081. self.getVariable('System', 'System Grows Older').get())
  1082. def setSystemPos(self, pos):
  1083. self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2]))
  1084. def setSystemHpr(self, pos):
  1085. self.particles.nodePath.setHpr(Vec3(pos[0], pos[1], pos[2]))
  1086. ## FACTORY PAGE ##
  1087. def selectFactoryType(self, type):
  1088. self.factoryNotebook.selectpage(type)
  1089. self.particles.setFactory(type)
  1090. self.updateFactoryWidgets()
  1091. def selectFactoryPage(self):
  1092. pass
  1093. def updateFactoryWidgets(self):
  1094. factory = self.particles.factory
  1095. lifespan = factory.getLifespanBase()
  1096. self.getWidget('Factory', 'Life Span').set(lifespan, 0)
  1097. lifespanSpread = factory.getLifespanSpread()
  1098. self.getWidget('Factory', 'Life Span Spread').set(lifespanSpread, 0)
  1099. mass = factory.getMassBase()
  1100. self.getWidget('Factory', 'Mass').set(mass, 0)
  1101. massSpread = factory.getMassSpread()
  1102. self.getWidget('Factory', 'Mass Spread').set(massSpread, 0)
  1103. terminalVelocity = factory.getTerminalVelocityBase()
  1104. self.getWidget('Factory', 'Terminal Velocity').set(terminalVelocity, 0)
  1105. terminalVelocitySpread = factory.getTerminalVelocitySpread()
  1106. self.getWidget('Factory', 'Terminal Vel. Spread').set(
  1107. terminalVelocitySpread, 0)
  1108. def setFactoryLifeSpan(self, value):
  1109. self.particles.factory.setLifespanBase(value)
  1110. def setFactoryLifeSpanSpread(self, value):
  1111. self.particles.factory.setLifespanSpread(value)
  1112. def setFactoryParticleMass(self, value):
  1113. self.particles.factory.setMassBase(value)
  1114. def setFactoryParticleMassSpread(self, value):
  1115. self.particles.factory.setMassSpread(value)
  1116. def setFactoryTerminalVelocity(self, value):
  1117. self.particles.factory.setTerminalVelocityBase(value)
  1118. def setFactoryTerminalVelocitySpread(self, value):
  1119. self.particles.factory.setTerminalVelocitySpread(value)
  1120. # Point Page #
  1121. # Z Spin Page #
  1122. def setFactoryZSpinInitialAngle(self, angle):
  1123. self.particles.factory.setInitialAngle(angle)
  1124. def setFactoryZSpinInitialAngleSpread(self, spread):
  1125. self.particles.factory.setInitialAngleSpread(spread)
  1126. def setFactoryZSpinFinalAngle(self, angle):
  1127. self.particles.factory.setFinalAngle(angle)
  1128. def setFactoryZSpinFinalAngleSpread(self, spread):
  1129. self.particles.factory.setFinalAngleSpread(spread)
  1130. def setFactoryZSpinAngularVelocity(self, vel):
  1131. self.particles.factory.setAngularVelocity(vel)
  1132. def setFactoryZSpinAngularVelocitySpread(self, spread):
  1133. self.particles.factory.setAngularVelocitySpread(spread)
  1134. ## EMITTER PAGE ##
  1135. def selectEmitterType(self, type):
  1136. self.emitterNotebook.selectpage(type)
  1137. self.particles.setEmitter(type)
  1138. self.updateEmitterWidgets()
  1139. def selectEmitterPage(self):
  1140. type = self.particles.emitter.__class__.__name__
  1141. self.emitterNotebook.selectpage(type)
  1142. self.getVariable('Emitter', 'Emitter Type').set(type)
  1143. def updateEmitterWidgets(self):
  1144. emitter = self.particles.emitter
  1145. self.setEmissionType(self.particles.emitter.getEmissionType())
  1146. amp = emitter.getAmplitude()
  1147. self.getWidget('Emitter', 'Velocity Multiplier').set(amp)
  1148. spread = emitter.getAmplitudeSpread()
  1149. self.getWidget('Emitter', 'Velocity Multiplier Spread').set(spread)
  1150. vec = emitter.getOffsetForce()
  1151. self.getWidget('Emitter', 'Offset Velocity').set(
  1152. [vec[0], vec[1], vec[2]], 0)
  1153. vec = emitter.getRadiateOrigin()
  1154. self.getWidget('Emitter', 'Radiate Origin').set(
  1155. [vec[0], vec[1], vec[2]], 0)
  1156. vec = emitter.getExplicitLaunchVector()
  1157. self.getWidget('Emitter', 'Explicit Velocity').set(
  1158. [vec[0], vec[1], vec[2]], 0)
  1159. if isinstance(emitter, BoxEmitter):
  1160. min = emitter.getMinBound()
  1161. self.getWidget('Box Emitter', 'Min').set(
  1162. [min[0], min[1], min[2]], 0)
  1163. max = emitter.getMaxBound()
  1164. self.getWidget('Box Emitter', 'Max').set(
  1165. [max[0], max[1], max[2]], 0)
  1166. elif isinstance(emitter, DiscEmitter):
  1167. radius = emitter.getRadius()
  1168. self.getWidget('Disc Emitter', 'Radius').set(radius, 0)
  1169. innerAngle = emitter.getInnerAngle()
  1170. self.getWidget('Disc Emitter', 'Inner Angle').set(innerAngle, 0)
  1171. innerMagnitude = emitter.getInnerMagnitude()
  1172. self.getWidget('Disc Emitter', 'Inner Velocity').set(
  1173. innerMagnitude, 0)
  1174. outerAngle = emitter.getOuterAngle()
  1175. self.getWidget('Disc Emitter', 'Outer Angle').set(outerAngle, 0)
  1176. outerMagnitude = emitter.getOuterMagnitude()
  1177. self.getWidget('Disc Emitter', 'Inner Velocity').set(
  1178. outerMagnitude, 0)
  1179. cubicLerping = emitter.getCubicLerping()
  1180. self.getVariable('Disc Emitter', 'Cubic Lerping').set(cubicLerping)
  1181. elif isinstance(emitter, LineEmitter):
  1182. min = emitter.getEndpoint1()
  1183. self.getWidget('Line Emitter', 'Min').set(
  1184. [min[0], min[1], min[2]], 0)
  1185. max = emitter.getEndpoint2()
  1186. self.getWidget('Line Emitter', 'Max').set(
  1187. [max[0], max[1], max[2]], 0)
  1188. elif isinstance(emitter, PointEmitter):
  1189. location = emitter.getLocation()
  1190. self.getWidget('Point Emitter', 'Position').set(
  1191. [location[0], location[1], location[2]], 0)
  1192. elif isinstance(emitter, RectangleEmitter):
  1193. min = emitter.getMinBound()
  1194. self.getWidget('Rectangle Emitter', 'Min').set(
  1195. [min[0], min[1]], 0)
  1196. max = emitter.getMaxBound()
  1197. self.getWidget('Rectangle Emitter', 'Max').set(
  1198. [max[0], max[1]], 0)
  1199. elif isinstance(emitter, RingEmitter):
  1200. radius = emitter.getRadius()
  1201. self.getWidget('Ring Emitter', 'Radius').set(radius, 0)
  1202. angle = emitter.getAngle()
  1203. self.getWidget('Ring Emitter', 'Angle').set(angle, 0)
  1204. elif isinstance(emitter, SphereVolumeEmitter):
  1205. radius = emitter.getRadius()
  1206. self.getWidget('Sphere Volume Emitter', 'Radius').set(radius, 0)
  1207. elif isinstance(emitter, SphereSurfaceEmitter):
  1208. radius = emitter.getRadius()
  1209. self.getWidget('Sphere Surface Emitter', 'Radius').set(radius, 0)
  1210. elif isinstance(emitter, TangentRingEmitter):
  1211. radius = emitter.getRadius()
  1212. self.getWidget('Tangent Ring Emitter', 'Radius').set(radius, 0)
  1213. # All #
  1214. def setEmissionType(self, newType = None):
  1215. if newType:
  1216. type = newType
  1217. self.emissionType.set(type)
  1218. else:
  1219. type = self.emissionType.get()
  1220. self.particles.emitter.setEmissionType(type)
  1221. if type == BaseParticleEmitter.ETEXPLICIT:
  1222. self.getWidget(
  1223. 'Emitter', 'Radiate Origin')['state'] = 'disabled'
  1224. self.getWidget(
  1225. 'Emitter', 'Explicit Velocity')['state'] = 'normal'
  1226. # Hide custom widgets
  1227. if isinstance(self.particles.emitter, DiscEmitter):
  1228. self.discCustomFrame.pack_forget()
  1229. elif isinstance(self.particles.emitter, RingEmitter):
  1230. self.ringCustomFrame.pack_forget()
  1231. elif type == BaseParticleEmitter.ETRADIATE:
  1232. self.getWidget(
  1233. 'Emitter', 'Radiate Origin')['state'] = 'normal'
  1234. self.getWidget(
  1235. 'Emitter', 'Explicit Velocity')['state'] = 'disabled'
  1236. # Hide custom widgets
  1237. if isinstance(self.particles.emitter, DiscEmitter):
  1238. self.discCustomFrame.pack_forget()
  1239. elif isinstance(self.particles.emitter, RingEmitter):
  1240. self.ringCustomFrame.pack_forget()
  1241. elif type == BaseParticleEmitter.ETCUSTOM:
  1242. self.getWidget(
  1243. 'Emitter', 'Radiate Origin')['state'] = 'disabled'
  1244. self.getWidget(
  1245. 'Emitter', 'Explicit Velocity')['state'] = 'disabled'
  1246. # Show custom widgets
  1247. if isinstance(self.particles.emitter, DiscEmitter):
  1248. self.discCustomFrame.pack(fill = BOTH, expand = 1)
  1249. elif isinstance(self.particles.emitter, RingEmitter):
  1250. self.ringCustomFrame.pack(fill = BOTH, expand = 1)
  1251. def setEmitterAmplitude(self, value):
  1252. self.particles.emitter.setAmplitude(value)
  1253. def setEmitterAmplitudeSpread(self, value):
  1254. self.particles.emitter.setAmplitudeSpread(value)
  1255. def setEmitterOffsetForce(self, vec):
  1256. self.particles.emitter.setOffsetForce(
  1257. Vec3(vec[0], vec[1], vec[2]))
  1258. def setEmitterRadiateOrigin(self, origin):
  1259. self.particles.emitter.setRadiateOrigin(
  1260. Point3(origin[0], origin[1], origin[2]))
  1261. def setEmitterExplicitLaunchVector(self, vec):
  1262. self.particles.emitter.setExplicitLaunchVector(
  1263. Vec3(vec[0], vec[1], vec[2]))
  1264. # Box #
  1265. def setEmitterBoxPoint1(self, point):
  1266. self.particles.emitter.setMinBound(Point3(point[0],
  1267. point[1],
  1268. point[2]))
  1269. def setEmitterBoxPoint2(self, point):
  1270. self.particles.emitter.setMaxBound(Point3(point[0],
  1271. point[1],
  1272. point[2]))
  1273. # Disc #
  1274. def setEmitterDiscRadius(self, radius):
  1275. self.particles.emitter.setRadius(radius)
  1276. def setEmitterDiscInnerAngle(self, angle):
  1277. self.particles.emitter.setInnerAngle(angle)
  1278. def setEmitterDiscInnerVelocity(self, velocity):
  1279. self.particles.emitter.setInnerMagnitude(velocity)
  1280. def setEmitterDiscOuterAngle(self, angle):
  1281. self.particles.emitter.setOuterAngle(angle)
  1282. def setEmitterDiscOuterVelocity(self, velocity):
  1283. self.particles.emitter.setOuterMagnitude(velocity)
  1284. def toggleEmitterDiscCubicLerping(self):
  1285. self.particles.emitter.setCubicLerping(
  1286. self.getVariable('Disc Emitter', 'Cubic Lerping').get())
  1287. # Line #
  1288. def setEmitterLinePoint1(self, point):
  1289. self.particles.emitter.setEndpoint1(Point3(point[0],
  1290. point[1],
  1291. point[2]))
  1292. def setEmitterLinePoint2(self, point):
  1293. self.particles.emitter.setEndpoint2(Point3(point[0],
  1294. point[1],
  1295. point[2]))
  1296. # Point #
  1297. def setEmitterPointPosition(self, pos):
  1298. self.particles.emitter.setLocation(Point3(pos[0], pos[1], pos[2]))
  1299. # Rectangle #
  1300. def setEmitterRectanglePoint1(self, point):
  1301. self.particles.emitter.setMinBound(Point2(point[0], point[1]))
  1302. def setEmitterRectanglePoint2(self, point):
  1303. self.particles.emitter.setMaxBound(Point2(point[0], point[1]))
  1304. # Ring #
  1305. def setEmitterRingRadius(self, radius):
  1306. self.particles.emitter.setRadius(radius)
  1307. def setEmitterRingLaunchAngle(self, angle):
  1308. self.particles.emitter.setAngle(angle)
  1309. # Sphere surface #
  1310. def setEmitterSphereSurfaceRadius(self, radius):
  1311. self.particles.emitter.setRadius(radius)
  1312. # Sphere volume #
  1313. def setEmitterSphereVolumeRadius(self, radius):
  1314. self.particles.emitter.setRadius(radius)
  1315. # Tangent ring #
  1316. def setEmitterTangentRingRadius(self, radius):
  1317. self.particles.emitter.setRadius(radius)
  1318. ## RENDERER PAGE ##
  1319. def selectRendererType(self, type):
  1320. self.rendererNotebook.selectpage(type)
  1321. self.particles.setRenderer(type)
  1322. self.updateRendererWidgets()
  1323. def updateRendererWidgets(self):
  1324. renderer = self.particles.renderer
  1325. alphaMode = renderer.getAlphaMode()
  1326. if alphaMode == BaseParticleRenderer.PRALPHANONE:
  1327. aMode = 'NO_ALPHA'
  1328. elif alphaMode == BaseParticleRenderer.PRALPHAOUT:
  1329. aMode = 'ALPHA_OUT'
  1330. elif alphaMode == BaseParticleRenderer.PRALPHAIN:
  1331. aMode = 'ALPHA_IN'
  1332. elif alphaMode == BaseParticleRenderer.PRALPHAUSER:
  1333. aMode = 'ALPHA_USER'
  1334. self.getVariable('Renderer', 'Alpha Mode').set(aMode)
  1335. userAlpha = renderer.getUserAlpha()
  1336. self.getWidget('Renderer', 'User Alpha').set(userAlpha)
  1337. if isinstance(renderer, LineParticleRenderer):
  1338. headColor = renderer.getHeadColor() * 255.0
  1339. self.getWidget('Line Renderer', 'Head Color').set(
  1340. [headColor[0], headColor[1], headColor[2], headColor[3]])
  1341. tailColor = renderer.getTailColor() * 255.0
  1342. self.getWidget('Line Renderer', 'Tail Color').set(
  1343. [tailColor[0], tailColor[1], tailColor[2], tailColor[3]])
  1344. elif isinstance(renderer, GeomParticleRenderer):
  1345. pass
  1346. elif isinstance(renderer, PointParticleRenderer):
  1347. pointSize = renderer.getPointSize()
  1348. self.getWidget('Point Renderer', 'Point Size').set(pointSize)
  1349. startColor = renderer.getStartColor() * 255.0
  1350. self.getWidget('Point Renderer', 'Start Color').set(
  1351. [startColor[0], startColor[1], startColor[2], startColor[3]])
  1352. endColor = renderer.getEndColor() * 255.0
  1353. self.getWidget('Point Renderer', 'End Color').set(
  1354. [endColor[0], endColor[1], endColor[2], endColor[3]])
  1355. blendType = renderer.getBlendType()
  1356. if (blendType == PointParticleRenderer.PPONECOLOR):
  1357. bType = "PP_ONE_COLOR"
  1358. elif (blendType == PointParticleRenderer.PPBLENDLIFE):
  1359. bType = "PP_BLEND_LIFE"
  1360. elif (blendType == PointParticleRenderer.PPBLENDVEL):
  1361. bType = "PP_BLEND_VEL"
  1362. self.getVariable('Point Renderer', 'Blend Type').set(bType)
  1363. blendMethod = renderer.getBlendMethod()
  1364. bMethod = "PP_NO_BLEND"
  1365. if (blendMethod == BaseParticleRenderer.PPNOBLEND):
  1366. bMethod = "PP_NO_BLEND"
  1367. elif (blendMethod == BaseParticleRenderer.PPBLENDLINEAR):
  1368. bMethod = "PP_BLEND_LINEAR"
  1369. elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC):
  1370. bMethod = "PP_BLEND_CUBIC"
  1371. self.getVariable('Point Renderer', 'Blend Method').set(bMethod)
  1372. elif isinstance(renderer, SparkleParticleRenderer):
  1373. centerColor = renderer.getCenterColor() * 255.0
  1374. self.getWidget('Sparkle Renderer', 'Center Color').set(
  1375. [centerColor[0], centerColor[1],
  1376. centerColor[2], centerColor[3]])
  1377. edgeColor = renderer.getEdgeColor() * 255.0
  1378. self.getWidget('Sparkle Renderer', 'Edge Color').set(
  1379. [edgeColor[0], edgeColor[1], edgeColor[2], edgeColor[3]])
  1380. birthRadius = renderer.getBirthRadius()
  1381. self.getWidget('Sparkle Renderer', 'Birth Radius').set(birthRadius)
  1382. deathRadius = renderer.getDeathRadius()
  1383. self.getWidget('Sparkle Renderer', 'Death Radius').set(deathRadius)
  1384. lifeScale = renderer.getLifeScale()
  1385. lScale = "SP_NO_SCALE"
  1386. if (lifeScale == SparkleParticleRenderer.SPSCALE):
  1387. lScale = "SP_SCALE"
  1388. self.getVariable('Sparkle Renderer', 'Life Scale').set(lScale)
  1389. elif isinstance(renderer, SpriteParticleRenderer):
  1390. color = renderer.getColor() * 255.0
  1391. # Update widgets to reflect current default values
  1392. # Texture
  1393. textureName = renderer.getSourceTextureName()
  1394. if textureName != None:
  1395. self.rendererSpriteTexture.set(textureName)
  1396. # File
  1397. fileName = renderer.getSourceFileName()
  1398. if fileName != None:
  1399. self.rendererSpriteFile.set(fileName)
  1400. # Node
  1401. nodeName = renderer.getSourceNodeName()
  1402. if nodeName != None:
  1403. self.rendererSpriteNode.set(nodeName)
  1404. self.getVariable('Sprite Renderer', 'X Scale').set(
  1405. renderer.getXScaleFlag())
  1406. self.getVariable('Sprite Renderer', 'Y Scale').set(
  1407. renderer.getYScaleFlag())
  1408. self.getVariable('Sprite Renderer', 'Anim Angle').set(
  1409. renderer.getAnimAngleFlag())
  1410. initialXScale = renderer.getInitialXScale()
  1411. self.getWidget('Sprite Renderer', 'Initial X Scale').set(
  1412. initialXScale)
  1413. initialYScale = renderer.getInitialYScale()
  1414. self.getWidget('Sprite Renderer', 'Initial Y Scale').set(
  1415. initialYScale)
  1416. finalXScale = renderer.getFinalXScale()
  1417. self.getWidget('Sprite Renderer', 'Final X Scale').set(
  1418. finalXScale)
  1419. finalYScale = renderer.getFinalYScale()
  1420. self.getWidget('Sprite Renderer', 'Final Y Scale').set(
  1421. finalYScale)
  1422. nonanimatedTheta = renderer.getNonanimatedTheta()
  1423. self.getWidget('Sprite Renderer', 'Non Animated Theta').set(
  1424. nonanimatedTheta)
  1425. blendMethod = renderer.getAlphaBlendMethod()
  1426. bMethod = "PP_NO_BLEND"
  1427. if (blendMethod == BaseParticleRenderer.PPNOBLEND):
  1428. bMethod = "PP_NO_BLEND"
  1429. elif (blendMethod == BaseParticleRenderer.PPBLENDLINEAR):
  1430. bMethod = "PP_BLEND_LINEAR"
  1431. elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC):
  1432. bMethod = "PP_BLEND_CUBIC"
  1433. self.getVariable('Sprite Renderer', 'Alpha Disable').set(
  1434. renderer.getAlphaDisable())
  1435. def selectRendererPage(self):
  1436. type = self.particles.renderer.__class__.__name__
  1437. self.rendererNotebook.selectpage(type)
  1438. self.getVariable('Renderer', 'Renderer Type').set(type)
  1439. # All #
  1440. def setRendererAlphaMode(self, alphaMode):
  1441. if alphaMode == 'NO_ALPHA':
  1442. aMode = BaseParticleRenderer.PRALPHANONE
  1443. elif alphaMode == 'ALPHA_OUT':
  1444. aMode = BaseParticleRenderer.PRALPHAOUT
  1445. elif alphaMode == 'ALPHA_IN':
  1446. aMode = BaseParticleRenderer.PRALPHAIN
  1447. elif alphaMode == 'ALPHA_USER':
  1448. aMode = BaseParticleRenderer.PRALPHAUSER
  1449. self.particles.renderer.setAlphaMode(aMode)
  1450. def setRendererUserAlpha(self, alpha):
  1451. self.particles.renderer.setUserAlpha(alpha)
  1452. # Line #
  1453. def setRendererLineHeadColor(self, color):
  1454. self.particles.renderer.setHeadColor(
  1455. Vec4(color[0]/255.0, color[1]/255.0,
  1456. color[2]/255.0, color[3]/255.0))
  1457. def setRendererLineTailColor(self, color):
  1458. self.particles.renderer.setTailColor(
  1459. Vec4(color[0]/255.0, color[1]/255.0,
  1460. color[2]/255.0, color[3]/255.0))
  1461. # Geom #
  1462. def setRendererGeomNode(self, event):
  1463. node = None
  1464. nodePath = loader.loadModel(self.rendererGeomNode.get())
  1465. if nodePath != None:
  1466. node = nodePath.node()
  1467. if (node != None):
  1468. self.particles.renderer.setGeomNode(node)
  1469. # Point #
  1470. def setRendererPointSize(self, size):
  1471. self.particles.renderer.setPointSize(size)
  1472. def setRendererPointStartColor(self, color):
  1473. self.particles.renderer.setStartColor(
  1474. Vec4(color[0]/255.0, color[1]/255.0,
  1475. color[2]/255.0, color[3]/255.0))
  1476. def setRendererPointEndColor(self, color):
  1477. self.particles.renderer.setEndColor(
  1478. Vec4(color[0]/255.0, color[1]/255.0,
  1479. color[2]/255.0, color[3]/255.0))
  1480. def rendererPointSelectBlendType(self, blendType):
  1481. if blendType == "PP_ONE_COLOR":
  1482. bType = PointParticleRenderer.PPONECOLOR
  1483. elif blendType == "PP_BLEND_LIFE":
  1484. bType = PointParticleRenderer.PPBLENDLIFE
  1485. elif blendType == "PP_BLEND_VEL":
  1486. bType = PointParticleRenderer.PPBLENDVEL
  1487. self.particles.renderer.setBlendType(bType)
  1488. def rendererPointSelectBlendMethod(self, blendMethod):
  1489. if blendMethod == "PP_NO_BLEND":
  1490. bMethod = BaseParticleRenderer.PPNOBLEND
  1491. elif blendMethod == "PP_BLEND_LINEAR":
  1492. bMethod = BaseParticleRenderer.PPBLENDLINEAR
  1493. elif blendMethod == "PP_BLEND_CUBIC":
  1494. bMethod = BaseParticleRenderer.PPBLENDCUBIC
  1495. self.particles.renderer.setBlendMethod(bMethod)
  1496. # Sparkle #
  1497. def setRendererSparkleCenterColor(self, color):
  1498. self.particles.renderer.setCenterColor(
  1499. Vec4(color[0]/255.0, color[1]/255.0,
  1500. color[2]/255.0, color[3]/255.0))
  1501. def setRendererSparkleEdgeColor(self, color):
  1502. self.particles.renderer.setEdgeColor(
  1503. Vec4(color[0]/255.0, color[1]/255.0,
  1504. color[2]/255.0, color[3]/255.0))
  1505. def setRendererSparkleBirthRadius(self, radius):
  1506. self.particles.renderer.setBirthRadius(radius)
  1507. def setRendererSparkleDeathRadius(self, radius):
  1508. self.particles.renderer.setDeathRadius(radius)
  1509. def setRendererSparkleLifeScale(self, lifeScaleMethod):
  1510. if lifeScaleMethod == 'SP_NO_SCALE':
  1511. lScale = SparkleParticleRenderer.SPNOSCALE
  1512. else:
  1513. lScale = SparkleParticleRenderer.SPSCALE
  1514. self.particles.renderer.setLifeScale(lScale)
  1515. # Sprite #
  1516. def setSpriteSourceType(self):
  1517. if self.rendererSpriteSourceType.get() == SpriteParticleRenderer.STTexture:
  1518. self.rendererSpriteTextureEntry['state'] = 'normal'
  1519. self.rendererSpriteFileEntry['state'] = 'disabled'
  1520. self.rendererSpriteNodeEntry['state'] = 'disabled'
  1521. self.rendererSpriteTextureEntry['background'] = '#FFFFFF'
  1522. self.rendererSpriteFileEntry['background'] = '#C0C0C0'
  1523. self.rendererSpriteNodeEntry['background'] = '#C0C0C0'
  1524. else:
  1525. self.rendererSpriteTextureEntry['state'] = 'disabled'
  1526. self.rendererSpriteFileEntry['state'] = 'normal'
  1527. self.rendererSpriteNodeEntry['state'] = 'normal'
  1528. self.rendererSpriteTextureEntry['background'] = '#C0C0C0'
  1529. self.rendererSpriteFileEntry['background'] = '#FFFFFF'
  1530. self.rendererSpriteNodeEntry['background'] = '#FFFFFF'
  1531. def setRendererSpriteTexture(self):
  1532. if self.rendererSpriteSourceType.get() == SpriteParticleRenderer.STTexture:
  1533. self.particles.renderer.setTextureFromFile(self.rendererSpriteTexture.get())
  1534. else:
  1535. self.particles.renderer.setTextureFromNode(
  1536. self.rendererSpriteFile.get(), self.rendererSpriteNode.get())
  1537. def toggleRendererSpriteXScale(self):
  1538. self.particles.renderer.setXScaleFlag(
  1539. self.getVariable('Sprite Renderer', 'X Scale').get())
  1540. def toggleRendererSpriteYScale(self):
  1541. self.particles.renderer.setYScaleFlag(
  1542. self.getVariable('Sprite Renderer', 'Y Scale').get())
  1543. def toggleRendererSpriteAnimAngle(self):
  1544. self.particles.renderer.setAnimAngleFlag(
  1545. self.getVariable('Sprite Renderer', 'Anim Angle').get())
  1546. def toggleAngularVelocity(self):
  1547. self.particles.factory.enableAngularVelocity(
  1548. self.getVariable('Z Spin Factory', 'Enable Angular Velocity').get())
  1549. def setRendererSpriteInitialXScale(self, xScale):
  1550. self.particles.renderer.setInitialXScale(xScale)
  1551. def setRendererSpriteFinalXScale(self, xScale):
  1552. self.particles.renderer.setFinalXScale(xScale)
  1553. def setRendererSpriteInitialYScale(self, yScale):
  1554. self.particles.renderer.setInitialYScale(yScale)
  1555. def setRendererSpriteFinalYScale(self, yScale):
  1556. self.particles.renderer.setFinalYScale(yScale)
  1557. def setRendererSpriteNonAnimatedTheta(self, theta):
  1558. self.particles.renderer.setNonanimatedTheta(theta)
  1559. def setRendererSpriteBlendMethod(self, blendMethod):
  1560. print blendMethod
  1561. if blendMethod == 'PP_NO_BLEND':
  1562. bMethod = BaseParticleRenderer.PPNOBLEND
  1563. elif blendMethod == 'PP_BLEND_LINEAR':
  1564. bMethod = BaseParticleRenderer.PPBLENDLINEAR
  1565. elif blendMethod == 'PP_BLEND_CUBIC':
  1566. bMethod = BaseParticleRenderer.PPBLENDCUBIC
  1567. else:
  1568. bMethod = BaseParticleRenderer.PPNOBLEND
  1569. self.particles.renderer.setAlphaBlendMethod(bMethod)
  1570. def toggleRendererSpriteAlphaDisable(self):
  1571. self.particles.renderer.setAlphaDisable(
  1572. self.getVariable('Sprite Renderer', 'Alpha Disable').get())
  1573. ## FORCEGROUP COMMANDS ##
  1574. def updateForceWidgets(self):
  1575. # Select appropriate notebook page
  1576. if self.forceGroup != None:
  1577. self.forceGroupNotebook.pack(fill = X)
  1578. self.forcePageName = (self.particleEffect.getName() + '-' +
  1579. self.forceGroup.getName())
  1580. self.forcePage = self.forcePagesDict.get(
  1581. self.forcePageName, None)
  1582. # Page doesn't exist, add it
  1583. if self.forcePage == None:
  1584. self.addForceGroupNotebookPage(
  1585. self.particleEffect, self.forceGroup)
  1586. self.forceGroupNotebook.selectpage(self.forcePageName)
  1587. else:
  1588. self.forceGroupNotebook.pack_forget()
  1589. def addLinearVectorForce(self):
  1590. self.addForce(LinearVectorForce())
  1591. def addLinearFrictionForce(self):
  1592. self.addForce(LinearFrictionForce())
  1593. def addLinearJitterForce(self):
  1594. self.addForce(LinearJitterForce())
  1595. def addLinearNoiseForce(self):
  1596. self.addForce(LinearNoiseForce())
  1597. def addLinearSinkForce(self):
  1598. self.addForce(LinearSinkForce())
  1599. def addLinearSourceForce(self):
  1600. self.addForce(LinearSourceForce())
  1601. def addLinearCylinderVortexForce(self):
  1602. self.addForce(LinearCylinderVortexForce())
  1603. def addLinearUserDefinedForce(self):
  1604. self.addForce(LinearUserDefinedForce())
  1605. def addForce(self, f):
  1606. if self.forceGroup == None:
  1607. self.createNewForceGroup()
  1608. self.forceGroup.addForce(f)
  1609. self.addForceWidget(self.forceGroup,f)
  1610. ## SYSTEM COMMANDS ##
  1611. def createNewEffect(self):
  1612. name = askstring('Particle Panel', 'Effect Name:',
  1613. parent = self.parent)
  1614. if name:
  1615. particles = seParticles.Particles()
  1616. particles.setBirthRate(0.02)
  1617. particles.setLitterSize(10)
  1618. particles.setLitterSpread(0)
  1619. particles.setFactory("PointParticleFactory")
  1620. particles.setRenderer("PointParticleRenderer")
  1621. particles.setEmitter("SphereVolumeEmitter")
  1622. particles.enable()
  1623. effect = seParticleEffect.ParticleEffect(name,particles)
  1624. self.effectsDict[name] = effect
  1625. self.updateMenusAndLabels()
  1626. self.selectEffectNamed(name)
  1627. self.emitter=loader.loadModel("sphere")
  1628. self.emitter.setName(name)
  1629. effect.reparentTo(self.emitter)
  1630. self.emitter.reparentTo(render)
  1631. effect.enable()
  1632. messenger.send('ParticlePanel_Added_Effect',[name,effect,self.emitter])
  1633. messenger.send('SGE_Update Explorer',[render])
  1634. def createNewParticles(self):
  1635. name = askstring('Particle Panel', 'Particles Name:',
  1636. parent = self.parent)
  1637. if name:
  1638. p = seParticles.Particles(name)
  1639. p.setBirthRate(0.02)
  1640. p.setLitterSize(10)
  1641. p.setLitterSpread(0)
  1642. p.setFactory("PointParticleFactory")
  1643. p.setRenderer("PointParticleRenderer")
  1644. p.setEmitter("SphereVolumeEmitter")
  1645. self.particleEffect.addParticles(p)
  1646. self.updateParticlesMenus()
  1647. self.selectParticlesNamed(name)
  1648. p.enable()
  1649. def createNewForceGroup(self):
  1650. name = askstring('Particle Panel', 'ForceGroup Name:',
  1651. parent = self.parent)
  1652. if name:
  1653. forceGroup = seForceGroup.ForceGroup(name)
  1654. self.particleEffect.addForceGroup(forceGroup)
  1655. self.updateForceGroupMenus()
  1656. self.addForceGroupNotebookPage(self.particleEffect, forceGroup)
  1657. self.selectForceGroupNamed(name)
  1658. forceGroup.enable()
  1659. def addForceGroupNotebookPage(self, particleEffect, forceGroup):
  1660. self.forcePageName = (particleEffect.getName() + '-' +
  1661. forceGroup.getName())
  1662. self.forcePage = self.forceGroupNotebook.add(self.forcePageName)
  1663. self.forcePagesDict[self.forcePageName] = self.forcePage
  1664. for force in forceGroup:
  1665. self.addForceWidget(forceGroup, force)
  1666. def addForceWidget(self, forceGroup, force):
  1667. forcePage = self.forcePage
  1668. pageName = self.forcePageName
  1669. # How many forces of the same type in the force group object
  1670. count = 0
  1671. for f in forceGroup:
  1672. if f.getClassType().eq(force.getClassType()):
  1673. count += 1
  1674. if isinstance(force, LinearVectorForce):
  1675. self.createLinearVectorForceWidget(
  1676. forcePage, pageName, count, force)
  1677. elif isinstance(force, LinearNoiseForce):
  1678. self.createLinearRandomForceWidget(
  1679. forcePage, pageName, count, force, 'Noise')
  1680. elif isinstance(force, LinearJitterForce):
  1681. self.createLinearRandomForceWidget(
  1682. forcePage, pageName, count, force, 'Jitter')
  1683. elif isinstance(force, LinearFrictionForce):
  1684. self.createLinearFrictionForceWidget(
  1685. forcePage, pageName, count, force)
  1686. elif isinstance(force, LinearCylinderVortexForce):
  1687. self.createLinearCylinderVortexForceWidget(
  1688. forcePage, pageName, count, force)
  1689. elif isinstance(force, LinearSinkForce):
  1690. self.createLinearDistanceForceWidget(
  1691. forcePage, pageName, count, force, 'Sink')
  1692. elif isinstance(force, LinearSourceForce):
  1693. self.createLinearDistanceForceWidget(
  1694. forcePage, pageName, count, force, 'Source')
  1695. elif isinstance(force, LinearUserDefinedForce):
  1696. # Nothing
  1697. pass
  1698. self.forceGroupNotebook.setnaturalsize()
  1699. def createForceFrame(self, forcePage, forceName, force):
  1700. frame = Frame(forcePage, relief = RAISED, borderwidth = 2)
  1701. lFrame = Frame(frame, relief = FLAT)
  1702. def removeForce(s = self, f = force, fr = frame):
  1703. s.forceGroup.removeForce(f)
  1704. fr.pack_forget()
  1705. b = Button(lFrame, text = 'X',
  1706. command = removeForce)
  1707. b.pack(side = 'right', expand = 0)
  1708. Label(lFrame, text = forceName,
  1709. foreground = 'Blue',
  1710. font=('MSSansSerif', 12, 'bold'),
  1711. ).pack(expand = 1, fill = 'x')
  1712. lFrame.pack(fill = 'x', expand =1)
  1713. frame.pack(pady = 3, fill = 'x', expand =0)
  1714. return frame
  1715. def createLinearForceWidgets(self, frame, pageName, forceName, force):
  1716. def setAmplitude(amp, f = force):
  1717. f.setAmplitude(amp)
  1718. def toggleMassDependent(s=self, f=force, p=pageName, n=forceName):
  1719. v = s.getVariable(p, n+' Mass Dependent')
  1720. f.setMassDependent(v.get())
  1721. def setVectorMasks(s=self, f=force, p=pageName, n=forceName):
  1722. xMask = s.getVariable(p, n+' Mask X').get()
  1723. yMask = s.getVariable(p, n+' Mask Y').get()
  1724. zMask = s.getVariable(p, n+' Mask Z').get()
  1725. f.setVectorMasks(xMask, yMask, zMask)
  1726. self.createFloater(frame, pageName, forceName + ' Amplitude',
  1727. 'Force amplitude multiplier',
  1728. command = setAmplitude,
  1729. value = force.getAmplitude())
  1730. cbf = Frame(frame, relief = FLAT)
  1731. self.createCheckbutton(cbf, pageName, forceName + ' Mass Dependent',
  1732. ('On: force depends on mass; ' +
  1733. 'Off: force does not depend on mass'),
  1734. toggleMassDependent,
  1735. force.getMassDependent())
  1736. self.createCheckbutton(cbf, pageName, forceName + ' Mask X',
  1737. 'On: enable force along X axis',
  1738. setVectorMasks, 1)
  1739. self.createCheckbutton(cbf, pageName, forceName + ' Mask Y',
  1740. 'On: enable force along X axis',
  1741. setVectorMasks, 1)
  1742. self.createCheckbutton(cbf, pageName, forceName + ' Mask Z',
  1743. 'On: enable force along X axis',
  1744. setVectorMasks, 1)
  1745. cbf.pack(fill = 'x', expand = 0)
  1746. def createForceActiveWidget(self, frame, pageName, forceName, force):
  1747. cbName = forceName + ' Active'
  1748. def toggle(s = self, f = force, p = pageName, n = cbName):
  1749. s.toggleForce(f, p, n)
  1750. self.createCheckbutton(frame, pageName, cbName,
  1751. 'On: force is enabled; Off: force is disabled',
  1752. toggle, 1)
  1753. def createLinearVectorForceWidget(self, forcePage, pageName,
  1754. count, force):
  1755. def setVec(vec, f = force):
  1756. f.setVector(vec[0], vec[1], vec[2])
  1757. forceName = 'Vector Force-' + `count`
  1758. frame = self.createForceFrame(forcePage, forceName, force)
  1759. self.createLinearForceWidgets(frame, pageName, forceName, force)
  1760. vec = force.getLocalVector()
  1761. self.createVector3Entry(frame, pageName, forceName,
  1762. 'Set force direction and magnitude',
  1763. command = setVec,
  1764. value = [vec[0], vec[1], vec[2]])
  1765. self.createForceActiveWidget(frame, pageName, forceName, force)
  1766. def createLinearRandomForceWidget(self, forcePage, pageName, count,
  1767. force, type):
  1768. forceName = type + ' Force-' + `count`
  1769. frame = self.createForceFrame(forcePage, forceName, force)
  1770. self.createLinearForceWidgets(frame, pageName, forceName, force)
  1771. self.createForceActiveWidget(frame, pageName, forceName, force)
  1772. def createLinearFrictionForceWidget(self, forcePage, pageName,
  1773. count, force):
  1774. def setCoef(coef, f = force):
  1775. f.setCoef(coef)
  1776. forceName = 'Friction Force-' + `count`
  1777. frame = self.createForceFrame(forcePage, forceName, force)
  1778. self.createLinearForceWidgets(frame, pageName, forceName, force)
  1779. self.createFloater(frame, pageName, forceName + ' Coef',
  1780. 'Set linear friction force',
  1781. command = setCoef, min = None,
  1782. value = force.getCoef())
  1783. self.createForceActiveWidget(frame, pageName, forceName, force)
  1784. def createLinearCylinderVortexForceWidget(self, forcePage, pageName,
  1785. count, force):
  1786. forceName = 'Vortex Force-' + `count`
  1787. def setCoef(coef, f = force):
  1788. f.setCoef(coef)
  1789. def setLength(length, f = force):
  1790. f.setLength(length)
  1791. def setRadius(radius, f = force):
  1792. f.setRadius(radius)
  1793. frame = self.createForceFrame(forcePage, forceName, force)
  1794. self.createLinearForceWidgets(frame, pageName, forceName, force)
  1795. self.createFloater(frame, pageName, forceName + ' Coef',
  1796. 'Set linear cylinder vortex coefficient',
  1797. command = setCoef,
  1798. value = force.getCoef())
  1799. self.createFloater(frame, pageName, forceName + ' Length',
  1800. 'Set linear cylinder vortex length',
  1801. command = setLength,
  1802. value = force.getLength())
  1803. self.createFloater(frame, pageName, forceName + ' Radius',
  1804. 'Set linear cylinder vortex radius',
  1805. command = setRadius,
  1806. value = force.getRadius())
  1807. self.createForceActiveWidget(frame, pageName, forceName, force)
  1808. def createLinearDistanceForceWidget(self, forcePage, pageName,
  1809. count, force, type):
  1810. def setFalloffType(type, f=force):
  1811. if type == 'FT_ONE_OVER_R':
  1812. #f.setFalloffType(LinearDistanceForce.FTONEOVERR)
  1813. f.setFalloffType(0)
  1814. if type == 'FT_ONE_OVER_R_SQUARED':
  1815. #f.setFalloffType(LinearDistanceForce.FTONEOVERRSQUARED)
  1816. f.setFalloffType(1)
  1817. if type == 'FT_ONE_OVER_R_CUBED':
  1818. #f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED)
  1819. f.setFalloffType(2)
  1820. def setForceCenter(vec, f = force):
  1821. f.setForceCenter(Point3(vec[0], vec[1], vec[2]))
  1822. def setRadius(radius, f = force):
  1823. f.setRadius(radius)
  1824. forceName = type + ' Force-' + `count`
  1825. frame = self.createForceFrame(forcePage, forceName, force)
  1826. self.createLinearForceWidgets(frame, pageName, forceName, force)
  1827. var = self.createOptionMenu(
  1828. frame, pageName, forceName + ' Falloff',
  1829. 'Set force falloff type',
  1830. ('FT_ONE_OVER_R',
  1831. 'FT_ONE_OVER_R_SQUARED',
  1832. 'FT_ONE_OVER_R_CUBED'),
  1833. command = setFalloffType)
  1834. self.getWidget(pageName, forceName + ' Falloff').configure(
  1835. label_width = 16)
  1836. falloff = force.getFalloffType()
  1837. if falloff == LinearDistanceForce.FTONEOVERR:
  1838. var.set('FT_ONE_OVER_R')
  1839. elif falloff == LinearDistanceForce.FTONEOVERRSQUARED:
  1840. var.set('FT_ONE_OVER_R_SQUARED')
  1841. elif falloff == LinearDistanceForce.FTONEOVERRCUBED:
  1842. var.set('FT_ONE_OVER_R_CUBED')
  1843. vec = force.getForceCenter()
  1844. self.createVector3Entry(frame, pageName, forceName + ' Center',
  1845. 'Set center of force',
  1846. command = setForceCenter,
  1847. label_width = 16,
  1848. value = [vec[0], vec[1], vec[2]])
  1849. self.createFloater(frame, pageName, forceName + ' Radius',
  1850. 'Set falloff radius',
  1851. command = setRadius,
  1852. min = 0.01,
  1853. value = force.getRadius())
  1854. self.createForceActiveWidget(frame, pageName, forceName, force)
  1855. ######################################################################
  1856. # Create demo in root window for testing.
  1857. if __name__ == '__main__':
  1858. root = Pmw.initialise()
  1859. pp = ParticlePanel()
  1860. #ve = VectorEntry(Toplevel(), relief = GROOVE)
  1861. #ve.pack()