@newkrok/three-particles 1.0.3 → 2.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 (44) hide show
  1. package/README.md +28 -12
  2. package/dist/bundle-report.json +1 -0
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +6 -0
  6. package/dist/js/effects/three-particles/index.d.ts +7 -0
  7. package/dist/js/effects/three-particles/index.d.ts.map +1 -0
  8. package/dist/js/effects/three-particles/index.js +6 -0
  9. package/dist/js/effects/three-particles/shaders/particle-system-fragment-shader.glsl.d.ts +3 -0
  10. package/dist/js/effects/three-particles/shaders/particle-system-fragment-shader.glsl.d.ts.map +1 -0
  11. package/{src → dist}/js/effects/three-particles/shaders/particle-system-fragment-shader.glsl.js +66 -67
  12. package/dist/js/effects/three-particles/shaders/particle-system-vertex-shader.glsl.d.ts +3 -0
  13. package/dist/js/effects/three-particles/shaders/particle-system-vertex-shader.glsl.d.ts.map +1 -0
  14. package/{src → dist}/js/effects/three-particles/shaders/particle-system-vertex-shader.glsl.js +32 -33
  15. package/dist/js/effects/three-particles/three-particles-bezier.d.ts +5 -0
  16. package/dist/js/effects/three-particles/three-particles-bezier.d.ts.map +1 -0
  17. package/dist/js/effects/three-particles/three-particles-bezier.js +62 -0
  18. package/dist/js/effects/three-particles/three-particles-curves.d.ts +37 -0
  19. package/dist/js/effects/three-particles/three-particles-curves.d.ts.map +1 -0
  20. package/dist/js/effects/three-particles/three-particles-curves.js +37 -0
  21. package/dist/js/effects/three-particles/three-particles-enums.d.ts +25 -0
  22. package/dist/js/effects/three-particles/three-particles-enums.d.ts.map +1 -0
  23. package/dist/js/effects/three-particles/three-particles-enums.js +1 -0
  24. package/dist/js/effects/three-particles/three-particles-modifiers.d.ts +11 -0
  25. package/dist/js/effects/three-particles/three-particles-modifiers.d.ts.map +1 -0
  26. package/dist/js/effects/three-particles/three-particles-modifiers.js +93 -0
  27. package/dist/js/effects/three-particles/three-particles-utils.d.ts +31 -0
  28. package/dist/js/effects/three-particles/three-particles-utils.d.ts.map +1 -0
  29. package/dist/js/effects/three-particles/three-particles-utils.js +145 -0
  30. package/dist/js/effects/three-particles/three-particles.d.ts +13 -0
  31. package/dist/js/effects/three-particles/three-particles.d.ts.map +1 -0
  32. package/dist/js/effects/three-particles/three-particles.js +642 -0
  33. package/dist/js/effects/three-particles/types.d.ts +1037 -0
  34. package/dist/js/effects/three-particles/types.d.ts.map +1 -0
  35. package/dist/js/effects/three-particles/types.js +1 -0
  36. package/dist/three-particles.min.js +1 -0
  37. package/package.json +87 -37
  38. package/src/js/effects/three-particles/three-particles-bezier.js +0 -36
  39. package/src/js/effects/three-particles/three-particles-curves.js +0 -75
  40. package/src/js/effects/three-particles/three-particles-enums.js +0 -23
  41. package/src/js/effects/three-particles/three-particles-modifiers.js +0 -133
  42. package/src/js/effects/three-particles/three-particles-utils.js +0 -212
  43. package/src/js/effects/three-particles.d.ts +0 -138
  44. package/src/js/effects/three-particles.js +0 -931
