@newkrok/three-particles 0.2.2 → 0.3.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.1" ... }, `
21
+ `"dependencies": { ... "@newkrok/three-particles": "0.2.2" ... }, `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newkrok/three-particles",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Particle system for ThreeJS",
5
5
  "main": "src/js/three-particles.js",
6
6
  "bin": {
@@ -23,7 +23,8 @@
23
23
  },
24
24
  "homepage": "https://github.com/NewKrok/three-particles#readme",
25
25
  "dependencies": {
26
- "three": "^0.135.0"
26
+ "three": "0.135.0",
27
+ "easing-functions": "1.0.1"
27
28
  },
28
29
  "scripts": {
29
30
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1,5 +1,5 @@
1
1
  const ParticleSystemVertexShader = `
2
- attribute float startSize;
2
+ attribute float size;
3
3
  attribute float colorR;
4
4
  attribute float colorG;
5
5
  attribute float colorB;
@@ -25,7 +25,7 @@ const ParticleSystemVertexShader = `
25
25
  vStartFrame = startFrame;
26
26
 
27
27
  vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
28
- gl_PointSize = startSize * (100.0 / length(mvPosition.xyz));
28
+ gl_PointSize = size * (100.0 / length(mvPosition.xyz));
29
29
  gl_Position = projectionMatrix * mvPosition;
30
30
  }
