@newkrok/three-particles 0.3.1 → 0.6.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.
package/README.md CHANGED
@@ -18,4 +18,4 @@ Install with npm
18
18
  `npm i @newkrok/three-particles`
19
19
 
20
20
  Add as a package.json dependency
21
- `"dependencies": { ... "@newkrok/three-particles": "0.2.2" ... }, `
21
+ `"dependencies": { ... "@newkrok/three-particles": "0.6.0" ... }, `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newkrok/three-particles",
3
- "version": "0.3.1",
3
+ "version": "0.6.0",
4
4
  "description": "Particle system for ThreeJS",
5
5
  "main": "src/js/three-particles.js",
6
6
  "bin": {
@@ -12,7 +12,10 @@
12
12
  },
13
13
  "keywords": [
14
14
  "threejs",
15
+ "particle",
15
16
  "particles",
17
+ "particle engine",
18
+ "effects",
16
19
  "3d",
17
20
  "lib"
18
21
  ],
@@ -23,8 +26,9 @@
23
26
  },
24
27
  "homepage": "https://github.com/NewKrok/three-particles#readme",
25
28
  "dependencies": {
26
- "three": "0.135.0",
27
- "easing-functions": "1.0.1"
29
+ "easing-functions": "1.0.1",
30
+ "three": "0.137.0",
31
+ "three-noise": "^1.1.1"
28
32
  },
