@rzmps/rzmps 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +1048 -0
  2. package/build/EmissionShape.d.ts +33 -0
  3. package/build/EmissionShape.js +129 -0
  4. package/build/Emitter.d.ts +58 -0
  5. package/build/Emitter.js +166 -0
  6. package/build/GpuModule.d.ts +28 -0
  7. package/build/GpuModule.js +28 -0
  8. package/build/Module.d.ts +19 -0
  9. package/build/Module.js +31 -0
  10. package/build/Particle.d.ts +60 -0
  11. package/build/Particle.js +52 -0
  12. package/build/ParticleForceField.d.ts +39 -0
  13. package/build/ParticleForceField.js +101 -0
  14. package/build/ParticleForceFieldHelper.d.ts +18 -0
  15. package/build/ParticleForceFieldHelper.js +103 -0
  16. package/build/ParticleSystem.d.ts +122 -0
  17. package/build/ParticleSystem.js +609 -0
  18. package/build/Renderer.d.ts +15 -0
  19. package/build/Renderer.js +11 -0
  20. package/build/assets/images/default.png +0 -0
  21. package/build/collision/ThreeCollisionBackend.d.ts +47 -0
  22. package/build/collision/ThreeCollisionBackend.js +340 -0
  23. package/build/enums/Axis.d.ts +6 -0
  24. package/build/enums/Axis.js +7 -0
  25. package/build/enums/DynamicValueType.d.ts +6 -0
  26. package/build/enums/DynamicValueType.js +7 -0
  27. package/build/enums/EmissionSource copy.d.ts +7 -0
  28. package/build/enums/EmissionSource copy.js +7 -0
  29. package/build/enums/EmissionSource.d.ts +7 -0
  30. package/build/enums/EmissionSource.js +7 -0
  31. package/build/enums/EndBehavior.d.ts +6 -0
  32. package/build/enums/EndBehavior.js +7 -0
  33. package/build/enums/SimulationSpace.d.ts +5 -0
  34. package/build/enums/SimulationSpace.js +6 -0
  35. package/build/enums/SpriteMaterialType.d.ts +5 -0
  36. package/build/enums/SpriteMaterialType.js +6 -0
  37. package/build/enums/TagSelectionMethod.d.ts +6 -0
  38. package/build/enums/TagSelectionMethod.js +7 -0
  39. package/build/enums/TrailMode copy.d.ts +5 -0
  40. package/build/enums/TrailMode copy.js +7 -0
  41. package/build/enums/TrailMode.d.ts +5 -0
  42. package/build/enums/TrailMode.js +7 -0
  43. package/build/enums/TrailTextureMode.d.ts +7 -0
  44. package/build/enums/TrailTextureMode.js +9 -0
  45. package/build/gpu/GpuDynamicValue.d.ts +9 -0
  46. package/build/gpu/GpuDynamicValue.js +113 -0
  47. package/build/gpu/GpuMath.d.ts +3 -0
  48. package/build/gpu/GpuMath.js +12 -0
  49. package/build/gpu/GpuParticleContext.d.ts +63 -0
  50. package/build/gpu/GpuParticleContext.js +291 -0
  51. package/build/helpers/acceptMultiple.d.ts +2 -0
  52. package/build/helpers/acceptMultiple.js +6 -0
  53. package/build/helpers/evaluateDynamic.d.ts +2 -0
  54. package/build/helpers/evaluateDynamic.js +18 -0
  55. package/build/helpers/evaluateDynamicColor.d.ts +3 -0
  56. package/build/helpers/evaluateDynamicColor.js +5 -0
  57. package/build/helpers/evaluateDynamicNumber.d.ts +2 -0
  58. package/build/helpers/evaluateDynamicNumber.js +4 -0
  59. package/build/helpers/evaluateDynamicVector3.d.ts +3 -0
  60. package/build/helpers/evaluateDynamicVector3.js +5 -0
  61. package/build/helpers/isPointInMesh.d.ts +2 -0
  62. package/build/helpers/isPointInMesh.js +8 -0
  63. package/build/helpers/particleRatio.d.ts +2 -0
  64. package/build/helpers/particleRatio.js +13 -0
  65. package/build/helpers/tagsIntersect.d.ts +2 -0
  66. package/build/helpers/tagsIntersect.js +4 -0
  67. package/build/index.d.ts +37 -0
  68. package/build/index.js +34 -0
  69. package/build/interfaces/ICollisionBackend.d.ts +21 -0
  70. package/build/interfaces/ICollisionBackend.js +1 -0
  71. package/build/interfaces/IParticleForceField.d.ts +5 -0
  72. package/build/interfaces/IParticleForceField.js +1 -0
  73. package/build/interfaces/InitialParticleValues.d.ts +18 -0
  74. package/build/interfaces/InitialParticleValues.js +1 -0
  75. package/build/materials/BasicSprite.d.ts +21 -0
  76. package/build/materials/BasicSprite.js +46 -0
  77. package/build/materials/UnlitSprite.d.ts +13 -0
  78. package/build/materials/UnlitSprite.js +31 -0
  79. package/build/materials/WebGPUBasicSprite.d.ts +8 -0
  80. package/build/materials/WebGPUBasicSprite.js +65 -0
  81. package/build/materials/WebGPUUnlitSprite.d.ts +8 -0
  82. package/build/materials/WebGPUUnlitSprite.js +49 -0
  83. package/build/modules/Audio.d.ts +60 -0
  84. package/build/modules/Audio.js +181 -0
  85. package/build/modules/Collision.d.ts +40 -0
  86. package/build/modules/Collision.js +162 -0
  87. package/build/modules/ColorBySpeed.d.ts +18 -0
  88. package/build/modules/ColorBySpeed.js +26 -0
  89. package/build/modules/ColorOverLifetime.d.ts +12 -0
  90. package/build/modules/ColorOverLifetime.js +17 -0
  91. package/build/modules/ExternalForces.d.ts +21 -0
  92. package/build/modules/ExternalForces.js +61 -0
  93. package/build/modules/ForceOverLifetime.d.ts +11 -0
  94. package/build/modules/ForceOverLifetime.js +12 -0
  95. package/build/modules/LimitVelocityOverLifetime.d.ts +16 -0
  96. package/build/modules/LimitVelocityOverLifetime.js +34 -0
  97. package/build/modules/MassOverLifetime.d.ts +11 -0
  98. package/build/modules/MassOverLifetime.js +15 -0
  99. package/build/modules/NoiseModule.d.ts +23 -0
  100. package/build/modules/NoiseModule.js +45 -0
  101. package/build/modules/RotationBySpeed.d.ts +14 -0
  102. package/build/modules/RotationBySpeed.js +19 -0
  103. package/build/modules/RotationOverLifetime.d.ts +11 -0
  104. package/build/modules/RotationOverLifetime.js +12 -0
  105. package/build/modules/ScaleBySpeed.d.ts +14 -0
  106. package/build/modules/ScaleBySpeed.js +19 -0
  107. package/build/modules/ScaleOverLifetime.d.ts +11 -0
  108. package/build/modules/ScaleOverLifetime.js +11 -0
  109. package/build/modules/TransformByNoise.d.ts +17 -0
  110. package/build/modules/TransformByNoise.js +32 -0
  111. package/build/modules/VelocityOverLifetime.d.ts +15 -0
  112. package/build/modules/VelocityOverLifetime.js +32 -0
  113. package/build/renderers/LightRenderer.d.ts +54 -0
  114. package/build/renderers/LightRenderer.js +141 -0
  115. package/build/renderers/MeshRenderer.d.ts +27 -0
  116. package/build/renderers/MeshRenderer.js +70 -0
  117. package/build/renderers/SpriteRenderer.d.ts +95 -0
  118. package/build/renderers/SpriteRenderer.js +456 -0
  119. package/build/renderers/TrailRenderer.d.ts +71 -0
  120. package/build/renderers/TrailRenderer.js +400 -0
  121. package/build/shaders/BasicSprite.frag.js +1 -0
  122. package/build/shaders/BasicSprite.vert.js +1 -0
  123. package/build/shaders/UnlitSprite.frag.js +1 -0
  124. package/build/shaders/UnlitSprite.vert.js +1 -0
  125. package/build/types/DynamicValue.d.ts +2 -0
  126. package/build/types/DynamicValue.js +1 -0
  127. package/build/types/EnumStringLiteral.d.ts +1 -0
  128. package/build/types/EnumStringLiteral.js +1 -0
  129. package/build/types/Multiple.d.ts +2 -0
  130. package/build/types/Multiple.js +1 -0
  131. package/build/types/Tag.d.ts +1 -0
  132. package/build/types/Tag.js +1 -0
  133. package/package.json +39 -0
