ParticlePanel.py 129 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929
  1. """PANDA3D Particle Panel"""
  2. __all__ = ['ParticlePanel']
  3. # Import Tkinter, Pmw, and the floater code from this directory tree.
  4. from panda3d.core import ColorBlendAttrib, ConfigVariableSearchPath, Filename, Point2, Point3, Vec3, Vec4, getModelPath
  5. from panda3d.physics import (
  6. BaseParticleEmitter,
  7. BaseParticleRenderer,
  8. BoxEmitter,
  9. ColorInterpolationFunctionConstant,
  10. ColorInterpolationFunctionLinear,
  11. ColorInterpolationFunctionSinusoid,
  12. ColorInterpolationFunctionStepwave,
  13. DiscEmitter,
  14. GeomParticleRenderer,
  15. LinearCylinderVortexForce,
  16. LinearDistanceForce,
  17. LinearFrictionForce,
  18. LinearJitterForce,
  19. LinearNoiseForce,
  20. LinearSinkForce,
  21. LinearSourceForce,
  22. LinearUserDefinedForce,
  23. LinearVectorForce,
  24. LineEmitter,
  25. LineParticleRenderer,
  26. PointEmitter,
  27. PointParticleRenderer,
  28. RectangleEmitter,
  29. RingEmitter,
  30. SparkleParticleRenderer,
  31. SphereSurfaceEmitter,
  32. SphereVolumeEmitter,
  33. SpriteAnim,
  34. SpriteParticleRenderer,
  35. TangentRingEmitter,
  36. )
  37. from direct.tkwidgets.AppShell import AppShell
  38. from direct.tkwidgets import Dial
  39. from direct.tkwidgets import Floater
  40. from direct.tkwidgets import Slider
  41. from direct.tkwidgets import VectorWidgets
  42. from direct.tkpanels import Placer
  43. from direct.particles import ForceGroup
  44. from direct.particles import Particles
  45. from direct.particles import ParticleEffect
  46. from tkinter.filedialog import askopenfilename, asksaveasfilename
  47. from tkinter.simpledialog import askstring
  48. import Pmw
  49. import os
  50. import tkinter as tk
  51. particlePath = ConfigVariableSearchPath("particle-path",
  52. "The directories to search for particle files to be loaded.")
  53. class ParticlePanel(AppShell):
  54. # Override class variables
  55. appname = 'Particle Panel'
  56. frameWidth = 375
  57. frameHeight = 675
  58. usecommandarea = 0
  59. usestatusarea = 0
  60. balloonState = 'both'
  61. def __init__(self, particleEffect = None, **kw):
  62. INITOPT = Pmw.INITOPT
  63. optiondefs = (
  64. ('title', self.appname, None),
  65. )
  66. self.defineoptions(kw, optiondefs)
  67. # Record particle effect
  68. if particleEffect is not None:
  69. self.particleEffect = particleEffect
  70. else:
  71. # Make sure particles are enabled
  72. base.enableParticles()
  73. # Or create a new one if none given
  74. particles = Particles.Particles()
  75. particles.setBirthRate(0.02)
  76. particles.setLitterSize(10)
  77. particles.setLitterSpread(0)
  78. particles.setFactory("PointParticleFactory")
  79. particles.setRenderer("PointParticleRenderer")
  80. particles.setEmitter("SphereVolumeEmitter")
  81. particles.enable()
  82. pe = ParticleEffect.ParticleEffect('effect-1', particles)
  83. self.particleEffect = pe
  84. pe.reparentTo(render)
  85. pe.enable()
  86. # Initialize application specific info
  87. AppShell.__init__(self)
  88. # Initialize panel Pmw options
  89. self.initialiseoptions(ParticlePanel)
  90. # Update panel values to reflect particle effect's state
  91. self.selectEffectNamed(next(iter(self.effectsDict)))
  92. # Make sure labels/menus reflect current state
  93. self.updateMenusAndLabels()
  94. # Make sure there is a page for each forceGroup objects
  95. for forceGroup in self.particleEffect.getForceGroupList():
  96. self.addForceGroupNotebookPage(self.particleEffect, forceGroup)
  97. def appInit(self):
  98. # Create dictionaries to keep track of panel objects
  99. self.widgetDict = {}
  100. self.variableDict = {}
  101. self.effectsDict = {}
  102. self.effectsDict[self.particleEffect.getName()] = (
  103. self.particleEffect)
  104. self.forcePagesDict = {}
  105. def createInterface(self):
  106. # Handle to the toplevels hull
  107. interior = self.interior()
  108. # Create particle panel menu items
  109. ## MENUBAR ENTRIES ##
  110. # FILE MENU
  111. # Get a handle on the file menu, and delete the Quit item that AppShell
  112. # created so we can add it back after adding the other items.
  113. self.menuBar.deletemenuitems('File', 0, 0)
  114. self.menuBar.addmenuitem('File', 'command',
  115. label='Load Params',
  116. command=self.loadParticleEffectFromFile)
  117. self.menuBar.addmenuitem('File', 'command',
  118. label='Save Params',
  119. command=self.saveParticleEffectToFile)
  120. self.menuBar.addmenuitem('File', 'command',
  121. label='Print Params',
  122. command=lambda s=self:s.particles.printParams())
  123. self.menuBar.addmenuitem('File', 'command', 'Quit this application',
  124. label='Quit',
  125. command=self.quit)
  126. # PARTICLE MANAGER MENU
  127. self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')
  128. self.particleMgrActive = tk.IntVar()
  129. self.particleMgrActive.set(base.isParticleMgrEnabled())
  130. self.menuBar.addmenuitem(
  131. 'ParticleMgr', 'checkbutton',
  132. 'Enable/Disable ParticleMgr',
  133. label = 'Active',
  134. variable = self.particleMgrActive,
  135. command = self.toggleParticleMgr)
  136. ## MENUBUTTON LABELS ##
  137. # Menubutton/label to identify the current objects being configured
  138. labelFrame = tk.Frame(interior)
  139. # Current effect
  140. self.effectsLabel = tk.Menubutton(labelFrame, width = 10,
  141. relief = tk.RAISED,
  142. borderwidth = 2,
  143. font=('MSSansSerif', 12, 'bold'),
  144. activebackground = '#909090')
  145. self.effectsLabelMenu = tk.Menu(self.effectsLabel, tearoff = 0)
  146. self.effectsLabel['menu'] = self.effectsLabelMenu
  147. self.effectsLabel.pack(side = tk.LEFT, fill = 'x', expand = 1)
  148. self.bind(self.effectsLabel,
  149. 'Select effect to configure or create new effect')
  150. self.effectsLabelMenu.add_command(label = 'Create New Effect',
  151. command = self.createNewEffect)
  152. self.effectsLabelMenu.add_command(
  153. label = 'Select Particle Effect',
  154. command = lambda s = self: base.direct.select(s.particleEffect))
  155. self.effectsLabelMenu.add_command(
  156. label = 'Place Particle Effect',
  157. command = lambda s = self: Placer.place(s.particleEffect))
  158. def togglePEVis(s = self):
  159. if s.particleEffect.isHidden():
  160. s.particleEffect.show()
  161. else:
  162. s.particleEffect.hide()
  163. self.effectsLabelMenu.add_command(
  164. label = 'Toggle Effect Vis',
  165. command = togglePEVis)
  166. self.effectsEnableMenu = tk.Menu(self.effectsLabelMenu, tearoff = 0)
  167. self.effectsLabelMenu.add_cascade(label = 'Enable/Disable',
  168. menu = self.effectsEnableMenu)
  169. self.effectsLabelMenu.add_separator()
  170. # Current particles
  171. self.particlesLabel = tk.Menubutton(labelFrame, width = 10,
  172. relief = tk.RAISED,
  173. borderwidth = 2,
  174. font=('MSSansSerif', 12, 'bold'),
  175. activebackground = '#909090')
  176. self.particlesLabelMenu = tk.Menu(self.particlesLabel, tearoff = 0)
  177. self.particlesLabel['menu'] = self.particlesLabelMenu
  178. self.particlesLabel.pack(side = tk.LEFT, fill = 'x', expand = 1)
  179. self.bind(self.particlesLabel,
  180. ('Select particles object to configure ' +
  181. 'or add new particles object to current effect'))
  182. self.particlesLabelMenu.add_command(label = 'Create New Particles',
  183. command = self.createNewParticles)
  184. self.particlesEnableMenu = tk.Menu(self.particlesLabelMenu, tearoff = 0)
  185. self.particlesLabelMenu.add_cascade(label = 'Enable/Disable',
  186. menu = self.particlesEnableMenu)
  187. self.particlesLabelMenu.add_separator()
  188. # Current force
  189. self.forceGroupLabel = tk.Menubutton(labelFrame, width = 10,
  190. relief = tk.RAISED,
  191. borderwidth = 2,
  192. font=('MSSansSerif', 12, 'bold'),
  193. activebackground = '#909090')
  194. self.forceGroupLabelMenu = tk.Menu(self.forceGroupLabel, tearoff = 0)
  195. self.forceGroupLabel['menu'] = self.forceGroupLabelMenu
  196. self.forceGroupLabel.pack(side = tk.LEFT, fill = 'x', expand = 1)
  197. self.bind(self.forceGroupLabel,
  198. ('Select force group to configure ' +
  199. 'or add a new force group to current effect'))
  200. self.forceGroupLabelMenu.add_command(
  201. label = 'Create New ForceGroup',
  202. command = self.createNewForceGroup)
  203. self.forceGroupEnableMenu = tk.Menu(self.forceGroupLabelMenu, tearoff = 0)
  204. self.forceGroupLabelMenu.add_cascade(label = 'Enable/Disable',
  205. menu = self.forceGroupEnableMenu)
  206. self.forceGroupLabelMenu.add_separator()
  207. # Pack labels
  208. labelFrame.pack(fill = 'x', expand = 0)
  209. # Create the toplevel notebook pages
  210. self.mainNotebook = Pmw.NoteBook(interior)
  211. self.mainNotebook.pack(fill = tk.BOTH, expand = 1)
  212. systemPage = self.mainNotebook.add('System')
  213. factoryPage = self.mainNotebook.add('Factory')
  214. emitterPage = self.mainNotebook.add('Emitter')
  215. rendererPage = self.mainNotebook.add('Renderer')
  216. forcePage = self.mainNotebook.add('Force')
  217. # Put this here so it isn't called right away
  218. self.mainNotebook['raisecommand'] = self.updateInfo
  219. ## SYSTEM PAGE WIDGETS ##
  220. # Create system floaters
  221. systemFloaterDefs = (
  222. ('System', 'Pool Size',
  223. 'Max number of simultaneous particles',
  224. self.setSystemPoolSize,
  225. 1.0, 2000000, 1.0),
  226. ('System', 'Birth Rate',
  227. 'Seconds between particle births',
  228. self.setSystemBirthRate,
  229. 0.0, None, None),
  230. ('System', 'Litter Size',
  231. 'Number of particle created at each birth',
  232. self.setSystemLitterSize,
  233. 1.0, 0x7fffffff, 1.0),
  234. ('System', 'Litter Spread',
  235. 'Variation in litter size',
  236. self.setSystemLitterSpread,
  237. 0.0, 0x7fffffff, 1.0),
  238. ('System', 'Lifespan',
  239. 'Age in seconds at which the system (vs. particles) should die',
  240. self.setSystemLifespan,
  241. 0.0, None, None)
  242. )
  243. self.createFloaters(systemPage, systemFloaterDefs)
  244. # Checkboxes
  245. self.createCheckbutton(
  246. systemPage, 'System', 'Render Space Velocities',
  247. ('On: velocities are in render space; ' +
  248. 'Off: velocities are in particle local space'),
  249. self.toggleSystemLocalVelocity, 0)
  250. self.createCheckbutton(
  251. systemPage, 'System', 'System Grows Older',
  252. 'On: system has a lifespan',
  253. self.toggleSystemGrowsOlder, 0)
  254. # Vector widgets
  255. pos = self.createVector3Entry(systemPage, 'System', 'Pos',
  256. 'Particle system position',
  257. command = self.setSystemPos)
  258. pos.addMenuItem('Popup Placer Panel', Placer.Placer)
  259. hpr = self.createVector3Entry(systemPage, 'System', 'Hpr',
  260. 'Particle system orientation',
  261. fGroup_labels = ('H', 'P', 'R'),
  262. command = self.setSystemHpr)
  263. hpr.addMenuItem('Popup Placer Panel', Placer.Placer)
  264. ## FACTORY PAGE WIDGETS ##
  265. self.createOptionMenu(
  266. factoryPage,
  267. 'Factory', 'Factory Type',
  268. 'Select type of particle factory',
  269. ('PointParticleFactory', 'ZSpinParticleFactory',
  270. #'OrientedParticleFactory'
  271. ),
  272. self.selectFactoryType)
  273. factoryWidgets = (
  274. ('Factory', 'Life Span',
  275. 'Average particle lifespan in seconds',
  276. self.setFactoryLifeSpan,
  277. 0.0, None, None),
  278. ('Factory', 'Life Span Spread',
  279. 'Variation in lifespan',
  280. self.setFactoryLifeSpanSpread,
  281. 0.0, None, None),
  282. ('Factory', 'Mass',
  283. 'Average particle mass',
  284. self.setFactoryParticleMass,
  285. 0.001, None, None),
  286. ('Factory', 'Mass Spread',
  287. 'Variation in particle mass',
  288. self.setFactoryParticleMassSpread,
  289. 0.0, None, None),
  290. ('Factory', 'Terminal Velocity',
  291. 'Cap on average particle velocity',
  292. self.setFactoryTerminalVelocity,
  293. 0.0, None, None),
  294. ('Factory', 'Terminal Vel. Spread',
  295. 'Variation in terminal velocity',
  296. self.setFactoryTerminalVelocitySpread,
  297. 0.0, None, None),
  298. )
  299. self.createFloaters(factoryPage, factoryWidgets)
  300. self.factoryNotebook = Pmw.NoteBook(factoryPage, tabpos = None)
  301. # Point page #
  302. factoryPointPage = self.factoryNotebook.add('PointParticleFactory')
  303. # Z spin page #
  304. zSpinPage = self.factoryNotebook.add('ZSpinParticleFactory')
  305. self.createCheckbutton(
  306. zSpinPage, 'Z Spin Factory', 'Enable Angular Velocity',
  307. 'On: angular velocity is used; Off: final angle is used',
  308. self.toggleAngularVelocity, 0, side = tk.TOP)
  309. self.createFloater(
  310. zSpinPage, 'Z Spin Factory', 'Angular Velocity',
  311. 'How fast sprites rotate',
  312. command = self.setFactoryZSpinAngularVelocity)
  313. self.createFloater(
  314. zSpinPage, 'Z Spin Factory', 'Angular Velocity Spread',
  315. 'Variation in how fast sprites rotate',
  316. command = self.setFactoryZSpinAngularVelocitySpread)
  317. self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Initial Angle',
  318. 'Starting angle in degrees',
  319. fRollover = 1,
  320. command = self.setFactoryZSpinInitialAngle)
  321. self.createAngleDial(
  322. zSpinPage, 'Z Spin Factory',
  323. 'Initial Angle Spread',
  324. 'Spread of the initial angle',
  325. fRollover = 1,
  326. command = self.setFactoryZSpinInitialAngleSpread)
  327. self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Final Angle',
  328. 'Final angle in degrees',
  329. fRollover = 1,
  330. command = self.setFactoryZSpinFinalAngle)
  331. self.createAngleDial(
  332. zSpinPage, 'Z Spin Factory',
  333. 'Final Angle Spread',
  334. 'Spread of the final angle',
  335. fRollover = 1,
  336. command = self.setFactoryZSpinFinalAngleSpread)
  337. # Oriented page #
  338. orientedPage = self.factoryNotebook.add('OrientedParticleFactory')
  339. tk.Label(orientedPage, text = 'Not implemented').pack(expand = 1,
  340. fill = tk.BOTH)
  341. self.factoryNotebook.pack(expand = 1, fill = tk.BOTH)
  342. ## EMITTER PAGE WIDGETS ##
  343. self.createOptionMenu(
  344. emitterPage, 'Emitter',
  345. 'Emitter Type',
  346. 'Select type of particle emitter',
  347. ('BoxEmitter', 'DiscEmitter', 'LineEmitter', 'PointEmitter',
  348. 'RectangleEmitter', 'RingEmitter', 'SphereVolumeEmitter',
  349. 'SphereSurfaceEmitter', 'TangentRingEmitter'),
  350. self.selectEmitterType)
  351. # Emitter modes
  352. self.emissionType = tk.IntVar()
  353. self.emissionType.set(BaseParticleEmitter.ETRADIATE)
  354. emissionFrame = tk.Frame(emitterPage)
  355. self.createRadiobutton(
  356. emissionFrame, 'left',
  357. 'Emitter', 'Explicit Emission',
  358. ('particles are all emitted in parallel, direction is based ' +
  359. 'on explicit velocity vector'),
  360. self.emissionType, BaseParticleEmitter.ETEXPLICIT,
  361. self.setEmissionType)
  362. self.createRadiobutton(
  363. emissionFrame, 'left',
  364. 'Emitter', 'Radiate Emission',
  365. 'particles are emitted away from a specific point',
  366. self.emissionType, BaseParticleEmitter.ETRADIATE,
  367. self.setEmissionType)
  368. self.createRadiobutton(
  369. emissionFrame, 'left',
  370. 'Emitter', 'Custom Emission',
  371. ('particles are emitted with a velocity that ' +
  372. 'is determined by the particular emitter'),
  373. self.emissionType, BaseParticleEmitter.ETCUSTOM,
  374. self.setEmissionType)
  375. emissionFrame.pack(fill = 'x', expand = 0)
  376. self.createFloater(
  377. emitterPage, 'Emitter', 'Velocity Multiplier',
  378. 'launch velocity multiplier (all emission modes)',
  379. command = self.setEmitterAmplitude,
  380. min = None)
  381. self.createFloater(
  382. emitterPage, 'Emitter', 'Velocity Multiplier Spread',
  383. 'spread for launch velocity multiplier (all emission modes)',
  384. command = self.setEmitterAmplitudeSpread)
  385. self.createVector3Entry(
  386. emitterPage, 'Emitter', 'Offset Velocity',
  387. 'Velocity vector applied to all particles',
  388. command = self.setEmitterOffsetForce)
  389. self.createVector3Entry(
  390. emitterPage, 'Emitter', 'Explicit Velocity',
  391. 'all particles launch with this velocity in Explicit mode',
  392. command = self.setEmitterExplicitLaunchVector)
  393. self.createVector3Entry(
  394. emitterPage, 'Emitter', 'Radiate Origin',
  395. 'particles launch away from this point in Radiate mode',
  396. command = self.setEmitterRadiateOrigin)
  397. self.emitterNotebook = Pmw.NoteBook(emitterPage, tabpos = None)
  398. # Box page #
  399. boxPage = self.emitterNotebook.add('BoxEmitter')
  400. self.createVector3Entry(boxPage, 'Box Emitter', 'Min',
  401. 'Min point defining emitter box',
  402. command = self.setEmitterBoxPoint1)
  403. self.createVector3Entry(boxPage, 'Box Emitter', 'Max',
  404. 'Max point defining emitter box',
  405. command = self.setEmitterBoxPoint2,
  406. value = (1.0, 1.0, 1.0))
  407. # Disc page #
  408. discPage = self.emitterNotebook.add('DiscEmitter')
  409. self.createFloater(discPage, 'Disc Emitter', 'Radius',
  410. 'Radius of disc',
  411. command = self.setEmitterDiscRadius,
  412. min = 0.01)
  413. customPage = self.discCustomFrame = tk.Frame(discPage)
  414. self.createAngleDial(customPage, 'Disc Emitter', 'Inner Angle',
  415. 'Particle launch angle at center of disc',
  416. command = self.setEmitterDiscInnerAngle)
  417. self.createFloater(customPage, 'Disc Emitter', 'Inner Velocity',
  418. 'Launch velocity multiplier at center of disc',
  419. command = self.setEmitterDiscInnerVelocity)
  420. self.createAngleDial(customPage, 'Disc Emitter', 'Outer Angle',
  421. 'Particle launch angle at outer edge of disc',
  422. command = self.setEmitterDiscOuterAngle)
  423. self.createFloater(customPage, 'Disc Emitter', 'Outer Velocity',
  424. 'Launch velocity multiplier at edge of disc',
  425. command = self.setEmitterDiscOuterVelocity)
  426. self.createCheckbutton(
  427. customPage, 'Disc Emitter', 'Cubic Lerping',
  428. 'On: magnitude/angle interpolation from center',
  429. self.toggleEmitterDiscCubicLerping, 0)
  430. customPage.pack(fill = tk.BOTH, expand = 1)
  431. # Line page #
  432. linePage = self.emitterNotebook.add('LineEmitter')
  433. self.createVector3Entry(linePage, 'Line Emitter', 'Min',
  434. 'Min point defining emitter line',
  435. command = self.setEmitterLinePoint1)
  436. self.createVector3Entry(linePage, 'Line Emitter', 'Max',
  437. 'Max point defining emitter line',
  438. command = self.setEmitterLinePoint2,
  439. value = (1.0, 0.0, 0.0))
  440. # Point page #
  441. emitterPointPage = self.emitterNotebook.add('PointEmitter')
  442. self.createVector3Entry(emitterPointPage, 'Point Emitter', 'Position',
  443. 'Position of emitter point',
  444. command = self.setEmitterPointPosition)
  445. # Rectangle #
  446. rectanglePage = self.emitterNotebook.add('RectangleEmitter')
  447. self.createVector2Entry(rectanglePage,
  448. 'Rectangle Emitter', 'Min',
  449. 'Point defining rectangle',
  450. command = self.setEmitterRectanglePoint1)
  451. self.createVector2Entry(rectanglePage,
  452. 'Rectangle Emitter', 'Max',
  453. 'Point defining rectangle',
  454. command = self.setEmitterRectanglePoint2)
  455. # Ring #
  456. ringPage = self.emitterNotebook.add('RingEmitter')
  457. self.createFloater(ringPage, 'Ring Emitter', 'Radius',
  458. 'Radius of ring',
  459. command = self.setEmitterRingRadius,
  460. min = 0.01)
  461. self.createFloater(ringPage, 'Ring Emitter', 'Radius Spread',
  462. 'Variation in radius of ring',
  463. command = self.setEmitterRingRadiusSpread,
  464. min = 0.0)
  465. self.ringCustomFrame = tk.Frame(ringPage)
  466. self.createAngleDial(self.ringCustomFrame, 'Ring Emitter', 'Angle',
  467. 'Particle launch angle',
  468. command = self.setEmitterRingLaunchAngle)
  469. self.ringCustomFrame.pack(fill = tk.BOTH, expand = 1)
  470. # Sphere volume #
  471. sphereVolumePage = self.emitterNotebook.add('SphereVolumeEmitter')
  472. self.createFloater(sphereVolumePage, 'Sphere Volume Emitter', 'Radius',
  473. 'Radius of sphere',
  474. command = self.setEmitterSphereVolumeRadius,
  475. min = 0.01)
  476. # Sphere surface #
  477. sphereSurfacePage = self.emitterNotebook.add('SphereSurfaceEmitter')
  478. self.createFloater(sphereSurfacePage, 'Sphere Surface Emitter',
  479. 'Radius',
  480. 'Radius of sphere',
  481. command = self.setEmitterSphereSurfaceRadius,
  482. min = 0.01)
  483. # Tangent ring #
  484. tangentRingPage = self.emitterNotebook.add('TangentRingEmitter')
  485. self.createFloater(tangentRingPage, 'Tangent Ring Emitter', 'Radius',
  486. 'Radius of ring',
  487. command = self.setEmitterTangentRingRadius,
  488. min = 0.01)
  489. self.createFloater(tangentRingPage, 'Tangent Ring Emitter',
  490. 'Radius Spread',
  491. 'Variation in radius of ring',
  492. command = self.setEmitterTangentRingRadiusSpread)
  493. self.emitterNotebook.pack(fill = tk.X)
  494. ## RENDERER PAGE WIDGETS ##
  495. self.createOptionMenu(
  496. rendererPage, 'Renderer', 'Renderer Type',
  497. 'Select type of particle renderer',
  498. ('LineParticleRenderer', 'GeomParticleRenderer',
  499. 'PointParticleRenderer', 'SparkleParticleRenderer',
  500. 'SpriteParticleRenderer'),
  501. self.selectRendererType)
  502. self.createOptionMenu(rendererPage,
  503. 'Renderer', 'Alpha Mode',
  504. "alpha setting over particles' lifetime",
  505. ('NO_ALPHA','ALPHA_IN','ALPHA_OUT',
  506. 'ALPHA_IN_OUT', 'ALPHA_USER'),
  507. self.setRendererAlphaMode)
  508. self.createSlider(
  509. rendererPage, 'Renderer', 'User Alpha',
  510. 'alpha value for ALPHA_USER alpha mode',
  511. command = self.setRendererUserAlpha)
  512. self.rendererNotebook = Pmw.NoteBook(rendererPage, tabpos = None)
  513. self.rendererNotebook.pack(fill = tk.BOTH, expand = 1)
  514. # Line page #
  515. linePage = self.rendererNotebook.add('LineParticleRenderer')
  516. self.createColorEntry(linePage, 'Line Renderer', 'Head Color',
  517. 'Head color of line',
  518. command = self.setRendererLineHeadColor)
  519. self.createColorEntry(linePage, 'Line Renderer', 'Tail Color',
  520. 'Tail color of line',
  521. command = self.setRendererLineTailColor)
  522. self.createFloater(linePage, 'Line Renderer', 'Line Scale Factor',
  523. 'Scale Factor applied to length of line',
  524. command = self.setRendererLineScaleFactor)
  525. ############################################################################
  526. # GEOM PARTICLE RENDERER PAGE #
  527. ############################################################################
  528. geomPage = self.rendererNotebook.add('GeomParticleRenderer')
  529. f = tk.Frame(geomPage)
  530. f.pack(fill = tk.X)
  531. # Geom Node input field
  532. tk.Label(f, width = 12, text = 'Geom Node', pady = 3).pack(side = tk.LEFT)
  533. self.rendererGeomNode = tk.StringVar()
  534. self.rendererGeomNodeEntry = tk.Entry(f, width = 12,
  535. textvariable = self.rendererGeomNode)
  536. self.rendererGeomNodeEntry.bind('<Return>', self.setRendererGeomNode)
  537. self.rendererGeomNodeEntry.pack(side = tk.LEFT, expand = 1, fill = tk.X)
  538. # Setup frames
  539. f = tk.Frame(geomPage)
  540. f.pack(fill = tk.BOTH, expand = 1)
  541. rendererGeomNotebook = Pmw.NoteBook(f)
  542. rendererGeomNotebook.pack(fill = tk.BOTH, expand = 1)
  543. rendererGeomBlendPage = rendererGeomNotebook.add('Blend')
  544. rendererGeomScalePage = rendererGeomNotebook.add('Scale')
  545. rendererGeomInterpolationPage = rendererGeomNotebook.add('Interpolate')
  546. ############################################################################
  547. # Blend tab
  548. p = tk.Frame(rendererGeomBlendPage)
  549. p.pack(fill = tk.X)
  550. self.createOptionMenu(p, 'Geom Renderer',
  551. 'Color Blend',
  552. 'How to render semi-transparent colors',
  553. ('MNone','MAdd','MSubtract','MInvSubtract','MMin','MMax'),
  554. self.setRendererGeomColorBlendMethod)
  555. self.createOptionMenu(p, 'Geom Renderer',
  556. 'Incoming Op.',
  557. 'See ColorBlendAttrib.h for explanation',
  558. ('OOne','OIncomingColor','OOneMinusIncomingColor','OFbufferColor',
  559. 'OOneMinusFbufferColor','OIncomingAlpha','OOneMinusIncomingAlpha',
  560. 'OFbufferAlpha','OOneMinusFbufferAlpha','OConstantColor',
  561. 'OOneMinusConstantColor','OConstantAlpha','OOneMinusConstantAlpha',
  562. 'OIncomingColorSaturate','OZero'),
  563. self.setRendererGeomColorBlendIncomingOperand)
  564. self.getVariable('Geom Renderer','Incoming Op.').set('OIncomingAlpha')
  565. self.createOptionMenu(p, 'Geom Renderer',
  566. 'Fbuffer Op.',
  567. 'See ColorBlendAttrib.h for explanation',
  568. ('OOne','OIncomingColor','OOneMinusIncomingColor','OFbufferColor',
  569. 'OOneMinusFbufferColor','OIncomingAlpha','OOneMinusIncomingAlpha',
  570. 'OFbufferAlpha','OOneMinusFbufferAlpha','OConstantColor',
  571. 'OOneMinusConstantColor','OConstantAlpha','OOneMinusConstantAlpha',
  572. 'OZero'),
  573. self.setRendererGeomColorBlendFbufferOperand)
  574. self.getVariable('Geom Renderer','Fbuffer Op.').set('OOneMinusIncomingAlpha')
  575. ############################################################################
  576. # Scale tab
  577. p = tk.Frame(rendererGeomScalePage)
  578. p.pack(fill = tk.X)
  579. self.createCheckbutton(
  580. p, 'Geom Renderer', 'X Scale',
  581. ("On: x scale is interpolated over particle's life; " +
  582. "Off: stays as start_X_Scale"),
  583. self.toggleRendererGeomXScale, 0, side = tk.LEFT)
  584. self.createCheckbutton(
  585. p, 'Geom Renderer', 'Y Scale',
  586. ("On: y scale is interpolated over particle's life; " +
  587. "Off: stays as start_Y_Scale"),
  588. self.toggleRendererGeomYScale, 0, side = tk.LEFT)
  589. self.createCheckbutton(
  590. p, 'Geom Renderer', 'Z Scale',
  591. ("On: z scale is interpolated over particle's life; " +
  592. "Off: stays as start_Z_Scale"),
  593. self.toggleRendererGeomZScale, 0, side = tk.LEFT)
  594. p = tk.Frame(rendererGeomScalePage)
  595. p.pack(fill = tk.X)
  596. self.createFloater(p, 'Geom Renderer',
  597. 'Initial X Scale',
  598. 'Initial X scaling factor',
  599. command = self.setRendererGeomInitialXScale)
  600. self.createFloater(p, 'Geom Renderer',
  601. 'Final X Scale',
  602. 'Final X scaling factor, if xScale enabled',
  603. command = self.setRendererGeomFinalXScale)
  604. self.createFloater(p, 'Geom Renderer',
  605. 'Initial Y Scale',
  606. 'Initial Y scaling factor',
  607. command = self.setRendererGeomInitialYScale)
  608. self.createFloater(p, 'Geom Renderer',
  609. 'Final Y Scale',
  610. 'Final Y scaling factor, if yScale enabled',
  611. command = self.setRendererGeomFinalYScale)
  612. self.createFloater(p, 'Geom Renderer',
  613. 'Initial Z Scale',
  614. 'Initial Z scaling factor',
  615. command = self.setRendererGeomInitialZScale)
  616. self.createFloater(p, 'Geom Renderer',
  617. 'Final Z Scale',
  618. 'Final Z scaling factor, if zScale enabled',
  619. command = self.setRendererGeomFinalZScale)
  620. ############################################################################
  621. # Interpolate tab
  622. p = tk.Frame(rendererGeomInterpolationPage)
  623. p.pack(fill = tk.X)
  624. addSegmentButton = tk.Menubutton(p, text = 'Add Segment',
  625. relief = tk.RAISED,
  626. borderwidth = 2,
  627. font=('MSSansSerif', 14, 'bold'),
  628. activebackground = '#909090')
  629. segmentMenu = tk.Menu(addSegmentButton)
  630. addSegmentButton['menu'] = segmentMenu
  631. segmentMenu.add_command(label = 'Add Constant segment',
  632. command = self.addConstantInterpolationSegment)
  633. segmentMenu.add_command(label = 'Add Linear segment',
  634. command = self.addLinearInterpolationSegment)
  635. segmentMenu.add_command(label = 'Add Stepwave segment',
  636. command = self.addStepwaveInterpolationSegment)
  637. segmentMenu.add_command(label = 'Add Sinusoid segment',
  638. command = self.addSinusoidInterpolationSegment)
  639. addSegmentButton.pack(expand = 0)
  640. sf = Pmw.ScrolledFrame(p, horizflex = 'elastic')
  641. sf.pack(fill = tk.BOTH, expand = 1)
  642. self.rendererGeomSegmentFrame = sf.interior()
  643. self.rendererGeomSegmentFrame.pack(fill = tk.BOTH, expand = 1)
  644. self.rendererSegmentWidgetList = []
  645. rendererGeomNotebook.setnaturalsize()
  646. ############################################################################
  647. # POINT PARTICLE RENDERER PAGE #
  648. ############################################################################
  649. rendererPointPage = self.rendererNotebook.add('PointParticleRenderer')
  650. self.createFloater(rendererPointPage, 'Point Renderer', 'Point Size',
  651. 'Width and height of points in pixels',
  652. command = self.setRendererPointSize)
  653. self.createColorEntry(rendererPointPage, 'Point Renderer',
  654. 'Start Color',
  655. 'Starting color of point',
  656. command = self.setRendererPointStartColor)
  657. self.createColorEntry(rendererPointPage, 'Point Renderer',
  658. 'End Color',
  659. 'Ending color of point',
  660. command = self.setRendererPointEndColor)
  661. self.createOptionMenu(rendererPointPage, 'Point Renderer',
  662. 'Blend Type',
  663. 'Type of color blending used for particle',
  664. ('PP_ONE_COLOR', 'PP_BLEND_LIFE',
  665. 'PP_BLEND_VEL'),
  666. self.rendererPointSelectBlendType)
  667. self.createOptionMenu(rendererPointPage, 'Point Renderer',
  668. 'Blend Method',
  669. 'Interpolation method between colors',
  670. ('PP_NO_BLEND', 'PP_BLEND_LINEAR',
  671. 'PP_BLEND_CUBIC'),
  672. self.rendererPointSelectBlendMethod)
  673. # Sparkle #
  674. sparklePage = self.rendererNotebook.add('SparkleParticleRenderer')
  675. self.createColorEntry(sparklePage, 'Sparkle Renderer',
  676. 'Center Color',
  677. 'Color of sparkle center',
  678. command = self.setRendererSparkleCenterColor)
  679. self.createColorEntry(sparklePage, 'Sparkle Renderer',
  680. 'Edge Color',
  681. 'Color of sparkle line endpoints',
  682. command = self.setRendererSparkleEdgeColor)
  683. self.createFloater(sparklePage, 'Sparkle Renderer',
  684. 'Birth Radius',
  685. 'Initial sparkle radius',
  686. command = self.setRendererSparkleBirthRadius)
  687. self.createFloater(sparklePage, 'Sparkle Renderer',
  688. 'Death Radius',
  689. 'Final sparkle radius',
  690. command = self.setRendererSparkleDeathRadius)
  691. self.createOptionMenu(sparklePage,
  692. 'Sparkle Renderer', 'Life Scale',
  693. 'Does particle scale over its lifetime?',
  694. ('SP_NO_SCALE', 'SP_SCALE'),
  695. self.setRendererSparkleLifeScale)
  696. # Sprite #
  697. spritePage = self.rendererNotebook.add('SpriteParticleRenderer')
  698. f = tk.Frame(spritePage)
  699. f.pack(fill = tk.BOTH, expand = 1)
  700. rendererSpriteNotebook = Pmw.NoteBook(f)
  701. rendererSpriteNotebook.pack(fill = tk.BOTH, expand = 1)
  702. rendererSpriteTexturePage = rendererSpriteNotebook.add('Texture')
  703. rendererSpriteScalePage = rendererSpriteNotebook.add('Scale')
  704. rendererSpriteBlendPage = rendererSpriteNotebook.add('Blend')
  705. rendererSpriteInterpolationPage = rendererSpriteNotebook.add('Interpolate')
  706. ##################################################################################
  707. p = tk.Frame(rendererSpriteTexturePage)
  708. p.pack(fill = tk.BOTH, expand = 1)
  709. bp = tk.Frame(p)
  710. bp.pack(expand = 0, side = tk.TOP)
  711. bbp = tk.Frame(bp)
  712. bbp.pack()
  713. self.createCheckbutton(
  714. bbp, 'Sprite Renderer', 'Enable Animation',
  715. ("On: Multitexture node will be animated; " +
  716. "Off: Only the first frame of a node is rendered"),
  717. self.setRendererSpriteAnimationEnable, 0, side = tk.LEFT)
  718. self.createFloater(bbp, 'Sprite Renderer', 'Frame Rate', 'Animation frame rate',
  719. command = self.setRendererSpriteAnimationFrameRate).pack(side = tk.LEFT)
  720. bbp = tk.Frame(bp)
  721. bbp.pack(pady=3)
  722. tk.Button(bbp, text = 'Add Texture',
  723. command = self.addRendererSpriteAnimationTexture).pack(pady = 3, padx = 15, side = tk.LEFT)
  724. tk.Button(bbp, text = 'Add Animation',
  725. command = self.addRendererSpriteAnimationFromNode).pack(pady = 3, padx = 15, side = tk.LEFT)
  726. pp = tk.Frame(p)
  727. pp.pack(fill = tk.BOTH, expand = 1, pady = 3)
  728. sf = Pmw.ScrolledFrame(pp, horizflex = 'elastic')
  729. sf.pack(fill = tk.BOTH, expand = 1)
  730. self.rendererSpriteAnimationFrame = sf.interior()
  731. self.rendererSpriteAnimationFrame.pack(fill = tk.BOTH, expand = 1)
  732. self.rendererSpriteAnimationWidgetList = []
  733. self.rendererSpriteTexture = tk.StringVar()
  734. self.rendererSpriteFile = tk.StringVar()
  735. self.rendererSpriteNode = tk.StringVar()
  736. ##################################################################################
  737. p = tk.Frame(rendererSpriteScalePage)
  738. p.pack(fill = tk.X)
  739. self.createCheckbutton(
  740. p, 'Sprite Renderer', 'X Scale',
  741. ("On: x scale is interpolated over particle's life; " +
  742. "Off: stays as start_X_Scale"),
  743. self.toggleRendererSpriteXScale, 0, side = tk.LEFT)
  744. self.createCheckbutton(
  745. p, 'Sprite Renderer', 'Y Scale',
  746. ("On: y scale is interpolated over particle's life; " +
  747. "Off: stays as start_Y_Scale"),
  748. self.toggleRendererSpriteYScale, 0, side = tk.LEFT)
  749. self.createCheckbutton(
  750. p, 'Sprite Renderer', 'Anim Angle',
  751. ("On: particles that are set to spin on the Z axis will " +
  752. "spin appropriately"),
  753. self.toggleRendererSpriteAnimAngle, 0, side = tk.LEFT)
  754. p = tk.Frame(rendererSpriteScalePage)
  755. p.pack(fill = tk.X)
  756. self.createFloater(p, 'Sprite Renderer',
  757. 'Initial X Scale',
  758. 'Initial X scaling factor',
  759. command = self.setRendererSpriteInitialXScale)
  760. self.createFloater(p, 'Sprite Renderer',
  761. 'Final X Scale',
  762. 'Final X scaling factor, if xScale enabled',
  763. command = self.setRendererSpriteFinalXScale)
  764. self.createFloater(p, 'Sprite Renderer',
  765. 'Initial Y Scale',
  766. 'Initial Y scaling factor',
  767. command = self.setRendererSpriteInitialYScale)
  768. self.createFloater(p, 'Sprite Renderer',
  769. 'Final Y Scale',
  770. 'Final Y scaling factor, if yScale enabled',
  771. command = self.setRendererSpriteFinalYScale)
  772. self.createAngleDial(p, 'Sprite Renderer',
  773. 'Non Animated Theta',
  774. ('If animAngle is false: counter clockwise ' +
  775. 'Z rotation of all sprites'),
  776. command = self.setRendererSpriteNonAnimatedTheta)
  777. p = tk.Frame(rendererSpriteBlendPage)
  778. p.pack(fill = tk.X)
  779. self.createOptionMenu(p, 'Sprite Renderer',
  780. 'Blend Type',
  781. 'Interpolation blend type for X and Y scaling',
  782. ('PP_NO_BLEND', 'PP_LINEAR', 'PP_CUBIC'),
  783. self.setRendererSpriteBlendMethod)
  784. self.createCheckbutton(
  785. p, 'Sprite Renderer', 'Alpha Disable',
  786. 'On: alpha blending is disabled',
  787. self.toggleRendererSpriteAlphaDisable, 0)
  788. self.createOptionMenu(p, 'Sprite Renderer',
  789. 'Color Blend',
  790. 'How to render semi-transparent colors',
  791. ('MNone','MAdd','MSubtract','MInvSubtract','MMin','MMax'),
  792. self.setRendererSpriteColorBlendMethod)
  793. self.createOptionMenu(p, 'Sprite Renderer',
  794. 'Incoming Op.',
  795. 'See ColorBlendAttrib.h for explanation',
  796. ('OOne','OIncomingColor','OOneMinusIncomingColor','OFbufferColor',
  797. 'OOneMinusFbufferColor','OIncomingAlpha','OOneMinusIncomingAlpha',
  798. 'OFbufferAlpha','OOneMinusFbufferAlpha','OConstantColor',
  799. 'OOneMinusConstantColor','OConstantAlpha','OOneMinusConstantAlpha',
  800. 'OIncomingColorSaturate','OZero'),
  801. self.setRendererSpriteColorBlendIncomingOperand)
  802. self.getVariable('Sprite Renderer','Incoming Op.').set('OIncomingAlpha')
  803. self.createOptionMenu(p, 'Sprite Renderer',
  804. 'Fbuffer Op.',
  805. 'See ColorBlendAttrib.h for explanation',
  806. ('OOne','OIncomingColor','OOneMinusIncomingColor','OFbufferColor',
  807. 'OOneMinusFbufferColor','OIncomingAlpha','OOneMinusIncomingAlpha',
  808. 'OFbufferAlpha','OOneMinusFbufferAlpha','OConstantColor',
  809. 'OOneMinusConstantColor','OConstantAlpha','OOneMinusConstantAlpha',
  810. 'OZero'),
  811. self.setRendererSpriteColorBlendFbufferOperand)
  812. self.getVariable('Sprite Renderer','Fbuffer Op.').set('OOneMinusIncomingAlpha')
  813. p = tk.Frame(rendererSpriteInterpolationPage)
  814. p.pack(fill = tk.BOTH, expand = 1)
  815. addSegmentButton = tk.Menubutton(p, text = 'Add Segment',
  816. relief = tk.RAISED,
  817. borderwidth = 2,
  818. font=('MSSansSerif', 14, 'bold'),
  819. activebackground = '#909090')
  820. segmentMenu = tk.Menu(addSegmentButton)
  821. addSegmentButton['menu'] = segmentMenu
  822. segmentMenu.add_command(label = 'Add Constant segment',
  823. command = self.addConstantInterpolationSegment)
  824. segmentMenu.add_command(label = 'Add Linear segment',
  825. command = self.addLinearInterpolationSegment)
  826. segmentMenu.add_command(label = 'Add Stepwave segment',
  827. command = self.addStepwaveInterpolationSegment)
  828. segmentMenu.add_command(label = 'Add Sinusoid segment',
  829. command = self.addSinusoidInterpolationSegment)
  830. addSegmentButton.pack(expand = 0)
  831. pp = tk.Frame(p)
  832. pp.pack(fill = tk.BOTH, expand = 1, pady = 3)
  833. sf = Pmw.ScrolledFrame(pp, horizflex = 'elastic')
  834. sf.pack(fill = tk.BOTH, expand = 1)
  835. self.rendererSpriteSegmentFrame = sf.interior()
  836. self.rendererSpriteSegmentFrame.pack(fill = tk.BOTH, expand = 1)
  837. self.rendererSegmentWidgetList = []
  838. rendererSpriteNotebook.setnaturalsize()
  839. ##########################################################
  840. ## FORCE PAGE WIDGETS ##
  841. self.addForceButton = tk.Menubutton(forcePage, text = 'Add Force',
  842. relief = tk.RAISED,
  843. borderwidth = 2,
  844. font=('MSSansSerif', 14, 'bold'),
  845. activebackground = '#909090')
  846. forceMenu = tk.Menu(self.addForceButton)
  847. self.addForceButton['menu'] = forceMenu
  848. # DERIVED FROM LINEAR FORCE
  849. # This also has: setVector
  850. forceMenu.add_command(label = 'Add Linear Vector Force',
  851. command = self.addLinearVectorForce)
  852. # Parameters: setAmplitude, setMassDependent, setVectorMasks
  853. forceMenu.add_command(label = 'Add Linear Noise Force',
  854. command = self.addLinearNoiseForce)
  855. forceMenu.add_command(label = 'Add Linear Jitter Force',
  856. command = self.addLinearJitterForce)
  857. # This also has setCoef
  858. forceMenu.add_command(label = 'Add Linear Friction Force',
  859. command = self.addLinearFrictionForce)
  860. # This also has: setCoef, setLength, setRadius,
  861. forceMenu.add_command(label = 'Add Linear Cylinder Vortex Force',
  862. command = self.addLinearCylinderVortexForce)
  863. # DERIVED FROM LINEAR DISTANCE FORCE
  864. # Parameters: setFalloffType, setForceCenter, setRadius
  865. forceMenu.add_command(label = 'Add Linear Sink Force',
  866. command = self.addLinearSinkForce)
  867. forceMenu.add_command(label = 'Add Linear Source Force',
  868. command = self.addLinearSourceForce)
  869. """
  870. # Avoid for now
  871. forceMenu.add_command(label = 'Add Linear User Defined Force',
  872. command = self.addLinearUserDefinedForce)
  873. """
  874. self.addForceButton.pack(expand = 0)
  875. # Scrolled frame to hold force widgets
  876. self.sf = Pmw.ScrolledFrame(forcePage, horizflex = 'elastic')
  877. self.sf.pack(fill = 'both', expand = 1)
  878. self.forceFrame = self.sf.interior()
  879. # Notebook to hold force widgets as the are added
  880. self.forceGroupNotebook = Pmw.NoteBook(self.forceFrame, tabpos = None)
  881. self.forceGroupNotebook.pack(fill = tk.X)
  882. ########################################################################
  883. self.factoryNotebook.setnaturalsize()
  884. self.emitterNotebook.setnaturalsize()
  885. self.rendererNotebook.setnaturalsize()
  886. self.forceGroupNotebook.setnaturalsize()
  887. self.mainNotebook.setnaturalsize()
  888. # Make sure input variables processed
  889. self.initialiseoptions(ParticlePanel)
  890. ### WIDGET UTILITY FUNCTIONS ###
  891. def createCheckbutton(self, parent, category, text,
  892. balloonHelp, command, initialState, side = 'top'):
  893. bool = tk.BooleanVar()
  894. bool.set(initialState)
  895. widget = tk.Checkbutton(parent, text = text, anchor = tk.W,
  896. variable = bool)
  897. # Do this after the widget so command isn't called on creation
  898. widget['command'] = command
  899. widget.pack(fill = tk.X, side = side)
  900. self.bind(widget, balloonHelp)
  901. self.widgetDict[category + '-' + text] = widget
  902. self.variableDict[category + '-' + text] = bool
  903. return widget
  904. def createRadiobutton(self, parent, side, category, text,
  905. balloonHelp, variable, value,
  906. command):
  907. widget = tk.Radiobutton(parent, text = text, anchor = tk.W,
  908. variable = variable, value = value)
  909. # Do this after the widget so command isn't called on creation
  910. widget['command'] = command
  911. widget.pack(side = side, fill = tk.X)
  912. self.bind(widget, balloonHelp)
  913. self.widgetDict[category + '-' + text] = widget
  914. return widget
  915. def createFloaters(self, parent, widgetDefinitions):
  916. widgets = []
  917. for category, label, balloonHelp, command, min, max, resolution in widgetDefinitions:
  918. widgets.append(
  919. self.createFloater(parent, category, label, balloonHelp,
  920. command, min, max, resolution)
  921. )
  922. return widgets
  923. def createFloater(self, parent, category, text, balloonHelp,
  924. command = None, min = 0.0, max = None, resolution = None,
  925. numDigits = None, **kw):
  926. kw['text'] = text
  927. kw['min'] = min
  928. if max is not None:
  929. kw['max'] = max
  930. kw['resolution'] = resolution
  931. if numDigits is None:
  932. # If this is apparently an integer setting, show no decimals.
  933. if resolution is not None and int(resolution) == resolution and \
  934. (min is None or int(min) == min) and \
  935. (max is None or int(max) == max):
  936. numDigits = 0
  937. else:
  938. numDigits = 3
  939. kw['numDigits'] = numDigits
  940. widget = Floater.Floater(parent, **kw)
  941. # Do this after the widget so command isn't called on creation
  942. widget['command'] = command
  943. widget.pack(fill = tk.X)
  944. self.bind(widget, balloonHelp)
  945. self.widgetDict[category + '-' + text] = widget
  946. return widget
  947. def createAngleDial(self, parent, category, text, balloonHelp,
  948. command = None, **kw):
  949. kw['text'] = text
  950. kw['style'] = 'mini'
  951. widget = Dial.AngleDial(parent, **kw)
  952. # Do this after the widget so command isn't called on creation
  953. widget['command'] = command
  954. widget.pack(fill = tk.X)
  955. self.bind(widget, balloonHelp)
  956. self.widgetDict[category + '-' + text] = widget
  957. return widget
  958. def createSlider(self, parent, category, text, balloonHelp,
  959. command = None, min = 0.0, max = 1.0,
  960. resolution = 0.001, **kw):
  961. kw['text'] = text
  962. kw['min'] = min
  963. kw['max'] = max
  964. kw['resolution'] = resolution
  965. widget = Slider.Slider(parent, **kw)
  966. # Do this after the widget so command isn't called on creation
  967. widget['command'] = command
  968. widget.pack(fill = tk.X)
  969. self.bind(widget, balloonHelp)
  970. self.widgetDict[category + '-' + text] = widget
  971. return widget
  972. def createVector2Entry(self, parent, category, text, balloonHelp,
  973. command = None, **kw):
  974. # Set label's text
  975. kw['text'] = text
  976. widget = VectorWidgets.Vector2Entry(parent, **kw)
  977. # Do this after the widget so command isn't called on creation
  978. widget['command'] = command
  979. widget.pack(fill = tk.X)
  980. self.bind(widget, balloonHelp)
  981. self.widgetDict[category + '-' + text] = widget
  982. return widget
  983. def createVector3Entry(self, parent, category, text, balloonHelp,
  984. command = None, **kw):
  985. # Set label's text
  986. kw['text'] = text
  987. widget = VectorWidgets.Vector3Entry(parent, **kw)
  988. # Do this after the widget so command isn't called on creation
  989. widget['command'] = command
  990. widget.pack(fill = tk.X)
  991. self.bind(widget, balloonHelp)
  992. self.widgetDict[category + '-' + text] = widget
  993. return widget
  994. def createColorEntry(self, parent, category, text, balloonHelp,
  995. command = None, **kw):
  996. # Set label's text
  997. kw['text'] = text
  998. widget = VectorWidgets.ColorEntry(parent, **kw)
  999. # Do this after the widget so command isn't called on creation
  1000. widget['command'] = command
  1001. widget.pack(fill = tk.X)
  1002. self.bind(widget, balloonHelp)
  1003. self.widgetDict[category + '-' + text] = widget
  1004. return widget
  1005. def createOptionMenu(self, parent, category, text, balloonHelp,
  1006. items, command):
  1007. optionVar = tk.StringVar()
  1008. if len(items) > 0:
  1009. optionVar.set(items[0])
  1010. widget = Pmw.OptionMenu(parent, labelpos = tk.W, label_text = text,
  1011. label_width = 12, menu_tearoff = 1,
  1012. menubutton_textvariable = optionVar,
  1013. items = items)
  1014. # Do this after the widget so command isn't called on creation
  1015. widget['command'] = command
  1016. widget.pack(fill = tk.X)
  1017. self.bind(widget.component('menubutton'), balloonHelp)
  1018. self.widgetDict[category + '-' + text] = widget
  1019. self.variableDict[category + '-' + text] = optionVar
  1020. return optionVar
  1021. def createComboBox(self, parent, category, text, balloonHelp,
  1022. items, command, history = 0):
  1023. widget = Pmw.ComboBox(parent,
  1024. labelpos = tk.W,
  1025. label_text = text,
  1026. label_anchor = 'w',
  1027. label_width = 12,
  1028. entry_width = 16,
  1029. history = history,
  1030. scrolledlist_items = items)
  1031. # Don't allow user to edit entryfield
  1032. widget.configure(entryfield_entry_state = 'disabled')
  1033. # Select first item if it exists
  1034. if len(items) > 0:
  1035. widget.selectitem(items[0])
  1036. # Bind selection command
  1037. widget['selectioncommand'] = command
  1038. widget.pack(side = 'left', expand = 0)
  1039. # Bind help
  1040. self.bind(widget, balloonHelp)
  1041. # Record widget
  1042. self.widgetDict[category + '-' + text] = widget
  1043. return widget
  1044. def updateMenusAndLabels(self):
  1045. self.updateMenus()
  1046. self.updateLabels()
  1047. def updateLabels(self):
  1048. self.effectsLabel['text'] = self.particleEffect.getName()
  1049. self.particlesLabel['text'] = self.particles.getName()
  1050. if self.forceGroup is not None:
  1051. self.forceGroupLabel['text'] = self.forceGroup.getName()
  1052. else:
  1053. self.forceGroupLabel['text'] = 'Force Group'
  1054. def updateMenus(self):
  1055. self.updateEffectsMenus()
  1056. self.updateParticlesMenus()
  1057. self.updateForceGroupMenus()
  1058. def updateEffectsMenus(self):
  1059. # Get rid of old effects entries if any
  1060. self.effectsEnableMenu.delete(0, 'end')
  1061. self.effectsLabelMenu.delete(5, 'end')
  1062. self.effectsLabelMenu.add_separator()
  1063. # Add in a checkbutton for each effect (to toggle on/off)
  1064. keys = sorted(self.effectsDict.keys())
  1065. for name in keys:
  1066. effect = self.effectsDict[name]
  1067. self.effectsLabelMenu.add_command(
  1068. label = effect.getName(),
  1069. command = (lambda s = self,
  1070. e = effect: s.selectEffectNamed(e.getName()))
  1071. )
  1072. effectActive = tk.IntVar()
  1073. effectActive.set(effect.isEnabled())
  1074. self.effectsEnableMenu.add_checkbutton(
  1075. label = effect.getName(),
  1076. variable = effectActive,
  1077. command = (lambda s = self,
  1078. e = effect,
  1079. v = effectActive: s.toggleEffect(e, v)))
  1080. def updateParticlesMenus(self):
  1081. # Get rid of old particles entries if any
  1082. self.particlesEnableMenu.delete(0, 'end')
  1083. self.particlesLabelMenu.delete(2, 'end')
  1084. self.particlesLabelMenu.add_separator()
  1085. # Add in a checkbutton for each effect (to toggle on/off)
  1086. particles = self.particleEffect.getParticlesList()
  1087. for name in sorted(x.getName() for x in particles):
  1088. particle = self.particleEffect.getParticlesNamed(name)
  1089. self.particlesLabelMenu.add_command(
  1090. label = name,
  1091. command = (lambda s = self,
  1092. n = name: s.selectParticlesNamed(n))
  1093. )
  1094. particleActive = tk.IntVar()
  1095. particleActive.set(particle.isEnabled())
  1096. self.particlesEnableMenu.add_checkbutton(
  1097. label = name,
  1098. variable = particleActive,
  1099. command = (lambda s = self,
  1100. p = particle,
  1101. v = particleActive: s.toggleParticles(p, v)))
  1102. def updateForceGroupMenus(self):
  1103. # Get rid of old forceGroup entries if any
  1104. self.forceGroupEnableMenu.delete(0, 'end')
  1105. self.forceGroupLabelMenu.delete(2, 'end')
  1106. self.forceGroupLabelMenu.add_separator()
  1107. # Add in a checkbutton for each effect (to toggle on/off)
  1108. forceGroupList = self.particleEffect.getForceGroupList()
  1109. for name in sorted(x.getName() for x in forceGroupList):
  1110. force = self.particleEffect.getForceGroupNamed(name)
  1111. self.forceGroupLabelMenu.add_command(
  1112. label = name,
  1113. command = (lambda s = self,
  1114. n = name: s.selectForceGroupNamed(n))
  1115. )
  1116. forceActive = tk.IntVar()
  1117. forceActive.set(force.isEnabled())
  1118. self.forceGroupEnableMenu.add_checkbutton(
  1119. label = name,
  1120. variable = forceActive,
  1121. command = (lambda s = self,
  1122. f = force,
  1123. v = forceActive: s.toggleForceGroup(f, v)))
  1124. def selectEffectNamed(self, name):
  1125. effect = self.effectsDict.get(name, None)
  1126. if effect is not None:
  1127. self.particleEffect = effect
  1128. # Default to first particle in particlesDict
  1129. self.particles = self.particleEffect.getParticlesList()[0]
  1130. # See if particle effect has any forceGroup
  1131. forceGroupList = self.particleEffect.getForceGroupList()
  1132. if len(forceGroupList) > 0:
  1133. self.forceGroup = forceGroupList[0]
  1134. else:
  1135. self.forceGroup = None
  1136. self.mainNotebook.selectpage('System')
  1137. self.updateInfo('System')
  1138. else:
  1139. print('ParticlePanel: No effect named ' + name)
  1140. def toggleEffect(self, effect, var):
  1141. if var.get():
  1142. effect.enable()
  1143. else:
  1144. effect.disable()
  1145. def selectParticlesNamed(self, name):
  1146. particles = self.particleEffect.getParticlesNamed(name)
  1147. if particles is not None:
  1148. self.particles = particles
  1149. self.updateInfo()
  1150. def toggleParticles(self, particles, var):
  1151. if var.get():
  1152. particles.enable()
  1153. else:
  1154. particles.disable()
  1155. def selectForceGroupNamed(self, name):
  1156. forceGroup = self.particleEffect.getForceGroupNamed(name)
  1157. if forceGroup is not None:
  1158. self.forceGroup = forceGroup
  1159. self.updateInfo('Force')
  1160. def toggleForceGroup(self, forceGroup, var):
  1161. if var.get():
  1162. forceGroup.enable()
  1163. else:
  1164. forceGroup.disable()
  1165. def toggleForce(self, force, pageName, variableName):
  1166. v = self.getVariable(pageName, variableName)
  1167. if v.get():
  1168. force.setActive(1)
  1169. else:
  1170. force.setActive(0)
  1171. def getWidget(self, category, text):
  1172. return self.widgetDict[category + '-' + text]
  1173. def getVariable(self, category, text):
  1174. return self.variableDict[category + '-' + text]
  1175. def loadParticleEffectFromFile(self):
  1176. # Find path to particle directory
  1177. pPath = particlePath
  1178. if pPath.getNumDirectories() > 0:
  1179. if repr(pPath.getDirectory(0)) == '.':
  1180. path = '.'
  1181. else:
  1182. path = pPath.getDirectory(0).toOsSpecific()
  1183. else:
  1184. path = '.'
  1185. if not os.path.isdir(path):
  1186. print('ParticlePanel Warning: Invalid default DNA directory!')
  1187. print('Using current directory')
  1188. path = '.'
  1189. particleFilename = askopenfilename(
  1190. defaultextension = '.ptf',
  1191. filetypes = (('Particle Files', '*.ptf'), ('All files', '*')),
  1192. initialdir = path,
  1193. title = 'Load Particle Effect',
  1194. parent = self.parent)
  1195. if particleFilename and particleFilename != 'None':
  1196. # Delete existing particles and forces
  1197. self.particleEffect.loadConfig(
  1198. Filename.fromOsSpecific(particleFilename))
  1199. self.selectEffectNamed(self.particleEffect.getName())
  1200. # Enable effect
  1201. self.particleEffect.enable()
  1202. def saveParticleEffectToFile(self):
  1203. # Find path to particle directory
  1204. pPath = particlePath
  1205. if pPath.getNumDirectories() > 0:
  1206. if repr(pPath.getDirectory(0)) == '.':
  1207. path = '.'
  1208. else:
  1209. path = pPath.getDirectory(0).toOsSpecific()
  1210. else:
  1211. path = '.'
  1212. if not os.path.isdir(path):
  1213. print('ParticlePanel Warning: Invalid default DNA directory!')
  1214. print('Using current directory')
  1215. path = '.'
  1216. particleFilename = asksaveasfilename(
  1217. defaultextension = '.ptf',
  1218. filetypes = (('Particle Files', '*.ptf'), ('All files', '*')),
  1219. initialdir = path,
  1220. title = 'Save Particle Effect as',
  1221. parent = self.parent)
  1222. if particleFilename:
  1223. self.particleEffect.saveConfig(Filename(particleFilename))
  1224. ### PARTICLE EFFECTS COMMANDS ###
  1225. def toggleParticleMgr(self):
  1226. if self.particleMgrActive.get():
  1227. base.enableParticles()
  1228. else:
  1229. base.disableParticles()
  1230. ### PARTICLE SYSTEM COMMANDS ###
  1231. def updateInfo(self, page = 'System'):
  1232. self.updateMenusAndLabels()
  1233. if page == 'System':
  1234. self.updateSystemWidgets()
  1235. elif page == 'Factory':
  1236. self.selectFactoryPage()
  1237. self.updateFactoryWidgets()
  1238. elif page == 'Emitter':
  1239. self.selectEmitterPage()
  1240. self.updateEmitterWidgets()
  1241. elif page == 'Renderer':
  1242. self.selectRendererPage()
  1243. self.updateRendererWidgets()
  1244. elif page == 'Force':
  1245. self.updateForceWidgets()
  1246. def toggleParticleEffect(self):
  1247. if self.getVariable('Effect', 'Active').get():
  1248. self.particleEffect.enable()
  1249. else:
  1250. self.particleEffect.disable()
  1251. ## SYSTEM PAGE ##
  1252. def updateSystemWidgets(self):
  1253. poolSize = self.particles.getPoolSize()
  1254. self.getWidget('System', 'Pool Size').set(int(poolSize), 0)
  1255. birthRate = self.particles.getBirthRate()
  1256. self.getWidget('System', 'Birth Rate').set(birthRate, 0)
  1257. litterSize = self.particles.getLitterSize()
  1258. self.getWidget('System', 'Litter Size').set(int(litterSize), 0)
  1259. litterSpread = self.particles.getLitterSpread()
  1260. self.getWidget('System', 'Litter Spread').set(litterSpread, 0)
  1261. systemLifespan = self.particles.getSystemLifespan()
  1262. self.getWidget('System', 'Lifespan').set(systemLifespan, 0)
  1263. pos = self.particles.nodePath.getPos()
  1264. self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0)
  1265. hpr = self.particles.nodePath.getHpr()
  1266. self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0)
  1267. self.getVariable('System', 'Render Space Velocities').set(
  1268. self.particles.getLocalVelocityFlag())
  1269. self.getVariable('System', 'System Grows Older').set(
  1270. self.particles.getSystemGrowsOlderFlag())
  1271. def setSystemPoolSize(self, value):
  1272. self.particles.setPoolSize(int(value))
  1273. def setSystemBirthRate(self, value):
  1274. self.particles.setBirthRate(value)
  1275. def setSystemLitterSize(self, value):
  1276. self.particles.setLitterSize(int(value))
  1277. def setSystemLitterSpread(self, value):
  1278. self.particles.setLitterSpread(int(value))
  1279. def setSystemLifespan(self, value):
  1280. self.particles.setSystemLifespan(value)
  1281. def toggleSystemLocalVelocity(self):
  1282. self.particles.setLocalVelocityFlag(
  1283. self.getVariable('System', 'Render Space Velocities').get())
  1284. def toggleSystemGrowsOlder(self):
  1285. self.particles.setSystemGrowsOlderFlag(
  1286. self.getVariable('System', 'System Grows Older').get())
  1287. def setSystemPos(self, pos):
  1288. self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2]))
  1289. def setSystemHpr(self, pos):
  1290. self.particles.nodePath.setHpr(Vec3(pos[0], pos[1], pos[2]))
  1291. ## FACTORY PAGE ##
  1292. def selectFactoryType(self, type):
  1293. self.factoryNotebook.selectpage(type)
  1294. self.particles.setFactory(type)
  1295. self.updateFactoryWidgets()
  1296. def selectFactoryPage(self):
  1297. pass
  1298. def updateFactoryWidgets(self):
  1299. factory = self.particles.factory
  1300. lifespan = factory.getLifespanBase()
  1301. self.getWidget('Factory', 'Life Span').set(lifespan, 0)
  1302. lifespanSpread = factory.getLifespanSpread()
  1303. self.getWidget('Factory', 'Life Span Spread').set(lifespanSpread, 0)
  1304. mass = factory.getMassBase()
  1305. self.getWidget('Factory', 'Mass').set(mass, 0)
  1306. massSpread = factory.getMassSpread()
  1307. self.getWidget('Factory', 'Mass Spread').set(massSpread, 0)
  1308. terminalVelocity = factory.getTerminalVelocityBase()
  1309. self.getWidget('Factory', 'Terminal Velocity').set(terminalVelocity, 0)
  1310. terminalVelocitySpread = factory.getTerminalVelocitySpread()
  1311. self.getWidget('Factory', 'Terminal Vel. Spread').set(
  1312. terminalVelocitySpread, 0)
  1313. def setFactoryLifeSpan(self, value):
  1314. self.particles.factory.setLifespanBase(value)
  1315. def setFactoryLifeSpanSpread(self, value):
  1316. self.particles.factory.setLifespanSpread(value)
  1317. def setFactoryParticleMass(self, value):
  1318. self.particles.factory.setMassBase(value)
  1319. def setFactoryParticleMassSpread(self, value):
  1320. self.particles.factory.setMassSpread(value)
  1321. def setFactoryTerminalVelocity(self, value):
  1322. self.particles.factory.setTerminalVelocityBase(value)
  1323. def setFactoryTerminalVelocitySpread(self, value):
  1324. self.particles.factory.setTerminalVelocitySpread(value)
  1325. # Point Page #
  1326. # Z Spin Page #
  1327. def setFactoryZSpinInitialAngle(self, angle):
  1328. self.particles.factory.setInitialAngle(angle)
  1329. def setFactoryZSpinInitialAngleSpread(self, spread):
  1330. self.particles.factory.setInitialAngleSpread(spread)
  1331. def setFactoryZSpinFinalAngle(self, angle):
  1332. self.particles.factory.setFinalAngle(angle)
  1333. def setFactoryZSpinFinalAngleSpread(self, spread):
  1334. self.particles.factory.setFinalAngleSpread(spread)
  1335. def setFactoryZSpinAngularVelocity(self, vel):
  1336. self.particles.factory.setAngularVelocity(vel)
  1337. def setFactoryZSpinAngularVelocitySpread(self, spread):
  1338. self.particles.factory.setAngularVelocitySpread(spread)
  1339. ## EMITTER PAGE ##
  1340. def selectEmitterType(self, type):
  1341. self.emitterNotebook.selectpage(type)
  1342. self.particles.setEmitter(type)
  1343. self.updateEmitterWidgets()
  1344. def selectEmitterPage(self):
  1345. type = self.particles.emitter.__class__.__name__
  1346. self.emitterNotebook.selectpage(type)
  1347. self.getVariable('Emitter', 'Emitter Type').set(type)
  1348. def updateEmitterWidgets(self):
  1349. emitter = self.particles.emitter
  1350. self.setEmissionType(self.particles.emitter.getEmissionType())
  1351. amp = emitter.getAmplitude()
  1352. self.getWidget('Emitter', 'Velocity Multiplier').set(amp)
  1353. spread = emitter.getAmplitudeSpread()
  1354. self.getWidget('Emitter', 'Velocity Multiplier Spread').set(spread)
  1355. vec = emitter.getOffsetForce()
  1356. self.getWidget('Emitter', 'Offset Velocity').set(
  1357. [vec[0], vec[1], vec[2]], 0)
  1358. vec = emitter.getRadiateOrigin()
  1359. self.getWidget('Emitter', 'Radiate Origin').set(
  1360. [vec[0], vec[1], vec[2]], 0)
  1361. vec = emitter.getExplicitLaunchVector()
  1362. self.getWidget('Emitter', 'Explicit Velocity').set(
  1363. [vec[0], vec[1], vec[2]], 0)
  1364. if isinstance(emitter, BoxEmitter):
  1365. min = emitter.getMinBound()
  1366. self.getWidget('Box Emitter', 'Min').set(
  1367. [min[0], min[1], min[2]], 0)
  1368. max = emitter.getMaxBound()
  1369. self.getWidget('Box Emitter', 'Max').set(
  1370. [max[0], max[1], max[2]], 0)
  1371. elif isinstance(emitter, DiscEmitter):
  1372. radius = emitter.getRadius()
  1373. self.getWidget('Disc Emitter', 'Radius').set(radius, 0)
  1374. innerAngle = emitter.getInnerAngle()
  1375. self.getWidget('Disc Emitter', 'Inner Angle').set(innerAngle, 0)
  1376. innerMagnitude = emitter.getInnerMagnitude()
  1377. self.getWidget('Disc Emitter', 'Inner Velocity').set(
  1378. innerMagnitude, 0)
  1379. outerAngle = emitter.getOuterAngle()
  1380. self.getWidget('Disc Emitter', 'Outer Angle').set(outerAngle, 0)
  1381. outerMagnitude = emitter.getOuterMagnitude()
  1382. self.getWidget('Disc Emitter', 'Inner Velocity').set(
  1383. outerMagnitude, 0)
  1384. cubicLerping = emitter.getCubicLerping()
  1385. self.getVariable('Disc Emitter', 'Cubic Lerping').set(cubicLerping)
  1386. elif isinstance(emitter, LineEmitter):
  1387. min = emitter.getEndpoint1()
  1388. self.getWidget('Line Emitter', 'Min').set(
  1389. [min[0], min[1], min[2]], 0)
  1390. max = emitter.getEndpoint2()
  1391. self.getWidget('Line Emitter', 'Max').set(
  1392. [max[0], max[1], max[2]], 0)
  1393. elif isinstance(emitter, PointEmitter):
  1394. location = emitter.getLocation()
  1395. self.getWidget('Point Emitter', 'Position').set(
  1396. [location[0], location[1], location[2]], 0)
  1397. elif isinstance(emitter, RectangleEmitter):
  1398. min = emitter.getMinBound()
  1399. self.getWidget('Rectangle Emitter', 'Min').set(
  1400. [min[0], min[1]], 0)
  1401. max = emitter.getMaxBound()
  1402. self.getWidget('Rectangle Emitter', 'Max').set(
  1403. [max[0], max[1]], 0)
  1404. elif isinstance(emitter, RingEmitter):
  1405. radius = emitter.getRadius()
  1406. self.getWidget('Ring Emitter', 'Radius').set(radius, 0)
  1407. radiusSpread = emitter.getRadiusSpread()
  1408. self.getWidget('Ring Emitter', 'Radius Spread').set(radiusSpread, 0)
  1409. angle = emitter.getAngle()
  1410. self.getWidget('Ring Emitter', 'Angle').set(angle, 0)
  1411. elif isinstance(emitter, SphereVolumeEmitter):
  1412. radius = emitter.getRadius()
  1413. self.getWidget('Sphere Volume Emitter', 'Radius').set(radius, 0)
  1414. elif isinstance(emitter, SphereSurfaceEmitter):
  1415. radius = emitter.getRadius()
  1416. self.getWidget('Sphere Surface Emitter', 'Radius').set(radius, 0)
  1417. elif isinstance(emitter, TangentRingEmitter):
  1418. radius = emitter.getRadius()
  1419. self.getWidget('Tangent Ring Emitter', 'Radius').set(radius, 0)
  1420. radiusSpread = emitter.getRadiusSpread()
  1421. self.getWidget('Tangent Ring Emitter', 'Radius Spread').set(
  1422. radiusSpread, 0)
  1423. # All #
  1424. def setEmissionType(self, newType = None):
  1425. if newType:
  1426. type = newType
  1427. self.emissionType.set(type)
  1428. else:
  1429. type = self.emissionType.get()
  1430. self.particles.emitter.setEmissionType(type)
  1431. if type == BaseParticleEmitter.ETEXPLICIT:
  1432. self.getWidget(
  1433. 'Emitter', 'Radiate Origin')['state'] = 'disabled'
  1434. self.getWidget(
  1435. 'Emitter', 'Explicit Velocity')['state'] = 'normal'
  1436. # Hide custom widgets
  1437. if isinstance(self.particles.emitter, DiscEmitter):
  1438. self.discCustomFrame.pack_forget()
  1439. elif isinstance(self.particles.emitter, RingEmitter):
  1440. self.ringCustomFrame.pack_forget()
  1441. elif type == BaseParticleEmitter.ETRADIATE:
  1442. self.getWidget(
  1443. 'Emitter', 'Radiate Origin')['state'] = 'normal'
  1444. self.getWidget(
  1445. 'Emitter', 'Explicit Velocity')['state'] = 'disabled'
  1446. # Hide custom widgets
  1447. if isinstance(self.particles.emitter, DiscEmitter):
  1448. self.discCustomFrame.pack_forget()
  1449. elif isinstance(self.particles.emitter, RingEmitter):
  1450. self.ringCustomFrame.pack_forget()
  1451. elif type == BaseParticleEmitter.ETCUSTOM:
  1452. self.getWidget(
  1453. 'Emitter', 'Radiate Origin')['state'] = 'disabled'
  1454. self.getWidget(
  1455. 'Emitter', 'Explicit Velocity')['state'] = 'disabled'
  1456. # Show custom widgets
  1457. if isinstance(self.particles.emitter, DiscEmitter):
  1458. self.discCustomFrame.pack(fill = tk.BOTH, expand = 1)
  1459. elif isinstance(self.particles.emitter, RingEmitter):
  1460. self.ringCustomFrame.pack(fill = tk.BOTH, expand = 1)
  1461. def setEmitterAmplitude(self, value):
  1462. self.particles.emitter.setAmplitude(value)
  1463. def setEmitterAmplitudeSpread(self, value):
  1464. self.particles.emitter.setAmplitudeSpread(value)
  1465. def setEmitterOffsetForce(self, vec):
  1466. self.particles.emitter.setOffsetForce(
  1467. Vec3(vec[0], vec[1], vec[2]))
  1468. def setEmitterRadiateOrigin(self, origin):
  1469. self.particles.emitter.setRadiateOrigin(
  1470. Point3(origin[0], origin[1], origin[2]))
  1471. def setEmitterExplicitLaunchVector(self, vec):
  1472. self.particles.emitter.setExplicitLaunchVector(
  1473. Vec3(vec[0], vec[1], vec[2]))
  1474. # Box #
  1475. def setEmitterBoxPoint1(self, point):
  1476. self.particles.emitter.setMinBound(Point3(point[0],
  1477. point[1],
  1478. point[2]))
  1479. def setEmitterBoxPoint2(self, point):
  1480. self.particles.emitter.setMaxBound(Point3(point[0],
  1481. point[1],
  1482. point[2]))
  1483. # Disc #
  1484. def setEmitterDiscRadius(self, radius):
  1485. self.particles.emitter.setRadius(radius)
  1486. def setEmitterDiscInnerAngle(self, angle):
  1487. self.particles.emitter.setInnerAngle(angle)
  1488. def setEmitterDiscInnerVelocity(self, velocity):
  1489. self.particles.emitter.setInnerMagnitude(velocity)
  1490. def setEmitterDiscOuterAngle(self, angle):
  1491. self.particles.emitter.setOuterAngle(angle)
  1492. def setEmitterDiscOuterVelocity(self, velocity):
  1493. self.particles.emitter.setOuterMagnitude(velocity)
  1494. def toggleEmitterDiscCubicLerping(self):
  1495. self.particles.emitter.setCubicLerping(
  1496. self.getVariable('Disc Emitter', 'Cubic Lerping').get())
  1497. # Line #
  1498. def setEmitterLinePoint1(self, point):
  1499. self.particles.emitter.setEndpoint1(Point3(point[0],
  1500. point[1],
  1501. point[2]))
  1502. def setEmitterLinePoint2(self, point):
  1503. self.particles.emitter.setEndpoint2(Point3(point[0],
  1504. point[1],
  1505. point[2]))
  1506. # Point #
  1507. def setEmitterPointPosition(self, pos):
  1508. self.particles.emitter.setLocation(Point3(pos[0], pos[1], pos[2]))
  1509. # Rectangle #
  1510. def setEmitterRectanglePoint1(self, point):
  1511. self.particles.emitter.setMinBound(Point2(point[0], point[1]))
  1512. def setEmitterRectanglePoint2(self, point):
  1513. self.particles.emitter.setMaxBound(Point2(point[0], point[1]))
  1514. # Ring #
  1515. def setEmitterRingRadius(self, radius):
  1516. self.particles.emitter.setRadius(radius)
  1517. def setEmitterRingRadiusSpread(self, radiusSpread):
  1518. self.particles.emitter.setRadiusSpread(radiusSpread)
  1519. def setEmitterRingLaunchAngle(self, angle):
  1520. self.particles.emitter.setAngle(angle)
  1521. # Sphere surface #
  1522. def setEmitterSphereSurfaceRadius(self, radius):
  1523. self.particles.emitter.setRadius(radius)
  1524. # Sphere volume #
  1525. def setEmitterSphereVolumeRadius(self, radius):
  1526. self.particles.emitter.setRadius(radius)
  1527. # Tangent ring #
  1528. def setEmitterTangentRingRadius(self, radius):
  1529. self.particles.emitter.setRadius(radius)
  1530. def setEmitterTangentRingRadiusSpread(self, radiusSpread):
  1531. self.particles.emitter.setRadiusSpread(radiusSpread)
  1532. ## RENDERER PAGE ##
  1533. def selectRendererType(self, type):
  1534. self.rendererNotebook.selectpage(type)
  1535. self.particles.setRenderer(type)
  1536. self.updateRendererWidgets()
  1537. def updateRendererWidgets(self):
  1538. renderer = self.particles.renderer
  1539. alphaMode = renderer.getAlphaMode()
  1540. if alphaMode == BaseParticleRenderer.PRALPHANONE:
  1541. aMode = 'NO_ALPHA'
  1542. elif alphaMode == BaseParticleRenderer.PRALPHAOUT:
  1543. aMode = 'ALPHA_OUT'
  1544. elif alphaMode == BaseParticleRenderer.PRALPHAIN:
  1545. aMode = 'ALPHA_IN'
  1546. elif alphaMode == BaseParticleRenderer.PRALPHAINOUT:
  1547. aMode = 'ALPHA_IN_OUT'
  1548. elif alphaMode == BaseParticleRenderer.PRALPHAUSER:
  1549. aMode = 'ALPHA_USER'
  1550. self.getVariable('Renderer', 'Alpha Mode').set(aMode)
  1551. userAlpha = renderer.getUserAlpha()
  1552. self.getWidget('Renderer', 'User Alpha').set(userAlpha)
  1553. if isinstance(renderer, LineParticleRenderer):
  1554. headColor = renderer.getHeadColor() * 255.0
  1555. self.getWidget('Line Renderer', 'Head Color').set(
  1556. [headColor[0], headColor[1], headColor[2], headColor[3]])
  1557. tailColor = renderer.getTailColor() * 255.0
  1558. self.getWidget('Line Renderer', 'Tail Color').set(
  1559. [tailColor[0], tailColor[1], tailColor[2], tailColor[3]])
  1560. self.getWidget('Line Renderer', 'Line Scale Factor').set(
  1561. renderer.getLineScaleFactor())
  1562. elif isinstance(renderer, GeomParticleRenderer):
  1563. self.getVariable('Geom Renderer', 'X Scale').set(
  1564. renderer.getXScaleFlag())
  1565. self.getVariable('Geom Renderer', 'Y Scale').set(
  1566. renderer.getYScaleFlag())
  1567. self.getVariable('Geom Renderer', 'Z Scale').set(
  1568. renderer.getZScaleFlag())
  1569. initialXScale = renderer.getInitialXScale()
  1570. self.getWidget('Geom Renderer', 'Initial X Scale').set(
  1571. initialXScale)
  1572. initialYScale = renderer.getInitialYScale()
  1573. self.getWidget('Geom Renderer', 'Initial Y Scale').set(
  1574. initialYScale)
  1575. initialZScale = renderer.getInitialZScale()
  1576. self.getWidget('Geom Renderer', 'Initial Z Scale').set(
  1577. initialZScale)
  1578. finalXScale = renderer.getFinalXScale()
  1579. self.getWidget('Geom Renderer', 'Final X Scale').set(
  1580. finalXScale)
  1581. finalYScale = renderer.getFinalYScale()
  1582. self.getWidget('Geom Renderer', 'Final Y Scale').set(
  1583. finalYScale)
  1584. finalZScale = renderer.getFinalZScale()
  1585. self.getWidget('Geom Renderer', 'Final Z Scale').set(
  1586. finalZScale)
  1587. if self.getVariable('Geom Renderer','Color Blend').get() in ['MAdd','MSubtract','MInvSubtract']:
  1588. self.getWidget('Geom Renderer','Incoming Op.').pack(fill = tk.X)
  1589. self.getWidget('Geom Renderer','Fbuffer Op.').pack(fill = tk.X)
  1590. else:
  1591. self.getWidget('Geom Renderer','Incoming Op.').pack_forget()
  1592. self.getWidget('Geom Renderer','Fbuffer Op.').pack_forget()
  1593. for x in self.rendererSegmentWidgetList:
  1594. x.pack_forget()
  1595. x.destroy()
  1596. self.rendererSegmentWidgetList = []
  1597. for id in self.particles.renderer.getColorInterpolationManager().getSegmentIdList().split():
  1598. self.createWidgetForExistingInterpolationSegment(eval(id))
  1599. elif isinstance(renderer, PointParticleRenderer):
  1600. pointSize = renderer.getPointSize()
  1601. self.getWidget('Point Renderer', 'Point Size').set(pointSize)
  1602. startColor = renderer.getStartColor() * 255.0
  1603. self.getWidget('Point Renderer', 'Start Color').set(
  1604. [startColor[0], startColor[1], startColor[2], startColor[3]])
  1605. endColor = renderer.getEndColor() * 255.0
  1606. self.getWidget('Point Renderer', 'End Color').set(
  1607. [endColor[0], endColor[1], endColor[2], endColor[3]])
  1608. blendType = renderer.getBlendType()
  1609. if blendType == PointParticleRenderer.PPONECOLOR:
  1610. bType = "PP_ONE_COLOR"
  1611. elif blendType == PointParticleRenderer.PPBLENDLIFE:
  1612. bType = "PP_BLEND_LIFE"
  1613. elif blendType == PointParticleRenderer.PPBLENDVEL:
  1614. bType = "PP_BLEND_VEL"
  1615. self.getVariable('Point Renderer', 'Blend Type').set(bType)
  1616. blendMethod = renderer.getBlendMethod()
  1617. bMethod = "PP_NO_BLEND"
  1618. if blendMethod == BaseParticleRenderer.PPNOBLEND:
  1619. bMethod = "PP_NO_BLEND"
  1620. elif blendMethod == BaseParticleRenderer.PPBLENDLINEAR:
  1621. bMethod = "PP_BLEND_LINEAR"
  1622. elif blendMethod == BaseParticleRenderer.PPBLENDCUBIC:
  1623. bMethod = "PP_BLEND_CUBIC"
  1624. self.getVariable('Point Renderer', 'Blend Method').set(bMethod)
  1625. elif isinstance(renderer, SparkleParticleRenderer):
  1626. centerColor = renderer.getCenterColor() * 255.0
  1627. self.getWidget('Sparkle Renderer', 'Center Color').set(
  1628. [centerColor[0], centerColor[1],
  1629. centerColor[2], centerColor[3]])
  1630. edgeColor = renderer.getEdgeColor() * 255.0
  1631. self.getWidget('Sparkle Renderer', 'Edge Color').set(
  1632. [edgeColor[0], edgeColor[1], edgeColor[2], edgeColor[3]])
  1633. birthRadius = renderer.getBirthRadius()
  1634. self.getWidget('Sparkle Renderer', 'Birth Radius').set(birthRadius)
  1635. deathRadius = renderer.getDeathRadius()
  1636. self.getWidget('Sparkle Renderer', 'Death Radius').set(deathRadius)
  1637. lifeScale = renderer.getLifeScale()
  1638. lScale = "SP_NO_SCALE"
  1639. if lifeScale == SparkleParticleRenderer.SPSCALE:
  1640. lScale = "SP_SCALE"
  1641. self.getVariable('Sparkle Renderer', 'Life Scale').set(lScale)
  1642. elif isinstance(renderer, SpriteParticleRenderer):
  1643. self.getWidget('Sprite Renderer','Frame Rate').set(renderer.getAnimateFramesRate(), 0)
  1644. self.getVariable('Sprite Renderer','Enable Animation').set(
  1645. renderer.getAnimateFramesEnable())
  1646. self.readSpriteRendererAnimations() # Updates widgets with renderer data.
  1647. self.getVariable('Sprite Renderer', 'X Scale').set(
  1648. renderer.getXScaleFlag())
  1649. self.getVariable('Sprite Renderer', 'Y Scale').set(
  1650. renderer.getYScaleFlag())
  1651. self.getVariable('Sprite Renderer', 'Anim Angle').set(
  1652. renderer.getAnimAngleFlag())
  1653. initialXScale = renderer.getInitialXScale()
  1654. self.getWidget('Sprite Renderer', 'Initial X Scale').set(
  1655. initialXScale)
  1656. initialYScale = renderer.getInitialYScale()
  1657. self.getWidget('Sprite Renderer', 'Initial Y Scale').set(
  1658. initialYScale)
  1659. finalXScale = renderer.getFinalXScale()
  1660. self.getWidget('Sprite Renderer', 'Final X Scale').set(
  1661. finalXScale)
  1662. finalYScale = renderer.getFinalYScale()
  1663. self.getWidget('Sprite Renderer', 'Final Y Scale').set(
  1664. finalYScale)
  1665. nonanimatedTheta = renderer.getNonanimatedTheta()
  1666. self.getWidget('Sprite Renderer', 'Non Animated Theta').set(
  1667. nonanimatedTheta)
  1668. blendMethod = renderer.getAlphaBlendMethod()
  1669. bMethod = "PP_NO_BLEND"
  1670. if blendMethod == BaseParticleRenderer.PPNOBLEND:
  1671. bMethod = "PP_NO_BLEND"
  1672. elif blendMethod == BaseParticleRenderer.PPBLENDLINEAR:
  1673. bMethod = "PP_BLEND_LINEAR"
  1674. elif blendMethod == BaseParticleRenderer.PPBLENDCUBIC:
  1675. bMethod = "PP_BLEND_CUBIC"
  1676. self.getVariable('Sprite Renderer', 'Alpha Disable').set(
  1677. renderer.getAlphaDisable())
  1678. if self.getVariable('Sprite Renderer','Color Blend').get() in ['MAdd','MSubtract','MInvSubtract']:
  1679. self.getWidget('Sprite Renderer','Incoming Op.').pack(fill = tk.X)
  1680. self.getWidget('Sprite Renderer','Fbuffer Op.').pack(fill = tk.X)
  1681. else:
  1682. self.getWidget('Sprite Renderer','Incoming Op.').pack_forget()
  1683. self.getWidget('Sprite Renderer','Fbuffer Op.').pack_forget()
  1684. for x in self.rendererSegmentWidgetList:
  1685. x.pack_forget()
  1686. x.destroy()
  1687. self.rendererSegmentWidgetList = []
  1688. for id in self.particles.renderer.getColorInterpolationManager().getSegmentIdList().split():
  1689. self.createWidgetForExistingInterpolationSegment(eval(id))
  1690. def selectRendererPage(self):
  1691. type = self.particles.renderer.__class__.__name__
  1692. if type == 'SpriteParticleRendererExt':
  1693. type = 'SpriteParticleRenderer'
  1694. self.rendererNotebook.selectpage(type)
  1695. self.getVariable('Renderer', 'Renderer Type').set(type)
  1696. # All #
  1697. def setRendererAlphaMode(self, alphaMode):
  1698. if alphaMode == 'NO_ALPHA':
  1699. aMode = BaseParticleRenderer.PRALPHANONE
  1700. elif alphaMode == 'ALPHA_OUT':
  1701. aMode = BaseParticleRenderer.PRALPHAOUT
  1702. elif alphaMode == 'ALPHA_IN':
  1703. aMode = BaseParticleRenderer.PRALPHAIN
  1704. elif alphaMode == 'ALPHA_IN_OUT':
  1705. aMode = BaseParticleRenderer.PRALPHAINOUT
  1706. elif alphaMode == 'ALPHA_USER':
  1707. aMode = BaseParticleRenderer.PRALPHAUSER
  1708. self.particles.renderer.setAlphaMode(aMode)
  1709. def setRendererUserAlpha(self, alpha):
  1710. self.particles.renderer.setUserAlpha(alpha)
  1711. # Line #
  1712. def setRendererLineHeadColor(self, color):
  1713. self.particles.renderer.setHeadColor(
  1714. Vec4(color[0]/255.0, color[1]/255.0,
  1715. color[2]/255.0, color[3]/255.0))
  1716. def setRendererLineTailColor(self, color):
  1717. self.particles.renderer.setTailColor(
  1718. Vec4(color[0]/255.0, color[1]/255.0,
  1719. color[2]/255.0, color[3]/255.0))
  1720. def setRendererLineScaleFactor(self, sf):
  1721. self.particles.renderer.setLineScaleFactor(sf)
  1722. # Geom #
  1723. def setRendererGeomNode(self, event):
  1724. node = None
  1725. nodePath = base.loader.loadModel(self.rendererGeomNode.get())
  1726. if nodePath is not None:
  1727. node = nodePath.node()
  1728. if node is not None:
  1729. self.particles.geomReference = self.rendererGeomNode.get()
  1730. self.particles.renderer.setGeomNode(node)
  1731. # Point #
  1732. def setRendererPointSize(self, size):
  1733. self.particles.renderer.setPointSize(size)
  1734. def setRendererPointStartColor(self, color):
  1735. self.particles.renderer.setStartColor(
  1736. Vec4(color[0]/255.0, color[1]/255.0,
  1737. color[2]/255.0, color[3]/255.0))
  1738. def setRendererPointEndColor(self, color):
  1739. self.particles.renderer.setEndColor(
  1740. Vec4(color[0]/255.0, color[1]/255.0,
  1741. color[2]/255.0, color[3]/255.0))
  1742. def rendererPointSelectBlendType(self, blendType):
  1743. if blendType == "PP_ONE_COLOR":
  1744. bType = PointParticleRenderer.PPONECOLOR
  1745. elif blendType == "PP_BLEND_LIFE":
  1746. bType = PointParticleRenderer.PPBLENDLIFE
  1747. elif blendType == "PP_BLEND_VEL":
  1748. bType = PointParticleRenderer.PPBLENDVEL
  1749. self.particles.renderer.setBlendType(bType)
  1750. def rendererPointSelectBlendMethod(self, blendMethod):
  1751. if blendMethod == "PP_NO_BLEND":
  1752. bMethod = BaseParticleRenderer.PPNOBLEND
  1753. elif blendMethod == "PP_BLEND_LINEAR":
  1754. bMethod = BaseParticleRenderer.PPBLENDLINEAR
  1755. elif blendMethod == "PP_BLEND_CUBIC":
  1756. bMethod = BaseParticleRenderer.PPBLENDCUBIC
  1757. self.particles.renderer.setBlendMethod(bMethod)
  1758. # Sparkle #
  1759. def setRendererSparkleCenterColor(self, color):
  1760. self.particles.renderer.setCenterColor(
  1761. Vec4(color[0]/255.0, color[1]/255.0,
  1762. color[2]/255.0, color[3]/255.0))
  1763. def setRendererSparkleEdgeColor(self, color):
  1764. self.particles.renderer.setEdgeColor(
  1765. Vec4(color[0]/255.0, color[1]/255.0,
  1766. color[2]/255.0, color[3]/255.0))
  1767. def setRendererSparkleBirthRadius(self, radius):
  1768. self.particles.renderer.setBirthRadius(radius)
  1769. def setRendererSparkleDeathRadius(self, radius):
  1770. self.particles.renderer.setDeathRadius(radius)
  1771. def setRendererSparkleLifeScale(self, lifeScaleMethod):
  1772. if lifeScaleMethod == 'SP_NO_SCALE':
  1773. lScale = SparkleParticleRenderer.SPNOSCALE
  1774. else:
  1775. lScale = SparkleParticleRenderer.SPSCALE
  1776. self.particles.renderer.setLifeScale(lScale)
  1777. # Sprite #
  1778. def setSpriteSourceType(self):
  1779. if self.rendererSpriteSourceType.get() == 0:
  1780. self.rendererSpriteTextureEntry['state'] = 'normal'
  1781. self.rendererSpriteFileEntry['state'] = 'disabled'
  1782. self.rendererSpriteNodeEntry['state'] = 'disabled'
  1783. self.rendererSpriteTextureEntry['background'] = '#FFFFFF'
  1784. self.rendererSpriteFileEntry['background'] = '#C0C0C0'
  1785. self.rendererSpriteNodeEntry['background'] = '#C0C0C0'
  1786. else:
  1787. self.rendererSpriteTextureEntry['state'] = 'disabled'
  1788. self.rendererSpriteFileEntry['state'] = 'normal'
  1789. self.rendererSpriteNodeEntry['state'] = 'normal'
  1790. self.rendererSpriteTextureEntry['background'] = '#C0C0C0'
  1791. self.rendererSpriteFileEntry['background'] = '#FFFFFF'
  1792. self.rendererSpriteNodeEntry['background'] = '#FFFFFF'
  1793. def setRendererSpriteAnimationFrameRate(self, rate):
  1794. self.particles.renderer.setAnimateFramesRate(rate)
  1795. def setRendererSpriteAnimationEnable(self):
  1796. self.particles.renderer.setAnimateFramesEnable(
  1797. self.getVariable('Sprite Renderer','Enable Animation').get())
  1798. def addRendererSpriteAnimationTexture(self):
  1799. ren = self.particles.getRenderer()
  1800. parent = self.rendererSpriteAnimationFrame
  1801. if ren.addTextureFromFile():
  1802. animId = len([x for x in self.rendererSpriteAnimationWidgetList if x and x.valid])
  1803. anim = ren.getAnim(animId)
  1804. frameNum = len([x for x in self.rendererSpriteAnimationWidgetList if x])
  1805. self.rendererSpriteAnimationWidgetList.append(
  1806. self.createSpriteAnimationTextureWidget(parent, anim, repr(frameNum)))
  1807. else:
  1808. animId = len([x for x in self.rendererSpriteAnimationWidgetList if x and x.valid])
  1809. anim = SpriteAnim.STTexture
  1810. frameNum = len([x for x in self.rendererSpriteAnimationWidgetList if x])
  1811. self.rendererSpriteAnimationWidgetList.append(
  1812. self.createSpriteAnimationTextureWidget(parent, anim, repr(frameNum)))
  1813. parent.pack(fill=tk.BOTH, expand=1)
  1814. def addRendererSpriteAnimationFromNode(self):
  1815. ren = self.particles.getRenderer()
  1816. parent = self.rendererSpriteAnimationFrame
  1817. if ren.addTextureFromNode():
  1818. animId = len([x for x in self.rendererSpriteAnimationWidgetList if x and x.valid])
  1819. anim = ren.getAnim(animId)
  1820. frameNum = len([x for x in self.rendererSpriteAnimationWidgetList if x])
  1821. self.rendererSpriteAnimationWidgetList.append(
  1822. self.createSpriteAnimationNodeWidget(parent, anim, repr(frameNum)))
  1823. else:
  1824. animId = len([x for x in self.rendererSpriteAnimationWidgetList if x and x.valid])
  1825. anim = SpriteAnim.STFromNode
  1826. frameNum = len([x for x in self.rendererSpriteAnimationWidgetList if x])
  1827. self.rendererSpriteAnimationWidgetList.append(
  1828. self.createSpriteAnimationNodeWidget(parent, anim, repr(frameNum)))
  1829. parent.pack(fill=tk.BOTH, expand=1)
  1830. def toggleRendererSpriteXScale(self):
  1831. self.particles.renderer.setXScaleFlag(
  1832. self.getVariable('Sprite Renderer', 'X Scale').get())
  1833. def toggleRendererSpriteYScale(self):
  1834. self.particles.renderer.setYScaleFlag(
  1835. self.getVariable('Sprite Renderer', 'Y Scale').get())
  1836. def toggleRendererSpriteAnimAngle(self):
  1837. self.particles.renderer.setAnimAngleFlag(
  1838. self.getVariable('Sprite Renderer', 'Anim Angle').get())
  1839. def toggleAngularVelocity(self):
  1840. self.particles.factory.enableAngularVelocity(
  1841. self.getVariable('Z Spin Factory', 'Enable Angular Velocity').get())
  1842. def setRendererSpriteInitialXScale(self, xScale):
  1843. self.particles.renderer.setInitialXScale(xScale)
  1844. def setRendererSpriteFinalXScale(self, xScale):
  1845. self.particles.renderer.setFinalXScale(xScale)
  1846. def setRendererSpriteInitialYScale(self, yScale):
  1847. self.particles.renderer.setInitialYScale(yScale)
  1848. def setRendererSpriteFinalYScale(self, yScale):
  1849. self.particles.renderer.setFinalYScale(yScale)
  1850. def setRendererSpriteNonAnimatedTheta(self, theta):
  1851. self.particles.renderer.setNonanimatedTheta(theta)
  1852. def setRendererSpriteBlendMethod(self, blendMethod):
  1853. if blendMethod == 'PP_NO_BLEND':
  1854. bMethod = BaseParticleRenderer.PPNOBLEND
  1855. elif blendMethod == 'PP_BLEND_LINEAR':
  1856. bMethod = BaseParticleRenderer.PPBLENDLINEAR
  1857. elif blendMethod == 'PP_BLEND_CUBIC':
  1858. bMethod = BaseParticleRenderer.PPBLENDCUBIC
  1859. else:
  1860. bMethod = BaseParticleRenderer.PPNOBLEND
  1861. def toggleRendererSpriteAlphaDisable(self):
  1862. self.particles.renderer.setAlphaDisable(
  1863. self.getVariable('Sprite Renderer', 'Alpha Disable').get())
  1864. def setRendererColorBlendAttrib(self, rendererName, blendMethodStr, incomingOperandStr, fbufferOperandStr):
  1865. self.particles.getRenderer().setColorBlendMode(getattr(ColorBlendAttrib, blendMethodStr),
  1866. getattr(ColorBlendAttrib, incomingOperandStr),
  1867. getattr(ColorBlendAttrib, fbufferOperandStr))
  1868. if blendMethodStr in ['MAdd','MSubtract','MInvSubtract']:
  1869. self.getWidget(rendererName,'Incoming Op.').pack(fill = tk.X)
  1870. self.getWidget(rendererName,'Fbuffer Op.').pack(fill = tk.X)
  1871. else:
  1872. self.getWidget(rendererName,'Incoming Op.').pack_forget()
  1873. self.getWidget(rendererName,'Fbuffer Op.').pack_forget()
  1874. self.updateRendererWidgets()
  1875. def setRendererSpriteColorBlendMethod(self, blendMethod):
  1876. blendMethodStr = blendMethod
  1877. incomingOperandStr = self.getVariable('Sprite Renderer','Incoming Op.').get()
  1878. fbufferOperandStr = self.getVariable('Sprite Renderer','Fbuffer Op.').get()
  1879. self.setRendererColorBlendAttrib('Sprite Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1880. def setRendererSpriteColorBlendIncomingOperand(self, operand):
  1881. blendMethodStr = self.getVariable('Sprite Renderer','Color Blend').get()
  1882. incomingOperandStr = operand
  1883. fbufferOperandStr = self.getVariable('Sprite Renderer','Fbuffer Op.').get()
  1884. self.setRendererColorBlendAttrib('Sprite Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1885. def setRendererSpriteColorBlendFbufferOperand(self, operand):
  1886. blendMethodStr = self.getVariable('Sprite Renderer','Color Blend').get()
  1887. incomingOperandStr = self.getVariable('Sprite Renderer','Incoming Op.').get()
  1888. fbufferOperandStr = operand
  1889. self.setRendererColorBlendAttrib('Sprite Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1890. # GeomParticleRenderer Functionality
  1891. def toggleRendererGeomXScale(self):
  1892. self.particles.renderer.setXScaleFlag(
  1893. self.getVariable('Geom Renderer', 'X Scale').get())
  1894. def toggleRendererGeomYScale(self):
  1895. self.particles.renderer.setYScaleFlag(
  1896. self.getVariable('Geom Renderer', 'Y Scale').get())
  1897. def toggleRendererGeomZScale(self):
  1898. self.particles.renderer.setZScaleFlag(
  1899. self.getVariable('Geom Renderer', 'Z Scale').get())
  1900. def setRendererGeomInitialXScale(self, xScale):
  1901. self.particles.renderer.setInitialXScale(xScale)
  1902. def setRendererGeomFinalXScale(self, xScale):
  1903. self.particles.renderer.setFinalXScale(xScale)
  1904. def setRendererGeomInitialYScale(self, yScale):
  1905. self.particles.renderer.setInitialYScale(yScale)
  1906. def setRendererGeomFinalYScale(self, yScale):
  1907. self.particles.renderer.setFinalYScale(yScale)
  1908. def setRendererGeomInitialZScale(self, zScale):
  1909. self.particles.renderer.setInitialZScale(zScale)
  1910. def setRendererGeomFinalZScale(self, zScale):
  1911. self.particles.renderer.setFinalZScale(zScale)
  1912. def setRendererGeomColorBlendMethod(self, blendMethod):
  1913. blendMethodStr = blendMethod
  1914. incomingOperandStr = self.getVariable('Geom Renderer','Incoming Op.').get()
  1915. fbufferOperandStr = self.getVariable('Geom Renderer','Fbuffer Op.').get()
  1916. self.setRendererColorBlendAttrib('Geom Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1917. def setRendererGeomColorBlendIncomingOperand(self, operand):
  1918. blendMethodStr = self.getVariable('Geom Renderer','Color Blend').get()
  1919. incomingOperandStr = operand
  1920. fbufferOperandStr = self.getVariable('Geom Renderer','Fbuffer Op.').get()
  1921. self.setRendererColorBlendAttrib('Geom Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1922. def setRendererGeomColorBlendFbufferOperand(self, operand):
  1923. blendMethodStr = self.getVariable('Geom Renderer','Color Blend').get()
  1924. incomingOperandStr = self.getVariable('Geom Renderer','Incoming Op.').get()
  1925. fbufferOperandStr = operand
  1926. self.setRendererColorBlendAttrib('Geom Renderer', blendMethodStr, incomingOperandStr, fbufferOperandStr)
  1927. def addConstantInterpolationSegment(self, id = None):
  1928. ren = self.particles.getRenderer()
  1929. cim = ren.getColorInterpolationManager()
  1930. if id is None:
  1931. seg = cim.getSegment(cim.addConstant())
  1932. else:
  1933. seg = cim.getSegment(id)
  1934. if ren.__class__.__name__ == 'SpriteParticleRendererExt':
  1935. parent = self.rendererSpriteSegmentFrame
  1936. segName = repr(len(self.rendererSegmentWidgetList))+':Constant'
  1937. self.rendererSegmentWidgetList.append(
  1938. self.createConstantInterpolationSegmentWidget(parent, segName, seg))
  1939. elif ren.__class__.__name__ == 'GeomParticleRenderer':
  1940. parent = self.rendererGeomSegmentFrame
  1941. segName = repr(len(self.rendererSegmentWidgetList))+':Constant'
  1942. self.rendererSegmentWidgetList.append(
  1943. self.createConstantInterpolationSegmentWidget(parent, segName, seg))
  1944. parent.pack(fill=tk.BOTH, expand=1)
  1945. def addLinearInterpolationSegment(self, id = None):
  1946. ren = self.particles.getRenderer()
  1947. cim = ren.getColorInterpolationManager()
  1948. if id is None:
  1949. seg = cim.getSegment(cim.addLinear())
  1950. else:
  1951. seg = cim.getSegment(id)
  1952. if ren.__class__.__name__ == 'SpriteParticleRendererExt':
  1953. parent = self.rendererSpriteSegmentFrame
  1954. segName = repr(len(self.rendererSegmentWidgetList))+':Linear'
  1955. self.rendererSegmentWidgetList.append(
  1956. self.createLinearInterpolationSegmentWidget(parent, segName, seg))
  1957. elif ren.__class__.__name__ == 'GeomParticleRenderer':
  1958. parent = self.rendererGeomSegmentFrame
  1959. segName = repr(len(self.rendererSegmentWidgetList))+':Linear'
  1960. self.rendererSegmentWidgetList.append(
  1961. self.createLinearInterpolationSegmentWidget(parent, segName, seg))
  1962. parent.pack(fill=tk.BOTH, expand=1)
  1963. def addStepwaveInterpolationSegment(self, id = None):
  1964. ren = self.particles.getRenderer()
  1965. cim = ren.getColorInterpolationManager()
  1966. if id is None:
  1967. seg = cim.getSegment(cim.addStepwave())
  1968. else:
  1969. seg = cim.getSegment(id)
  1970. if ren.__class__.__name__ == 'SpriteParticleRendererExt':
  1971. parent = self.rendererSpriteSegmentFrame
  1972. segName = repr(len(self.rendererSegmentWidgetList))+':Stepwave'
  1973. self.rendererSegmentWidgetList.append(
  1974. self.createStepwaveInterpolationSegmentWidget(parent, segName, seg))
  1975. elif ren.__class__.__name__ == 'GeomParticleRenderer':
  1976. parent = self.rendererGeomSegmentFrame
  1977. segName = repr(len(self.rendererSegmentWidgetList))+':Stepwave'
  1978. self.rendererSegmentWidgetList.append(
  1979. self.createStepwaveInterpolationSegmentWidget(parent, segName, seg))
  1980. parent.pack(fill=tk.BOTH, expand=1)
  1981. def addSinusoidInterpolationSegment(self, id = None):
  1982. ren = self.particles.getRenderer()
  1983. cim = ren.getColorInterpolationManager()
  1984. if id is None:
  1985. seg = cim.getSegment(cim.addSinusoid())
  1986. else:
  1987. seg = cim.getSegment(id)
  1988. if ren.__class__.__name__ == 'SpriteParticleRendererExt':
  1989. parent = self.rendererSpriteSegmentFrame
  1990. segName = repr(len(self.rendererSegmentWidgetList))+':Sinusoid'
  1991. self.rendererSegmentWidgetList.append(
  1992. self.createSinusoidInterpolationSegmentWidget(parent, segName, seg))
  1993. elif ren.__class__.__name__ == 'GeomParticleRenderer':
  1994. parent = self.rendererGeomSegmentFrame
  1995. segName = repr(len(self.rendererSegmentWidgetList))+':Sinusoid'
  1996. self.rendererSegmentWidgetList.append(
  1997. self.createSinusoidInterpolationSegmentWidget(parent, segName, seg))
  1998. parent.pack(fill=tk.BOTH, expand=1)
  1999. def createWidgetForExistingInterpolationSegment(self, id):
  2000. ren = self.particles.getRenderer()
  2001. cim = ren.getColorInterpolationManager()
  2002. seg = cim.getSegment(id)
  2003. assert seg
  2004. fun = seg.getFunction()
  2005. if isinstance(fun,ColorInterpolationFunctionSinusoid):
  2006. self.addSinusoidInterpolationSegment(id)
  2007. elif isinstance(fun,ColorInterpolationFunctionStepwave):
  2008. self.addStepwaveInterpolationSegment(id)
  2009. elif isinstance(fun,ColorInterpolationFunctionLinear):
  2010. self.addLinearInterpolationSegment(id)
  2011. elif isinstance(fun,ColorInterpolationFunctionConstant):
  2012. self.addConstantInterpolationSegment(id)
  2013. def createInterpolationSegmentFrame(self, parent, segName, seg):
  2014. frame = tk.Frame(parent, relief = tk.RAISED, borderwidth = 2)
  2015. lFrame = tk.Frame(frame, relief = tk.FLAT)
  2016. def removeInterpolationSegmentFrame(s = self, seg = seg, fr = frame):
  2017. s.particles.getRenderer().getColorInterpolationManager().clearSegment(seg.getId())
  2018. fr.pack_forget()
  2019. def setSegEnabled(s=self, n=segName):
  2020. enabled = s.getVariable('Sprite Renderer', n+' Enabled')
  2021. seg.setEnabled(enabled.get())
  2022. def setIsModulated(s=self, n=segName):
  2023. modulated = s.getVariable('Sprite Renderer', n+' isModulated')
  2024. seg.setIsModulated(modulated.get())
  2025. def setSegBegin(time):
  2026. seg.setTimeBegin(time)
  2027. def setSegEnd(time):
  2028. seg.setTimeEnd(time)
  2029. tk.Button(lFrame, text = 'X',
  2030. command = removeInterpolationSegmentFrame).pack(side = tk.RIGHT, expand = 0)
  2031. tk.Label(lFrame, text = segName,
  2032. foreground = 'Blue',
  2033. font = ('MSSansSerif', 12, 'bold'),
  2034. ).pack(fill = tk.X, expand = 1)
  2035. lFrame.pack(fill = tk.X, expand = 1)
  2036. lFrame = tk.Frame(frame, relief = tk.FLAT)
  2037. self.createCheckbutton(
  2038. lFrame, 'Sprite Renderer', segName + ' Enabled',
  2039. ('On: Enabled\n' +
  2040. 'Off: Disabled'),
  2041. command = setSegEnabled, initialState = seg.isEnabled())
  2042. self.createCheckbutton(
  2043. lFrame, 'Sprite Renderer', segName + ' isModulated',
  2044. ('On: Modulate\n' +
  2045. 'Off: Add'),
  2046. command = setIsModulated, initialState = seg.isModulated())
  2047. lFrame.pack(fill = tk.X, expand = 1)
  2048. f = tk.Frame(frame)
  2049. self.createSlider(f,
  2050. 'Sprite Renderer', segName + ' Begin',
  2051. '',
  2052. command = setSegBegin,
  2053. value = seg.getTimeBegin())
  2054. self.createSlider(f,'Sprite Renderer', segName + ' End',
  2055. '',
  2056. command = setSegEnd,
  2057. value = seg.getTimeEnd())
  2058. f.pack(fill = tk.X, expand = 0)
  2059. frame.pack(pady = 3, fill = tk.X, expand = 0)
  2060. return frame
  2061. def createConstantInterpolationSegmentWidget(self, parent, segName, segment):
  2062. fun = segment.getFunction()
  2063. def setSegColorA(color):
  2064. fun.setColorA(
  2065. Vec4(color[0]/255.0, color[1]/255.0,
  2066. color[2]/255.0, color[3]/255.0))
  2067. frame = self.createInterpolationSegmentFrame(parent, segName, segment)
  2068. f = tk.Frame(frame)
  2069. c = fun.getColorA()
  2070. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2071. self.createColorEntry(f,'Sprite Renderer', segName + ' Color A',
  2072. '',
  2073. command = setSegColorA,
  2074. value = c)
  2075. f.pack(fill = tk.X)
  2076. return frame
  2077. def createLinearInterpolationSegmentWidget(self, parent, segName, segment):
  2078. fun = segment.getFunction()
  2079. def setSegColorA(color):
  2080. fun.setColorA(
  2081. Vec4(color[0]/255.0, color[1]/255.0,
  2082. color[2]/255.0, color[3]/255.0))
  2083. def setSegColorB(color):
  2084. fun.setColorB(
  2085. Vec4(color[0]/255.0, color[1]/255.0,
  2086. color[2]/255.0, color[3]/255.0))
  2087. frame = self.createInterpolationSegmentFrame(parent, segName, segment)
  2088. f = tk.Frame(frame)
  2089. c = fun.getColorA()
  2090. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2091. self.createColorEntry(f,'Sprite Renderer', segName + ' Color A',
  2092. '',
  2093. command = setSegColorA,
  2094. value = c)
  2095. c = fun.getColorB()
  2096. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2097. self.createColorEntry(f,'Sprite Renderer', segName + ' Color B',
  2098. '',
  2099. command = setSegColorB,
  2100. value = c)
  2101. f.pack(fill = tk.X)
  2102. return frame
  2103. def createStepwaveInterpolationSegmentWidget(self, parent, segName, segment):
  2104. fun = segment.getFunction()
  2105. def setColorA(color):
  2106. fun.setColorA(
  2107. Vec4(color[0]/255.0, color[1]/255.0,
  2108. color[2]/255.0, color[3]/255.0))
  2109. def setColorB(color):
  2110. fun.setColorB(
  2111. Vec4(color[0]/255.0, color[1]/255.0,
  2112. color[2]/255.0, color[3]/255.0))
  2113. def setWidthA(width):
  2114. fun.setWidthA(width)
  2115. def setWidthB(width):
  2116. fun.setWidthB(width)
  2117. frame = self.createInterpolationSegmentFrame(parent, segName, segment)
  2118. f = tk.Frame(frame)
  2119. c = fun.getColorA()
  2120. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2121. self.createColorEntry(f,'Sprite Renderer', segName + ' Color A',
  2122. '',
  2123. command = setColorA,
  2124. value = c)
  2125. c = fun.getColorB()
  2126. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2127. self.createColorEntry(f,'Sprite Renderer', segName + ' Color B',
  2128. '',
  2129. command = setColorB,
  2130. value = c)
  2131. w = fun.getWidthA()
  2132. self.createSlider(f,'Sprite Renderer', segName + ' Width A',
  2133. '',
  2134. command = setWidthA,
  2135. value = w)
  2136. w = fun.getWidthB()
  2137. self.createSlider(f,'Sprite Renderer', segName + ' Width B',
  2138. '',
  2139. command = setWidthB,
  2140. value = w)
  2141. f.pack(fill = tk.X)
  2142. return frame
  2143. def createSinusoidInterpolationSegmentWidget(self, parent, segName, segment):
  2144. fun = segment.getFunction()
  2145. def setColorA(color):
  2146. fun.setColorA(
  2147. Vec4(color[0]/255.0, color[1]/255.0,
  2148. color[2]/255.0, color[3]/255.0))
  2149. def setColorB(color):
  2150. fun.setColorB(
  2151. Vec4(color[0]/255.0, color[1]/255.0,
  2152. color[2]/255.0, color[3]/255.0))
  2153. def setPeriod(period):
  2154. fun.setPeriod(period)
  2155. frame = self.createInterpolationSegmentFrame(parent, segName, segment)
  2156. f = tk.Frame(frame)
  2157. c = fun.getColorA()
  2158. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2159. self.createColorEntry(f,'Sprite Renderer', segName + ' Color A',
  2160. '',
  2161. command = setColorA,
  2162. value = c)
  2163. c = fun.getColorB()
  2164. c = [c[0]*255.0, c[1]*255.0, c[2]*255.0, c[3]*255.0]
  2165. self.createColorEntry(f,'Sprite Renderer', segName + ' Color B',
  2166. '',
  2167. command = setColorB,
  2168. value = c)
  2169. p = fun.getPeriod()
  2170. self.createFloater(f,'Sprite Renderer', segName + ' Period',
  2171. '',
  2172. command = setPeriod,
  2173. value = p)
  2174. f.pack(fill = tk.X)
  2175. return frame
  2176. def createSpriteAnimationFrame(self, parent, anim, animName):
  2177. ren = self.particles.getRenderer()
  2178. frame = tk.Frame(parent, relief = tk.RAISED, borderwidth = 2)
  2179. frame.pack(pady = 1, fill = tk.X, expand = 0)
  2180. lFrame = tk.Frame(frame, relief = tk.FLAT)
  2181. lFrame.pack(fill = tk.X, expand = 1)
  2182. def delete(s = self, fr = frame):
  2183. i = s.rendererSpriteAnimationWidgetList.index(fr)
  2184. s.rendererSpriteAnimationWidgetList[i] = None
  2185. fr.pack_forget()
  2186. fr.destroy()
  2187. s.writeSpriteRendererAnimations()
  2188. s.readSpriteRendererAnimations()
  2189. tk.Button(lFrame, text = 'X', foreground = 'Red', font = ('MSSansSerif', 8, 'bold'),
  2190. command = delete).pack(side = tk.RIGHT, expand = 0)
  2191. if anim == SpriteAnim.STTexture or anim == SpriteAnim.STFromNode:
  2192. frame.valid = False
  2193. frame.animSourceType = anim
  2194. if anim == SpriteAnim.STTexture:
  2195. type = 'Texture'
  2196. else:
  2197. type = 'From Node'
  2198. else:
  2199. frame.valid = True
  2200. if anim.getSourceType() == SpriteAnim.STTexture:
  2201. frame.animSourceType = SpriteAnim.STTexture
  2202. type = 'Texture'
  2203. else:
  2204. frame.animSourceType = SpriteAnim.STFromNode
  2205. type = 'From Node'
  2206. tk.Label(lFrame, text = animName+': '+type,
  2207. foreground = 'Blue',
  2208. font = ('MSSansSerif', 12, 'bold'),
  2209. ).pack(fill = tk.X, expand = 1)
  2210. return frame
  2211. def createSpriteAnimationTextureWidget(self, parent, anim, animName):
  2212. ren = self.particles.getRenderer()
  2213. frame = self.createSpriteAnimationFrame(parent, anim, animName)
  2214. f = tk.Frame(frame)
  2215. f.pack(fill=tk.X)
  2216. tk.Label(f, text = 'Texture: ', font = ('MSSansSerif', 12), width=7).pack(side = tk.LEFT)
  2217. strVar = tk.StringVar()
  2218. entry = tk.Entry(f, textvariable = strVar).pack(padx=3, pady=3, side=tk.LEFT, fill=tk.X, expand=1)
  2219. if frame.valid:
  2220. strVar.set(anim.getTexSource())
  2221. else:
  2222. strVar.set('Base model path: ' + repr(getModelPath().getValue()))
  2223. def checkForTexture(strVar = strVar):
  2224. tex = base.loader.loadTexture(strVar.get())
  2225. if tex:
  2226. frame.valid = True
  2227. else:
  2228. frame.valid = False
  2229. self.writeSpriteRendererAnimations()
  2230. tk.Button(f, text = 'Update',
  2231. command = checkForTexture).pack(side=tk.LEFT)
  2232. self.variableDict['Sprite Renderer-'+animName+' Anim Texture'] = strVar
  2233. self.widgetDict['Sprite Renderer-'+animName+' Anim Texture'] = entry
  2234. return frame
  2235. def createSpriteAnimationNodeWidget(self, parent, anim, animName):
  2236. ren = self.particles.getRenderer()
  2237. frame = self.createSpriteAnimationFrame(parent, anim, animName)
  2238. f = tk.Frame(frame)
  2239. f.pack(fill=tk.X)
  2240. lf = tk.Frame(f)
  2241. lf.pack(fill=tk.X, expand=1)
  2242. tk.Label(lf, text = 'Model: ', font = ('MSSansSerif', 12), width=7).pack(side = tk.LEFT)
  2243. mStrVar = tk.StringVar()
  2244. entry = tk.Entry(lf, textvariable = mStrVar).pack(padx=3, pady=3, side=tk.LEFT, fill=tk.X, expand=1)
  2245. if frame.valid:
  2246. mStrVar.set(anim.getModelSource())
  2247. else:
  2248. mStrVar.set('Base model path: ' + repr(getModelPath().getValue()))
  2249. mlf = lf
  2250. self.variableDict['Sprite Renderer-'+animName+' Anim Model'] = mStrVar
  2251. self.widgetDict['Sprite Renderer-'+animName+' Anim Model'] = entry
  2252. lf = tk.Frame(f)
  2253. lf.pack(fill=tk.X, expand=1)
  2254. tk.Label(lf, text = 'Node: ', font = ('MSSansSerif', 12), width=7).pack(side = tk.LEFT)
  2255. nStrVar = tk.StringVar()
  2256. entry = tk.Entry(lf, textvariable = nStrVar).pack(padx=3, pady=3, side=tk.LEFT, fill=tk.X, expand=1)
  2257. if frame.valid:
  2258. nStrVar.set(anim.getNodeSource())
  2259. else:
  2260. nStrVar.set('**/*')
  2261. nlf = lf
  2262. self.variableDict['Sprite Renderer-'+animName+' Anim Node'] = nStrVar
  2263. self.widgetDict['Sprite Renderer-'+animName+' Anim Node'] = entry
  2264. def checkForNode(modelStrVar=mStrVar, nodeStrVar=nStrVar):
  2265. mod = base.loader.loadModel(modelStrVar.get())
  2266. if mod:
  2267. node = mod.find(nodeStrVar.get())
  2268. if node:
  2269. frame.valid = True
  2270. else:
  2271. frame.valid = False
  2272. else:
  2273. frame.valid = False
  2274. self.writeSpriteRendererAnimations()
  2275. tk.Button(mlf, text = 'Update',
  2276. command = checkForNode).pack(side=tk.LEFT)
  2277. tk.Button(nlf, text = 'Update',
  2278. command = checkForNode).pack(side=tk.LEFT)
  2279. return frame
  2280. # get animation info from renderer into panel
  2281. def readSpriteRendererAnimations(self):
  2282. ren = self.particles.getRenderer()
  2283. for widget in self.rendererSpriteAnimationWidgetList:
  2284. if widget:
  2285. widget.pack_forget()
  2286. widget.destroy()
  2287. self.rendererSpriteAnimationWidgetList = []
  2288. for anim in [ren.getAnim(x) for x in range(ren.getNumAnims())]:
  2289. if anim.getSourceType() == SpriteAnim.STTexture:
  2290. w = self.createSpriteAnimationTextureWidget(self.rendererSpriteAnimationFrame, anim, repr(len(self.rendererSpriteAnimationWidgetList)))
  2291. else:
  2292. w = self.createSpriteAnimationNodeWidget(self.rendererSpriteAnimationFrame, anim, repr(len(self.rendererSpriteAnimationWidgetList)))
  2293. self.rendererSpriteAnimationWidgetList.append(w)
  2294. # set animation info from panel into renderer
  2295. def writeSpriteRendererAnimations(self):
  2296. ren = self.particles.getRenderer()
  2297. for x in range(ren.getNumAnims()):
  2298. ren.removeAnimation(0)
  2299. for x in range(len(self.rendererSpriteAnimationWidgetList)):
  2300. widget = self.rendererSpriteAnimationWidgetList[x]
  2301. if widget and widget.valid:
  2302. if widget.animSourceType == SpriteAnim.STTexture:
  2303. texSource = self.getVariable('Sprite Renderer', repr(x) + ' Anim Texture').get()
  2304. ren.addTextureFromFile(texSource)
  2305. else:
  2306. modelSource = self.getVariable('Sprite Renderer', repr(x) + ' Anim Model').get()
  2307. nodeSource = self.getVariable('Sprite Renderer', repr(x) + ' Anim Node').get()
  2308. ren.addTextureFromNode(modelSource, nodeSource)
  2309. ## FORCEGROUP COMMANDS ##
  2310. def updateForceWidgets(self):
  2311. # Select appropriate notebook page
  2312. if self.forceGroup is not None:
  2313. self.forceGroupNotebook.pack(fill = tk.X)
  2314. self.forcePageName = (self.particleEffect.getName() + '-' +
  2315. self.forceGroup.getName())
  2316. self.forcePage = self.forcePagesDict.get(
  2317. self.forcePageName, None)
  2318. # Page doesn't exist, add it
  2319. if self.forcePage is None:
  2320. self.addForceGroupNotebookPage(
  2321. self.particleEffect, self.forceGroup)
  2322. self.forceGroupNotebook.selectpage(self.forcePageName)
  2323. else:
  2324. self.forceGroupNotebook.pack_forget()
  2325. def addLinearVectorForce(self):
  2326. self.addForce(LinearVectorForce())
  2327. def addLinearFrictionForce(self):
  2328. self.addForce(LinearFrictionForce())
  2329. def addLinearJitterForce(self):
  2330. self.addForce(LinearJitterForce())
  2331. def addLinearNoiseForce(self):
  2332. self.addForce(LinearNoiseForce())
  2333. def addLinearSinkForce(self):
  2334. self.addForce(LinearSinkForce())
  2335. def addLinearSourceForce(self):
  2336. self.addForce(LinearSourceForce())
  2337. def addLinearCylinderVortexForce(self):
  2338. self.addForce(LinearCylinderVortexForce())
  2339. def addLinearUserDefinedForce(self):
  2340. self.addForce(LinearUserDefinedForce())
  2341. def addForce(self, f):
  2342. if self.forceGroup is None:
  2343. self.createNewForceGroup()
  2344. self.forceGroup.addForce(f)
  2345. self.addForceWidget(self.forceGroup, f)
  2346. ## SYSTEM COMMANDS ##
  2347. def createNewEffect(self):
  2348. name = askstring('Particle Panel', 'Effect Name:',
  2349. parent = self.parent)
  2350. if name:
  2351. particles = Particles.Particles()
  2352. particles.setBirthRate(0.02)
  2353. particles.setLitterSize(10)
  2354. particles.setLitterSpread(0)
  2355. particles.setFactory("PointParticleFactory")
  2356. particles.setRenderer("PointParticleRenderer")
  2357. particles.setEmitter("SphereVolumeEmitter")
  2358. particles.enable()
  2359. effect = ParticleEffect.ParticleEffect(name, particles)
  2360. self.effectsDict[name] = effect
  2361. self.updateMenusAndLabels()
  2362. self.selectEffectNamed(name)
  2363. effect.reparentTo(render)
  2364. effect.enable()
  2365. def createNewParticles(self):
  2366. name = askstring('Particle Panel', 'Particles Name:',
  2367. parent = self.parent)
  2368. if name:
  2369. p = Particles.Particles(name)
  2370. self.particleEffect.addParticles(p)
  2371. self.updateParticlesMenus()
  2372. self.selectParticlesNamed(name)
  2373. p.enable()
  2374. def createNewForceGroup(self):
  2375. name = askstring('Particle Panel', 'ForceGroup Name:',
  2376. parent = self.parent)
  2377. if name:
  2378. forceGroup = ForceGroup.ForceGroup(name)
  2379. self.particleEffect.addForceGroup(forceGroup)
  2380. self.updateForceGroupMenus()
  2381. self.addForceGroupNotebookPage(self.particleEffect, forceGroup)
  2382. self.selectForceGroupNamed(name)
  2383. forceGroup.enable()
  2384. def addForceGroupNotebookPage(self, particleEffect, forceGroup):
  2385. self.forcePageName = (particleEffect.getName() + '-' +
  2386. forceGroup.getName())
  2387. self.forcePage = self.forceGroupNotebook.add(self.forcePageName)
  2388. self.forcePagesDict[self.forcePageName] = self.forcePage
  2389. for force in forceGroup:
  2390. self.addForceWidget(forceGroup, force)
  2391. def addForceWidget(self, forceGroup, force):
  2392. forcePage = self.forcePage
  2393. pageName = self.forcePageName
  2394. # How many forces of the same type in the force group object
  2395. count = 0
  2396. for f in forceGroup:
  2397. if f.getClassType() == force.getClassType():
  2398. count += 1
  2399. if isinstance(force, LinearVectorForce):
  2400. self.createLinearVectorForceWidget(
  2401. forcePage, pageName, count, force)
  2402. elif isinstance(force, LinearNoiseForce):
  2403. self.createLinearRandomForceWidget(
  2404. forcePage, pageName, count, force, 'Noise')
  2405. elif isinstance(force, LinearJitterForce):
  2406. self.createLinearRandomForceWidget(
  2407. forcePage, pageName, count, force, 'Jitter')
  2408. elif isinstance(force, LinearFrictionForce):
  2409. self.createLinearFrictionForceWidget(
  2410. forcePage, pageName, count, force)
  2411. elif isinstance(force, LinearCylinderVortexForce):
  2412. self.createLinearCylinderVortexForceWidget(
  2413. forcePage, pageName, count, force)
  2414. elif isinstance(force, LinearSinkForce):
  2415. self.createLinearDistanceForceWidget(
  2416. forcePage, pageName, count, force, 'Sink')
  2417. elif isinstance(force, LinearSourceForce):
  2418. self.createLinearDistanceForceWidget(
  2419. forcePage, pageName, count, force, 'Source')
  2420. elif isinstance(force, LinearUserDefinedForce):
  2421. # Nothing
  2422. pass
  2423. self.forceGroupNotebook.setnaturalsize()
  2424. def createForceFrame(self, forcePage, forceName, force):
  2425. frame = tk.Frame(forcePage, relief = tk.RAISED, borderwidth = 2)
  2426. lFrame = tk.Frame(frame, relief = tk.FLAT)
  2427. def removeForce(s = self, f = force, fr = frame):
  2428. s.forceGroup.removeForce(f)
  2429. fr.pack_forget()
  2430. b = tk.Button(lFrame, text = 'X',
  2431. command = removeForce)
  2432. b.pack(side = 'right', expand = 0)
  2433. tk.Label(lFrame, text = forceName,
  2434. foreground = 'Blue',
  2435. font=('MSSansSerif', 12, 'bold'),
  2436. ).pack(expand = 1, fill = 'x')
  2437. lFrame.pack(fill = 'x', expand =1)
  2438. frame.pack(pady = 3, fill = 'x', expand =0)
  2439. return frame
  2440. def createLinearForceWidgets(self, frame, pageName, forceName, force):
  2441. def setAmplitude(amp, f = force):
  2442. f.setAmplitude(amp)
  2443. def toggleMassDependent(s=self, f=force, p=pageName, n=forceName):
  2444. v = s.getVariable(p, n+' Mass Dependent')
  2445. f.setMassDependent(v.get())
  2446. def setVectorMasks(s=self, f=force, p=pageName, n=forceName):
  2447. xMask = s.getVariable(p, n+' Mask X').get()
  2448. yMask = s.getVariable(p, n+' Mask Y').get()
  2449. zMask = s.getVariable(p, n+' Mask Z').get()
  2450. f.setVectorMasks(xMask, yMask, zMask)
  2451. self.createFloater(frame, pageName, forceName + ' Amplitude',
  2452. 'Force amplitude multiplier',
  2453. command = setAmplitude,
  2454. value = force.getAmplitude())
  2455. cbf = tk.Frame(frame, relief = tk.FLAT)
  2456. self.createCheckbutton(cbf, pageName, forceName + ' Mass Dependent',
  2457. ('On: force depends on mass; ' +
  2458. 'Off: force does not depend on mass'),
  2459. toggleMassDependent,
  2460. force.getMassDependent())
  2461. self.createCheckbutton(cbf, pageName, forceName + ' Mask X',
  2462. 'On: enable force along X axis',
  2463. setVectorMasks, 1)
  2464. self.createCheckbutton(cbf, pageName, forceName + ' Mask Y',
  2465. 'On: enable force along X axis',
  2466. setVectorMasks, 1)
  2467. self.createCheckbutton(cbf, pageName, forceName + ' Mask Z',
  2468. 'On: enable force along X axis',
  2469. setVectorMasks, 1)
  2470. cbf.pack(fill = 'x', expand = 0)
  2471. def createForceActiveWidget(self, frame, pageName, forceName, force):
  2472. cbName = forceName + ' Active'
  2473. def toggle(s = self, f = force, p = pageName, n = cbName):
  2474. s.toggleForce(f, p, n)
  2475. self.createCheckbutton(frame, pageName, cbName,
  2476. 'On: force is enabled; Off: force is disabled',
  2477. toggle, 1)
  2478. def createLinearVectorForceWidget(self, forcePage, pageName,
  2479. count, force):
  2480. def setVec(vec, f = force):
  2481. f.setVector(vec[0], vec[1], vec[2])
  2482. forceName = 'Vector Force-' + repr(count)
  2483. frame = self.createForceFrame(forcePage, forceName, force)
  2484. self.createLinearForceWidgets(frame, pageName, forceName, force)
  2485. vec = force.getLocalVector()
  2486. self.createVector3Entry(frame, pageName, forceName,
  2487. 'Set force direction and magnitude',
  2488. command = setVec,
  2489. value = [vec[0], vec[1], vec[2]])
  2490. self.createForceActiveWidget(frame, pageName, forceName, force)
  2491. def createLinearRandomForceWidget(self, forcePage, pageName, count,
  2492. force, type):
  2493. forceName = type + ' Force-' + repr(count)
  2494. frame = self.createForceFrame(forcePage, forceName, force)
  2495. self.createLinearForceWidgets(frame, pageName, forceName, force)
  2496. self.createForceActiveWidget(frame, pageName, forceName, force)
  2497. def createLinearFrictionForceWidget(self, forcePage, pageName,
  2498. count, force):
  2499. def setCoef(coef, f = force):
  2500. f.setCoef(coef)
  2501. forceName = 'Friction Force-' + repr(count)
  2502. frame = self.createForceFrame(forcePage, forceName, force)
  2503. self.createLinearForceWidgets(frame, pageName, forceName, force)
  2504. self.createFloater(frame, pageName, forceName + ' Coef',
  2505. 'Set linear friction force',
  2506. command = setCoef, min = None,
  2507. value = force.getCoef())
  2508. self.createForceActiveWidget(frame, pageName, forceName, force)
  2509. def createLinearCylinderVortexForceWidget(self, forcePage, pageName,
  2510. count, force):
  2511. forceName = 'Vortex Force-' + repr(count)
  2512. def setCoef(coef, f = force):
  2513. f.setCoef(coef)
  2514. def setLength(length, f = force):
  2515. f.setLength(length)
  2516. def setRadius(radius, f = force):
  2517. f.setRadius(radius)
  2518. frame = self.createForceFrame(forcePage, forceName, force)
  2519. self.createLinearForceWidgets(frame, pageName, forceName, force)
  2520. self.createFloater(frame, pageName, forceName + ' Coef',
  2521. 'Set linear cylinder vortex coefficient',
  2522. command = setCoef,
  2523. value = force.getCoef())
  2524. self.createFloater(frame, pageName, forceName + ' Length',
  2525. 'Set linear cylinder vortex length',
  2526. command = setLength,
  2527. value = force.getLength())
  2528. self.createFloater(frame, pageName, forceName + ' Radius',
  2529. 'Set linear cylinder vortex radius',
  2530. command = setRadius,
  2531. value = force.getRadius())
  2532. self.createForceActiveWidget(frame, pageName, forceName, force)
  2533. def createLinearDistanceForceWidget(self, forcePage, pageName,
  2534. count, force, type):
  2535. def setFalloffType(type, f=force):
  2536. if type == 'FT_ONE_OVER_R':
  2537. #f.setFalloffType(LinearDistanceForce.FTONEOVERR)
  2538. f.setFalloffType(0)
  2539. if type == 'FT_ONE_OVER_R_SQUARED':
  2540. #f.setFalloffType(LinearDistanceForce.FTONEOVERRSQUARED)
  2541. f.setFalloffType(1)
  2542. if type == 'FT_ONE_OVER_R_CUBED':
  2543. #f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED)
  2544. f.setFalloffType(2)
  2545. if type == 'FT_ONE_OVER_R_OVER_DISTANCE':
  2546. f.setFalloffType(3)
  2547. if type == 'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED':
  2548. f.setFalloffType(4)
  2549. if type == 'FT_ONE_OVER_R_OVER_DISTANCE_CUBED':
  2550. f.setFalloffType(5)
  2551. def setForceCenter(vec, f = force):
  2552. f.setForceCenter(Point3(vec[0], vec[1], vec[2]))
  2553. def setRadius(radius, f = force):
  2554. f.setRadius(radius)
  2555. forceName = type + ' Force-' + repr(count)
  2556. frame = self.createForceFrame(forcePage, forceName, force)
  2557. self.createLinearForceWidgets(frame, pageName, forceName, force)
  2558. var = self.createOptionMenu(
  2559. frame, pageName, forceName + ' Falloff',
  2560. 'Set force falloff type',
  2561. ('FT_ONE_OVER_R',
  2562. 'FT_ONE_OVER_R_SQUARED',
  2563. 'FT_ONE_OVER_R_CUBED',
  2564. 'FT_ONE_OVER_R_OVER_DISTANCE',
  2565. 'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED',
  2566. 'FT_ONE_OVER_R_OVER_DISTANCE_CUBED'),
  2567. command = setFalloffType)
  2568. self.getWidget(pageName, forceName + ' Falloff').configure(
  2569. label_width = 16)
  2570. falloff = force.getFalloffType()
  2571. if falloff == LinearDistanceForce.FTONEOVERR:
  2572. var.set('FT_ONE_OVER_R')
  2573. elif falloff == LinearDistanceForce.FTONEOVERRSQUARED:
  2574. var.set('FT_ONE_OVER_R_SQUARED')
  2575. elif falloff == LinearDistanceForce.FTONEOVERRCUBED:
  2576. var.set('FT_ONE_OVER_R_CUBED')
  2577. elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCE:
  2578. var.set('FT_ONE_OVER_R_OVER_DISTANCE')
  2579. elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCESQUARED:
  2580. var.set('FT_ONE_OVER_R_OVER_DISTANCE_SQUARED')
  2581. elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCECUBED:
  2582. var.set('FT_ONE_OVER_R_OVER_DISTANCE_CUBED')
  2583. vec = force.getForceCenter()
  2584. self.createVector3Entry(frame, pageName, forceName + ' Center',
  2585. 'Set center of force',
  2586. command = setForceCenter,
  2587. label_width = 16,
  2588. value = [vec[0], vec[1], vec[2]])
  2589. self.createFloater(frame, pageName, forceName + ' Radius',
  2590. 'Set falloff radius',
  2591. command = setRadius,
  2592. min = 0.01,
  2593. value = force.getRadius())
  2594. self.createForceActiveWidget(frame, pageName, forceName, force)