31
31
  `;
@@ -0,0 +1,74 @@
1
+ import Easing from "easing-functions";
2
+
3
+ export const CurveFunction = {
4
+ LINEAR: "LINEAR",
5
+ QUADRATIC_IN: "QUADRATIC_IN",
6
+ QUADRATIC_OUT: "QUADRATIC_OUT",
7
+ QUADRATIC_IN_OUT: "QUADRATIC_IN_OUT",
8
+ CUBIC_IN: "CUBIC_IN",
9
+ CUBIC_OUT: "CUBIC_OUT",
10
+ CUBIC_IN_OUT: "CUBIC_IN_OUT",
11
+ QUARTIC_IN: "QUARTIC_IN",
12
+ QUARTIC_OUT: "QUARTIC_OUT",
13
+ QUARTIC_IN_OUT: "QUARTIC_IN_OUT",
14
+ QUINTIC_IN: "QUINTIC_IN",
15
+ QUINTIC_OUT: "QUINTIC_OUT",
16
+ QUINTIC_IN_OUT: "QUINTIC_IN_OUT",
17
+ SINUSOIDAL_IN: "SINUSOIDAL_IN",
18
+ SINUSOIDAL_OUT: "SINUSOIDAL_OUT",
19
+ SINUSOIDAL_IN_OUT: "SINUSOIDAL_IN_OUT",
20
+ EXPONENTIAL_IN: "EXPONENTIAL_IN",
21
+ EXPONENTIAL_OUT: "EXPONENTIAL_OUT",
22
+ EXPONENTIAL_IN_OUT: "EXPONENTIAL_IN_OUT",
23
+ CIRCULAR_IN: "CIRCULAR_IN",
24
+ CIRCULAR_OUT: "CIRCULAR_OUT",
25
+ CIRCULAR_IN_OUT: "CIRCULAR_IN_OUT",
26
+ ELASTIC_IN: "ELASTIC_IN",
27
+ ELASTIC_OUT: "ELASTIC_OUT",
28
+ ELASTIC_IN_OUT: "ELASTIC_IN_OUT",
29
+ BACK_IN: "BACK_IN",
30
+ BACK_OUT: "BACK_OUT",
31
+ BACK_IN_OUT: "BACK_IN_OUT",
32
+ BOUNCE_IN: "BOUNCE_IN",
33
+ BOUNCE_OUT: "BOUNCE_OUT",
34
+ BOUNCE_IN_OUT: "BOUNCE_IN_OUT",
35
+ };
36
+
37
+ const CurveFunctionMap = {
38
+ [CurveFunction.LINEAR]: Easing.Linear.None,
39
+ [CurveFunction.QUADRATIC_IN]: Easing.Quadratic.In,
40
+ [CurveFunction.QUADRATIC_OUT]: Easing.Quadratic.Out,
41
+ [CurveFunction.QUADRATIC_IN_OUT]: Easing.Quadratic.InOut,
42
+ [CurveFunction.CUBIC_IN]: Easing.Cubic.In,
43
+ [CurveFunction.CUBIC_OUT]: Easing.Cubic.Out,
44
+ [CurveFunction.CUBIC_IN_OUT]: Easing.Cubic.InOut,
45
+ [CurveFunction.QUARTIC_IN]: Easing.Quartic.In,
46
+ [CurveFunction.QUARTIC_OUT]: Easing.Quartic.Out,
47
+ [CurveFunction.QUARTIC_IN_OUT]: Easing.Quartic.InOut,
48
+ [CurveFunction.QUINTIC_IN]: Easing.Quintic.In,
49
+ [CurveFunction.QUINTIC_OUT]: Easing.Quintic.Out,
50
+ [CurveFunction.QUINTIC_IN_OUT]: Easing.Quintic.InOut,
51
+ [CurveFunction.SINUSOIDAL_IN]: Easing.Sinusoidal.In,
52
+ [CurveFunction.SINUSOIDAL_OUT]: Easing.Sinusoidal.Out,
53
+ [CurveFunction.SINUSOIDAL_IN_OUT]: Easing.Sinusoidal.InOut,
54
+ [CurveFunction.EXPONENTIAL_IN]: Easing.Exponential.In,
55
+ [CurveFunction.EXPONENTIAL_OUT]: Easing.Exponential.Out,
56
+ [CurveFunction.EXPONENTIAL_IN_OUT]: Easing.Exponential.InOut,
57
+ [CurveFunction.CIRCULAR_IN]: Easing.Circular.In,
58
+ [CurveFunction.CIRCULAR_OUT]: Easing.Circular.Out,
59
+ [CurveFunction.CIRCULAR_IN_OUT]: Easing.Circular.InOut,
60
+ [CurveFunction.ELASTIC_IN]: Easing.Elastic.In,
61
+ [CurveFunction.ELASTIC_OUT]: Easing.Elastic.Out,
62
+ [CurveFunction.ELASTIC_IN_OUT]: Easing.Elastic.InOut,
63
+ [CurveFunction.BACK_IN]: Easing.Back.In,
64
+ [CurveFunction.BACK_OUT]: Easing.Back.Out,
65
+ [CurveFunction.BACK_IN_OUT]: Easing.Back.InOut,
66
+ [CurveFunction.BOUNCE_IN]: Easing.Bounce.In,
67
+ [CurveFunction.BOUNCE_OUT]: Easing.Bounce.Out,
68
+ [CurveFunction.BOUNCE_IN_OUT]: Easing.Bounce.InOut,
69
+ };
70
+
71
+ export const getCurveFunction = (curveFunction) =>
72
+ typeof curveFunction === "function"
73
+ ? curveFunction
74
+ : CurveFunctionMap[curveFunction];
@@ -0,0 +1,44 @@
1
+ import { getCurveFunction } from "./three-particles-curves.js";
2
+
3
+ const modifiers = [
4
+ // {key:"colorOverLifetime", attributeKeys:["colorR", "colorG", "colorB"]},
5
+ {
6
+ key: "opacityOverLifetime",
7
+ attributeKeys: ["colorA"],
8
+ startValueKeys: ["startOpacity"],
9
+ },
10
+ {
11
+ key: "sizeOverLifetime",
12
+ attributeKeys: ["size"],
13
+ startValueKeys: ["startSize"],
14
+ },
15
+ ];
16
+
17
+ export const applyModifiers = ({
18
+ startValues,
19
+ normalizedConfig,
20
+ attributes,
21
+ particleLifetimePercentage,
22
+ particleIndex,
23
+ forceUpdate = false,
24
+ }) => {
25
+ modifiers.forEach(({ key, attributeKeys, startValueKeys }) => {
26
+ const modifier = normalizedConfig[key];
27
+ if (modifier.isActive) {
28
+ const multiplier = getCurveFunction(modifier.curveFunction)(
29
+ particleLifetimePercentage
30
+ );
31
+ attributeKeys.forEach((attributeKey, index) => {
32
+ attributes[attributeKey].array[particleIndex] =
33
+ startValues[startValueKeys[index]][particleIndex] * multiplier;
34
+ attributes[attributeKey].needsUpdate = true;
35
+ });
36
+ } else if (forceUpdate) {
37
+ attributeKeys.forEach((attributeKey, index) => {
38
+ attributes[attributeKey].array[particleIndex] =
39
+ startValues[startValueKeys[index]][particleIndex];
40
+ attributes[attributeKey].needsUpdate = true;
41
+ });
42
+ }
43
+ });
44
+ };
@@ -1,6 +1,6 @@
1
1
  import * as THREE from "three/build/three.module.js";
2
2
 
3
- export const deepMerge = (
3
+ export const patchObject = (
4
4
  objectA,
5
5
  objectB,
6
6
  config = { skippedProperties: [], applyToFirstObject: false }
@@ -9,7 +9,7 @@ export const deepMerge = (
9
9
  Object.keys(objectA).forEach((key) => {
10
10
  if (!config.skippedProperties || !config.skippedProperties.includes(key)) {
11
11
  if (typeof objectA[key] === "object" && objectA[key] && objectB[key]) {
12
- result[key] = deepMerge(objectA[key], objectB[key], config);
12
+ result[key] = patchObject(objectA[key], objectB[key], config);
13
13
  } else {
14
14
  result[key] = objectB[key] === 0 ? 0 : objectB[key] || objectA[key];
15
15
  if (config.applyToFirstObject) objectA[key] = result[key];
@@ -100,7 +100,7 @@ export const calculateRandomPositionAndVelocityOnCone = (
100
100
  sinNormalizedAngle *
101
101
  speedMultiplierByPosition *
102
102
  randomizedSpeed,
103
- -Math.cos(normalizedAngle) * randomizedSpeed
103
+ Math.cos(normalizedAngle) * randomizedSpeed
104
104
  );
105
105
  };
106
106
 
@@ -157,5 +157,5 @@ export const calculateRandomPositionAndVelocityOnRectangle = (
157
157
  startSpeed.min,
158
158
  startSpeed.max
159
159
  );
160
- velocity.set(0, 0, -randomizedSpeed);
160
+ velocity.set(0, 0, randomizedSpeed);
161
161
  };
@@ -5,11 +5,13 @@ import {
5
5
  calculateRandomPositionAndVelocityOnCone,
6
6
  calculateRandomPositionAndVelocityOnRectangle,
7
7
  calculateRandomPositionAndVelocityOnSphere,
8
- deepMerge,
9
- } from "./three-particles-utils.js";
8
+ patchObject,
9
+ } from "./three-particles/three-particles-utils.js";
10
10
 
11
- import ParticleSystemFragmentShader from "./shaders/particle-system-fragment-shader.glsl.js";
12
- import ParticleSystemVertexShader from "./shaders/particle-system-vertex-shader.glsl.js";
11
+ import { CurveFunction } from "./three-particles/three-particles-curves.js";
12
+ import ParticleSystemFragmentShader from "./three-particles/shaders/particle-system-fragment-shader.glsl.js";
13
+ import ParticleSystemVertexShader from "./three-particles/shaders/particle-system-vertex-shader.glsl.js";
14
+ import { applyModifiers } from "./three-particles/three-particles-modifiers.js";
13
15
 
14
16
  // Float32Array is not enough accurate when we are storing timestamp in it so we just remove unnecessary time
15
17
  const float32Helper = 1638200000000;
@@ -38,6 +40,11 @@ export const getDefaultParticleSystemConfig = () =>
38
40
  JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
39
41
 
40
42
  const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
43
+ transform: {
44
+ position: { x: 0, y: 0, z: 0 },
45
+ rotation: { x: 0, y: 0, z: 0 },
46
+ scale: { x: 1, y: 1, z: 1 },
47
+ },
41
48
  duration: 5.0,
42
49
  looping: true,
43
50
  startDelay: { min: 0.0, max: 0.0 },
@@ -81,6 +88,18 @@ const DEFAULT_PARTICLE_SYSTEM_CONFIG = {
81
88
  },
82
89
  },
83
90
  map: null,
91
+ sizeOverLifetime: {
92
+ isActive: false,
93
+ curveFunction: CurveFunction.LINEAR,
94
+ },
95
+ /* colorOverLifetime: {
96
+ isActive: false,
97
+ curveFunction: CurveFunction.LINEAR,
98
+ }, */
99
+ opacityOverLifetime: {
100
+ isActive: false,
101
+ curveFunction: CurveFunction.LINEAR,
102
+ },
84
103
  textureSheetAnimation: {
85
104
  tiles: new THREE.Vector2(1.0, 1.0),
86
105
  timeMode: TimeMode.LIFETIME,
@@ -158,11 +177,21 @@ export const createParticleSystem = (
158
177
  config = DEFAULT_PARTICLE_SYSTEM_CONFIG
159
178
  ) => {
160
179
  const now = Date.now();
161
- const lastWorldPosition = new THREE.Vector3(-99999, -99999, -99999);
162
- const worldPositionChange = new THREE.Vector3();
163
- const generalData = { distanceFromLastEmitByDistance: 0 };
180
+ const generalData = {
181
+ distanceFromLastEmitByDistance: 0,
182
+ lastWorldPosition: new THREE.Vector3(-99999),
183
+ currentWorldPosition: new THREE.Vector3(-99999),
184
+ worldPositionChange: new THREE.Vector3(),
185
+ worldQuaternion: new THREE.Quaternion(),
186
+ lastWorldQuaternion: new THREE.Quaternion(-99999),
187
+ worldEuler: new THREE.Euler(),
188
+ gravityVelocity: new THREE.Vector3(0, 0, 0),
189
+ startValues: {},
190
+ };
164
191
 
192
+ const normalizedConfig = patchObject(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
165
193
  const {
194
+ transform,
166
195
  duration,
167
196
  looping,
168
197
  startDelay,
@@ -181,7 +210,7 @@ export const createParticleSystem = (
181
210
  onUpdate,
182
211
  onComplete,
183
212
  textureSheetAnimation,
184
- } = deepMerge(DEFAULT_PARTICLE_SYSTEM_CONFIG, config);
213
+ } = normalizedConfig;
185
214
 
186
215
  const startPositions = Array.from(
187
216
  { length: maxParticles },
@@ -192,6 +221,16 @@ export const createParticleSystem = (
192
221
  () => new THREE.Vector3()
193
222
  );
194
223
 
224
+ const startValueKeys = ["startSize", "startOpacity"];
225
+ startValueKeys.forEach((key) => {
226
+ generalData.startValues[key] = Array.from({ length: maxParticles }, () =>
227
+ THREE.MathUtils.randFloat(
228
+ normalizedConfig[key].min,
229
+ normalizedConfig[key].max
230
+ )
231
+ );
232
+ });
233
+
195
234
  const material = new THREE.ShaderMaterial({
196
235
  uniforms: {
197
236
  elapsed: {
@@ -258,22 +297,16 @@ export const createParticleSystem = (
258
297
 
259
298
  createFloat32AttributesRequest("opacity", 0);
260
299
 
261
- createFloat32Attributes({
262
- geometry,
263
- propertyName: "rotation",
264
- maxParticles,
265
- factory: () =>
266
- THREE.Math.degToRad(
267
- THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
268
- ),
269
- });
300
+ createFloat32AttributesRequest("rotation", () =>
301
+ THREE.Math.degToRad(
302
+ THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
303
+ )
304
+ );
270
305
 
271
- createFloat32Attributes({
272
- geometry,
273
- propertyName: "startSize",
274
- maxParticles,
275
- factory: () => THREE.MathUtils.randFloat(startSize.min, startSize.max),
276
- });
306
+ createFloat32AttributesRequest(
307
+ "size",
308
+ (_, index) => generalData.startValues.startSize[index]
309
+ );
277
310
 
278
311
  createFloat32AttributesRequest("rotation", 0);
279
312
 
@@ -300,8 +333,6 @@ export const createParticleSystem = (
300
333
 
301
334
  const deactivateParticle = (particleIndex) => {
302
335
  geometry.attributes.isActive.array[particleIndex] = false;
303
- geometry.attributes.lifetime.array[particleIndex] = 0;
304
- geometry.attributes.lifetime.needsUpdate = true;
305
336
  geometry.attributes.colorA.array[particleIndex] = 0;
306
337
  geometry.attributes.colorA.needsUpdate = true;
307
338
  };
@@ -328,12 +359,6 @@ export const createParticleSystem = (
328
359
  colorRandomRatio * (startColor.max.b - startColor.min.b);
329
360
  geometry.attributes.colorB.needsUpdate = true;
330
361
 
331
- geometry.attributes.colorA.array[particleIndex] = THREE.MathUtils.randFloat(
332
- startOpacity.min,
333
- startOpacity.max
334
- );
335
- geometry.attributes.colorA.needsUpdate = true;
336
-
337
362
  geometry.attributes.startFrame.array[particleIndex] =
338
363
  THREE.MathUtils.randInt(
339
364
  textureSheetAnimation.startFrame.min,
@@ -345,14 +370,17 @@ export const createParticleSystem = (
345
370
  THREE.MathUtils.randFloat(startLifetime.min, startLifetime.max) * 1000;
346
371
  geometry.attributes.startLifetime.needsUpdate = true;
347
372
 
348
- geometry.attributes.startSize.array[particleIndex] =
373
+ generalData.startValues.startSize[particleIndex] =
349
374
  THREE.MathUtils.randFloat(startSize.min, startSize.max);
350
- geometry.attributes.startSize.needsUpdate = true;
375
+ generalData.startValues.startOpacity[particleIndex] =
376
+ THREE.MathUtils.randFloat(startOpacity.min, startOpacity.max);
351
377
 
352
378
  geometry.attributes.rotation.array[particleIndex] = THREE.Math.degToRad(
353
379
  THREE.MathUtils.randFloat(startRotation.min, startRotation.max)
354
380
  );
381
+
355
382
  geometry.attributes.rotation.needsUpdate = true;
383
+ geometry.attributes.colorB.needsUpdate = true;
356
384
 
357
385
  calculatePositionAndVelocity(
358
386
  shape,
@@ -371,19 +399,32 @@ export const createParticleSystem = (
371
399
 
372
400
  geometry.attributes.lifetime.array[particleIndex] = 0;
373
401
  geometry.attributes.lifetime.needsUpdate = true;
402
+
403
+ applyModifiers({
404
+ startValues: generalData.startValues,
405
+ normalizedConfig,
406
+ attributes: particleSystem.geometry.attributes,
407
+ particleLifetimePercentage: 0,
408
+ particleIndex,
409
+ forceUpdate: true,
410
+ });
374
411
  };
375
412
 
376
413
  const particleSystem = new THREE.Points(geometry, material);
377
414
  particleSystem.sortParticles = true;
378
415
 
416
+ particleSystem.position.copy(transform.position);
417
+ particleSystem.rotation.x = THREE.Math.degToRad(transform.rotation.x);
418
+ particleSystem.rotation.y = THREE.Math.degToRad(transform.rotation.y);
419
+ particleSystem.rotation.z = THREE.Math.degToRad(transform.rotation.z);
420
+ particleSystem.scale.copy(transform.scale);
421
+
379
422
  const calculatedCreationTime =
380
423
  now + THREE.MathUtils.randFloat(startDelay.min, startDelay.max) * 1000;
381
424
 
382
425
  createdParticleSystems.push({
383
426
  particleSystem,
384
427
  generalData,
385
- lastWorldPosition,
386
- worldPositionChange,
387
428
  onUpdate,
388
429
  onComplete,
389
430
  creationTime: calculatedCreationTime,
@@ -393,6 +434,7 @@ export const createParticleSystem = (
393
434
  simulationSpace,
394
435
  gravity,
395
436
  emission,
437
+ normalizedConfig,
396
438
  iterationCount: 0,
397
439
  velocities,
398
440
  deactivateParticle,
@@ -417,8 +459,6 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
417
459
  const {
418
460
  onUpdate,
419
461
  generalData,
420
- lastWorldPosition,
421
- worldPositionChange,
422
462
  onComplete,
423
463
  particleSystem,
424
464
  creationTime,
@@ -426,6 +466,7 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
426
466
  duration,
427
467
  looping,
428
468
  emission,
469
+ normalizedConfig,
429
470
  iterationCount,
430
471
  velocities,
431
472
  deactivateParticle,
@@ -433,21 +474,50 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
433
474
  simulationSpace,
434
475
  gravity,
435
476
  } = props;
477
+
478
+ const {
479
+ lastWorldPosition,
480
+ currentWorldPosition,
481
+ worldPositionChange,
482
+ lastWorldQuaternion,
483
+ worldQuaternion,
484
+ worldEuler,
485
+ gravityVelocity,
486
+ } = generalData;
487
+
436
488
  const lifetime = now - creationTime;
437
489
  particleSystem.material.uniforms.elapsed.value = elapsed;
438
490
 
439
- if (
440
- lastWorldPosition.x !== -99999 &&
441
- lastWorldPosition.y !== -99999 &&
442
- lastWorldPosition.z !== -99999
443
- )
491
+ particleSystem.getWorldPosition(currentWorldPosition);
492
+ if (lastWorldPosition.x !== -99999)
444
493
  worldPositionChange.set(
445
- particleSystem.position.x - lastWorldPosition.x,
446
- particleSystem.position.y - lastWorldPosition.y,
447
- particleSystem.position.z - lastWorldPosition.z
494
+ currentWorldPosition.x - lastWorldPosition.x,
495
+ currentWorldPosition.y - lastWorldPosition.y,
496
+ currentWorldPosition.z - lastWorldPosition.z
448
497
  );
449
498
  generalData.distanceFromLastEmitByDistance += worldPositionChange.length();
450
- lastWorldPosition.copy(particleSystem.position);
499
+ particleSystem.getWorldPosition(lastWorldPosition);
500
+
501
+ particleSystem.getWorldQuaternion(worldQuaternion);
502
+ if (
503
+ lastWorldQuaternion.x === -99999 ||
504
+ lastWorldQuaternion.x != worldQuaternion.x ||
505
+ lastWorldQuaternion.y != worldQuaternion.y ||
506
+ lastWorldQuaternion.z != worldQuaternion.z
507
+ ) {
508
+ worldEuler.setFromQuaternion(worldQuaternion);
509
+ lastWorldQuaternion.copy(worldQuaternion);
510
+
511
+ const tempPosX = particleSystem.position.x;
512
+ const tempPosY = particleSystem.position.y;
513
+ const tempPosZ = particleSystem.position.z;
514
+ gravityVelocity.set(0, gravity, 0);
515
+ particleSystem.position.set(0, 0, 0);
516
+ particleSystem.updateMatrixWorld();
517
+ particleSystem.worldToLocal(gravityVelocity);
518
+ particleSystem.position.set(tempPosX, tempPosY, tempPosZ);
519
+ particleSystem.updateMatrixWorld();
520
+ }
451
521
 
452
522
  particleSystem.geometry.attributes.creationTime.array.forEach(
453
523
  (entry, index) => {
@@ -460,7 +530,9 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
460
530
  deactivateParticle(index);
461
531
  else {
462
532
  const velocity = velocities[index];
463
- velocity.y -= gravity;
533
+ velocity.x -= gravityVelocity.x;
534
+ velocity.y -= gravityVelocity.y;
535
+ velocity.z -= gravityVelocity.z;
464
536
 
465
537
  if (
466
538
  gravity !== 0 ||
@@ -486,12 +558,16 @@ export const updateParticleSystems = ({ now, delta, elapsed }) => {
486
558
  particleLifetime;
487
559
  particleSystem.geometry.attributes.lifetime.needsUpdate = true;
488
560
 
489
- // TEMP
490
- particleSystem.geometry.attributes.colorA.array[index] =
491
- 1 -
561
+ const particleLifetimePercentage =
492
562
  particleLifetime /
493
- particleSystem.geometry.attributes.startLifetime.array[index];
494
- particleSystem.geometry.attributes.colorA.needsUpdate = true;
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
+ });
495
571
  }
496
572
  }
497
573
  }