@@ -0,0 +1,609 @@
1
+ import * as THREE from 'three';
2
+ import Emitter from './Emitter';
3
+ import acceptMultiple from './helpers/acceptMultiple';
4
+ import SpriteRenderer from './renderers/SpriteRenderer';
5
+ import evaluateDynamicNumber from './helpers/evaluateDynamicNumber';
6
+ import particleRatio from './helpers/particleRatio';
7
+ import { EndBehavior } from './enums/EndBehavior';
8
+ import { SimulationSpace } from './enums/SimulationSpace';
9
+ class ParticleSystem extends THREE.Object3D {
10
+ get simulationSpace() {
11
+ return this._simulationSpace;
12
+ }
13
+ set simulationSpace(value) {
14
+ if (value === this._simulationSpace)
15
+ return;
16
+ this.convertParticlesToSimulationSpace(value);
17
+ this._simulationSpace = value;
18
+ this.subSystems.forEach((_options, subSystem) => {
19
+ subSystem.simulationSpace = value;
20
+ });
21
+ this.syncRendererParents();
22
+ }
23
+ get scene() { return this._scene; }
24
+ get sceneCamera() { return this._camera; }
25
+ get sceneRenderer() { return this._renderer; }
26
+ get isSubSystem() { return !!this._subSystemParent; }
27
+ get destroyed() { return this._destroyed; }
28
+ constructor(options = {}) {
29
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
30
+ super();
31
+ this.particles = [];
32
+ this.emitters = [];
33
+ this.modules = [];
34
+ this.renderers = [];
35
+ this.subSystems = new Map();
36
+ this._simulationSpace = SimulationSpace.Local;
37
+ this.deltaTime = 0;
38
+ this._deathListeners = [];
39
+ this._spawnListeners = [];
40
+ this._collisionListeners = [];
41
+ this._emissionRuns = [];
42
+ this._nextEmissionRunId = 0;
43
+ this._playing = true;
44
+ this._paused = false;
45
+ this._elapsedTime = 0;
46
+ this._ended = false;
47
+ this._destroyed = false;
48
+ this._rendererObjects = new Set();
49
+ this._worldRendererRoot = new THREE.Group();
50
+ this.emitters = (_b = acceptMultiple((_a = options.emitters) !== null && _a !== void 0 ? _a : new Emitter())) !== null && _b !== void 0 ? _b : [];
51
+ this.renderers = (_d = acceptMultiple((_c = options.renderers) !== null && _c !== void 0 ? _c : new SpriteRenderer())) !== null && _d !== void 0 ? _d : [];
52
+ this.modules = (_e = acceptMultiple(options.modules)) !== null && _e !== void 0 ? _e : [];
53
+ this.simulationSpeed = (_f = options.simulationSpeed) !== null && _f !== void 0 ? _f : 1;
54
+ this.duration = (_g = options.duration) !== null && _g !== void 0 ? _g : 10;
55
+ this.looping = (_h = options.looping) !== null && _h !== void 0 ? _h : true;
56
+ this.endBehavior = (_j = options.endBehavior) !== null && _j !== void 0 ? _j : EndBehavior.None;
57
+ // If gravity is passed in, gravityModifier will be set to 1.
58
+ // In effect, this means gravity will be turned off by default,
59
+ // but if either gravity or gravityModifier are specified, they will be used.
60
+ this.gravity = (_k = options.gravity) !== null && _k !== void 0 ? _k : new THREE.Vector3(0, -9.81, 0);
61
+ this.gravityModifier = (_l = options.gravityModifier) !== null && _l !== void 0 ? _l : (options.gravity ? 1 : 0);
62
+ this._simulationSpace = (_m = options.simulationSpace) !== null && _m !== void 0 ? _m : this._simulationSpace;
63
+ this._worldRendererRoot.name = 'ParticleSystem World Renderers';
64
+ this.lastFrame = Date.now();
65
+ this.emitters.forEach((e) => e.setup(this));
66
+ this.renderers.forEach((r) => r.setup(this));
67
+ // Store these here so that other renderers/modules can access easily
68
+ // Dummy mesh that renders nothing allows us to catch the onBeforeRender hook
69
+ const dummy = new THREE.Mesh();
70
+ this.add(dummy);
71
+ dummy.onBeforeRender = (renderer, scene, camera) => {
72
+ this._renderer = renderer;
73
+ this._scene = scene;
74
+ this._camera = camera;
75
+ };
76
+ // Cleanup on remove from scene. Anything done here should not be permanent.
77
+ // If the system is removed from one scene and added to another, it should
78
+ // still function.
79
+ this.addEventListener('removed', () => this.cleanup());
80
+ }
81
+ /*
82
+ * PROCESSING
83
+ */
84
+ update() {
85
+ if (this._destroyed)
86
+ return;
87
+ // Subsystems are owned and ticked by parent, avoid double update
88
+ if (this._subSystemParent)
89
+ return;
90
+ // Tick delta time before checking for pause so that it stays consistent betwee frames, not between processed frames
91
+ this._calculateDeltaTime();
92
+ // Check for pauses
93
+ if (this._paused)
94
+ return;
95
+ this.syncRendererParents();
96
+ if (this._playing)
97
+ this._updateSystemTime();
98
+ if (this._playing)
99
+ this.emitters.forEach((emitter) => {
100
+ const particles = emitter.update(this.particles, this.getEmitterContext());
101
+ particles.forEach((p) => this._notifySpawn(p));
102
+ });
103
+ // Particle processing
104
+ this._processParticles();
105
+ // Subsystems
106
+ this._updateSubSystems();
107
+ this._handleEndBehavior();
108
+ }
109
+ _calculateDeltaTime() {
110
+ const now = Date.now();
111
+ this.deltaTime = this._scaleDeltaTime((now - this.lastFrame) / 1000);
112
+ this.lastFrame = now;
113
+ }
114
+ _scaleDeltaTime(deltaTime) {
115
+ const speed = Number.isFinite(this.simulationSpeed)
116
+ ? Math.max(0, this.simulationSpeed)
117
+ : 1;
118
+ return deltaTime * speed;
119
+ }
120
+ _processParticles() {
121
+ // Moudle preparation
122
+ this.modules
123
+ .flatMap((module) => module.withDependents())
124
+ .forEach((module) => module.prepare(this, this.deltaTime));
125
+ // Run pre modules
126
+ this.modules
127
+ .flatMap((module) => module.withDependents())
128
+ .filter((module) => module.priority < 0)
129
+ .forEach((module) => module.modify(this.particles, this.deltaTime));
130
+ // Update particles
131
+ this._updateParticles();
132
+ // Run post modules
133
+ this.modules
134
+ .flatMap((module) => module.withDependents())
135
+ .filter((module) => module.priority >= 0)
136
+ .sort((a, b) => a.priority - b.priority)
137
+ .forEach((module) => module.modify(this.particles, this.deltaTime));
138
+ this.renderers.forEach((renderer) => {
139
+ renderer.update(this.particles, this, this.deltaTime);
140
+ });
141
+ }
142
+ _updateParticles() {
143
+ // Iterate over a copy of particles so that we can safely splice particles on death inside the loop
144
+ [...this.particles].forEach((p, index) => {
145
+ // Apply gravity
146
+ p.velocity.addScaledVector(this.gravity, this.deltaTime * evaluateDynamicNumber(this.gravityModifier, p.time, p.id));
147
+ // Update time
148
+ p.realtime += this.deltaTime * 1000;
149
+ p.time = (p.realtime / 1000) / p.lifetime;
150
+ // Update transform
151
+ p.position.addScaledVector(p.velocity, this.deltaTime * p.speed);
152
+ p.rotation.addScaledVector(p.angularVelocity, this.deltaTime * p.speed);
153
+ p.scale.addScaledVector(p.scalarVelocity, this.deltaTime * p.speed);
154
+ // Update velocities
155
+ p.velocity.addScaledVector(p.acceleration, this.deltaTime * p.speed);
156
+ p.angularVelocity.addScaledVector(p.angularAcceleration, this.deltaTime * p.speed);
157
+ p.scalarVelocity.addScaledVector(p.scalarAcceleration, this.deltaTime * p.speed);
158
+ // Kill old particles
159
+ if (p.realtime > p.lifetime * 1000) {
160
+ this._notifyDeath(p);
161
+ this.particles.splice(index, 1);
162
+ this.subSystems.forEach((_options, subSystem) => {
163
+ subSystem.emitters.forEach((emitter) => emitter.clearContext(p.id));
164
+ });
165
+ }
166
+ });
167
+ }
168
+ _updateSubSystems() {
169
+ this.subSystems.forEach((options, subSystem) => {
170
+ subSystem._updateAsSubSystem(this.particles, options, this.deltaTime);
171
+ });
172
+ }
173
+ _updateAsSubSystem(parentParticles, options, parentDeltaTime) {
174
+ if (!this._playing || this._paused)
175
+ return;
176
+ this.syncRendererParents();
177
+ this.deltaTime = this._scaleDeltaTime(parentDeltaTime);
178
+ this._updateSystemTime();
179
+ if (options.emitContinuous) {
180
+ parentParticles
181
+ .filter((particle) => this._canEmitForParticle(particle, options))
182
+ .forEach((particle) => {
183
+ const transform = this._particleEmissionTransform(particle, options.inheritScale);
184
+ this.emitters.forEach((emitter) => {
185
+ const particles = emitter.updateAt(this.particles, {
186
+ key: particle.id,
187
+ transform,
188
+ time: options.inheritLifetime ? particle.time : undefined,
189
+ duration: options.inheritLifetime ? particle.lifetime : undefined,
190
+ looping: options.inheritLifetime ? false : this.looping,
191
+ color: options.inheritColor ? particle.color : undefined,
192
+ alpha: options.inheritAlpha ? particle.alpha : undefined,
193
+ mass: options.inheritMass ? particle.mass : undefined,
194
+ tags: particle.tags,
195
+ });
196
+ particles.forEach((p) => this._notifySpawn(p));
197
+ });
198
+ });
199
+ }
200
+ this._updateEmissionRuns(options);
201
+ this._processParticles();
202
+ this._updateSubSystems();
203
+ }
204
+ _updateEmissionRuns(options) {
205
+ for (let i = this._emissionRuns.length - 1; i >= 0; i -= 1) {
206
+ const run = this._emissionRuns[i];
207
+ run.realtime += this.deltaTime;
208
+ let finished = true;
209
+ this.emitters.forEach((emitter) => {
210
+ var _a;
211
+ const duration = (_a = run.duration) !== null && _a !== void 0 ? _a : this.duration;
212
+ const elapsed = run.realtime;
213
+ const startTime = 0;
214
+ const time = startTime + (elapsed / duration);
215
+ // Ignore looping emitters
216
+ if (time < 1) {
217
+ finished = false;
218
+ const newParticles = emitter.updateAt(this.particles, {
219
+ key: run.id,
220
+ transform: run.transform,
221
+ time,
222
+ duration,
223
+ elapsedTime: elapsed,
224
+ looping: false,
225
+ color: options.inheritColor ? run.particle.color : undefined,
226
+ });
227
+ newParticles.forEach((particle) => this._notifySpawn(particle));
228
+ }
229
+ else {
230
+ emitter.clearContext(run.id);
231
+ }
232
+ });
233
+ if (finished) {
234
+ this._emissionRuns.splice(i, 1);
235
+ }
236
+ }
237
+ }
238
+ _canEmitForParticle(particle, options) {
239
+ var _a;
240
+ if (!particleRatio(particle, (_a = options.ratio) !== null && _a !== void 0 ? _a : 1))
241
+ return false;
242
+ return typeof options.shouldEmit === 'function'
243
+ ? options.shouldEmit(particle)
244
+ : options.shouldEmit;
245
+ }
246
+ _startEmissionRunAtParticle(particle, options) {
247
+ const now = Date.now();
248
+ this._playing = true;
249
+ this._paused = false;
250
+ this._ended = false;
251
+ this.lastFrame = now;
252
+ this._emissionRuns.push({
253
+ id: `event_${this._nextEmissionRunId++}`,
254
+ transform: this._particleEmissionTransform(particle),
255
+ startTime: now,
256
+ realtime: 0,
257
+ // If lifetime is inherited, use the parent's lifetime instead
258
+ // of the subsystem emitter's configured duration.
259
+ duration: options.inheritLifetime
260
+ ? particle.lifetime
261
+ : this.duration,
262
+ // Pass particle info -- clone so reference doesn't get destroyed
263
+ particle: {
264
+ position: particle.position.clone(),
265
+ rotation: particle.rotation.clone(),
266
+ scale: particle.scale.clone(),
267
+ color: particle.color.clone(),
268
+ tags: particle.tags ? [...particle.tags] : undefined,
269
+ alpha: particle.alpha,
270
+ lifetime: particle.lifetime,
271
+ mass: particle.mass,
272
+ },
273
+ });
274
+ }
275
+ _particleEmissionTransform(particle, inheritScale = false) {
276
+ const quaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(particle.rotation.x, particle.rotation.y, particle.rotation.z));
277
+ return new THREE.Matrix4().compose(particle.position, quaternion, inheritScale ? particle.scale : new THREE.Vector3(1, 1, 1));
278
+ }
279
+ /*
280
+ * CONTROL
281
+ */
282
+ // Start emitting
283
+ start() {
284
+ const now = Date.now();
285
+ this._playing = true;
286
+ this._paused = false;
287
+ this._ended = false;
288
+ this._elapsedTime = 0;
289
+ this.lastFrame = now;
290
+ this.emitters.forEach((emitter) => emitter.reset());
291
+ this.subSystems.forEach((_options, subSystem) => {
292
+ subSystem.start();
293
+ });
294
+ this._emissionRuns.forEach((run) => {
295
+ this.emitters.forEach((emitter) => emitter.clearContext(run.id));
296
+ });
297
+ this._emissionRuns.length = 0;
298
+ }
299
+ // Pause emission and simulation
300
+ pause() {
301
+ if (!this._playing || this._paused)
302
+ return;
303
+ this._paused = true;
304
+ this.subSystems.forEach((_options, subSystem) => {
305
+ subSystem.pause();
306
+ });
307
+ }
308
+ resume() {
309
+ if (!this._playing || !this._paused)
310
+ return;
311
+ this._paused = false;
312
+ this.lastFrame = Date.now();
313
+ const now = Date.now();
314
+ this.particles.forEach((p) => {
315
+ p.startTime = now - p.realtime;
316
+ });
317
+ this._emissionRuns.forEach((e) => {
318
+ e.startTime = now - e.realtime * 1000;
319
+ });
320
+ this.subSystems.forEach((_options, subSystem) => {
321
+ subSystem.resume();
322
+ });
323
+ }
324
+ // Stop emission and optionally clear particles
325
+ stop(clearParticles) {
326
+ this._playing = false;
327
+ this._paused = false;
328
+ this._ended = true;
329
+ this.subSystems.forEach((_options, subSystem) => {
330
+ subSystem.stop(clearParticles);
331
+ });
332
+ this._emissionRuns.forEach((run) => {
333
+ this.emitters.forEach((emitter) => {
334
+ emitter.clearContext(run.id);
335
+ });
336
+ });
337
+ this._emissionRuns.length = 0;
338
+ if (clearParticles)
339
+ this.clearParticles();
340
+ }
341
+ destroy() {
342
+ if (this._destroyed)
343
+ return;
344
+ this._destroyed = true;
345
+ this.stop(true);
346
+ this.subSystems.forEach((_options, subSystem) => {
347
+ subSystem.destroy();
348
+ });
349
+ this.renderers.forEach((renderer) => {
350
+ renderer.destroy();
351
+ });
352
+ this.removeFromParent();
353
+ this.cleanup();
354
+ this.dispatchEvent({ type: 'destroyed' });
355
+ }
356
+ // Clear particles
357
+ clearParticles() {
358
+ this.particles.length = 0;
359
+ this.renderers.forEach((renderer) => {
360
+ renderer.update(this.particles, this);
361
+ });
362
+ this.subSystems.forEach((_options, subSystem) => {
363
+ subSystem.clearParticles();
364
+ });
365
+ }
366
+ /*
367
+ * ACCESSING
368
+ */
369
+ addEmitter(emitter) {
370
+ this.emitters.push(emitter);
371
+ emitter.setup(this);
372
+ return this;
373
+ }
374
+ removeEmitter(emitter) {
375
+ const index = this.emitters.indexOf(emitter);
376
+ if (index !== -1) {
377
+ this.emitters.splice(index, 1);
378
+ this.remove(emitter.source);
379
+ }
380
+ return this;
381
+ }
382
+ addModule(module) {
383
+ this.modules.push(module);
384
+ return this;
385
+ }
386
+ removeModule(module) {
387
+ const index = this.modules.indexOf(module);
388
+ if (index !== -1) {
389
+ this.modules.splice(index, 1);
390
+ module.cleanup();
391
+ }
392
+ return this;
393
+ }
394
+ addRenderer(renderer) {
395
+ this.renderers.push(renderer);
396
+ renderer.setup(this);
397
+ this.syncRendererParents();
398
+ return this;
399
+ }
400
+ removeRenderer(renderer) {
401
+ const index = this.renderers.indexOf(renderer);
402
+ if (index !== -1) {
403
+ this.modules.splice(index, 1);
404
+ renderer.destroy();
405
+ }
406
+ return this;
407
+ }
408
+ addRendererObject(object) {
409
+ this._rendererObjects.add(object);
410
+ this.getRendererParent().add(object);
411
+ }
412
+ addSubSystem(subSystem, options) {
413
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
414
+ // No adding recursive self
415
+ if (subSystem === this)
416
+ return this;
417
+ // Replace existing parent
418
+ if (subSystem._subSystemParent && subSystem._subSystemParent !== this) {
419
+ subSystem._subSystemParent.removeSubSystem(subSystem);
420
+ }
421
+ // Configure emission condition
422
+ const emitOnCollision = (_a = options.emitOnCollision) !== null && _a !== void 0 ? _a : false;
423
+ const emitOnSpawn = (_b = options.emitOnSpawn) !== null && _b !== void 0 ? _b : false;
424
+ const emitOnDeath = (_c = options.emitOnDeath) !== null && _c !== void 0 ? _c : false;
425
+ const emitContinuous = (_d = options.emitContinuous) !== null && _d !== void 0 ? _d : !(emitOnCollision || emitOnDeath || emitOnSpawn);
426
+ this.subSystems.set(subSystem, {
427
+ shouldEmit: (_e = options.shouldEmit) !== null && _e !== void 0 ? _e : true,
428
+ ratio: THREE.MathUtils.clamp((_f = options.ratio) !== null && _f !== void 0 ? _f : 1, 0, 1),
429
+ emitContinuous,
430
+ emitOnCollision,
431
+ emitOnSpawn,
432
+ emitOnDeath,
433
+ inheritScale: (_g = options.inheritScale) !== null && _g !== void 0 ? _g : true,
434
+ inheritLifetime: (_h = options.inheritLifetime) !== null && _h !== void 0 ? _h : emitContinuous,
435
+ inheritColor: (_j = options.inheritColor) !== null && _j !== void 0 ? _j : true,
436
+ inheritAlpha: (_k = options.inheritAlpha) !== null && _k !== void 0 ? _k : true,
437
+ inheritMass: (_l = options.inheritMass) !== null && _l !== void 0 ? _l : true,
438
+ });
439
+ subSystem._subSystemParent = this;
440
+ subSystem.simulationSpace = this.simulationSpace;
441
+ // Identity local transform makes the subsystem share this system's space.
442
+ subSystem.position.set(0, 0, 0);
443
+ subSystem.rotation.set(0, 0, 0);
444
+ subSystem.scale.set(1, 1, 1);
445
+ this.add(subSystem);
446
+ return this;
447
+ }
448
+ removeSubSystem(subSystem) {
449
+ if (!this.subSystems.delete(subSystem))
450
+ return this;
451
+ subSystem._subSystemParent = undefined;
452
+ if (subSystem.parent === this)
453
+ this.remove(subSystem);
454
+ return this;
455
+ }
456
+ /*
457
+ * LISTENERS
458
+ */
459
+ onDeath(listener) {
460
+ this._deathListeners.push(listener);
461
+ return this;
462
+ }
463
+ removeDeathListener(listener) {
464
+ this._deathListeners = this._deathListeners.filter((value) => value !== listener);
465
+ return this;
466
+ }
467
+ onSpawn(listener) {
468
+ this._spawnListeners.push(listener);
469
+ return this;
470
+ }
471
+ removeSpawnListener(listener) {
472
+ this._spawnListeners = this._spawnListeners.filter((value) => value !== listener);
473
+ return this;
474
+ }
475
+ onCollision(listener) {
476
+ this._collisionListeners.push(listener);
477
+ return this;
478
+ }
479
+ removeCollisionListener(listener) {
480
+ this._collisionListeners = this._collisionListeners.filter((value) => value !== listener);
481
+ return this;
482
+ }
483
+ // Called by Collision after a hit is found.
484
+ notifyCollision(particle, collision) {
485
+ this._collisionListeners.forEach((listener) => listener(particle, collision));
486
+ this.subSystems.forEach((options, subSystem) => {
487
+ if (options.emitOnCollision && this._canEmitForParticle(particle, options)) {
488
+ subSystem._startEmissionRunAtParticle(particle, options);
489
+ }
490
+ });
491
+ }
492
+ _notifyDeath(particle) {
493
+ this._deathListeners.forEach((listener) => listener(particle));
494
+ this.subSystems.forEach((options, subSystem) => {
495
+ if (options.emitOnDeath && this._canEmitForParticle(particle, options)) {
496
+ subSystem._startEmissionRunAtParticle(particle, options);
497
+ }
498
+ });
499
+ }
500
+ _notifySpawn(particle) {
501
+ this._spawnListeners.forEach((listener) => listener(particle));
502
+ this.subSystems.forEach((options, subSystem) => {
503
+ if (options.emitOnSpawn && this._canEmitForParticle(particle, options)) {
504
+ subSystem._startEmissionRunAtParticle(particle, options);
505
+ }
506
+ });
507
+ }
508
+ cleanup() {
509
+ this.modules
510
+ .flatMap((module) => module.withDependents())
511
+ .forEach((module) => module.cleanup());
512
+ this._worldRendererRoot.removeFromParent();
513
+ }
514
+ getEmitterContext() {
515
+ const context = {
516
+ time: this.duration === 0 ? 1 : this._elapsedTime / this.duration,
517
+ duration: this.duration,
518
+ elapsedTime: this._elapsedTime,
519
+ looping: this.looping,
520
+ };
521
+ if (this.simulationSpace !== SimulationSpace.World)
522
+ return context;
523
+ this.updateWorldMatrix(true, false);
524
+ return Object.assign(Object.assign({}, context), { transform: this.matrixWorld.clone() });
525
+ }
526
+ _updateSystemTime() {
527
+ this._elapsedTime += this.deltaTime;
528
+ if (this.looping) {
529
+ while (this.duration > 0 && this._elapsedTime >= this.duration) {
530
+ this._elapsedTime -= this.duration;
531
+ this.emitters.forEach((emitter) => emitter.reset());
532
+ }
533
+ return;
534
+ }
535
+ // Importantly, this isn't allowed to "end" if it has a parent
536
+ // If the parent is alive, this must stay alive
537
+ if (this._elapsedTime >= this.duration && !this.isSubSystem) {
538
+ this._elapsedTime = this.duration;
539
+ this._playing = false;
540
+ this._ended = true;
541
+ }
542
+ }
543
+ _handleEndBehavior() {
544
+ if (!this._ended || this.looping || this._destroyed)
545
+ return;
546
+ if (this.endBehavior === EndBehavior.DestroyImmediate) {
547
+ this.destroy();
548
+ }
549
+ else if (this.endBehavior === EndBehavior.Destroy && this.particles.length === 0) {
550
+ this.destroy();
551
+ }
552
+ }
553
+ getRendererParent() {
554
+ if (this.simulationSpace !== SimulationSpace.World)
555
+ return this;
556
+ return this._worldRendererRoot;
557
+ }
558
+ syncRendererParents() {
559
+ const worldRendererParent = this.getWorldRendererParent();
560
+ if (this.simulationSpace === SimulationSpace.World) {
561
+ if (worldRendererParent && this._worldRendererRoot.parent !== worldRendererParent) {
562
+ worldRendererParent.add(this._worldRendererRoot);
563
+ }
564
+ }
565
+ else if (this._worldRendererRoot.parent) {
566
+ this._worldRendererRoot.removeFromParent();
567
+ }
568
+ const parent = this.getRendererParent();
569
+ this._rendererObjects.forEach((object) => {
570
+ if (object.parent !== parent)
571
+ parent.add(object);
572
+ });
573
+ }
574
+ getWorldRendererParent() {
575
+ let parent = this.parent;
576
+ while (parent instanceof ParticleSystem) {
577
+ parent = parent.parent;
578
+ }
579
+ return parent !== null && parent !== void 0 ? parent : undefined;
580
+ }
581
+ convertParticlesToSimulationSpace(space) {
582
+ this.updateWorldMatrix(true, false);
583
+ if (space === SimulationSpace.World) {
584
+ this.particles.forEach((particle) => {
585
+ this.localToWorld(particle.position);
586
+ this.localDirectionToWorld(particle.velocity);
587
+ this.localDirectionToWorld(particle.acceleration);
588
+ this.localDirectionToWorld(particle.scalarVelocity);
589
+ this.localDirectionToWorld(particle.scalarAcceleration);
590
+ particle.cacheStartValues();
591
+ });
592
+ }
593
+ else {
594
+ const normalMatrix = new THREE.Matrix3().getNormalMatrix(this.matrixWorld).invert();
595
+ this.particles.forEach((particle) => {
596
+ this.worldToLocal(particle.position);
597
+ particle.velocity.applyMatrix3(normalMatrix);
598
+ particle.acceleration.applyMatrix3(normalMatrix);
599
+ particle.scalarVelocity.applyMatrix3(normalMatrix);
600
+ particle.scalarAcceleration.applyMatrix3(normalMatrix);
601
+ particle.cacheStartValues();
602
+ });
603
+ }
604
+ }
605
+ localDirectionToWorld(vector) {
606
+ vector.applyMatrix3(new THREE.Matrix3().getNormalMatrix(this.matrixWorld));
607
+ }
608
+ }
609
+ export default ParticleSystem;
@@ -0,0 +1,15 @@
1
+ import Particle from './Particle';
2
+ import ParticleSystem from './ParticleSystem';
3
+ import { StrictMultiple } from './types/Multiple';
4
+ import { Tag } from './types/Tag';
5
+ export interface RendererOptions {
6
+ tags: StrictMultiple<Tag>;
7
+ }
8
+ export default abstract class Renderer {
9
+ tags?: Tag[];
10
+ constructor(options?: Partial<RendererOptions>);
11
+ abstract setup(system: ParticleSystem): void;
12
+ protected abstract _update(particles: Particle[], system: ParticleSystem, deltaTime: number): void;
13
+ update(particles: Particle[], system: ParticleSystem, deltaTime?: number): void;
14
+ abstract destroy(): void;
15
+ }
@@ -0,0 +1,11 @@
1
+ import acceptMultiple from './helpers/acceptMultiple';
2
+ import tagsIntersect from './helpers/tagsIntersect';
3
+ export default class Renderer {
4
+ constructor(options = {}) {
5
+ this.tags = acceptMultiple(options.tags);
6
+ }
7
+ update(particles, system, deltaTime = 0) {
8
+ // Call update on particles in the group
9
+ this._update(particles.filter((p) => { var _a; return !this.tags || tagsIntersect(this.tags, (_a = p.tags) !== null && _a !== void 0 ? _a : []); }), system, deltaTime);
10
+ }
11
+ }
Binary file
@@ -0,0 +1,47 @@
1
+ import * as THREE from 'three';
2
+ import { Octree } from 'three/examples/jsm/math/Octree.js';
3
+ import { ICollisionBackend, CollisionHit, CollisionQuery } from '../interfaces/ICollisionBackend';
4
+ export interface ThreeCollisionBackendOptions {
5
+ world?: THREE.Object3D;
6
+ staticObjects?: THREE.Object3D[];
7
+ dynamicObjects?: THREE.Object3D[];
8
+ staticAfter?: number;
9
+ timeQuality?: number;
10
+ refreshQuality?: number;
11
+ objectFilter?: (object: THREE.Object3D) => boolean;
12
+ staticObjectFilter?: (object: THREE.Object3D) => boolean;
13
+ dynamicObjectFilter?: (object: THREE.Object3D) => boolean;
14
+ }
15
+ declare class ThreeCollisionBackend implements ICollisionBackend {
16
+ staticOctree: Octree;
17
+ dynamicOctree: Octree;
18
+ world?: THREE.Object3D;
19
+ staticObjects?: THREE.Object3D[];
20
+ dynamicObjects?: THREE.Object3D[];
21
+ objectFilter?: (object: THREE.Object3D) => boolean;
22
+ staticObjectFilter?: (object: THREE.Object3D) => boolean;
23
+ dynamicObjectFilter?: (object: THREE.Object3D) => boolean;
24
+ staticAfter: number;
25
+ timeQuality: number;
26
+ refreshQuality: number;
27
+ private trackedObjects;
28
+ private elapsedTime;
29
+ private dynamicUpdateAccumulator;
30
+ private refreshAccumulator;
31
+ constructor(options?: ThreeCollisionBackendOptions);
32
+ update(deltaTime: number): void;
33
+ collide(query: CollisionQuery): CollisionHit | null;
34
+ private initialize;
35
+ private refreshObjects;
36
+ private hasExplicitLists;
37
+ private collectExplicitObjects;
38
+ private collectWorldObjects;
39
+ private collectRoots;
40
+ private trackObject;
41
+ private rebuildStaticOctree;
42
+ private rebuildDynamicOctree;
43
+ private buildOctree;
44
+ private addMeshToOctree;
45
+ private matrixChanged;
46
+ }
47
+ export default ThreeCollisionBackend;