@@ -0,0 +1,642 @@
1
+ import { ObjectUtils } from '@newkrok/three-utils';
2
+ import * as THREE from 'three';
3
+ import { Gyroscope } from 'three/examples/jsm/misc/Gyroscope.js';
4
+ import { FBM } from 'three-noise/build/three-noise.module.js';
5
+ import ParticleSystemFragmentShader from './shaders/particle-system-fragment-shader.glsl.js';
6
+ import ParticleSystemVertexShader from './shaders/particle-system-vertex-shader.glsl.js';
7
+ import { removeBezierCurveFunction } from './three-particles-bezier.js';
8
+ import { applyModifiers } from './three-particles-modifiers.js';
9
+ import { calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, getCurveFunctionFromConfig, isLifeTimeCurve, } from './three-particles-utils.js';
10
+ export * from './types.js';
11
+ let _particleSystemId = 0;
12
+ let createdParticleSystems = [];
13
+ export const blendingMap = {
14
+ 'THREE.NoBlending': THREE.NoBlending,
15
+ 'THREE.NormalBlending': THREE.NormalBlending,
16
+ 'THREE.AdditiveBlending': THREE.AdditiveBlending,
17
+ 'THREE.SubtractiveBlending': THREE.SubtractiveBlending,
18
+ 'THREE.MultiplyBlending': THREE.MultiplyBlending,
19
+ };
20
+ export const getDefaultParticleSystemConfig = () => JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
21
+ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
22
+ transform: {
23
+ position: new THREE.Vector3(),
24
+ rotation: new THREE.Vector3(),
25
+ scale: new THREE.Vector3(1, 1, 1),
26
+ },
27
+ duration: 5.0,
28
+ looping: true,
29
+ startDelay: 0,
30
+ startLifetime: 5.0,
31
+ startSpeed: 1.0,
32
+ startSize: 1.0,
33
+ startOpacity: 1.0,
34
+ startRotation: 0.0,
35
+ startColor: {
36
+ min: { r: 1.0, g: 1.0, b: 1.0 },
37
+ max: { r: 1.0, g: 1.0, b: 1.0 },
38
+ },
39
+ gravity: 0.0,
40
+ simulationSpace: "LOCAL" /* SimulationSpace.LOCAL */,
41
+ maxParticles: 100.0,
42
+ emission: {
43
+ rateOverTime: 10.0,
44
+ rateOverDistance: 0.0,
45
+ },
46
+ shape: {
47
+ shape: "SPHERE" /* Shape.SPHERE */,
48
+ sphere: {
49
+ radius: 1.0,
50
+ radiusThickness: 1.0,
51
+ arc: 360.0,
52
+ },
53
+ cone: {
54
+ angle: 25.0,
55
+ radius: 1.0,
56
+ radiusThickness: 1.0,
57
+ arc: 360.0,
58
+ },
59
+ circle: {
60
+ radius: 1.0,
61
+ radiusThickness: 1.0,
62
+ arc: 360.0,
63
+ },
64
+ rectangle: {
65
+ rotation: { x: 0.0, y: 0.0 }, // TODO: add z rotation
66
+ scale: { x: 1.0, y: 1.0 },
67
+ },
68
+ box: {
69
+ scale: { x: 1.0, y: 1.0, z: 1.0 },
70
+ emitFrom: "VOLUME" /* EmitFrom.VOLUME */,
71
+ },
72
+ },
73
+ map: undefined,
74
+ renderer: {
75
+ blending: THREE.NormalBlending,
76
+ discardBackgroundColor: false,
77
+ backgroundColorTolerance: 1.0,
78
+ backgroundColor: { r: 1.0, g: 1.0, b: 1.0 },
79
+ transparent: true,
80
+ depthTest: true,
81
+ depthWrite: false,
82
+ },
83
+ velocityOverLifetime: {
84
+ isActive: false,
85
+ linear: {
86
+ x: 0,
87
+ y: 0,
88
+ z: 0,
89
+ },
90
+ orbital: {
91
+ x: 0,
92
+ y: 0,
93
+ z: 0,
94
+ },
95
+ },
96
+ sizeOverLifetime: {
97
+ isActive: false,
98
+ lifetimeCurve: {
99
+ type: "BEZIER" /* LifeTimeCurve.BEZIER */,
100
+ scale: 1,
101
+ bezierPoints: [
102
+ { x: 0, y: 0, percentage: 0 },
103
+ { x: 1, y: 1, percentage: 1 },
104
+ ],
105
+ },
106
+ },
107
+ /* colorOverLifetime: {
108
+ isActive: false,
109
+ lifetimeCurve: {
110
+ type: LifeTimeCurve.EASING,
111
+ scale: 1,
112
+ curveFunction: CurveFunctionId.LINEAR,
113
+ },
114
+ }, */
115
+ opacityOverLifetime: {
116
+ isActive: false,
117
+ lifetimeCurve: {
118
+ type: "BEZIER" /* LifeTimeCurve.BEZIER */,
119
+ scale: 1,
120
+ bezierPoints: [
121
+ { x: 0, y: 0, percentage: 0 },
122
+ { x: 1, y: 1, percentage: 1 },
123
+ ],
124
+ },
125
+ },
126
+ rotationOverLifetime: {
127
+ isActive: false,
128
+ min: 0.0,
129
+ max: 0.0,
130
+ },
131
+ noise: {
132
+ isActive: false,
133
+ useRandomOffset: false,
134
+ strength: 1.0,
135
+ frequency: 0.5,
136
+ octaves: 1,
137
+ positionAmount: 1.0,
138
+ rotationAmount: 0.0,
139
+ sizeAmount: 0.0,
140
+ },
141
+ textureSheetAnimation: {
142
+ tiles: new THREE.Vector2(1.0, 1.0),
143
+ timeMode: "LIFETIME" /* TimeMode.LIFETIME */,
144
+ fps: 30.0,
145
+ startFrame: 0,
146
+ },
147
+ };
148
+ const createFloat32Attributes = ({ geometry, propertyName, maxParticles, factory, }) => {
149
+ geometry.setAttribute(propertyName, new THREE.BufferAttribute(new Float32Array(Array.from({ length: maxParticles }, typeof factory === 'function' ? factory : () => factory)), 1));
150
+ };
151
+ const calculatePositionAndVelocity = (generalData, { shape, sphere, cone, circle, rectangle, box }, startSpeed, position, velocity) => {
152
+ const calculatedStartSpeed = calculateValue(generalData.particleSystemId, startSpeed, generalData.normalizedLifetimePercentage);
153
+ switch (shape) {
154
+ case "SPHERE" /* Shape.SPHERE */:
155
+ calculateRandomPositionAndVelocityOnSphere(position, generalData.wrapperQuaternion, velocity, calculatedStartSpeed, sphere);
156
+ break;
157
+ case "CONE" /* Shape.CONE */:
158
+ calculateRandomPositionAndVelocityOnCone(position, generalData.wrapperQuaternion, velocity, calculatedStartSpeed, cone);
159
+ break;
160
+ case "CIRCLE" /* Shape.CIRCLE */:
161
+ calculateRandomPositionAndVelocityOnCircle(position, generalData.wrapperQuaternion, velocity, calculatedStartSpeed, circle);
162
+ break;
163
+ case "RECTANGLE" /* Shape.RECTANGLE */:
164
+ calculateRandomPositionAndVelocityOnRectangle(position, generalData.wrapperQuaternion, velocity, calculatedStartSpeed, rectangle);
165
+ break;
166
+ case "BOX" /* Shape.BOX */:
167
+ calculateRandomPositionAndVelocityOnBox(position, generalData.wrapperQuaternion, velocity, calculatedStartSpeed, box);
168
+ break;
169
+ }
170
+ };
171
+ const destroyParticleSystem = (particleSystem) => {
172
+ createdParticleSystems = createdParticleSystems.filter(({ particleSystem: savedParticleSystem, wrapper, generalData: { particleSystemId }, }) => {
173
+ if (savedParticleSystem !== particleSystem &&
174
+ wrapper !== particleSystem) {
175
+ return true;
176
+ }
177
+ removeBezierCurveFunction(particleSystemId);
178
+ savedParticleSystem.geometry.dispose();
179
+ if (Array.isArray(savedParticleSystem.material))
180
+ savedParticleSystem.material.forEach((material) => material.dispose());
181
+ else
182
+ savedParticleSystem.material.dispose();
183
+ if (savedParticleSystem.parent)
184
+ savedParticleSystem.parent.remove(savedParticleSystem);
185
+ return false;
186
+ });
187
+ };
188
+ export const createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow) => {
189
+ const now = externalNow || Date.now();
190
+ const generalData = {
191
+ particleSystemId: _particleSystemId++,
192
+ normalizedLifetimePercentage: 0,
193
+ distanceFromLastEmitByDistance: 0,
194
+ lastWorldPosition: new THREE.Vector3(-99999),
195
+ currentWorldPosition: new THREE.Vector3(-99999),
196
+ worldPositionChange: new THREE.Vector3(),
197
+ worldQuaternion: new THREE.Quaternion(),
198
+ wrapperQuaternion: new THREE.Quaternion(),
199
+ lastWorldQuaternion: new THREE.Quaternion(-99999),
200
+ worldEuler: new THREE.Euler(),
201
+ gravityVelocity: new THREE.Vector3(0, 0, 0),
202
+ startValues: {},
203
+ linearVelocityData: undefined,
204
+ orbitalVelocityData: undefined,
205
+ lifetimeValues: {},
206
+ creationTimes: [],
207
+ noise: {
208
+ isActive: false,
209
+ strength: 0,
210
+ positionAmount: 0,
211
+ rotationAmount: 0,
212
+ sizeAmount: 0,
213
+ },
214
+ isEnabled: true,
215
+ };
216
+ const normalizedConfig = ObjectUtils.patchObject(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
217
+ const { transform, duration, looping, startDelay, startLifetime, startSpeed, startSize, startRotation, startColor, startOpacity, gravity, simulationSpace, maxParticles, emission, shape, map, renderer, noise, velocityOverLifetime, onUpdate, onComplete, textureSheetAnimation, } = normalizedConfig;
218
+ if (typeof renderer?.blending === 'string')
219
+ renderer.blending = blendingMap[renderer.blending];
220
+ const startPositions = Array.from({ length: maxParticles }, () => new THREE.Vector3());
221
+ const velocities = Array.from({ length: maxParticles }, () => new THREE.Vector3());
222
+ generalData.creationTimes = Array.from({ length: maxParticles }, () => 0);
223
+ if (velocityOverLifetime.isActive) {
224
+ generalData.linearVelocityData = Array.from({ length: maxParticles }, () => ({
225
+ speed: new THREE.Vector3(velocityOverLifetime.linear.x
226
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.linear.x, 0)
227
+ : 0, velocityOverLifetime.linear.y
228
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.linear.y, 0)
229
+ : 0, velocityOverLifetime.linear.z
230
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.linear.z, 0)
231
+ : 0),
232
+ valueModifiers: {
233
+ x: isLifeTimeCurve(velocityOverLifetime.linear.x || 0)
234
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.linear.x)
235
+ : undefined,
236
+ y: isLifeTimeCurve(velocityOverLifetime.linear.y || 0)
237
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.linear.y)
238
+ : undefined,
239
+ z: isLifeTimeCurve(velocityOverLifetime.linear.z || 0)
240
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.linear.z)
241
+ : undefined,
242
+ },
243
+ }));
244
+ generalData.orbitalVelocityData = Array.from({ length: maxParticles }, () => ({
245
+ speed: new THREE.Vector3(velocityOverLifetime.orbital.x
246
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.orbital.x, 0)
247
+ : 0, velocityOverLifetime.orbital.y
248
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.orbital.y, 0)
249
+ : 0, velocityOverLifetime.orbital.z
250
+ ? calculateValue(generalData.particleSystemId, velocityOverLifetime.orbital.z, 0)
251
+ : 0),
252
+ valueModifiers: {
253
+ x: isLifeTimeCurve(velocityOverLifetime.orbital.x || 0)
254
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.orbital.x)
255
+ : undefined,
256
+ y: isLifeTimeCurve(velocityOverLifetime.orbital.y || 0)
257
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.orbital.y)
258
+ : undefined,
259
+ z: isLifeTimeCurve(velocityOverLifetime.orbital.z || 0)
260
+ ? getCurveFunctionFromConfig(generalData.particleSystemId, velocityOverLifetime.orbital.z)
261
+ : undefined,
262
+ },
263
+ positionOffset: new THREE.Vector3(),
264
+ }));
265
+ }
266
+ const startValueKeys = [
267
+ 'startSize',
268
+ 'startOpacity',
269
+ ];
270
+ startValueKeys.forEach((key) => {
271
+ generalData.startValues[key] = Array.from({ length: maxParticles }, () => calculateValue(generalData.particleSystemId, normalizedConfig[key], 0));
272
+ });
273
+ const lifetimeValueKeys = [
274
+ 'rotationOverLifetime',
275
+ ];
276
+ lifetimeValueKeys.forEach((key) => {
277
+ const value = normalizedConfig[key];
278
+ if (value.isActive)
279
+ generalData.lifetimeValues[key] = Array.from({ length: maxParticles }, () => THREE.MathUtils.randFloat(value.min, value.max));
280
+ });
281
+ generalData.noise = {
282
+ isActive: noise.isActive,
283
+ strength: noise.strength,
284
+ positionAmount: noise.positionAmount,
285
+ rotationAmount: noise.rotationAmount,
286
+ sizeAmount: noise.sizeAmount,
287
+ sampler: noise.isActive
288
+ ? new FBM({
289
+ seed: Math.random(),
290
+ scale: noise.frequency,
291
+ octaves: noise.octaves,
292
+ })
293
+ : undefined,
294
+ offsets: noise.useRandomOffset
295
+ ? Array.from({ length: maxParticles }, () => Math.random() * 100)
296
+ : undefined,
297
+ };
298
+ const material = new THREE.ShaderMaterial({
299
+ uniforms: {
300
+ elapsed: {
301
+ value: 0.0,
302
+ },
303
+ map: {
304
+ value: map,
305
+ },
306
+ tiles: {
307
+ value: textureSheetAnimation.tiles,
308
+ },
309
+ fps: {
310
+ value: textureSheetAnimation.fps,
311
+ },
312
+ useFPSForFrameIndex: {
313
+ value: textureSheetAnimation.timeMode === "FPS" /* TimeMode.FPS */,
314
+ },
315
+ backgroundColor: {
316
+ value: renderer.backgroundColor,
317
+ },
318
+ discardBackgroundColor: {
319
+ value: renderer.discardBackgroundColor,
320
+ },
321
+ backgroundColorTolerance: {
322
+ value: renderer.backgroundColorTolerance,
323
+ },
324
+ },
325
+ vertexShader: ParticleSystemVertexShader,
326
+ fragmentShader: ParticleSystemFragmentShader,
327
+ transparent: renderer.transparent,
328
+ blending: renderer.blending,
329
+ depthTest: renderer.depthTest,
330
+ depthWrite: renderer.depthWrite,
331
+ });
332
+ const geometry = new THREE.BufferGeometry();
333
+ for (let i = 0; i < maxParticles; i++)
334
+ calculatePositionAndVelocity(generalData, shape, startSpeed, startPositions[i], velocities[i]);
335
+ geometry.setFromPoints(Array.from({ length: maxParticles }, (_, index) => startPositions[index].clone()));
336
+ const createFloat32AttributesRequest = (propertyName, factory) => {
337
+ createFloat32Attributes({
338
+ geometry,
339
+ propertyName,
340
+ maxParticles,
341
+ factory,
342
+ });
343
+ };
344
+ createFloat32AttributesRequest('isActive', 0);
345
+ createFloat32AttributesRequest('lifetime', 0);
346
+ createFloat32AttributesRequest('startLifetime', () => calculateValue(generalData.particleSystemId, startLifetime, 0) * 1000);
347
+ createFloat32AttributesRequest('startFrame', () => textureSheetAnimation.startFrame
348
+ ? calculateValue(generalData.particleSystemId, textureSheetAnimation.startFrame, 0)
349
+ : 0);
350
+ createFloat32AttributesRequest('opacity', () => calculateValue(generalData.particleSystemId, startOpacity, 0));
351
+ createFloat32AttributesRequest('rotation', () => calculateValue(generalData.particleSystemId, startRotation, 0));
352
+ createFloat32AttributesRequest('size', (_, index) => generalData.startValues.startSize[index]);
353
+ createFloat32AttributesRequest('rotation', 0);
354
+ const colorRandomRatio = Math.random();
355
+ createFloat32AttributesRequest('colorR', () => startColor.min.r +
356
+ colorRandomRatio * (startColor.max.r - startColor.min.r));
357
+ createFloat32AttributesRequest('colorG', () => startColor.min.g +
358
+ colorRandomRatio * (startColor.max.g - startColor.min.g));
359
+ createFloat32AttributesRequest('colorB', () => startColor.min.b +
360
+ colorRandomRatio * (startColor.max.b - startColor.min.b));
361
+ createFloat32AttributesRequest('colorA', 0);
362
+ const deactivateParticle = (particleIndex) => {
363
+ geometry.attributes.isActive.array[particleIndex] = 0;
364
+ geometry.attributes.colorA.array[particleIndex] = 0;
365
+ geometry.attributes.colorA.needsUpdate = true;
366
+ };
367
+ const activateParticle = ({ particleIndex, activationTime, position, }) => {
368
+ geometry.attributes.isActive.array[particleIndex] = 1;
369
+ generalData.creationTimes[particleIndex] = activationTime;
370
+ if (generalData.noise.offsets)
371
+ generalData.noise.offsets[particleIndex] = Math.random() * 100;
372
+ const colorRandomRatio = Math.random();
373
+ geometry.attributes.colorR.array[particleIndex] =
374
+ startColor.min.r +
375
+ colorRandomRatio * (startColor.max.r - startColor.min.r);
376
+ geometry.attributes.colorR.needsUpdate = true;
377
+ geometry.attributes.colorG.array[particleIndex] =
378
+ startColor.min.g +
379
+ colorRandomRatio * (startColor.max.g - startColor.min.g);
380
+ geometry.attributes.colorG.needsUpdate = true;
381
+ geometry.attributes.colorB.array[particleIndex] =
382
+ startColor.min.b +
383
+ colorRandomRatio * (startColor.max.b - startColor.min.b);
384
+ geometry.attributes.colorB.needsUpdate = true;
385
+ geometry.attributes.startFrame.array[particleIndex] =
386
+ textureSheetAnimation.startFrame
387
+ ? calculateValue(generalData.particleSystemId, textureSheetAnimation.startFrame, 0)
388
+ : 0;
389
+ geometry.attributes.startFrame.needsUpdate = true;
390
+ geometry.attributes.startLifetime.array[particleIndex] =
391
+ calculateValue(generalData.particleSystemId, startLifetime, generalData.normalizedLifetimePercentage) * 1000;
392
+ geometry.attributes.startLifetime.needsUpdate = true;
393
+ generalData.startValues.startSize[particleIndex] = calculateValue(generalData.particleSystemId, startSize, generalData.normalizedLifetimePercentage);
394
+ geometry.attributes.size.array[particleIndex] =
395
+ generalData.startValues.startSize[particleIndex];
396
+ geometry.attributes.size.needsUpdate = true;
397
+ generalData.startValues.startOpacity[particleIndex] = calculateValue(generalData.particleSystemId, startOpacity, generalData.normalizedLifetimePercentage);
398
+ geometry.attributes.colorA.array[particleIndex] =
399
+ generalData.startValues.startOpacity[particleIndex];
400
+ geometry.attributes.colorA.needsUpdate = true;
401
+ geometry.attributes.rotation.array[particleIndex] = calculateValue(generalData.particleSystemId, startRotation, generalData.normalizedLifetimePercentage);
402
+ geometry.attributes.rotation.needsUpdate = true;
403
+ if (normalizedConfig.rotationOverLifetime.isActive)
404
+ generalData.lifetimeValues.rotationOverLifetime[particleIndex] =
405
+ THREE.MathUtils.randFloat(normalizedConfig.rotationOverLifetime.min, normalizedConfig.rotationOverLifetime.max);
406
+ calculatePositionAndVelocity(generalData, shape, startSpeed, startPositions[particleIndex], velocities[particleIndex]);
407
+ const positionIndex = Math.floor(particleIndex * 3);
408
+ geometry.attributes.position.array[positionIndex] =
409
+ position.x + startPositions[particleIndex].x;
410
+ geometry.attributes.position.array[positionIndex + 1] =
411
+ position.y + startPositions[particleIndex].y;
412
+ geometry.attributes.position.array[positionIndex + 2] =
413
+ position.z + startPositions[particleIndex].z;
414
+ geometry.attributes.position.needsUpdate = true;
415
+ if (generalData.linearVelocityData) {
416
+ generalData.linearVelocityData[particleIndex].speed.set(normalizedConfig.velocityOverLifetime.linear.x
417
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.linear.x, 0)
418
+ : 0, normalizedConfig.velocityOverLifetime.linear.y
419
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.linear.y, 0)
420
+ : 0, normalizedConfig.velocityOverLifetime.linear.z
421
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.linear.z, 0)
422
+ : 0);
423
+ }
424
+ if (generalData.orbitalVelocityData) {
425
+ generalData.orbitalVelocityData[particleIndex].speed.set(normalizedConfig.velocityOverLifetime.orbital.x
426
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.orbital.x, 0)
427
+ : 0, normalizedConfig.velocityOverLifetime.orbital.y
428
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.orbital.y, 0)
429
+ : 0, normalizedConfig.velocityOverLifetime.orbital.z
430
+ ? calculateValue(generalData.particleSystemId, normalizedConfig.velocityOverLifetime.orbital.z, 0)
431
+ : 0);
432
+ generalData.orbitalVelocityData[particleIndex].positionOffset.set(startPositions[particleIndex].x, startPositions[particleIndex].y, startPositions[particleIndex].z);
433
+ }
434
+ geometry.attributes.lifetime.array[particleIndex] = 0;
435
+ geometry.attributes.lifetime.needsUpdate = true;
436
+ applyModifiers({
437
+ delta: 0,
438
+ generalData,
439
+ normalizedConfig,
440
+ attributes: particleSystem.geometry.attributes,
441
+ particleLifetimePercentage: 0,
442
+ particleIndex,
443
+ });
444
+ };
445
+ let particleSystem = new THREE.Points(geometry, material);
446
+ particleSystem.position.copy(transform.position);
447
+ particleSystem.rotation.x = THREE.MathUtils.degToRad(transform.rotation.x);
448
+ particleSystem.rotation.y = THREE.MathUtils.degToRad(transform.rotation.y);
449
+ particleSystem.rotation.z = THREE.MathUtils.degToRad(transform.rotation.z);
450
+ particleSystem.scale.copy(transform.scale);
451
+ const calculatedCreationTime = now + calculateValue(generalData.particleSystemId, startDelay) * 1000;
452
+ let wrapper;
453
+ if (normalizedConfig.simulationSpace === "WORLD" /* SimulationSpace.WORLD */) {
454
+ wrapper = new Gyroscope();
455
+ wrapper.add(particleSystem);
456
+ }
457
+ createdParticleSystems.push({
458
+ particleSystem,
459
+ wrapper,
460
+ generalData,
461
+ onUpdate,
462
+ onComplete,
463
+ creationTime: calculatedCreationTime,
464
+ lastEmissionTime: calculatedCreationTime,
465
+ duration,
466
+ looping,
467
+ simulationSpace,
468
+ gravity,
469
+ emission,
470
+ normalizedConfig,
471
+ iterationCount: 0,
472
+ velocities,
473
+ deactivateParticle,
474
+ activateParticle,
475
+ });
476
+ const resumeEmitter = () => (generalData.isEnabled = true);
477
+ const pauseEmitter = () => (generalData.isEnabled = false);
478
+ const dispose = () => destroyParticleSystem(particleSystem);
479
+ return {
480
+ instance: wrapper || particleSystem,
481
+ resumeEmitter,
482
+ pauseEmitter,
483
+ dispose,
484
+ };
485
+ };
486
+ export const updateParticleSystems = ({ now, delta, elapsed }) => {
487
+ createdParticleSystems.forEach((props) => {
488
+ const { onUpdate, generalData, onComplete, particleSystem, wrapper, creationTime, lastEmissionTime, duration, looping, emission, normalizedConfig, iterationCount, velocities, deactivateParticle, activateParticle, simulationSpace, gravity, } = props;
489
+ const lifetime = now - creationTime;
490
+ const normalizedLifetime = lifetime % (duration * 1000);
491
+ generalData.normalizedLifetimePercentage = Math.max(Math.min(normalizedLifetime / (duration * 1000), 1), 0);
492
+ const { lastWorldPosition, currentWorldPosition, worldPositionChange, lastWorldQuaternion, worldQuaternion, worldEuler, gravityVelocity, isEnabled, } = generalData;
493
+ if (wrapper?.parent)
494
+ generalData.wrapperQuaternion.copy(wrapper.parent.quaternion);
495
+ const lastWorldPositionSnapshot = { ...lastWorldPosition };
496
+ if (Array.isArray(particleSystem.material))
497
+ particleSystem.material.forEach((material) => {
498
+ if (material instanceof THREE.ShaderMaterial)
499
+ material.uniforms.elapsed.value = elapsed;
500
+ });
501
+ else {
502
+ if (particleSystem.material instanceof THREE.ShaderMaterial)
503
+ particleSystem.material.uniforms.elapsed.value = elapsed;
504
+ }
505
+ particleSystem.getWorldPosition(currentWorldPosition);
506
+ if (lastWorldPosition.x !== -99999) {
507
+ worldPositionChange.set(currentWorldPosition.x - lastWorldPosition.x, currentWorldPosition.y - lastWorldPosition.y, currentWorldPosition.z - lastWorldPosition.z);
508
+ }
509
+ generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
510
+ particleSystem.getWorldPosition(lastWorldPosition);
511
+ particleSystem.getWorldQuaternion(worldQuaternion);
512
+ if (lastWorldQuaternion.x === -99999 ||
513
+ lastWorldQuaternion.x !== worldQuaternion.x ||
514
+ lastWorldQuaternion.y !== worldQuaternion.y ||
515
+ lastWorldQuaternion.z !== worldQuaternion.z) {
516
+ worldEuler.setFromQuaternion(worldQuaternion);
517
+ lastWorldQuaternion.copy(worldQuaternion);
518
+ gravityVelocity.set(lastWorldPosition.x, lastWorldPosition.y + gravity, lastWorldPosition.z);
519
+ particleSystem.worldToLocal(gravityVelocity);
520
+ }
521
+ generalData.creationTimes.forEach((entry, index) => {
522
+ if (particleSystem.geometry.attributes.isActive.array[index]) {
523
+ const particleLifetime = now - entry;
524
+ if (particleLifetime >
525
+ particleSystem.geometry.attributes.startLifetime.array[index])
526
+ deactivateParticle(index);
527
+ else {
528
+ const velocity = velocities[index];
529
+ velocity.x -= gravityVelocity.x * delta;
530
+ velocity.y -= gravityVelocity.y * delta;
531
+ velocity.z -= gravityVelocity.z * delta;
532
+ if (gravity !== 0 ||
533
+ velocity.x !== 0 ||
534
+ velocity.y !== 0 ||
535
+ velocity.z !== 0 ||
536
+ worldPositionChange.x !== 0 ||
537
+ worldPositionChange.y !== 0 ||
538
+ worldPositionChange.z !== 0) {
539
+ const positionIndex = index * 3;
540
+ const positionArr = particleSystem.geometry.attributes.position.array;
541
+ if (simulationSpace === "WORLD" /* SimulationSpace.WORLD */) {
542
+ positionArr[positionIndex] -= worldPositionChange.x;
543
+ positionArr[positionIndex + 1] -= worldPositionChange.y;
544
+ positionArr[positionIndex + 2] -= worldPositionChange.z;
545
+ }
546
+ positionArr[positionIndex] += velocity.x * delta;
547
+ positionArr[positionIndex + 1] += velocity.y * delta;
548
+ positionArr[positionIndex + 2] += velocity.z * delta;
549
+ particleSystem.geometry.attributes.position.needsUpdate = true;
550
+ }
551
+ particleSystem.geometry.attributes.lifetime.array[index] =
552
+ particleLifetime;
553
+ particleSystem.geometry.attributes.lifetime.needsUpdate = true;
554
+ const particleLifetimePercentage = particleLifetime /
555
+ particleSystem.geometry.attributes.startLifetime.array[index];
556
+ applyModifiers({
557
+ delta,
558
+ generalData,
559
+ normalizedConfig,
560
+ attributes: particleSystem.geometry.attributes,
561
+ particleLifetimePercentage,
562
+ particleIndex: index,
563
+ });
564
+ }
565
+ }
566
+ });
567
+ if (isEnabled && (looping || lifetime < duration * 1000)) {
568
+ const emissionDelta = now - lastEmissionTime;
569
+ const neededParticlesByTime = emission.rateOverTime
570
+ ? Math.floor(calculateValue(generalData.particleSystemId, emission.rateOverTime, generalData.normalizedLifetimePercentage) *
571
+ (emissionDelta / 1000))
572
+ : 0;
573
+ const rateOverDistance = emission.rateOverDistance
574
+ ? calculateValue(generalData.particleSystemId, emission.rateOverDistance, generalData.normalizedLifetimePercentage)
575
+ : 0;
576
+ const neededParticlesByDistance = rateOverDistance > 0 && generalData.distanceFromLastEmitByDistance > 0
577
+ ? Math.floor(generalData.distanceFromLastEmitByDistance /
578
+ (1 / rateOverDistance))
579
+ : 0;
580
+ const distanceStep = neededParticlesByDistance > 0
581
+ ? {
582
+ x: (currentWorldPosition.x - lastWorldPositionSnapshot.x) /
583
+ neededParticlesByDistance,
584
+ y: (currentWorldPosition.y - lastWorldPositionSnapshot.y) /
585
+ neededParticlesByDistance,
586
+ z: (currentWorldPosition.z - lastWorldPositionSnapshot.z) /
587
+ neededParticlesByDistance,
588
+ }
589
+ : null;
590
+ const neededParticles = neededParticlesByTime + neededParticlesByDistance;
591
+ if (rateOverDistance > 0 && neededParticlesByDistance >= 1) {
592
+ generalData.distanceFromLastEmitByDistance = 0;
593
+ }
594
+ if (neededParticles > 0) {
595
+ let generatedParticlesByDistanceNeeds = 0;
596
+ for (let i = 0; i < neededParticles; i++) {
597
+ let particleIndex = -1;
598
+ particleSystem.geometry.attributes.isActive.array.find((isActive, index) => {
599
+ if (!isActive) {
600
+ particleIndex = index;
601
+ return true;
602
+ }
603
+ return false;
604
+ });
605
+ if (particleIndex !== -1 &&
606
+ particleIndex <
607
+ particleSystem.geometry.attributes.isActive.array.length) {
608
+ let position = { x: 0, y: 0, z: 0 };
609
+ if (distanceStep &&
610
+ generatedParticlesByDistanceNeeds < neededParticlesByDistance) {
611
+ position = {
612
+ x: distanceStep.x * generatedParticlesByDistanceNeeds,
613
+ y: distanceStep.y * generatedParticlesByDistanceNeeds,
614
+ z: distanceStep.z * generatedParticlesByDistanceNeeds,
615
+ };
616
+ generatedParticlesByDistanceNeeds++;
617
+ }
618
+ activateParticle({
619
+ particleIndex,
620
+ activationTime: now,
621
+ position,
622
+ });
623
+ props.lastEmissionTime = now;
624
+ }
625
+ }
626
+ }
627
+ if (onUpdate)
628
+ onUpdate({
629
+ particleSystem,
630
+ delta,
631
+ elapsed,
632
+ lifetime,
633
+ normalizedLifetime,
634
+ iterationCount: iterationCount + 1,
635
+ });
636
+ }
637
+ else if (onComplete)
638
+ onComplete({
639
+ particleSystem,
640
+ });
641
+ });
642
+ };