forked from panda3d/panda3d
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParticleEffect.py
More file actions
270 lines (225 loc) · 8.91 KB
/
ParticleEffect.py
File metadata and controls
270 lines (225 loc) · 8.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
from panda3d.core import *
# Leave these imports in, they may be used by ptf files.
from panda3d.physics import *
from . import Particles
from . import ForceGroup
from direct.directnotify import DirectNotifyGlobal
class ParticleEffect(NodePath):
notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect')
pid = 1
def __init__(self, name=None, particles=None):
if name is None:
name = 'particle-effect-%d' % ParticleEffect.pid
ParticleEffect.pid += 1
NodePath.__init__(self, name)
# Record particle effect name
self.name = name
# Enabled flag
self.fEnabled = 0
# Dictionary of particles and forceGroups
self.particlesDict = {}
self.forceGroupDict = {}
# The effect's particle system
if particles is not None:
self.addParticles(particles)
self.renderParent = None
def birthLitter(self):
for p in self.particlesDict.values():
p.birthLitter()
def cleanup(self):
self.removeNode()
self.disable()
if self.__isValid():
for f in self.forceGroupDict.values():
f.cleanup()
for p in self.particlesDict.values():
p.cleanup()
del self.forceGroupDict
del self.particlesDict
del self.renderParent
def getName(self):
# override NodePath.getName()
return self.name
def reset(self):
self.removeAllForces()
self.removeAllParticles()
self.forceGroupDict = {}
self.particlesDict = {}
def start(self, parent=None, renderParent=None):
assert self.notify.debug('start() - name: %s' % self.name)
self.renderParent = renderParent
self.enable()
if parent is not None:
self.reparentTo(parent)
def enable(self):
# band-aid added for client crash - grw
if self.__isValid():
if self.renderParent:
for p in self.particlesDict.values():
p.setRenderParent(self.renderParent.node())
for f in self.forceGroupDict.values():
f.enable()
for p in self.particlesDict.values():
p.enable()
self.fEnabled = 1
def disable(self):
self.detachNode()
# band-aid added for client crash - grw
if self.__isValid():
for p in self.particlesDict.values():
p.setRenderParent(p.node)
for f in self.forceGroupDict.values():
f.disable()
for p in self.particlesDict.values():
p.disable()
self.fEnabled = 0
def isEnabled(self):
"""
Note: this may be misleading if enable(), disable() not used
"""
return self.fEnabled
def addForceGroup(self, forceGroup):
forceGroup.nodePath.reparentTo(self)
forceGroup.particleEffect = self
self.forceGroupDict[forceGroup.getName()] = forceGroup
# Associate the force group with all particles
for i in range(len(forceGroup)):
self.addForce(forceGroup[i])
def addForce(self, force):
for p in list(self.particlesDict.values()):
p.addForce(force)
def removeForceGroup(self, forceGroup):
# Remove forces from all particles
for i in range(len(forceGroup)):
self.removeForce(forceGroup[i])
forceGroup.nodePath.removeNode()
forceGroup.particleEffect = None
self.forceGroupDict.pop(forceGroup.getName(), None)
def removeForce(self, force):
for p in list(self.particlesDict.values()):
p.removeForce(force)
def removeAllForces(self):
for fg in list(self.forceGroupDict.values()):
self.removeForceGroup(fg)
def addParticles(self, particles):
particles.nodePath.reparentTo(self)
self.particlesDict[particles.getName()] = particles
# Associate all forces in all force groups with the particles
for fg in list(self.forceGroupDict.values()):
for i in range(len(fg)):
particles.addForce(fg[i])
def removeParticles(self, particles):
if particles is None:
self.notify.warning('removeParticles() - particles == None!')
return
particles.nodePath.detachNode()
self.particlesDict.pop(particles.getName(), None)
# Remove all forces from the particles
for fg in list(self.forceGroupDict.values()):
for f in fg:
particles.removeForce(f)
def removeAllParticles(self):
for p in list(self.particlesDict.values()):
self.removeParticles(p)
def getParticlesList(self):
return list(self.particlesDict.values())
def getParticlesNamed(self, name):
return self.particlesDict.get(name, None)
def getParticlesDict(self):
return self.particlesDict
def getForceGroupList(self):
return list(self.forceGroupDict.values())
def getForceGroupNamed(self, name):
return self.forceGroupDict.get(name, None)
def getForceGroupDict(self):
return self.forceGroupDict
def saveConfig(self, filename):
filename = Filename(filename)
with open(filename.toOsSpecific(), 'w') as f:
# Add a blank line
f.write('\n')
# Make sure we start with a clean slate
f.write('self.reset()\n')
pos = self.getPos()
hpr = self.getHpr()
scale = self.getScale()
f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
(pos[0], pos[1], pos[2]))
f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
(hpr[0], hpr[1], hpr[2]))
f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
(scale[0], scale[1], scale[2]))
# Save all the particles to file
num = 0
for p in list(self.particlesDict.values()):
target = 'p%d' % num
num = num + 1
f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
p.printParams(f, target)
f.write('self.addParticles(%s)\n' % target)
# Save all the forces to file
num = 0
for fg in list(self.forceGroupDict.values()):
target = 'f%d' % num
num = num + 1
f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
fg.getName())
fg.printParams(f, target)
f.write('self.addForceGroup(%s)\n' % target)
def loadConfig(self, filename):
fn = Filename(filename)
vfs = VirtualFileSystem.getGlobalPtr()
try:
if not vfs.resolveFilename(fn, getModelPath().value) and not fn.isRegularFile():
raise FileNotFoundError("could not find particle file: %s" % (filename))
data = vfs.readFile(fn, True)
data = data.replace(b'\r', b'')
exec(data)
except:
self.notify.warning('loadConfig: failed to load particle file: '+ repr(filename))
raise
def accelerate(self,time,stepCount = 1,stepTime=0.0):
for particles in self.getParticlesList():
particles.accelerate(time,stepCount,stepTime)
def clearToInitial(self):
for particles in self.getParticlesList():
particles.clearToInitial()
def softStop(self):
for particles in self.getParticlesList():
particles.softStop()
def softStart(self, firstBirthDelay=None):
if self.__isValid():
for particles in self.getParticlesList():
if firstBirthDelay is not None:
particles.softStart(br=-1, first_birth_delay=firstBirthDelay)
else:
particles.softStart()
else:
# Not asserting here since we want to crash live clients for more expedient bugfix
# (Sorry, live clients)
self.notify.error('Trying to start effect(%s) after cleanup.' % (self.getName(),))
def __isValid(self):
return hasattr(self, 'forceGroupDict') and \
hasattr(self, 'particlesDict')
# Snake-case aliases.
is_enabled = isEnabled
add_force_group = addForceGroup
add_force = addForce
remove_force_group = removeForceGroup
remove_force = removeForce
remove_all_forces = removeAllForces
add_particles = addParticles
remove_particles = removeParticles
remove_all_particles = removeAllParticles
get_particles_list = getParticlesList
get_particles_named = getParticlesNamed
get_particles_dict = getParticlesDict
get_force_group_list = getForceGroupList
get_force_group_named = getForceGroupNamed
get_force_group_dict = getForceGroupDict
save_config = saveConfig
load_config = loadConfig
clear_to_initial = clearToInitial
soft_stop = softStop
soft_start = softStart
birth_litter = birthLitter