29
33
  "scripts": {
30
34
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -0,0 +1,36 @@
1
+ const nCr = (n, k) => {
2
+ let z = 1;
3
+ for (let i = 1; i <= k; i++) z *= (n + 1 - i) / i;
4
+ return z;
5
+ };
6
+
7
+ export const createBezierCurveFunction = (bezierPoints) => (percentage) => {
8
+ if (percentage < 0) return bezierPoints[0];
9
+ if (percentage > 1) return bezierPoints[bezierPoints.length - 1];
10
+
11
+ let start = 0;
12
+ let stop = bezierPoints.length - 1;
13
+
14
+ bezierPoints.find((point, index) => {
15
+ const result = percentage < point.percentage;
16
+ if (result) stop = index;
17
+ else if (point.percentage !== undefined) start = index;
18
+ return result;
19
+ });
20
+
21
+ const n = stop - start;
22
+ const calculatedPercentage =
23
+ (percentage - bezierPoints[start].percentage) /
24
+ (bezierPoints[stop].percentage - bezierPoints[start].percentage);
25
+
26
+ let value = 0;
27
+ for (let i = 0; i <= n; i++) {
28
+ const p = bezierPoints[start + i];
29
+ const c =
30
+ nCr(n, i) *
31
+ Math.pow(1 - calculatedPercentage, n - i) *
32
+ Math.pow(calculatedPercentage, i);
33
+ value += c * p.y;
34
+ }
35
+ return value;
36
+ };
@@ -1,6 +1,7 @@
1
1
  import Easing from "easing-functions";
2
2
 
3
3
  export const CurveFunction = {
4
+ BEZIER: "BEZIER",
4
5
  LINEAR: "LINEAR",
5
6
  QUADRATIC_IN: "QUADRATIC_IN",
6
7
  QUADRATIC_OUT: "QUADRATIC_OUT",
@@ -1,6 +1,10 @@
1
+ import * as THREE from "three";
2
+
1
3
  import { getCurveFunction } from "./three-particles-curves.js";
2
4
 
3
- const modifiers = [
5
+ const noiseInput = new THREE.Vector3(0, 0, 0);
6
+
7
+ const curveModifiers = [
4
8
  // {key:"colorOverLifetime", attributeKeys:["colorR", "colorG", "colorB"]},
5
9
  {
6
10
  key: "opacityOverLifetime",
@@ -15,17 +19,20 @@ const modifiers = [
15
19
  ];
16
20
 
17
21
  export const applyModifiers = ({
22
+ delta,
23
+ noise,
18
24
  startValues,
25
+ lifetimeValues,
19
26
  normalizedConfig,
20
27
  attributes,
21
28
  particleLifetimePercentage,
22
29
  particleIndex,
23
30
  forceUpdate = false,
24
31
  }) => {
25
- modifiers.forEach(({ key, attributeKeys, startValueKeys }) => {
26
- const modifier = normalizedConfig[key];
27
- if (modifier.isActive) {
28
- const multiplier = getCurveFunction(modifier.curveFunction)(
32
+ curveModifiers.forEach(({ key, attributeKeys, startValueKeys }) => {
33
+ const curveModifier = normalizedConfig[key];
34
+ if (curveModifier.isActive) {
35
+ const multiplier = getCurveFunction(curveModifier.curveFunction)(
29
36
  particleLifetimePercentage
30
37
  );
31
38
  attributeKeys.forEach((attributeKey, index) => {
@@ -41,4 +48,58 @@ export const applyModifiers = ({
41
48
  });
42
49
  }
43
50
  });
51
+
52
+ if (lifetimeValues.rotationOverLifetime) {
53
+ attributes.rotation.array[particleIndex] +=
54
+ lifetimeValues.rotationOverLifetime[particleIndex] * delta * 0.02;
55
+ attributes.rotation.needsUpdate = true;
56
+ }
57
+
58
+ if (noise.isActive) {
59
+ const {
60
+ sampler,
61
+ strength,
62
+ offsets,
63
+ positionAmount,
64
+ rotationAmount,
65
+ sizeAmount,
66
+ } = noise;
67
+ const positionIndex = particleIndex * 3;
68
+ const positionArr = attributes.position.array;
69
+ let noiseOnPosition;
70
+
71
+ const noisePosition =
72
+ (particleLifetimePercentage + (offsets ? offsets[particleIndex] : 0)) *
73
+ 10 *
74
+ strength;
75
+ const noisePower = 0.15 * strength;
76
+
77
+ noiseInput.set(noisePosition, 0, 0);
78
+ noiseOnPosition = sampler.get3(noiseInput);
79
+ positionArr[positionIndex] += noiseOnPosition * noisePower * positionAmount;
80
+
81
+ if (rotationAmount !== 0) {
82
+ attributes.rotation.array[particleIndex] +=
83
+ noiseOnPosition * noisePower * rotationAmount;
84
+ attributes.rotation.needsUpdate = true;
85
+ }
86
+
87
+ if (sizeAmount !== 0) {
88
+ attributes.size.array[particleIndex] +=
89
+ noiseOnPosition * noisePower * sizeAmount;
90
+ attributes.size.needsUpdate = true;
91
+ }
92
+
93
+ noiseInput.set(noisePosition, noisePosition, 0);
94
+ noiseOnPosition = sampler.get3(noiseInput);
95
+ positionArr[positionIndex + 1] +=
96
+ noiseOnPosition * noisePower * positionAmount;
97
+
98
+ noiseInput.set(noisePosition, noisePosition, noisePosition);
99
+ noiseOnPosition = sampler.get3(noiseInput);
100
+ positionArr[positionIndex + 2] +=
101
+ noiseOnPosition * noisePower * positionAmount;
102
+
103
+ attributes.position.needsUpdate = true;
104
+ }
44
105
  };
@@ -1,4 +1,6 @@
1
- import * as THREE from "three/build/three.module.js";
1
+ import * as THREE from "three";
2
+
3
+ import { EmitFrom } from "../three-particles.js";
2
4
 
3
5
  export const patchObject = (
4
6
  objectA,
@@ -8,10 +10,20 @@ export const patchObject = (
8
10
  const result = {};
9
11
  Object.keys(objectA).forEach((key) => {
10
12
  if (!config.skippedProperties || !config.skippedProperties.includes(key)) {
11
- if (typeof objectA[key] === "object" && objectA[key] && objectB[key]) {
13
+ if (
14
+ typeof objectA[key] === "object" &&
15
+ objectA[key] &&
16
+ objectB[key] &&
17
+ !Array.isArray(objectA[key])
18
+ ) {
12
19
  result[key] = patchObject(objectA[key], objectB[key], config);
13
20
  } else {
14
- result[key] = objectB[key] === 0 ? 0 : objectB[key] || objectA[key];
21
+ result[key] =
22
+ objectB[key] === 0
23
+ ? 0
24
+ : objectB[key] === false
25
+ ? false
26
+ : objectB[key] || objectA[key];
15
27
  if (config.applyToFirstObject) objectA[key] = result[key];
16
28
  }
17
29
  }
@@ -104,6 +116,54 @@ export const calculateRandomPositionAndVelocityOnCone = (
104
116
  );
105
117
  };
106
118
 
119
+ export const calculateRandomPositionAndVelocityOnBox = (
120
+ position,
121
+ velocity,
122
+ startSpeed,
123
+ { scale, emitFrom }
124
+ ) => {
125
+ switch (emitFrom) {
126
+ case EmitFrom.VOLUME:
127
+ position.x = Math.random() * scale.x - scale.x / 2;
128
+ position.y = Math.random() * scale.y - scale.y / 2;
129
+ position.z = Math.random() * scale.z - scale.z / 2;
130
+ break;
131
+
132
+ case EmitFrom.SHELL:
133
+ const side = Math.floor(Math.random() * 6);
134
+ const perpendicularAxis = side % 3;
135
+ const shellResult = [];
136
+ shellResult[perpendicularAxis] = side > 2 ? 1 : 0;
137
+ shellResult[(perpendicularAxis + 1) % 3] = Math.random();
138
+ shellResult[(perpendicularAxis + 2) % 3] = Math.random();
139
+ position.x = shellResult[0] * scale.x - scale.x / 2;
140
+ position.y = shellResult[1] * scale.y - scale.y / 2;
141
+ position.z = shellResult[2] * scale.z - scale.z / 2;
142
+ break;
143
+
144
+ case EmitFrom.EDGE:
145
+ const side2 = Math.floor(Math.random() * 6);
146
+ const perpendicularAxis2 = side2 % 3;
147
+ const edge = Math.floor(Math.random() * 4);
148
+ const edgeResult = [];
149
+ edgeResult[perpendicularAxis2] = side2 > 2 ? 1 : 0;
150
+ edgeResult[(perpendicularAxis2 + 1) % 3] =
151
+ edge < 2 ? Math.random() : edge - 2;
152
+ edgeResult[(perpendicularAxis2 + 2) % 3] =
153
+ edge < 2 ? edge : Math.random();
154
+ position.x = edgeResult[0] * scale.x - scale.x / 2;
155
+ position.y = edgeResult[1] * scale.y - scale.y / 2;
156
+ position.z = edgeResult[2] * scale.z - scale.z / 2;
157
+ break;
158
+ }
159
+
160
+ const randomizedSpeed = THREE.MathUtils.randFloat(
161
+ startSpeed.min,
162
+ startSpeed.max
163
+ );
164
+ velocity.set(0, 0, randomizedSpeed);
165
+ };
166
+
107
167
  export const calculateRandomPositionAndVelocityOnCircle = (
108
168
  position,
109
169
  velocity,
@@ -1,6 +1,7 @@
1
- import * as THREE from "three/build/three.module.js";
1
+ import * as THREE from "three";
2
2
 
3
3
  import {
4
+ calculateRandomPositionAndVelocityOnBox,
4
5
  calculateRandomPositionAndVelocityOnCircle,
5
6
  calculateRandomPositionAndVelocityOnCone,
6
7
  calculateRandomPositionAndVelocityOnRectangle,
@@ -9,12 +10,11 @@ import {
9
10
  } from "./three-particles/three-particles-utils.js";
10
11
 
11
12
  import { CurveFunction } from "./three-particles/three-particles-curves.js";
13
+ import { FBM } from "three-noise/build/three-noise.module.js";
12
14
  import ParticleSystemFragmentShader from "./three-particles/shaders/particle-system-fragment-shader.glsl.js";
13
15
  import ParticleSystemVertexShader from "./three-particles/shaders/particle-system-vertex-shader.glsl.js";
14
16
  import { applyModifiers } from "./three-particles/three-particles-modifiers.js";
15
-
16
- // Float32Array is not enough accurate when we are storing timestamp in it so we just remove unnecessary time
17
- const float32Helper = 1638200000000;
17
+ import { createBezierCurveFunction } from "./three-particles/three-particles-bezier";
18
18
 
19
19
  let createdParticleSystems = [];
20
20
 
@@ -31,11 +31,25 @@ export const Shape = {
31
31
  RECTANGLE: "RECTANGLE",
32
32
  };
33
33
 
34
+ export const EmitFrom = {
35
+ VOLUME: "VOLUME",
36
+ SHELL: "SHELL",
37
+ EDGE: "EDGE",
38
+ };
39
+
34
40
  export const TimeMode = {
35
41
  LIFETIME: "LIFETIME",
36
42
  FPS: "FPS",
37
43
  };
38
44
 
45
+ export const blendingMap = {
46
+ "THREE.NoBlending": THREE.NoBlending,
47
+ "THREE.NormalBlending": THREE.NormalBlending,
48
+ "THREE.AdditiveBlending": THREE.AdditiveBlending,
49
+ "THREE.SubtractiveBlending": THREE.SubtractiveBlending,
50
+ "THREE.MultiplyBlending": THREE.MultiplyBlending,
51
+ };
52
+
39
53
  export const getDefaultParticleSystemConfig = () =>
40
54
  JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
41
55
 
@@ -86,11 +100,38 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
86
100
  rotation: { x: 0.0, y: 0.0 }, // TODO: add z rotation
87
101
  scale: { x: 1.0, y: 1.0 },
88
102
  },
103
+ box: {
104
+ scale: { x: 1.0, y: 1.0, z: 1.0 },
105
+ emitFrom: EmitFrom.VOLUME,
106
+ },
89
107
  },
90
108
  map: null,
109
+ renderer: {
110
+ blending: THREE.NormalBlending,
111
+ transparent: true,
112
+ depthTest: true,
113
+ depthWrite: false,
114
+ },
115
+ velocityOverLifetime: {
116
+ isActive: false,
117
+ linear: {
118
+ x: { min: 0, max: 0 },
119
+ y: { min: 0, max: 0 },
120
+ z: { min: 0, max: 0 },
121
+ },
122
+ orbital: {
123
+ x: { min: 0, max: 0 },
124
+ y: { min: 0, max: 0 },
125
+ z: { min: 0, max: 0 },
126
+ },
127
+ },
91
128
  sizeOverLifetime: {
92
129
  isActive: false,
93
- curveFunction: CurveFunction.LINEAR,
130
+ curveFunction: CurveFunction.BEZIER,
131
+ bezierPoints: [
132
+ { x: 0, y: 0, percentage: 0 },
133
+ { x: 1, y: 1, percentage: 1 },
134
+ ],
94
135
  },
95
136
  /* colorOverLifetime: {
96
137
  isActive: false,
@@ -98,7 +139,26 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
98
139
  }, */
99
140
  opacityOverLifetime: {
100
141
  isActive: false,
101
- curveFunction: CurveFunction.LINEAR,
142
+ curveFunction: CurveFunction.BEZIER,
143
+ bezierPoints: [
144
+ { x: 0, y: 0, percentage: 0 },
145
+ { x: 1, y: 1, percentage: 1 },
146
+ ],
147
+ },
148
+ rotationOverLifetime: {
149
+ isActive: false,
150
+ min: 0.0,
151
+ max: 0.0,
152
+ },
153
+ noise: {
154
+ isActive: false,
155
+ useRandomOffset: false,
156
+ strength: 1.0,
157
+ frequency: 0.5,
158
+ octaves: 1,
159
+ positionAmount: 1.0,
160
+ rotationAmount: 0.0,
161
+ sizeAmount: 0.0,
102
162
  },
103
163
  textureSheetAnimation: {
104
164
  tiles: new THREE.Vector2(1.0, 1.0),
@@ -129,10 +189,11 @@ const createFloat32Attributes = ({
129
189
  };
130
190
 
131
191
  const calculatePositionAndVelocity = (
132
- { shape, sphere, cone, circle, rectangle },
192
+ { shape, sphere, cone, circle, rectangle, box },
133
193
  startSpeed,
134
194
  position,
135
- velocity
195
+ velocity,
196
+ velocityOverLifetime
136
197
  ) => {
137
198
  switch (shape) {
138
199
  case Shape.SPHERE:
@@ -170,6 +231,30 @@ const calculatePositionAndVelocity = (
170
231
  rectangle
171
232
  );
172
233
  break;
234
+
235
+ case Shape.BOX:
236
+ calculateRandomPositionAndVelocityOnBox(
237
+ position,
238
+ velocity,
239
+ startSpeed,
240
+ box
241
+ );
242
+ break;
243
+ }
244
+
245
+ if (velocityOverLifetime.isActive) {
246
+ velocity.x += THREE.MathUtils.randFloat(
247
+ velocityOverLifetime.linear.x.min,
248
+ velocityOverLifetime.linear.x.max
249
+ );
250
+ velocity.y += THREE.MathUtils.randFloat(
251
+ velocityOverLifetime.linear.y.min,
252
+ velocityOverLifetime.linear.y.max
253
+ );
254
+ velocity.z += THREE.MathUtils.randFloat(
255
+ velocityOverLifetime.linear.z.min,
256
+ velocityOverLifetime.linear.z.max
257
+ );
173
258
  }
174
259
  };
175
260
 
@@ -187,9 +272,28 @@ export const createParticleSystem = (
187
272
  worldEuler: new THREE.Euler(),
188
273
  gravityVelocity: new THREE.Vector3(0, 0, 0),
189
274
  startValues: {},
275
+ lifetimeValues: {},
276
+ creationTimes: [],
277
+ noise: null,
190
278
  };
191
279
 
192
280
  const normalizedConfig = patchObject(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
281
+
282
+ const bezierCompatibleProperties = [
283
+ "sizeOverLifetime",
284
+ "opacityOverLifetime",
285
+ ];
286
+ bezierCompatibleProperties.forEach((key) => {
287
+ if (
288
+ normalizedConfig[key].isActive &&
289
+ normalizedConfig[key].curveFunction === CurveFunction.BEZIER &&
290
+ normalizedConfig[key].bezierPoints
291
+ )
292
+ normalizedConfig[key].curveFunction = createBezierCurveFunction(
293
+ normalizedConfig[key].bezierPoints
294
+ );
295
+ });
296
+
193
297
  const {
194
298
  transform,
195
299
  duration,
@@ -207,11 +311,17 @@ export const createParticleSystem = (
207
311
  emission,
208
312
  shape,
209
313
  map,
314
+ renderer,
315
+ noise,
316
+ velocityOverLifetime,
210
317
  onUpdate,
211
318
  onComplete,
212
319
  textureSheetAnimation,
213
320
  } = normalizedConfig;
214
321
 
322
+ if (typeof renderer.blending === "string")
323
+ renderer.blending = blendingMap[renderer.blending];
324
+
215
325
  const startPositions = Array.from(
216
326
  { length: maxParticles },
217
327
  () => new THREE.Vector3()
@@ -221,6 +331,8 @@ export const createParticleSystem = (
221
331
  () => new THREE.Vector3()
222
332
  );
223
333
 
334
+ generalData.creationTimes = Array.from({ length: maxParticles }, () => 0);
335
+
224
336
  const startValueKeys = ["startSize", "startOpacity"];
225
337
  startValueKeys.forEach((key) => {
226
338
  generalData.startValues[key] = Array.from({ length: maxParticles }, () =>
@@ -231,6 +343,37 @@ export const createParticleSystem = (
231
343
  );
232
344
  });
233
345
 
346
+ const lifetimeValueKeys = ["rotationOverLifetime"];
347
+ lifetimeValueKeys.forEach((key) => {
348
+ if (normalizedConfig[key].isActive)
349
+ generalData.lifetimeValues[key] = Array.from(
350
+ { length: maxParticles },
351
+ () =>
352
+ THREE.MathUtils.randFloat(
353
+ normalizedConfig[key].min,
354
+ normalizedConfig[key].max
355
+ )
356
+ );
357
+ });
358
+
359
+ generalData.noise = {
360
+ isActive: noise.isActive,
361
+ strength: noise.strength,
362
+ positionAmount: noise.positionAmount,
363
+ rotationAmount: noise.rotationAmount,
364
+ sizeAmount: noise.sizeAmount,
365
+ sampler: noise.isActive
366
+ ? new FBM({
367
+ seed: Math.random(),
368
+ scale: noise.frequency,
369
+ octaves: noise.octaves,
370
+ })
371
+ : null,
372
+ offsets: noise.useRandomOffset
373
+ ? Array.from({ length: maxParticles }, () => Math.random() * 100)
374
+ : null,
375
+ };
376
+
234
377
  const material = new THREE.ShaderMaterial({
235
378
  uniforms: {
236
379
  elapsed: {
@@ -251,10 +394,10 @@ export const createParticleSystem = (
251
394
  },
252
395
  vertexShader: ParticleSystemVertexShader,
253
396
  fragmentShader: ParticleSystemFragmentShader,
254
- transparent: true,
255
- blending: THREE.AdditiveBlending,
256
- depthTest: true,
257
- depthWrite: false,
397
+ transparent: renderer.transparent,
398
+ blending: renderer.blending,
399
+ depthTest: renderer.depthTest,
400
+ depthWrite: renderer.depthWrite,
258
401
  });
259
402
 
260
403
  const geometry = new THREE.BufferGeometry();
@@ -264,7 +407,8 @@ export const createParticleSystem = (
264
407
  shape,
265
408
  startSpeed,
266
409
  startPositions[i],
267
- velocities[i]
410
+ velocities[i],
411
+ velocityOverLifetime
268
412
  );
269
413
 
270
414
  geometry.setFromPoints(
@@ -283,7 +427,6 @@ export const createParticleSystem = (
283
427
  };
284
428
 
285
429
  createFloat32AttributesRequest("isActive", false);
286
- createFloat32AttributesRequest("creationTime", 0);
287
430
  createFloat32AttributesRequest("lifetime", 0);
288
431
  createFloat32AttributesRequest("startLifetime", () =>
289
432
  THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max)
@@ -339,8 +482,10 @@ export const createParticleSystem = (
339
482
 
340
483
  const activateParticle = ({ particleIndex, activationTime }) => {
341
484
  geometry.attributes.isActive.array[particleIndex] = true;
342
- geometry.attributes.creationTime.array[particleIndex] =
343
- activationTime - float32Helper;
485
+ generalData.creationTimes[particleIndex] = activationTime;
486
+
487
+ if (generalData.noise.offsets)
488
+ generalData.noise.offsets[particleIndex] = Math.random() * 100;
344
489
 
345
490
  const colorRandomRatio = Math.random();
346
491
 
@@ -379,6 +524,13 @@ export const createParticleSystem = (
379
524
  THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
380
525
  );
381
526
 
527
+ if (normalizedConfig.rotationOverLifetime.isActive)
528
+ generalData.lifetimeValues.rotationOverLifetime[particleIndex] =
529
+ THREE.MathUtils.randFloat(
530
+ normalizedConfig.rotationOverLifetime.min,
531
+ normalizedConfig.rotationOverLifetime.max
532
+ );
533
+
382
534
  geometry.attributes.rotation.needsUpdate = true;
383
535
  geometry.attributes.colorB.needsUpdate = true;
384
536
 
@@ -386,7 +538,8 @@ export const createParticleSystem = (
386
538
  shape,
387
539
  startSpeed,
388
540
  startPositions[particleIndex],
389
- velocities[particleIndex]
541
+ velocities[particleIndex],
542
+ velocityOverLifetime
390
543
  );
391
544
  const positionIndex = Math.floor(particleIndex * 3);
392
545
  geometry.attributes.position.array[positionIndex] =
@@ -395,13 +548,17 @@ export const createParticleSystem = (
395
548
  startPositions[particleIndex].y;
396
549
  geometry.attributes.position.array[positionIndex + 2] =
397
550
  startPositions[particleIndex].z;
398
- particleSystem.geometry.attributes.position.needsUpdate = true;
551
+ geometry.attributes.position.needsUpdate = true;
399
552
 
400
553
  geometry.attributes.lifetime.array[particleIndex] = 0;
401
554
  geometry.attributes.lifetime.needsUpdate = true;
402
555
 
403
556
  applyModifiers({
557
+ delta: 0,
558
+ elapsed: 0,
559
+ noise: generalData.noise,
404
560
  startValues: generalData.startValues,
561
+ lifetimeValues: generalData.lifetimeValues,
405
562
  normalizedConfig,
406
563
  attributes: particleSystem.geometry.attributes,
407
564
  particleLifetimePercentage: 0,
@@ -489,12 +646,13 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
489
646
  particleSystem.material.uniforms.elapsed.value = elapsed;
490
647
 
491
648
  particleSystem.getWorldPosition(currentWorldPosition);
492
- if (lastWorldPosition.x !== -99999)
649
+ if (lastWorldPosition.x !== -99999) {
493
650
  worldPositionChange.set(
494
651
  currentWorldPosition.x - lastWorldPosition.x,
495
652
  currentWorldPosition.y - lastWorldPosition.y,
496
653
  currentWorldPosition.z - lastWorldPosition.z
497
654
  );
655
+ }
498
656
  generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
499
657
  particleSystem.getWorldPosition(lastWorldPosition);
500
658
 
@@ -519,59 +677,65 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
519
677
  particleSystem.updateMatrixWorld();
520
678
  }
521
679
 
522
- particleSystem.geometry.attributes.creationTime.array.forEach(
523
- (entry, index) => {
524
- if (particleSystem.geometry.attributes.isActive.array[index]) {
525
- const particleLifetime = now - float32Helper - entry;
680
+ generalData.creationTimes.forEach((entry, index) => {
681
+ if (particleSystem.geometry.attributes.isActive.array[index]) {
682
+ const particleLifetime = now - entry;
683
+ if (
684
+ particleLifetime >
685
+ particleSystem.geometry.attributes.startLifetime.array[index]
686
+ )
687
+ deactivateParticle(index);
688
+ else {
689
+ const velocity = velocities[index];
690
+ velocity.x -= gravityVelocity.x;
691
+ velocity.y -= gravityVelocity.y;
692
+ velocity.z -= gravityVelocity.z;
693
+
526
694
  if (
527
- particleLifetime >
528
- particleSystem.geometry.attributes.startLifetime.array[index]
529
- )
530
- deactivateParticle(index);
531
- else {
532
- const velocity = velocities[index];
533
- velocity.x -= gravityVelocity.x;
534
- velocity.y -= gravityVelocity.y;
535
- velocity.z -= gravityVelocity.z;
536
-
537
- if (
538
- gravity !== 0 ||
539
- velocity.x !== 0 ||
540
- velocity.y !== 0 ||
541
- velocity.z !== 0
542
- ) {
543
- const positionIndex = index * 3;
544
- const positionArr =
545
- particleSystem.geometry.attributes.position.array;
546
- if (simulationSpace === SimulationSpace.WORLD) {
547
- positionArr[positionIndex] -= worldPositionChange.x;
548
- positionArr[positionIndex + 1] -= worldPositionChange.y;
549
- positionArr[positionIndex + 2] -= worldPositionChange.z;
550
- }
551
- positionArr[positionIndex] += velocity.x * delta;
552
- positionArr[positionIndex + 1] += velocity.y * delta;
553
- positionArr[positionIndex + 2] += velocity.z * delta;
554
- particleSystem.geometry.attributes.position.needsUpdate = true;
695
+ gravity !== 0 ||
696
+ velocity.x !== 0 ||
697
+ velocity.y !== 0 ||
698
+ velocity.z !== 0 ||
699
+ worldPositionChange.x !== 0 ||
700
+ worldPositionChange.y !== 0 ||
701
+ worldPositionChange.z !== 0
702
+ ) {
703
+ const positionIndex = index * 3;
704
+ const positionArr =
705
+ particleSystem.geometry.attributes.position.array;
706
+
707
+ if (simulationSpace === SimulationSpace.WORLD) {
708
+ positionArr[positionIndex] -= worldPositionChange.x;
709
+ positionArr[positionIndex + 1] -= worldPositionChange.y;
710
+ positionArr[positionIndex + 2] -= worldPositionChange.z;
555
711
  }
556
-
557
- particleSystem.geometry.attributes.lifetime.array[index] =
558
- particleLifetime;
559
- particleSystem.geometry.attributes.lifetime.needsUpdate = true;
560
-
561
- const particleLifetimePercentage =
562
- particleLifetime /
563
- particleSystem.geometry.attributes.startLifetime.array[index];
564
- applyModifiers({
565
- startValues: generalData.startValues,
566
- normalizedConfig,
567
- attributes: particleSystem.geometry.attributes,
568
- particleLifetimePercentage,
569
- particleIndex: index,
570
- });
712
+ positionArr[positionIndex] += velocity.x * delta;
713
+ positionArr[positionIndex + 1] += velocity.y * delta;
714
+ positionArr[positionIndex + 2] += velocity.z * delta;
715
+ particleSystem.geometry.attributes.position.needsUpdate = true;
571
716
  }
717
+
718
+ particleSystem.geometry.attributes.lifetime.array[index] =
719
+ particleLifetime;
720
+ particleSystem.geometry.attributes.lifetime.needsUpdate = true;
721
+
722
+ const particleLifetimePercentage =
723
+ particleLifetime /
724
+ particleSystem.geometry.attributes.startLifetime.array[index];
725
+ applyModifiers({
726
+ delta,
727
+ elapsed,
728
+ noise: generalData.noise,
729
+ startValues: generalData.startValues,
730
+ lifetimeValues: generalData.lifetimeValues,
731
+ normalizedConfig,
732
+ attributes: particleSystem.geometry.attributes,
733
+ particleLifetimePercentage,
734
+ particleIndex: index,
735
+ });
572
736
  }
573
737
  }
574
- );
738
+ });
575
739
 
576
740
  if (looping || lifetime < duration * 1000) {
577
741
  const emissionDelta = now - lastEmissionTime;