@newkrok/three-particles 2.15.2 → 2.16.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Easing from 'easing-functions';
2
- import * as THREE4 from 'three';
2
+ import * as THREE5 from 'three';
3
3
  import { ObjectUtils } from '@newkrok/three-utils';
4
4
  import { Gyroscope } from 'three/examples/jsm/misc/Gyroscope.js';
5
5
  import { FBM } from 'three-noise/build/three-noise.module.js';
@@ -60,40 +60,40 @@ var removeBezierCurveFunction = (particleSystemId) => {
60
60
  }
61
61
  };
62
62
  var getBezierCacheSize = () => cache.length;
63
- var CurveFunctionId = /* @__PURE__ */ ((CurveFunctionId2) => {
64
- CurveFunctionId2["BEZIER"] = "BEZIER";
65
- CurveFunctionId2["LINEAR"] = "LINEAR";
66
- CurveFunctionId2["QUADRATIC_IN"] = "QUADRATIC_IN";
67
- CurveFunctionId2["QUADRATIC_OUT"] = "QUADRATIC_OUT";
68
- CurveFunctionId2["QUADRATIC_IN_OUT"] = "QUADRATIC_IN_OUT";
69
- CurveFunctionId2["CUBIC_IN"] = "CUBIC_IN";
70
- CurveFunctionId2["CUBIC_OUT"] = "CUBIC_OUT";
71
- CurveFunctionId2["CUBIC_IN_OUT"] = "CUBIC_IN_OUT";
72
- CurveFunctionId2["QUARTIC_IN"] = "QUARTIC_IN";
73
- CurveFunctionId2["QUARTIC_OUT"] = "QUARTIC_OUT";
74
- CurveFunctionId2["QUARTIC_IN_OUT"] = "QUARTIC_IN_OUT";
75
- CurveFunctionId2["QUINTIC_IN"] = "QUINTIC_IN";
76
- CurveFunctionId2["QUINTIC_OUT"] = "QUINTIC_OUT";
77
- CurveFunctionId2["QUINTIC_IN_OUT"] = "QUINTIC_IN_OUT";
78
- CurveFunctionId2["SINUSOIDAL_IN"] = "SINUSOIDAL_IN";
79
- CurveFunctionId2["SINUSOIDAL_OUT"] = "SINUSOIDAL_OUT";
80
- CurveFunctionId2["SINUSOIDAL_IN_OUT"] = "SINUSOIDAL_IN_OUT";
81
- CurveFunctionId2["EXPONENTIAL_IN"] = "EXPONENTIAL_IN";
82
- CurveFunctionId2["EXPONENTIAL_OUT"] = "EXPONENTIAL_OUT";
83
- CurveFunctionId2["EXPONENTIAL_IN_OUT"] = "EXPONENTIAL_IN_OUT";
84
- CurveFunctionId2["CIRCULAR_IN"] = "CIRCULAR_IN";
85
- CurveFunctionId2["CIRCULAR_OUT"] = "CIRCULAR_OUT";
86
- CurveFunctionId2["CIRCULAR_IN_OUT"] = "CIRCULAR_IN_OUT";
87
- CurveFunctionId2["ELASTIC_IN"] = "ELASTIC_IN";
88
- CurveFunctionId2["ELASTIC_OUT"] = "ELASTIC_OUT";
89
- CurveFunctionId2["ELASTIC_IN_OUT"] = "ELASTIC_IN_OUT";
90
- CurveFunctionId2["BACK_IN"] = "BACK_IN";
91
- CurveFunctionId2["BACK_OUT"] = "BACK_OUT";
92
- CurveFunctionId2["BACK_IN_OUT"] = "BACK_IN_OUT";
93
- CurveFunctionId2["BOUNCE_IN"] = "BOUNCE_IN";
94
- CurveFunctionId2["BOUNCE_OUT"] = "BOUNCE_OUT";
95
- CurveFunctionId2["BOUNCE_IN_OUT"] = "BOUNCE_IN_OUT";
96
- return CurveFunctionId2;
63
+ var CurveFunctionId = /* @__PURE__ */ ((CurveFunctionId3) => {
64
+ CurveFunctionId3["BEZIER"] = "BEZIER";
65
+ CurveFunctionId3["LINEAR"] = "LINEAR";
66
+ CurveFunctionId3["QUADRATIC_IN"] = "QUADRATIC_IN";
67
+ CurveFunctionId3["QUADRATIC_OUT"] = "QUADRATIC_OUT";
68
+ CurveFunctionId3["QUADRATIC_IN_OUT"] = "QUADRATIC_IN_OUT";
69
+ CurveFunctionId3["CUBIC_IN"] = "CUBIC_IN";
70
+ CurveFunctionId3["CUBIC_OUT"] = "CUBIC_OUT";
71
+ CurveFunctionId3["CUBIC_IN_OUT"] = "CUBIC_IN_OUT";
72
+ CurveFunctionId3["QUARTIC_IN"] = "QUARTIC_IN";
73
+ CurveFunctionId3["QUARTIC_OUT"] = "QUARTIC_OUT";
74
+ CurveFunctionId3["QUARTIC_IN_OUT"] = "QUARTIC_IN_OUT";
75
+ CurveFunctionId3["QUINTIC_IN"] = "QUINTIC_IN";
76
+ CurveFunctionId3["QUINTIC_OUT"] = "QUINTIC_OUT";
77
+ CurveFunctionId3["QUINTIC_IN_OUT"] = "QUINTIC_IN_OUT";
78
+ CurveFunctionId3["SINUSOIDAL_IN"] = "SINUSOIDAL_IN";
79
+ CurveFunctionId3["SINUSOIDAL_OUT"] = "SINUSOIDAL_OUT";
80
+ CurveFunctionId3["SINUSOIDAL_IN_OUT"] = "SINUSOIDAL_IN_OUT";
81
+ CurveFunctionId3["EXPONENTIAL_IN"] = "EXPONENTIAL_IN";
82
+ CurveFunctionId3["EXPONENTIAL_OUT"] = "EXPONENTIAL_OUT";
83
+ CurveFunctionId3["EXPONENTIAL_IN_OUT"] = "EXPONENTIAL_IN_OUT";
84
+ CurveFunctionId3["CIRCULAR_IN"] = "CIRCULAR_IN";
85
+ CurveFunctionId3["CIRCULAR_OUT"] = "CIRCULAR_OUT";
86
+ CurveFunctionId3["CIRCULAR_IN_OUT"] = "CIRCULAR_IN_OUT";
87
+ CurveFunctionId3["ELASTIC_IN"] = "ELASTIC_IN";
88
+ CurveFunctionId3["ELASTIC_OUT"] = "ELASTIC_OUT";
89
+ CurveFunctionId3["ELASTIC_IN_OUT"] = "ELASTIC_IN_OUT";
90
+ CurveFunctionId3["BACK_IN"] = "BACK_IN";
91
+ CurveFunctionId3["BACK_OUT"] = "BACK_OUT";
92
+ CurveFunctionId3["BACK_IN_OUT"] = "BACK_IN_OUT";
93
+ CurveFunctionId3["BOUNCE_IN"] = "BOUNCE_IN";
94
+ CurveFunctionId3["BOUNCE_OUT"] = "BOUNCE_OUT";
95
+ CurveFunctionId3["BOUNCE_IN_OUT"] = "BOUNCE_IN_OUT";
96
+ return CurveFunctionId3;
97
97
  })(CurveFunctionId || {});
98
98
  var curveFunctionIdMap = {
99
99
  ["LINEAR" /* LINEAR */]: Easing.Linear.None,
@@ -165,10 +165,10 @@ var SubEmitterTrigger = /* @__PURE__ */ ((SubEmitterTrigger2) => {
165
165
  SubEmitterTrigger2["DEATH"] = "DEATH";
166
166
  return SubEmitterTrigger2;
167
167
  })(SubEmitterTrigger || {});
168
- var ForceFieldType = /* @__PURE__ */ ((ForceFieldType2) => {
169
- ForceFieldType2["POINT"] = "POINT";
170
- ForceFieldType2["DIRECTIONAL"] = "DIRECTIONAL";
171
- return ForceFieldType2;
168
+ var ForceFieldType = /* @__PURE__ */ ((ForceFieldType3) => {
169
+ ForceFieldType3["POINT"] = "POINT";
170
+ ForceFieldType3["DIRECTIONAL"] = "DIRECTIONAL";
171
+ return ForceFieldType3;
172
172
  })(ForceFieldType || {});
173
173
  var RendererType = /* @__PURE__ */ ((RendererType2) => {
174
174
  RendererType2["POINTS"] = "POINTS";
@@ -177,12 +177,37 @@ var RendererType = /* @__PURE__ */ ((RendererType2) => {
177
177
  RendererType2["MESH"] = "MESH";
178
178
  return RendererType2;
179
179
  })(RendererType || {});
180
- var ForceFieldFalloff = /* @__PURE__ */ ((ForceFieldFalloff2) => {
181
- ForceFieldFalloff2["NONE"] = "NONE";
182
- ForceFieldFalloff2["LINEAR"] = "LINEAR";
183
- ForceFieldFalloff2["QUADRATIC"] = "QUADRATIC";
184
- return ForceFieldFalloff2;
180
+ var ForceFieldFalloff = /* @__PURE__ */ ((ForceFieldFalloff3) => {
181
+ ForceFieldFalloff3["NONE"] = "NONE";
182
+ ForceFieldFalloff3["LINEAR"] = "LINEAR";
183
+ ForceFieldFalloff3["QUADRATIC"] = "QUADRATIC";
184
+ return ForceFieldFalloff3;
185
185
  })(ForceFieldFalloff || {});
186
+ var CollisionPlaneMode = /* @__PURE__ */ ((CollisionPlaneMode3) => {
187
+ CollisionPlaneMode3["KILL"] = "KILL";
188
+ CollisionPlaneMode3["CLAMP"] = "CLAMP";
189
+ CollisionPlaneMode3["BOUNCE"] = "BOUNCE";
190
+ return CollisionPlaneMode3;
191
+ })(CollisionPlaneMode || {});
192
+ var SimulationBackend = /* @__PURE__ */ ((SimulationBackend2) => {
193
+ SimulationBackend2["AUTO"] = "AUTO";
194
+ SimulationBackend2["CPU"] = "CPU";
195
+ SimulationBackend2["GPU"] = "GPU";
196
+ return SimulationBackend2;
197
+ })(SimulationBackend || {});
198
+
199
+ // src/js/effects/three-particles/three-particles-constants.ts
200
+ var SCALAR_STRIDE = 10;
201
+ var S_IS_ACTIVE = 0;
202
+ var S_LIFETIME = 1;
203
+ var S_START_LIFETIME = 2;
204
+ var S_START_FRAME = 3;
205
+ var S_SIZE = 4;
206
+ var S_ROTATION = 5;
207
+ var S_COLOR_R = 6;
208
+ var S_COLOR_G = 7;
209
+ var S_COLOR_B = 8;
210
+ var S_COLOR_A = 9;
186
211
  var calculateRandomPositionAndVelocityOnSphere = (position, quaternion, velocity, speed, {
187
212
  radius,
188
213
  radiusThickness,
@@ -227,7 +252,7 @@ var calculateRandomPositionAndVelocityOnCone = (position, quaternion, velocity,
227
252
  position.applyQuaternion(quaternion);
228
253
  const positionLength = position.length();
229
254
  const normalizedAngle = Math.abs(
230
- positionLength / radius * THREE4.MathUtils.degToRad(angle)
255
+ positionLength / radius * THREE5.MathUtils.degToRad(angle)
231
256
  );
232
257
  const sinNormalizedAngle = Math.sin(normalizedAngle);
233
258
  const speedMultiplierByPosition = 1 / positionLength;
@@ -302,8 +327,8 @@ var calculateRandomPositionAndVelocityOnRectangle = (position, quaternion, veloc
302
327
  const _rotation = rotation;
303
328
  const xOffset = Math.random() * _scale.x - _scale.x / 2;
304
329
  const yOffset = Math.random() * _scale.y - _scale.y / 2;
305
- const rotationX = THREE4.MathUtils.degToRad(_rotation.x);
306
- const rotationY = THREE4.MathUtils.degToRad(_rotation.y);
330
+ const rotationX = THREE5.MathUtils.degToRad(_rotation.x);
331
+ const rotationY = THREE5.MathUtils.degToRad(_rotation.y);
307
332
  position.x = xOffset * Math.cos(rotationY);
308
333
  position.y = yOffset * Math.cos(rotationX);
309
334
  position.z = xOffset * Math.sin(rotationY) - yOffset * Math.sin(rotationX);
@@ -320,7 +345,7 @@ var createDefaultMeshTexture = () => {
320
345
  if (context) {
321
346
  context.fillStyle = "white";
322
347
  context.fillRect(0, 0, 1, 1);
323
- const texture = new THREE4.CanvasTexture(canvas);
348
+ const texture = new THREE5.CanvasTexture(canvas);
324
349
  texture.needsUpdate = true;
325
350
  return texture;
326
351
  }
@@ -344,7 +369,7 @@ var createDefaultParticleTexture = () => {
344
369
  context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
345
370
  context.fillStyle = "white";
346
371
  context.fill();
347
- const texture = new THREE4.CanvasTexture(canvas);
372
+ const texture = new THREE5.CanvasTexture(canvas);
348
373
  texture.needsUpdate = true;
349
374
  return texture;
350
375
  } else {
@@ -381,20 +406,21 @@ var calculateValue = (particleSystemId, value, time = 0) => {
381
406
  if (value.min === value.max) {
382
407
  return value.min ?? 0;
383
408
  }
384
- return THREE4.MathUtils.randFloat(value.min ?? 0, value.max ?? 1);
409
+ return THREE5.MathUtils.randFloat(value.min ?? 0, value.max ?? 1);
385
410
  }
386
411
  const lifetimeCurve = value;
387
412
  return getCurveFunctionFromConfig(particleSystemId, lifetimeCurve)(time) * (lifetimeCurve.scale ?? 1);
388
413
  };
389
414
 
390
415
  // src/js/effects/three-particles/three-particles-modifiers.ts
391
- var noiseInput = new THREE4.Vector3(0, 0, 0);
392
- var orbitalEuler = new THREE4.Euler();
416
+ var noiseInput = new THREE5.Vector3(0, 0, 0);
417
+ var orbitalEuler = new THREE5.Euler();
393
418
  var applyModifiers = ({
394
419
  delta,
395
420
  generalData,
396
421
  normalizedConfig,
397
422
  attributes,
423
+ scalarArray,
398
424
  particleLifetimePercentage,
399
425
  particleIndex
400
426
  }) => {
@@ -408,6 +434,7 @@ var applyModifiers = ({
408
434
  } = generalData;
409
435
  const positionIndex = particleIndex * 3;
410
436
  const positionArr = attributes.position.array;
437
+ const base = particleIndex * SCALAR_STRIDE;
411
438
  if (linearVelocityData) {
412
439
  const { speed, valueModifiers } = linearVelocityData[particleIndex];
413
440
  const normalizedXSpeed = valueModifiers.x ? valueModifiers.x(particleLifetimePercentage) : speed.x;
@@ -443,8 +470,7 @@ var applyModifiers = ({
443
470
  normalizedConfig.sizeOverLifetime.lifetimeCurve,
444
471
  particleLifetimePercentage
445
472
  );
446
- attributes.size.array[particleIndex] = startValues.startSize[particleIndex] * multiplier;
447
- attributes.size.needsUpdate = true;
473
+ scalarArray[base + S_SIZE] = startValues.startSize[particleIndex] * multiplier;
448
474
  }
449
475
  if (normalizedConfig.opacityOverLifetime.isActive) {
450
476
  const multiplier = calculateValue(
@@ -452,8 +478,7 @@ var applyModifiers = ({
452
478
  normalizedConfig.opacityOverLifetime.lifetimeCurve,
453
479
  particleLifetimePercentage
454
480
  );
455
- attributes.colorA.array[particleIndex] = startValues.startOpacity[particleIndex] * multiplier;
456
- attributes.colorA.needsUpdate = true;
481
+ scalarArray[base + S_COLOR_A] = startValues.startOpacity[particleIndex] * multiplier;
457
482
  }
458
483
  if (normalizedConfig.colorOverLifetime.isActive) {
459
484
  const rMultiplier = calculateValue(
@@ -471,16 +496,12 @@ var applyModifiers = ({
471
496
  normalizedConfig.colorOverLifetime.b,
472
497
  particleLifetimePercentage
473
498
  );
474
- attributes.colorR.array[particleIndex] = startValues.startColorR[particleIndex] * rMultiplier;
475
- attributes.colorG.array[particleIndex] = startValues.startColorG[particleIndex] * gMultiplier;
476
- attributes.colorB.array[particleIndex] = startValues.startColorB[particleIndex] * bMultiplier;
477
- attributes.colorR.needsUpdate = true;
478
- attributes.colorG.needsUpdate = true;
479
- attributes.colorB.needsUpdate = true;
499
+ scalarArray[base + S_COLOR_R] = startValues.startColorR[particleIndex] * rMultiplier;
500
+ scalarArray[base + S_COLOR_G] = startValues.startColorG[particleIndex] * gMultiplier;
501
+ scalarArray[base + S_COLOR_B] = startValues.startColorB[particleIndex] * bMultiplier;
480
502
  }
481
503
  if (lifetimeValues.rotationOverLifetime) {
482
- attributes.rotation.array[particleIndex] += lifetimeValues.rotationOverLifetime[particleIndex] * delta * 0.02;
483
- attributes.rotation.needsUpdate = true;
504
+ scalarArray[base + S_ROTATION] += lifetimeValues.rotationOverLifetime[particleIndex] * delta * 0.02;
484
505
  }
485
506
  if (noise.isActive) {
486
507
  const {
@@ -498,12 +519,10 @@ var applyModifiers = ({
498
519
  noiseOnPosition = sampler.get3(noiseInput);
499
520
  positionArr[positionIndex] += noiseOnPosition * noisePower * positionAmount;
500
521
  if (rotationAmount !== 0) {
501
- attributes.rotation.array[particleIndex] += noiseOnPosition * noisePower * rotationAmount;
502
- attributes.rotation.needsUpdate = true;
522
+ scalarArray[base + S_ROTATION] += noiseOnPosition * noisePower * rotationAmount;
503
523
  }
504
524
  if (sizeAmount !== 0) {
505
- attributes.size.array[particleIndex] += noiseOnPosition * noisePower * sizeAmount;
506
- attributes.size.needsUpdate = true;
525
+ scalarArray[base + S_SIZE] += noiseOnPosition * noisePower * sizeAmount;
507
526
  }
508
527
  noiseInput.set(noisePosition, noisePosition, 0);
509
528
  noiseOnPosition = sampler.get3(noiseInput);
@@ -514,7 +533,7 @@ var applyModifiers = ({
514
533
  attributes.position.needsUpdate = true;
515
534
  }
516
535
  if (attributes.quat) {
517
- const rotZ = attributes.rotation.array[particleIndex];
536
+ const rotZ = scalarArray[base + S_ROTATION];
518
537
  const halfZ = rotZ * 0.5;
519
538
  const qi = particleIndex * 4;
520
539
  attributes.quat.array[qi] = 0;
@@ -525,6 +544,21 @@ var applyModifiers = ({
525
544
  }
526
545
  };
527
546
 
547
+ // src/js/effects/three-particles/three-particles-renderer-detect.ts
548
+ function isComputeCapableRenderer(renderer) {
549
+ return renderer !== null && renderer !== void 0 && typeof renderer === "object" && "compute" in renderer && typeof renderer.compute === "function" && "hasFeature" in renderer && typeof renderer.hasFeature === "function";
550
+ }
551
+ function resolveSimulationBackend(renderer, preference = "AUTO" /* AUTO */) {
552
+ const gpuCapable = isComputeCapableRenderer(renderer);
553
+ if (preference === "CPU" /* CPU */) {
554
+ return "CPU" /* CPU */;
555
+ }
556
+ if (preference === "GPU" /* GPU */) {
557
+ return gpuCapable ? "GPU" /* GPU */ : "CPU" /* CPU */;
558
+ }
559
+ return gpuCapable ? "GPU" /* GPU */ : "CPU" /* CPU */;
560
+ }
561
+
528
562
  // src/js/effects/three-particles/shaders/instanced-particle-fragment-shader.glsl.ts
529
563
  var InstancedParticleFragmentShader = `
530
564
  uniform sampler2D map;
@@ -616,10 +650,7 @@ var instanced_particle_fragment_shader_glsl_default = InstancedParticleFragmentS
616
650
  // src/js/effects/three-particles/shaders/instanced-particle-vertex-shader.glsl.ts
617
651
  var InstancedParticleVertexShader = `
618
652
  attribute float instanceSize;
619
- attribute float instanceColorR;
620
- attribute float instanceColorG;
621
- attribute float instanceColorB;
622
- attribute float instanceColorA;
653
+ attribute vec4 instanceColor;
623
654
  attribute float instanceLifetime;
624
655
  attribute float instanceStartLifetime;
625
656
  attribute float instanceRotation;
@@ -641,7 +672,14 @@ var InstancedParticleVertexShader = `
641
672
 
642
673
  void main()
643
674
  {
644
- vColor = vec4(instanceColorR, instanceColorG, instanceColorB, instanceColorA);
675
+ // Early-out for dead particles: skip all transforms and emit a degenerate
676
+ // position that produces zero-area triangles.
677
+ if (instanceColor.a <= 0.0) {
678
+ gl_Position = vec4(0.0, 0.0, 0.0, 0.0);
679
+ return;
680
+ }
681
+
682
+ vColor = instanceColor;
645
683
  vLifetime = instanceLifetime;
646
684
  vStartLifetime = instanceStartLifetime;
647
685
  vStartFrame = instanceStartFrame;
@@ -766,10 +804,7 @@ var mesh_particle_fragment_shader_glsl_default = MeshParticleFragmentShader;
766
804
  // src/js/effects/three-particles/shaders/mesh-particle-vertex-shader.glsl.ts
767
805
  var MeshParticleVertexShader = `
768
806
  attribute float instanceSize;
769
- attribute float instanceColorR;
770
- attribute float instanceColorG;
771
- attribute float instanceColorB;
772
- attribute float instanceColorA;
807
+ attribute vec4 instanceColor;
773
808
  attribute float instanceLifetime;
774
809
  attribute float instanceStartLifetime;
775
810
  attribute float instanceRotation;
@@ -796,7 +831,14 @@ var MeshParticleVertexShader = `
796
831
 
797
832
  void main()
798
833
  {
799
- vColor = vec4(instanceColorR, instanceColorG, instanceColorB, instanceColorA);
834
+ // Early-out for dead particles: skip all expensive transforms and emit
835
+ // a degenerate position that produces zero-area triangles.
836
+ if (instanceColor.a <= 0.0) {
837
+ gl_Position = vec4(0.0, 0.0, 0.0, 0.0);
838
+ return;
839
+ }
840
+
841
+ vColor = instanceColor;
800
842
  vLifetime = instanceLifetime;
801
843
  vStartLifetime = instanceStartLifetime;
802
844
  vStartFrame = instanceStartFrame;
@@ -922,10 +964,7 @@ var particle_system_fragment_shader_glsl_default = ParticleSystemFragmentShader;
922
964
  // src/js/effects/three-particles/shaders/particle-system-vertex-shader.glsl.ts
923
965
  var ParticleSystemVertexShader = `
924
966
  attribute float size;
925
- attribute float colorR;
926
- attribute float colorG;
927
- attribute float colorB;
928
- attribute float colorA;
967
+ attribute vec4 color;
929
968
  attribute float lifetime;
930
969
  attribute float startLifetime;
931
970
  attribute float rotation;
@@ -944,7 +983,7 @@ var ParticleSystemVertexShader = `
944
983
 
945
984
  void main()
946
985
  {
947
- vColor = vec4(colorR, colorG, colorB, colorA);
986
+ vColor = color;
948
987
  vLifetime = lifetime;
949
988
  vStartLifetime = startLifetime;
950
989
  vRotation = rotation;
@@ -1088,7 +1127,69 @@ var TrailVertexShader = `
1088
1127
  }
1089
1128
  `;
1090
1129
  var trail_vertex_shader_glsl_default = TrailVertexShader;
1091
- var _forceDirection = new THREE4.Vector3();
1130
+ var _planeToParticle = new THREE5.Vector3();
1131
+ var _normalComponent = new THREE5.Vector3();
1132
+ var applyCollisionPlanes = ({
1133
+ collisionPlanes,
1134
+ velocity,
1135
+ positionArr,
1136
+ positionIndex,
1137
+ scalarArr,
1138
+ scalarBase,
1139
+ deactivateParticle,
1140
+ particleIndex
1141
+ }) => {
1142
+ for (let i = 0; i < collisionPlanes.length; i++) {
1143
+ const plane = collisionPlanes[i];
1144
+ if (!plane.isActive) continue;
1145
+ const normal = plane.normal;
1146
+ const planePos = plane.position;
1147
+ _planeToParticle.set(
1148
+ positionArr[positionIndex] - planePos.x,
1149
+ positionArr[positionIndex + 1] - planePos.y,
1150
+ positionArr[positionIndex + 2] - planePos.z
1151
+ );
1152
+ const signedDistance = _planeToParticle.dot(normal);
1153
+ if (signedDistance >= 0) continue;
1154
+ switch (plane.mode) {
1155
+ case "KILL" /* KILL */:
1156
+ deactivateParticle(particleIndex);
1157
+ return true;
1158
+ case "CLAMP" /* CLAMP */:
1159
+ positionArr[positionIndex] = positionArr[positionIndex] - signedDistance * normal.x;
1160
+ positionArr[positionIndex + 1] = positionArr[positionIndex + 1] - signedDistance * normal.y;
1161
+ positionArr[positionIndex + 2] = positionArr[positionIndex + 2] - signedDistance * normal.z;
1162
+ const velDotNormal = velocity.x * normal.x + velocity.y * normal.y + velocity.z * normal.z;
1163
+ if (velDotNormal < 0) {
1164
+ velocity.x -= velDotNormal * normal.x;
1165
+ velocity.y -= velDotNormal * normal.y;
1166
+ velocity.z -= velDotNormal * normal.z;
1167
+ }
1168
+ break;
1169
+ case "BOUNCE" /* BOUNCE */: {
1170
+ positionArr[positionIndex] = positionArr[positionIndex] - signedDistance * normal.x;
1171
+ positionArr[positionIndex + 1] = positionArr[positionIndex + 1] - signedDistance * normal.y;
1172
+ positionArr[positionIndex + 2] = positionArr[positionIndex + 2] - signedDistance * normal.z;
1173
+ const vDotN = velocity.x * normal.x + velocity.y * normal.y + velocity.z * normal.z;
1174
+ _normalComponent.set(
1175
+ 2 * vDotN * normal.x,
1176
+ 2 * vDotN * normal.y,
1177
+ 2 * vDotN * normal.z
1178
+ );
1179
+ velocity.x = (velocity.x - _normalComponent.x) * plane.dampen;
1180
+ velocity.y = (velocity.y - _normalComponent.y) * plane.dampen;
1181
+ velocity.z = (velocity.z - _normalComponent.z) * plane.dampen;
1182
+ if (plane.lifetimeLoss > 0) {
1183
+ const startLifetime = scalarArr[scalarBase + S_START_LIFETIME];
1184
+ scalarArr[scalarBase + S_LIFETIME] += plane.lifetimeLoss * startLifetime;
1185
+ }
1186
+ break;
1187
+ }
1188
+ }
1189
+ }
1190
+ return false;
1191
+ };
1192
+ var _forceDirection = new THREE5.Vector3();
1092
1193
  var applyPointForce = (field, strength, velocity, positionArr, positionIndex, delta) => {
1093
1194
  _forceDirection.set(
1094
1195
  field.position.x - positionArr[positionIndex],
@@ -1169,15 +1270,29 @@ var normalizeTrailCurve = (curve, defaultCurve) => {
1169
1270
  };
1170
1271
  var _particleSystemId = 0;
1171
1272
  var createdParticleSystems = [];
1172
- var _subEmitterPosition = new THREE4.Vector3();
1173
- var _lastWorldPositionSnapshot = new THREE4.Vector3();
1174
- var _localForceFieldPos = new THREE4.Vector3();
1175
- var _localForceFieldDir = new THREE4.Vector3();
1176
- var _inverseQuat = new THREE4.Quaternion();
1273
+ var setUniformFloat = (u, v) => {
1274
+ u.value = v;
1275
+ };
1276
+ var setUniformVec3 = (u, x, y, z) => {
1277
+ u.value.set(x, y, z);
1278
+ };
1279
+ var _tslMaterialFactory = null;
1280
+ var registerTSLMaterialFactory = (factory) => {
1281
+ _tslMaterialFactory = factory;
1282
+ };
1283
+ var _subEmitterPosition = new THREE5.Vector3();
1284
+ var _shadowOrbitalEuler = new THREE5.Euler(0, 0, 0, "XYZ");
1285
+ var _lastWorldPositionSnapshot = new THREE5.Vector3();
1286
+ var _localForceFieldPos = new THREE5.Vector3();
1287
+ var _localForceFieldDir = new THREE5.Vector3();
1288
+ var _inverseQuat = new THREE5.Quaternion();
1177
1289
  var _localForceFields = [];
1178
- new THREE4.Vector3();
1179
- new THREE4.Vector3();
1180
- new THREE4.Vector3();
1290
+ var _localCollisionPlanePos = new THREE5.Vector3();
1291
+ var _localCollisionPlaneNormal = new THREE5.Vector3();
1292
+ var _localCollisionPlanes = [];
1293
+ new THREE5.Vector3();
1294
+ new THREE5.Vector3();
1295
+ new THREE5.Vector3();
1181
1296
  var _distanceStep = { x: 0, y: 0, z: 0 };
1182
1297
  var _tempPosition = { x: 0, y: 0, z: 0 };
1183
1298
  var _modifierParams = {
@@ -1185,32 +1300,41 @@ var _modifierParams = {
1185
1300
  generalData: null,
1186
1301
  normalizedConfig: null,
1187
1302
  attributes: null,
1303
+ scalarArray: null,
1188
1304
  particleLifetimePercentage: 0,
1189
1305
  particleIndex: 0
1190
1306
  };
1191
- var toVector3 = (v, fallback) => v ? new THREE4.Vector3(v.x ?? 0, v.y ?? 0, v.z ?? 0) : fallback.clone();
1307
+ var toVector3 = (v, fallback) => v ? new THREE5.Vector3(v.x ?? 0, v.y ?? 0, v.z ?? 0) : fallback.clone();
1192
1308
  var normalizeForceFields = (rawForceFields) => (rawForceFields ?? []).map((ff) => ({
1193
1309
  isActive: ff.isActive ?? true,
1194
1310
  type: ff.type ?? "POINT" /* POINT */,
1195
- position: toVector3(ff.position, new THREE4.Vector3(0, 0, 0)),
1196
- direction: toVector3(ff.direction, new THREE4.Vector3(0, 1, 0)).normalize(),
1311
+ position: toVector3(ff.position, new THREE5.Vector3(0, 0, 0)),
1312
+ direction: toVector3(ff.direction, new THREE5.Vector3(0, 1, 0)).normalize(),
1197
1313
  strength: ff.strength ?? 1,
1198
1314
  range: Math.max(0, ff.range ?? Infinity),
1199
1315
  falloff: ff.falloff ?? "LINEAR" /* LINEAR */
1200
1316
  }));
1317
+ var normalizeCollisionPlanes = (rawPlanes) => (rawPlanes ?? []).map((cp) => ({
1318
+ isActive: cp.isActive ?? true,
1319
+ position: toVector3(cp.position, new THREE5.Vector3(0, 0, 0)),
1320
+ normal: toVector3(cp.normal, new THREE5.Vector3(0, 1, 0)).normalize(),
1321
+ mode: cp.mode ?? "KILL" /* KILL */,
1322
+ dampen: Math.max(0, Math.min(1, cp.dampen ?? 0.5)),
1323
+ lifetimeLoss: Math.max(0, Math.min(1, cp.lifetimeLoss ?? 0))
1324
+ }));
1201
1325
  var blendingMap = {
1202
- "THREE.NoBlending": THREE4.NoBlending,
1203
- "THREE.NormalBlending": THREE4.NormalBlending,
1204
- "THREE.AdditiveBlending": THREE4.AdditiveBlending,
1205
- "THREE.SubtractiveBlending": THREE4.SubtractiveBlending,
1206
- "THREE.MultiplyBlending": THREE4.MultiplyBlending
1326
+ "THREE.NoBlending": THREE5.NoBlending,
1327
+ "THREE.NormalBlending": THREE5.NormalBlending,
1328
+ "THREE.AdditiveBlending": THREE5.AdditiveBlending,
1329
+ "THREE.SubtractiveBlending": THREE5.SubtractiveBlending,
1330
+ "THREE.MultiplyBlending": THREE5.MultiplyBlending
1207
1331
  };
1208
1332
  var getDefaultParticleSystemConfig = () => JSON.parse(JSON.stringify(DEFAULT_PARTICLE_SYSTEM_CONFIG));
1209
1333
  var DEFAULT_PARTICLE_SYSTEM_CONFIG = {
1210
1334
  transform: {
1211
- position: new THREE4.Vector3(),
1212
- rotation: new THREE4.Vector3(),
1213
- scale: new THREE4.Vector3(1, 1, 1)
1335
+ position: new THREE5.Vector3(),
1336
+ rotation: new THREE5.Vector3(),
1337
+ scale: new THREE5.Vector3(1, 1, 1)
1214
1338
  },
1215
1339
  duration: 5,
1216
1340
  looping: true,
@@ -1226,6 +1350,7 @@ var DEFAULT_PARTICLE_SYSTEM_CONFIG = {
1226
1350
  },
1227
1351
  gravity: 0,
1228
1352
  simulationSpace: "LOCAL" /* LOCAL */,
1353
+ simulationBackend: "AUTO" /* AUTO */,
1229
1354
  maxParticles: 100,
1230
1355
  emission: {
1231
1356
  rateOverTime: 10,
@@ -1262,7 +1387,7 @@ var DEFAULT_PARTICLE_SYSTEM_CONFIG = {
1262
1387
  },
1263
1388
  map: void 0,
1264
1389
  renderer: {
1265
- blending: THREE4.NormalBlending,
1390
+ blending: THREE5.NormalBlending,
1266
1391
  discardBackgroundColor: false,
1267
1392
  backgroundColorTolerance: 1,
1268
1393
  backgroundColor: { r: 1, g: 1, b: 1 },
@@ -1352,30 +1477,13 @@ var DEFAULT_PARTICLE_SYSTEM_CONFIG = {
1352
1477
  sizeAmount: 0
1353
1478
  },
1354
1479
  textureSheetAnimation: {
1355
- tiles: new THREE4.Vector2(1, 1),
1480
+ tiles: new THREE5.Vector2(1, 1),
1356
1481
  timeMode: "LIFETIME" /* LIFETIME */,
1357
1482
  fps: 30,
1358
1483
  startFrame: 0
1359
1484
  },
1360
- forceFields: []
1361
- };
1362
- var createFloat32Attributes = ({
1363
- geometry,
1364
- propertyName,
1365
- maxParticles,
1366
- factory,
1367
- instanced
1368
- }) => {
1369
- const array = new Float32Array(maxParticles);
1370
- if (typeof factory === "function") {
1371
- for (let i = 0; i < maxParticles; i++) {
1372
- array[i] = factory(void 0, i);
1373
- }
1374
- } else {
1375
- array.fill(factory);
1376
- }
1377
- const attr = instanced ? new THREE4.InstancedBufferAttribute(array, 1) : new THREE4.BufferAttribute(array, 1);
1378
- geometry.setAttribute(propertyName, attr);
1485
+ forceFields: [],
1486
+ collisionPlanes: []
1379
1487
  };
1380
1488
  var calculatePositionAndVelocity = (generalData, { shape, sphere, cone, circle, rectangle, box }, startSpeed, position, velocity) => {
1381
1489
  const calculatedStartSpeed = calculateValue(
@@ -1467,14 +1575,14 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1467
1575
  particleSystemId: _particleSystemId++,
1468
1576
  normalizedLifetimePercentage: 0,
1469
1577
  distanceFromLastEmitByDistance: 0,
1470
- lastWorldPosition: new THREE4.Vector3(-99999),
1471
- currentWorldPosition: new THREE4.Vector3(-99999),
1472
- worldPositionChange: new THREE4.Vector3(),
1473
- worldQuaternion: new THREE4.Quaternion(),
1474
- wrapperQuaternion: new THREE4.Quaternion(),
1475
- lastWorldQuaternion: new THREE4.Quaternion(-99999),
1476
- worldEuler: new THREE4.Euler(),
1477
- gravityVelocity: new THREE4.Vector3(0, 0, 0),
1578
+ lastWorldPosition: new THREE5.Vector3(-99999),
1579
+ currentWorldPosition: new THREE5.Vector3(-99999),
1580
+ worldPositionChange: new THREE5.Vector3(),
1581
+ worldQuaternion: new THREE5.Quaternion(),
1582
+ wrapperQuaternion: new THREE5.Quaternion(),
1583
+ lastWorldQuaternion: new THREE5.Quaternion(-99999),
1584
+ worldEuler: new THREE5.Euler(),
1585
+ gravityVelocity: new THREE5.Vector3(0, 0, 0),
1478
1586
  startValues: {},
1479
1587
  linearVelocityData: void 0,
1480
1588
  orbitalVelocityData: void 0,
@@ -1484,9 +1592,11 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1484
1592
  isActive: false,
1485
1593
  strength: 0,
1486
1594
  noisePower: 0,
1595
+ frequency: 0.5,
1487
1596
  positionAmount: 0,
1488
1597
  rotationAmount: 0,
1489
- sizeAmount: 0
1598
+ sizeAmount: 0,
1599
+ fbmMax: 1
1490
1600
  },
1491
1601
  isEnabled: true
1492
1602
  };
@@ -1496,6 +1606,10 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1496
1606
  { applyToFirstObject: false, skippedProperties: [] }
1497
1607
  );
1498
1608
  let particleMap = normalizedConfig.map || (normalizedConfig.renderer.rendererType === "MESH" /* MESH */ ? createDefaultMeshTexture() : createDefaultParticleTexture());
1609
+ if (particleMap) {
1610
+ particleMap.wrapS = THREE5.ClampToEdgeWrapping;
1611
+ particleMap.wrapT = THREE5.ClampToEdgeWrapping;
1612
+ }
1499
1613
  const {
1500
1614
  transform,
1501
1615
  duration,
@@ -1522,15 +1636,16 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1522
1636
  forceFields: rawForceFields
1523
1637
  } = normalizedConfig;
1524
1638
  const normalizedForceFields = normalizeForceFields(rawForceFields);
1639
+ const normalizedCollisionPlanes = normalizeCollisionPlanes(normalizedConfig.collisionPlanes);
1525
1640
  if (typeof renderer?.blending === "string")
1526
1641
  renderer.blending = blendingMap[renderer.blending];
1527
1642
  const startPositions = Array.from(
1528
1643
  { length: maxParticles },
1529
- () => new THREE4.Vector3()
1644
+ () => new THREE5.Vector3()
1530
1645
  );
1531
1646
  const velocities = Array.from(
1532
1647
  { length: maxParticles },
1533
- () => new THREE4.Vector3()
1648
+ () => new THREE5.Vector3()
1534
1649
  );
1535
1650
  generalData.creationTimes = Array.from({ length: maxParticles }, () => 0);
1536
1651
  const freeList = Array.from(
@@ -1541,7 +1656,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1541
1656
  generalData.linearVelocityData = Array.from(
1542
1657
  { length: maxParticles },
1543
1658
  () => ({
1544
- speed: new THREE4.Vector3(
1659
+ speed: new THREE5.Vector3(
1545
1660
  velocityOverLifetime.linear.x ? calculateValue(
1546
1661
  generalData.particleSystemId,
1547
1662
  velocityOverLifetime.linear.x,
@@ -1577,7 +1692,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1577
1692
  generalData.orbitalVelocityData = Array.from(
1578
1693
  { length: maxParticles },
1579
1694
  () => ({
1580
- speed: new THREE4.Vector3(
1695
+ speed: new THREE5.Vector3(
1581
1696
  velocityOverLifetime.orbital.x ? calculateValue(
1582
1697
  generalData.particleSystemId,
1583
1698
  velocityOverLifetime.orbital.x,
@@ -1608,7 +1723,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1608
1723
  velocityOverLifetime.orbital.z
1609
1724
  ) : void 0
1610
1725
  },
1611
- positionOffset: new THREE4.Vector3()
1726
+ positionOffset: new THREE5.Vector3()
1612
1727
  })
1613
1728
  );
1614
1729
  }
@@ -1646,16 +1761,19 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1646
1761
  if (value.isActive)
1647
1762
  generalData.lifetimeValues[key] = Array.from(
1648
1763
  { length: maxParticles },
1649
- () => THREE4.MathUtils.randFloat(value.min, value.max)
1764
+ () => THREE5.MathUtils.randFloat(value.min, value.max)
1650
1765
  );
1651
1766
  });
1767
+ const fbmMax = 2 - Math.pow(2, -noise.octaves);
1652
1768
  generalData.noise = {
1653
1769
  isActive: noise.isActive,
1654
1770
  strength: noise.strength,
1655
1771
  noisePower: 0.15 * noise.strength,
1772
+ frequency: noise.frequency,
1656
1773
  positionAmount: noise.positionAmount,
1657
1774
  rotationAmount: noise.rotationAmount,
1658
1775
  sizeAmount: noise.sizeAmount,
1776
+ fbmMax,
1659
1777
  sampler: noise.isActive ? new FBM({
1660
1778
  seed: Math.random(),
1661
1779
  scale: noise.frequency,
@@ -1727,7 +1845,12 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1727
1845
  const sharedUniforms = {
1728
1846
  elapsed: { value: 0 },
1729
1847
  map: { value: particleMap },
1730
- tiles: { value: textureSheetAnimation.tiles },
1848
+ tiles: {
1849
+ value: new THREE5.Vector2(
1850
+ textureSheetAnimation.tiles?.x ?? 1,
1851
+ textureSheetAnimation.tiles?.y ?? 1
1852
+ )
1853
+ },
1731
1854
  fps: { value: textureSheetAnimation.fps },
1732
1855
  useFPSForFrameIndex: {
1733
1856
  value: textureSheetAnimation.timeMode === "FPS" /* FPS */
@@ -1743,7 +1866,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1743
1866
  sceneDepthTexture: {
1744
1867
  value: renderer.softParticles?.depthTexture ?? null
1745
1868
  },
1746
- cameraNearFar: { value: new THREE4.Vector2(0.1, 1e3) }
1869
+ cameraNearFar: { value: new THREE5.Vector2(0.1, 1e3) }
1747
1870
  };
1748
1871
  const getVertexShader = () => {
1749
1872
  if (useMesh) return mesh_particle_vertex_shader_glsl_default;
@@ -1755,14 +1878,41 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1755
1878
  if (useInstancing) return instanced_particle_fragment_shader_glsl_default;
1756
1879
  return particle_system_fragment_shader_glsl_default;
1757
1880
  };
1758
- const material = new THREE4.ShaderMaterial({
1759
- uniforms: sharedUniforms,
1760
- vertexShader: getVertexShader(),
1761
- fragmentShader: getFragmentShader(),
1881
+ const useTSL = _tslMaterialFactory !== null;
1882
+ const useGPUCompute = useTSL && !useTrail && normalizedConfig.simulationBackend !== "CPU" /* CPU */ && !!_tslMaterialFactory?.createComputePipeline && !!_tslMaterialFactory.writeParticleToModifierBuffers && !!_tslMaterialFactory.deactivateParticleInModifierBuffers && !!_tslMaterialFactory.flushEmitQueue;
1883
+ let gpuPipeline = null;
1884
+ if (useGPUCompute) {
1885
+ gpuPipeline = _tslMaterialFactory.createComputePipeline(
1886
+ maxParticles,
1887
+ useInstancedAttributes,
1888
+ normalizedConfig,
1889
+ generalData.particleSystemId,
1890
+ normalizedForceFields.length,
1891
+ normalizedCollisionPlanes.length
1892
+ );
1893
+ if (gpuPipeline && _tslMaterialFactory.registerCurveDataLength) {
1894
+ _tslMaterialFactory.registerCurveDataLength(
1895
+ gpuPipeline.buffers,
1896
+ gpuPipeline.curveDataLength
1897
+ );
1898
+ }
1899
+ }
1900
+ const rendererConfig = {
1762
1901
  transparent: renderer.transparent,
1763
1902
  blending: renderer.blending,
1764
1903
  depthTest: renderer.depthTest,
1765
1904
  depthWrite: renderer.depthWrite
1905
+ };
1906
+ const material = useTSL ? _tslMaterialFactory.createTSLParticleMaterial(
1907
+ renderer.rendererType ?? "POINTS" /* POINTS */,
1908
+ sharedUniforms,
1909
+ rendererConfig,
1910
+ useGPUCompute
1911
+ ) : new THREE5.ShaderMaterial({
1912
+ uniforms: sharedUniforms,
1913
+ vertexShader: getVertexShader(),
1914
+ fragmentShader: getFragmentShader(),
1915
+ ...rendererConfig
1766
1916
  });
1767
1917
  let geometry;
1768
1918
  if (useMesh) {
@@ -1772,7 +1922,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1772
1922
  "RendererType.MESH requires a mesh configuration with a geometry. Set renderer.mesh.geometry to a THREE.BufferGeometry instance."
1773
1923
  );
1774
1924
  }
1775
- const instancedGeometry = new THREE4.InstancedBufferGeometry();
1925
+ const instancedGeometry = new THREE5.InstancedBufferGeometry();
1776
1926
  const sourceGeom = meshConfig.geometry;
1777
1927
  const srcPos = sourceGeom.getAttribute("position");
1778
1928
  if (srcPos) instancedGeometry.setAttribute("position", srcPos);
@@ -1785,7 +1935,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1785
1935
  instancedGeometry.instanceCount = maxParticles;
1786
1936
  geometry = instancedGeometry;
1787
1937
  } else if (useInstancing) {
1788
- const instancedGeometry = new THREE4.InstancedBufferGeometry();
1938
+ const instancedGeometry = new THREE5.InstancedBufferGeometry();
1789
1939
  const quadPositions = new Float32Array([
1790
1940
  -0.5,
1791
1941
  -0.5,
@@ -1803,13 +1953,13 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1803
1953
  const quadIndices = new Uint16Array([0, 1, 2, 0, 2, 3]);
1804
1954
  instancedGeometry.setAttribute(
1805
1955
  "position",
1806
- new THREE4.BufferAttribute(quadPositions, 3)
1956
+ new THREE5.BufferAttribute(quadPositions, 3)
1807
1957
  );
1808
- instancedGeometry.setIndex(new THREE4.BufferAttribute(quadIndices, 1));
1958
+ instancedGeometry.setIndex(new THREE5.BufferAttribute(quadIndices, 1));
1809
1959
  instancedGeometry.instanceCount = maxParticles;
1810
1960
  geometry = instancedGeometry;
1811
1961
  } else {
1812
- geometry = new THREE4.BufferGeometry();
1962
+ geometry = new THREE5.BufferGeometry();
1813
1963
  }
1814
1964
  for (let i = 0; i < maxParticles; i++)
1815
1965
  calculatePositionAndVelocity(
@@ -1819,116 +1969,126 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1819
1969
  startPositions[i],
1820
1970
  velocities[i]
1821
1971
  );
1822
- const positionArray = new Float32Array(maxParticles * 3);
1972
+ const scalarArray = new Float32Array(maxParticles * SCALAR_STRIDE);
1823
1973
  for (let i = 0; i < maxParticles; i++) {
1824
- positionArray[i * 3] = startPositions[i].x;
1825
- positionArray[i * 3 + 1] = startPositions[i].y;
1826
- positionArray[i * 3 + 2] = startPositions[i].z;
1827
- }
1828
- const positionAttribute = useInstancedAttributes ? new THREE4.InstancedBufferAttribute(positionArray, 3) : new THREE4.BufferAttribute(positionArray, 3);
1829
- geometry.setAttribute(posAttr, positionAttribute);
1830
- createFloat32Attributes({
1831
- geometry,
1832
- propertyName: attr("isActive"),
1833
- maxParticles,
1834
- factory: 0,
1835
- instanced: useInstancedAttributes
1836
- });
1837
- createFloat32Attributes({
1838
- geometry,
1839
- propertyName: attr("lifetime"),
1840
- maxParticles,
1841
- factory: 0,
1842
- instanced: useInstancedAttributes
1843
- });
1844
- createFloat32Attributes({
1845
- geometry,
1846
- propertyName: attr("startLifetime"),
1847
- maxParticles,
1848
- factory: () => calculateValue(generalData.particleSystemId, startLifetime, 0) * 1e3,
1849
- instanced: useInstancedAttributes
1850
- });
1851
- createFloat32Attributes({
1852
- geometry,
1853
- propertyName: attr("startFrame"),
1854
- maxParticles,
1855
- factory: () => textureSheetAnimation.startFrame ? calculateValue(
1974
+ const base = i * SCALAR_STRIDE;
1975
+ scalarArray[base + S_IS_ACTIVE] = 0;
1976
+ scalarArray[base + S_LIFETIME] = 0;
1977
+ scalarArray[base + S_START_LIFETIME] = calculateValue(generalData.particleSystemId, startLifetime, 0) * 1e3;
1978
+ scalarArray[base + S_START_FRAME] = textureSheetAnimation.startFrame ? calculateValue(
1856
1979
  generalData.particleSystemId,
1857
1980
  textureSheetAnimation.startFrame,
1858
1981
  0
1859
- ) : 0,
1860
- instanced: useInstancedAttributes
1861
- });
1862
- createFloat32Attributes({
1863
- geometry,
1864
- propertyName: attr("size"),
1865
- maxParticles,
1866
- factory: (_, index) => generalData.startValues.startSize[index],
1867
- instanced: useInstancedAttributes
1868
- });
1869
- createFloat32Attributes({
1870
- geometry,
1871
- propertyName: attr("rotation"),
1872
- maxParticles,
1873
- factory: 0,
1874
- instanced: useInstancedAttributes
1875
- });
1876
- const colorRandomRatio = Math.random();
1877
- createFloat32Attributes({
1878
- geometry,
1879
- propertyName: attr("colorR"),
1880
- maxParticles,
1881
- factory: () => startColor.min.r + colorRandomRatio * (startColor.max.r - startColor.min.r),
1882
- instanced: useInstancedAttributes
1883
- });
1884
- createFloat32Attributes({
1885
- geometry,
1886
- propertyName: attr("colorG"),
1887
- maxParticles,
1888
- factory: () => startColor.min.g + colorRandomRatio * (startColor.max.g - startColor.min.g),
1889
- instanced: useInstancedAttributes
1890
- });
1891
- createFloat32Attributes({
1892
- geometry,
1893
- propertyName: attr("colorB"),
1894
- maxParticles,
1895
- factory: () => startColor.min.b + colorRandomRatio * (startColor.max.b - startColor.min.b),
1896
- instanced: useInstancedAttributes
1897
- });
1898
- createFloat32Attributes({
1899
- geometry,
1900
- propertyName: attr("colorA"),
1901
- maxParticles,
1902
- factory: 0,
1903
- instanced: useInstancedAttributes
1904
- });
1905
- if (useMesh) {
1982
+ ) : 0;
1983
+ scalarArray[base + S_SIZE] = generalData.startValues.startSize[i];
1984
+ scalarArray[base + S_ROTATION] = 0;
1985
+ const colorRandomRatio = Math.random();
1986
+ scalarArray[base + S_COLOR_R] = startColor.min.r + colorRandomRatio * (startColor.max.r - startColor.min.r);
1987
+ scalarArray[base + S_COLOR_G] = startColor.min.g + colorRandomRatio * (startColor.max.g - startColor.min.g);
1988
+ scalarArray[base + S_COLOR_B] = startColor.min.b + colorRandomRatio * (startColor.max.b - startColor.min.b);
1989
+ scalarArray[base + S_COLOR_A] = 0;
1990
+ }
1991
+ const scalarInterleavedBuffer = useInstancedAttributes ? new THREE5.InstancedInterleavedBuffer(scalarArray, SCALAR_STRIDE) : new THREE5.InterleavedBuffer(scalarArray, SCALAR_STRIDE);
1992
+ if (useGPUCompute && gpuPipeline) {
1993
+ const gpuBuf = gpuPipeline.buffers;
1994
+ geometry.setAttribute(posAttr, gpuBuf.position);
1995
+ geometry.setAttribute(attr("color"), gpuBuf.color);
1996
+ geometry.setAttribute(attr("particleState"), gpuBuf.particleState);
1997
+ geometry.setAttribute(attr("startValues"), gpuBuf.startValues);
1998
+ } else {
1999
+ const positionArray = new Float32Array(maxParticles * 3);
2000
+ for (let i = 0; i < maxParticles; i++) {
2001
+ positionArray[i * 3] = startPositions[i].x;
2002
+ positionArray[i * 3 + 1] = startPositions[i].y;
2003
+ positionArray[i * 3 + 2] = startPositions[i].z;
2004
+ }
2005
+ const positionAttribute = useInstancedAttributes ? new THREE5.InstancedBufferAttribute(positionArray, 3) : new THREE5.BufferAttribute(positionArray, 3);
2006
+ geometry.setAttribute(posAttr, positionAttribute);
2007
+ geometry.setAttribute(
2008
+ attr("isActive"),
2009
+ new THREE5.InterleavedBufferAttribute(
2010
+ scalarInterleavedBuffer,
2011
+ 1,
2012
+ S_IS_ACTIVE
2013
+ )
2014
+ );
2015
+ geometry.setAttribute(
2016
+ attr("lifetime"),
2017
+ new THREE5.InterleavedBufferAttribute(
2018
+ scalarInterleavedBuffer,
2019
+ 1,
2020
+ S_LIFETIME
2021
+ )
2022
+ );
2023
+ geometry.setAttribute(
2024
+ attr("startLifetime"),
2025
+ new THREE5.InterleavedBufferAttribute(
2026
+ scalarInterleavedBuffer,
2027
+ 1,
2028
+ S_START_LIFETIME
2029
+ )
2030
+ );
2031
+ geometry.setAttribute(
2032
+ attr("startFrame"),
2033
+ new THREE5.InterleavedBufferAttribute(
2034
+ scalarInterleavedBuffer,
2035
+ 1,
2036
+ S_START_FRAME
2037
+ )
2038
+ );
2039
+ geometry.setAttribute(
2040
+ attr("size"),
2041
+ new THREE5.InterleavedBufferAttribute(scalarInterleavedBuffer, 1, S_SIZE)
2042
+ );
2043
+ geometry.setAttribute(
2044
+ attr("rotation"),
2045
+ new THREE5.InterleavedBufferAttribute(
2046
+ scalarInterleavedBuffer,
2047
+ 1,
2048
+ S_ROTATION
2049
+ )
2050
+ );
2051
+ geometry.setAttribute(
2052
+ attr("color"),
2053
+ new THREE5.InterleavedBufferAttribute(
2054
+ scalarInterleavedBuffer,
2055
+ 4,
2056
+ S_COLOR_R
2057
+ )
2058
+ );
2059
+ }
2060
+ if (useMesh && !useGPUCompute) {
1906
2061
  const quatArray = new Float32Array(maxParticles * 4);
1907
2062
  for (let i = 0; i < maxParticles; i++) {
1908
2063
  quatArray[i * 4 + 3] = 1;
1909
2064
  }
1910
2065
  geometry.setAttribute(
1911
2066
  attr("quat"),
1912
- new THREE4.InstancedBufferAttribute(quatArray, 4)
2067
+ new THREE5.InstancedBufferAttribute(quatArray, 4)
1913
2068
  );
1914
2069
  }
1915
2070
  const a = geometry.attributes;
1916
2071
  const aIsActive = a[attr("isActive")];
1917
- const aColorR = a[attr("colorR")];
1918
- const aColorG = a[attr("colorG")];
1919
- const aColorB = a[attr("colorB")];
1920
- const aColorA = a[attr("colorA")];
2072
+ const aColor = a[attr("color")];
1921
2073
  const aStartFrame = a[attr("startFrame")];
1922
2074
  const aStartLifetime = a[attr("startLifetime")];
1923
2075
  const aSize = a[attr("size")];
1924
2076
  const aRotation = a[attr("rotation")];
1925
2077
  const aLifetime = a[attr("lifetime")];
1926
2078
  const aPosition = a[posAttr];
1927
- const aQuat = useMesh ? a[attr("quat")] : void 0;
2079
+ const aQuat = useMesh && !useGPUCompute ? a[attr("quat")] : void 0;
1928
2080
  const deactivateParticle = (particleIndex) => {
1929
- aIsActive.array[particleIndex] = 0;
1930
- aColorA.array[particleIndex] = 0;
1931
- aColorA.needsUpdate = true;
2081
+ const base = particleIndex * SCALAR_STRIDE;
2082
+ scalarArray[base + S_IS_ACTIVE] = 0;
2083
+ scalarArray[base + S_COLOR_A] = 0;
2084
+ if (useGPUCompute && gpuPipeline) {
2085
+ _tslMaterialFactory.deactivateParticleInModifierBuffers(
2086
+ gpuPipeline.buffers,
2087
+ particleIndex
2088
+ );
2089
+ } else {
2090
+ scalarInterleavedBuffer.needsUpdate = true;
2091
+ }
1932
2092
  freeList.push(particleIndex);
1933
2093
  };
1934
2094
  const activateParticle = ({
@@ -1936,7 +2096,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1936
2096
  activationTime,
1937
2097
  position
1938
2098
  }) => {
1939
- aIsActive.array[particleIndex] = 1;
2099
+ const base = particleIndex * SCALAR_STRIDE;
2100
+ scalarArray[base + S_IS_ACTIVE] = 1;
1940
2101
  generalData.creationTimes[particleIndex] = activationTime;
1941
2102
  if (generalData.positionHistoryCount) {
1942
2103
  generalData.positionHistoryCount[particleIndex] = 0;
@@ -1956,51 +2117,43 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1956
2117
  }
1957
2118
  if (generalData.noise.offsets)
1958
2119
  generalData.noise.offsets[particleIndex] = Math.random() * 100;
1959
- const colorRandomRatio2 = Math.random();
2120
+ const colorRandomRatio = Math.random();
1960
2121
  const cfgStartColor = normalizedConfig.startColor;
1961
- aColorR.array[particleIndex] = cfgStartColor.min.r + colorRandomRatio2 * (cfgStartColor.max.r - cfgStartColor.min.r);
1962
- aColorR.needsUpdate = true;
1963
- aColorG.array[particleIndex] = cfgStartColor.min.g + colorRandomRatio2 * (cfgStartColor.max.g - cfgStartColor.min.g);
1964
- aColorG.needsUpdate = true;
1965
- aColorB.array[particleIndex] = cfgStartColor.min.b + colorRandomRatio2 * (cfgStartColor.max.b - cfgStartColor.min.b);
1966
- aColorB.needsUpdate = true;
1967
- generalData.startValues.startColorR[particleIndex] = aColorR.array[particleIndex];
1968
- generalData.startValues.startColorG[particleIndex] = aColorG.array[particleIndex];
1969
- generalData.startValues.startColorB[particleIndex] = aColorB.array[particleIndex];
1970
- aStartFrame.array[particleIndex] = normalizedConfig.textureSheetAnimation.startFrame ? calculateValue(
2122
+ scalarArray[base + S_COLOR_R] = cfgStartColor.min.r + colorRandomRatio * (cfgStartColor.max.r - cfgStartColor.min.r);
2123
+ scalarArray[base + S_COLOR_G] = cfgStartColor.min.g + colorRandomRatio * (cfgStartColor.max.g - cfgStartColor.min.g);
2124
+ scalarArray[base + S_COLOR_B] = cfgStartColor.min.b + colorRandomRatio * (cfgStartColor.max.b - cfgStartColor.min.b);
2125
+ generalData.startValues.startColorR[particleIndex] = scalarArray[base + S_COLOR_R];
2126
+ generalData.startValues.startColorG[particleIndex] = scalarArray[base + S_COLOR_G];
2127
+ generalData.startValues.startColorB[particleIndex] = scalarArray[base + S_COLOR_B];
2128
+ scalarArray[base + S_START_FRAME] = normalizedConfig.textureSheetAnimation.startFrame ? calculateValue(
1971
2129
  generalData.particleSystemId,
1972
2130
  normalizedConfig.textureSheetAnimation.startFrame,
1973
2131
  0
1974
2132
  ) : 0;
1975
- aStartFrame.needsUpdate = true;
1976
- aStartLifetime.array[particleIndex] = calculateValue(
2133
+ scalarArray[base + S_START_LIFETIME] = calculateValue(
1977
2134
  generalData.particleSystemId,
1978
2135
  normalizedConfig.startLifetime,
1979
2136
  generalData.normalizedLifetimePercentage
1980
2137
  ) * 1e3;
1981
- aStartLifetime.needsUpdate = true;
1982
2138
  generalData.startValues.startSize[particleIndex] = calculateValue(
1983
2139
  generalData.particleSystemId,
1984
2140
  normalizedConfig.startSize,
1985
2141
  generalData.normalizedLifetimePercentage
1986
2142
  );
1987
- aSize.array[particleIndex] = generalData.startValues.startSize[particleIndex];
1988
- aSize.needsUpdate = true;
2143
+ scalarArray[base + S_SIZE] = generalData.startValues.startSize[particleIndex];
1989
2144
  generalData.startValues.startOpacity[particleIndex] = calculateValue(
1990
2145
  generalData.particleSystemId,
1991
2146
  normalizedConfig.startOpacity,
1992
2147
  generalData.normalizedLifetimePercentage
1993
2148
  );
1994
- aColorA.array[particleIndex] = generalData.startValues.startOpacity[particleIndex];
1995
- aColorA.needsUpdate = true;
1996
- aRotation.array[particleIndex] = calculateValue(
2149
+ scalarArray[base + S_COLOR_A] = generalData.startValues.startOpacity[particleIndex];
2150
+ scalarArray[base + S_ROTATION] = calculateValue(
1997
2151
  generalData.particleSystemId,
1998
2152
  normalizedConfig.startRotation,
1999
2153
  generalData.normalizedLifetimePercentage
2000
2154
  );
2001
- aRotation.needsUpdate = true;
2002
2155
  if (aQuat) {
2003
- const rotZ = aRotation.array[particleIndex];
2156
+ const rotZ = scalarArray[base + S_ROTATION];
2004
2157
  const halfZ = rotZ * 0.5;
2005
2158
  const qi = particleIndex * 4;
2006
2159
  aQuat.array[qi] = 0;
@@ -2010,7 +2163,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2010
2163
  aQuat.needsUpdate = true;
2011
2164
  }
2012
2165
  if (normalizedConfig.rotationOverLifetime.isActive)
2013
- generalData.lifetimeValues.rotationOverLifetime[particleIndex] = THREE4.MathUtils.randFloat(
2166
+ generalData.lifetimeValues.rotationOverLifetime[particleIndex] = THREE5.MathUtils.randFloat(
2014
2167
  normalizedConfig.rotationOverLifetime.min,
2015
2168
  normalizedConfig.rotationOverLifetime.max
2016
2169
  );
@@ -2021,11 +2174,15 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2021
2174
  startPositions[particleIndex],
2022
2175
  velocities[particleIndex]
2023
2176
  );
2024
- const positionIndex = Math.floor(particleIndex * 3);
2025
- aPosition.array[positionIndex] = position.x + startPositions[particleIndex].x;
2026
- aPosition.array[positionIndex + 1] = position.y + startPositions[particleIndex].y;
2027
- aPosition.array[positionIndex + 2] = position.z + startPositions[particleIndex].z;
2028
- aPosition.needsUpdate = true;
2177
+ {
2178
+ const positionIndex = particleIndex * 3;
2179
+ aPosition.array[positionIndex] = position.x + startPositions[particleIndex].x;
2180
+ aPosition.array[positionIndex + 1] = position.y + startPositions[particleIndex].y;
2181
+ aPosition.array[positionIndex + 2] = position.z + startPositions[particleIndex].z;
2182
+ if (!useGPUCompute) {
2183
+ aPosition.needsUpdate = true;
2184
+ }
2185
+ }
2029
2186
  if (generalData.linearVelocityData) {
2030
2187
  generalData.linearVelocityData[particleIndex].speed.set(
2031
2188
  normalizedConfig.velocityOverLifetime.linear.x ? calculateValue(
@@ -2069,16 +2226,56 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2069
2226
  startPositions[particleIndex].z
2070
2227
  );
2071
2228
  }
2072
- aLifetime.array[particleIndex] = 0;
2073
- aLifetime.needsUpdate = true;
2074
- applyModifiers({
2075
- delta: 0,
2076
- generalData,
2077
- normalizedConfig,
2078
- attributes: mappedAttributes,
2079
- particleLifetimePercentage: 0,
2080
- particleIndex
2081
- });
2229
+ scalarArray[base + S_LIFETIME] = 0;
2230
+ if (useGPUCompute && gpuPipeline) {
2231
+ _tslMaterialFactory.writeParticleToModifierBuffers(
2232
+ gpuPipeline.buffers,
2233
+ particleIndex,
2234
+ {
2235
+ position: {
2236
+ x: position.x + startPositions[particleIndex].x,
2237
+ y: position.y + startPositions[particleIndex].y,
2238
+ z: position.z + startPositions[particleIndex].z
2239
+ },
2240
+ velocity: {
2241
+ x: velocities[particleIndex].x,
2242
+ y: velocities[particleIndex].y,
2243
+ z: velocities[particleIndex].z
2244
+ },
2245
+ startLifetime: scalarArray[base + S_START_LIFETIME],
2246
+ colorA: scalarArray[base + S_COLOR_A],
2247
+ size: scalarArray[base + S_SIZE],
2248
+ rotation: scalarArray[base + S_ROTATION],
2249
+ colorR: scalarArray[base + S_COLOR_R],
2250
+ colorG: scalarArray[base + S_COLOR_G],
2251
+ colorB: scalarArray[base + S_COLOR_B],
2252
+ startSize: generalData.startValues.startSize[particleIndex],
2253
+ startOpacity: generalData.startValues.startOpacity[particleIndex],
2254
+ startColorR: generalData.startValues.startColorR[particleIndex],
2255
+ startColorG: generalData.startValues.startColorG[particleIndex],
2256
+ startColorB: generalData.startValues.startColorB[particleIndex],
2257
+ rotationSpeed: generalData.lifetimeValues.rotationOverLifetime ? generalData.lifetimeValues.rotationOverLifetime[particleIndex] : 0,
2258
+ noiseOffset: generalData.noise.offsets ? generalData.noise.offsets[particleIndex] : 0,
2259
+ startFrame: scalarArray[base + S_START_FRAME],
2260
+ orbitalOffset: {
2261
+ x: startPositions[particleIndex].x,
2262
+ y: startPositions[particleIndex].y,
2263
+ z: startPositions[particleIndex].z
2264
+ }
2265
+ }
2266
+ );
2267
+ } else {
2268
+ scalarInterleavedBuffer.needsUpdate = true;
2269
+ applyModifiers({
2270
+ delta: 0,
2271
+ generalData,
2272
+ normalizedConfig,
2273
+ attributes: mappedAttributes,
2274
+ scalarArray,
2275
+ particleLifetimePercentage: 0,
2276
+ particleIndex
2277
+ });
2278
+ }
2082
2279
  };
2083
2280
  const subEmitterArr = subEmitters ?? [];
2084
2281
  const deathSubEmitters = subEmitterArr.filter(
@@ -2094,17 +2291,17 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2094
2291
  const cleanupCompletedInstances = (instances) => {
2095
2292
  for (let i = instances.length - 1; i >= 0; i--) {
2096
2293
  const sub = instances[i];
2097
- const obj3d = sub.instance instanceof THREE4.Points || sub.instance instanceof THREE4.Mesh ? sub.instance : sub.instance.children[0];
2294
+ const obj3d = sub.instance instanceof THREE5.Points || sub.instance instanceof THREE5.Mesh ? sub.instance : sub.instance.children[0];
2098
2295
  const geomAttrs = obj3d?.geometry?.attributes;
2099
- const isActiveArr = geomAttrs ? geomAttrs.isActive?.array ?? geomAttrs.instanceIsActive?.array : void 0;
2100
- if (!isActiveArr) {
2296
+ const isActiveAttr = geomAttrs ? geomAttrs.isActive ?? geomAttrs.instanceIsActive : void 0;
2297
+ if (!isActiveAttr) {
2101
2298
  sub.dispose();
2102
2299
  instances.splice(i, 1);
2103
2300
  continue;
2104
2301
  }
2105
2302
  let hasActive = false;
2106
- for (let j = 0; j < isActiveArr.length; j++) {
2107
- if (isActiveArr[j]) {
2303
+ for (let j = 0; j < isActiveAttr.count; j++) {
2304
+ if (isActiveAttr.getX(j)) {
2108
2305
  hasActive = true;
2109
2306
  break;
2110
2307
  }
@@ -2128,9 +2325,12 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2128
2325
  {
2129
2326
  ...subConfig.config,
2130
2327
  looping: false,
2328
+ // Sub-emitters must always use CPU simulation because their compute
2329
+ // nodes cannot be dispatched independently by the parent system.
2330
+ simulationBackend: "CPU" /* CPU */,
2131
2331
  transform: {
2132
2332
  ...subConfig.config.transform,
2133
- position: new THREE4.Vector3(position.x, position.y, position.z)
2333
+ position: new THREE5.Vector3(position.x, position.y, position.z)
2134
2334
  },
2135
2335
  renderer: {
2136
2336
  ...subConfig.config.renderer ?? {},
@@ -2165,7 +2365,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2165
2365
  const totalVertices = maxParticles * verticesPerParticle;
2166
2366
  const indicesPerParticle = (trailLength - 1) * 6;
2167
2367
  const totalIndices = maxParticles * indicesPerParticle;
2168
- trailGeometry = new THREE4.BufferGeometry();
2368
+ trailGeometry = new THREE5.BufferGeometry();
2169
2369
  const trailPositions = new Float32Array(totalVertices * 3);
2170
2370
  const trailNextPositions = new Float32Array(totalVertices * 3);
2171
2371
  const trailAlphas = new Float32Array(totalVertices);
@@ -2192,63 +2392,63 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2192
2392
  trailIndices[i + 5] = v + 2;
2193
2393
  }
2194
2394
  }
2195
- trailPositionAttr = new THREE4.BufferAttribute(trailPositions, 3);
2196
- trailPositionAttr.setUsage(THREE4.DynamicDrawUsage);
2197
- trailNextAttr = new THREE4.BufferAttribute(trailNextPositions, 3);
2198
- trailNextAttr.setUsage(THREE4.DynamicDrawUsage);
2199
- trailAlphaAttr = new THREE4.BufferAttribute(trailAlphas, 1);
2200
- trailAlphaAttr.setUsage(THREE4.DynamicDrawUsage);
2201
- trailColorAttr = new THREE4.BufferAttribute(trailColors, 4);
2202
- trailColorAttr.setUsage(THREE4.DynamicDrawUsage);
2203
- trailHalfWidthAttr = new THREE4.BufferAttribute(trailHalfWidths, 1);
2204
- trailHalfWidthAttr.setUsage(THREE4.DynamicDrawUsage);
2205
- trailUVAttr = new THREE4.BufferAttribute(trailUVs, 2);
2206
- trailUVAttr.setUsage(THREE4.DynamicDrawUsage);
2207
- trailIndexAttr = new THREE4.BufferAttribute(trailIndices, 1);
2395
+ trailPositionAttr = new THREE5.BufferAttribute(trailPositions, 3);
2396
+ trailPositionAttr.setUsage(THREE5.DynamicDrawUsage);
2397
+ trailNextAttr = new THREE5.BufferAttribute(trailNextPositions, 3);
2398
+ trailNextAttr.setUsage(THREE5.DynamicDrawUsage);
2399
+ trailAlphaAttr = new THREE5.BufferAttribute(trailAlphas, 1);
2400
+ trailAlphaAttr.setUsage(THREE5.DynamicDrawUsage);
2401
+ trailColorAttr = new THREE5.BufferAttribute(trailColors, 4);
2402
+ trailColorAttr.setUsage(THREE5.DynamicDrawUsage);
2403
+ trailHalfWidthAttr = new THREE5.BufferAttribute(trailHalfWidths, 1);
2404
+ trailHalfWidthAttr.setUsage(THREE5.DynamicDrawUsage);
2405
+ trailUVAttr = new THREE5.BufferAttribute(trailUVs, 2);
2406
+ trailUVAttr.setUsage(THREE5.DynamicDrawUsage);
2407
+ trailIndexAttr = new THREE5.BufferAttribute(trailIndices, 1);
2208
2408
  trailGeometry.setAttribute("position", trailPositionAttr);
2209
2409
  trailGeometry.setAttribute("trailNext", trailNextAttr);
2210
2410
  trailGeometry.setAttribute("trailAlpha", trailAlphaAttr);
2211
2411
  trailGeometry.setAttribute("trailColor", trailColorAttr);
2212
2412
  trailGeometry.setAttribute(
2213
2413
  "trailOffset",
2214
- new THREE4.BufferAttribute(trailOffsets, 1)
2414
+ new THREE5.BufferAttribute(trailOffsets, 1)
2215
2415
  );
2216
2416
  trailGeometry.setAttribute("trailHalfWidth", trailHalfWidthAttr);
2217
2417
  trailGeometry.setAttribute("trailUV", trailUVAttr);
2218
2418
  trailGeometry.setIndex(trailIndexAttr);
2219
- const trailMaterial = new THREE4.ShaderMaterial({
2220
- uniforms: {
2221
- map: { value: particleMap },
2222
- useMap: { value: !!particleMap },
2223
- discardBackgroundColor: { value: renderer.discardBackgroundColor },
2224
- backgroundColor: { value: renderer.backgroundColor },
2225
- backgroundColorTolerance: { value: renderer.backgroundColorTolerance },
2226
- softParticlesEnabled: { value: softParticlesEnabled },
2227
- softParticlesIntensity: {
2228
- value: Math.max(renderer.softParticles?.intensity ?? 1, 1e-3)
2229
- },
2230
- sceneDepthTexture: {
2231
- value: renderer.softParticles?.depthTexture ?? null
2232
- },
2233
- cameraNearFar: { value: new THREE4.Vector2(0.1, 1e3) }
2419
+ const trailUniformValues = {
2420
+ map: { value: particleMap },
2421
+ useMap: { value: !!particleMap },
2422
+ discardBackgroundColor: { value: renderer.discardBackgroundColor },
2423
+ backgroundColor: { value: renderer.backgroundColor },
2424
+ backgroundColorTolerance: { value: renderer.backgroundColorTolerance },
2425
+ softParticlesEnabled: { value: softParticlesEnabled },
2426
+ softParticlesIntensity: {
2427
+ value: Math.max(renderer.softParticles?.intensity ?? 1, 1e-3)
2428
+ },
2429
+ sceneDepthTexture: {
2430
+ value: renderer.softParticles?.depthTexture ?? null
2234
2431
  },
2432
+ cameraNearFar: { value: new THREE5.Vector2(0.1, 1e3) }
2433
+ };
2434
+ const trailMaterial = useTSL ? _tslMaterialFactory.createTSLTrailMaterial(
2435
+ trailUniformValues,
2436
+ rendererConfig
2437
+ ) : new THREE5.ShaderMaterial({
2438
+ uniforms: trailUniformValues,
2235
2439
  vertexShader: trail_vertex_shader_glsl_default,
2236
2440
  fragmentShader: trail_fragment_shader_glsl_default,
2237
- transparent: renderer.transparent,
2238
- blending: renderer.blending,
2239
- depthTest: renderer.depthTest,
2240
- depthWrite: renderer.depthWrite,
2241
- side: THREE4.DoubleSide
2441
+ ...rendererConfig,
2442
+ side: THREE5.DoubleSide
2242
2443
  });
2243
- trailMesh = new THREE4.Mesh(trailGeometry, trailMaterial);
2444
+ trailMesh = new THREE5.Mesh(trailGeometry, trailMaterial);
2244
2445
  trailMesh.frustumCulled = false;
2245
- const trailCameraPos = new THREE4.Vector3();
2446
+ const trailCameraPos = new THREE5.Vector3();
2246
2447
  trailMesh.onBeforeRender = (_renderer, _scene, camera) => {
2247
2448
  camera.getWorldPosition(trailCameraPos);
2248
2449
  if (softParticlesEnabled && camera.isPerspectiveCamera) {
2249
2450
  const perspCam = camera;
2250
- const trailUniforms = trailMaterial.uniforms;
2251
- trailUniforms.cameraNearFar.value.set(
2451
+ trailUniformValues.cameraNearFar.value.set(
2252
2452
  perspCam.near,
2253
2453
  perspCam.far
2254
2454
  );
@@ -2280,11 +2480,11 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2280
2480
  };
2281
2481
  }
2282
2482
  }
2283
- let particleSystem = useInstancing || useMesh ? new THREE4.Mesh(geometry, material) : new THREE4.Points(geometry, material);
2284
- if (useInstancing || softParticlesEnabled) {
2483
+ let particleSystem = useInstancing || useMesh ? new THREE5.Mesh(geometry, material) : new THREE5.Points(geometry, material);
2484
+ if (useInstancing || softParticlesEnabled || useGPUCompute) {
2285
2485
  particleSystem.onBeforeRender = (glRenderer, _scene, camera) => {
2286
2486
  if (useInstancing) {
2287
- const size = glRenderer.getSize(new THREE4.Vector2());
2487
+ const size = glRenderer.getSize(new THREE5.Vector2());
2288
2488
  sharedUniforms.viewportHeight.value = size.y * glRenderer.getPixelRatio();
2289
2489
  }
2290
2490
  if (softParticlesEnabled && camera.isPerspectiveCamera) {
@@ -2301,9 +2501,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2301
2501
  particleSystem.add(trailMesh);
2302
2502
  }
2303
2503
  particleSystem.position.copy(transform.position);
2304
- particleSystem.rotation.x = THREE4.MathUtils.degToRad(transform.rotation.x);
2305
- particleSystem.rotation.y = THREE4.MathUtils.degToRad(transform.rotation.y);
2306
- particleSystem.rotation.z = THREE4.MathUtils.degToRad(transform.rotation.z);
2504
+ particleSystem.rotation.x = THREE5.MathUtils.degToRad(transform.rotation.x);
2505
+ particleSystem.rotation.y = THREE5.MathUtils.degToRad(transform.rotation.y);
2506
+ particleSystem.rotation.z = THREE5.MathUtils.degToRad(transform.rotation.z);
2307
2507
  particleSystem.scale.copy(transform.scale);
2308
2508
  const mappedAttributes = {
2309
2509
  position: aPosition,
@@ -2313,10 +2513,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2313
2513
  startFrame: aStartFrame,
2314
2514
  size: aSize,
2315
2515
  rotation: aRotation,
2316
- colorR: aColorR,
2317
- colorG: aColorG,
2318
- colorB: aColorB,
2319
- colorA: aColorA,
2516
+ color: aColor,
2320
2517
  ...useMesh ? { quat: aQuat } : {}
2321
2518
  };
2322
2519
  const calculatedCreationTime = now + calculateValue(generalData.particleSystemId, startDelay) * 1e3;
@@ -2334,6 +2531,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2334
2531
  positionArr[posIdx + 1],
2335
2532
  positionArr[posIdx + 2]
2336
2533
  );
2534
+ if (simulationSpace === "LOCAL" /* LOCAL */) {
2535
+ particleSystem.localToWorld(_subEmitterPosition);
2536
+ }
2337
2537
  spawnSubEmitters(
2338
2538
  deathSubEmitters,
2339
2539
  _subEmitterPosition,
@@ -2348,6 +2548,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2348
2548
  positionArr[posIdx + 1],
2349
2549
  positionArr[posIdx + 2]
2350
2550
  );
2551
+ if (simulationSpace === "LOCAL" /* LOCAL */) {
2552
+ particleSystem.localToWorld(_subEmitterPosition);
2553
+ }
2351
2554
  spawnSubEmitters(
2352
2555
  birthSubEmitters,
2353
2556
  _subEmitterPosition,
@@ -2359,7 +2562,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2359
2562
  particleSystem,
2360
2563
  wrapper,
2361
2564
  mappedAttributes,
2362
- elapsedUniform: material.uniforms.elapsed,
2565
+ scalarArray,
2566
+ scalarInterleavedBuffer,
2567
+ elapsedUniform: sharedUniforms.elapsed,
2363
2568
  generalData,
2364
2569
  onUpdate,
2365
2570
  onComplete,
@@ -2370,6 +2575,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2370
2575
  simulationSpace,
2371
2576
  gravity,
2372
2577
  normalizedForceFields,
2578
+ normalizedCollisionPlanes,
2373
2579
  emission,
2374
2580
  normalizedConfig,
2375
2581
  iterationCount: 0,
@@ -2379,6 +2585,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2379
2585
  activateParticle,
2380
2586
  onParticleDeath,
2381
2587
  onParticleBirth,
2588
+ useGPUCompute: useGPUCompute && gpuPipeline !== null,
2589
+ computePipeline: gpuPipeline ?? void 0,
2590
+ computeDispatchReady: false,
2382
2591
  ...useTrail ? {
2383
2592
  trailMesh,
2384
2593
  trailPositionAttr,
@@ -2440,15 +2649,22 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2440
2649
  cfg.forceFields
2441
2650
  );
2442
2651
  }
2652
+ if (partialConfig.collisionPlanes !== void 0) {
2653
+ instanceData.normalizedCollisionPlanes = normalizeCollisionPlanes(
2654
+ cfg.collisionPlanes
2655
+ );
2656
+ }
2443
2657
  if (partialConfig.noise !== void 0) {
2444
2658
  const n = cfg.noise;
2445
2659
  generalData.noise = {
2446
2660
  isActive: n.isActive,
2447
2661
  strength: n.strength,
2448
2662
  noisePower: 0.15 * n.strength,
2663
+ frequency: n.frequency,
2449
2664
  positionAmount: n.positionAmount,
2450
2665
  rotationAmount: n.rotationAmount,
2451
2666
  sizeAmount: n.sizeAmount,
2667
+ fbmMax: 2 - Math.pow(2, -n.octaves),
2452
2668
  sampler: n.isActive ? new FBM({
2453
2669
  seed: Math.random(),
2454
2670
  scale: n.frequency,
@@ -2464,7 +2680,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2464
2680
  pauseEmitter,
2465
2681
  dispose,
2466
2682
  update,
2467
- updateConfig
2683
+ updateConfig,
2684
+ computeNode: gpuPipeline?.computeNode ?? null
2468
2685
  };
2469
2686
  };
2470
2687
  var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
@@ -2489,11 +2706,15 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2489
2706
  simulationSpace,
2490
2707
  gravity,
2491
2708
  normalizedForceFields,
2709
+ normalizedCollisionPlanes,
2492
2710
  onParticleDeath,
2493
2711
  onParticleBirth,
2494
- mappedAttributes: ma
2712
+ mappedAttributes: ma,
2713
+ useGPUCompute,
2714
+ computePipeline
2495
2715
  } = props;
2496
2716
  const hasForceFields = normalizedForceFields.length > 0;
2717
+ const hasCollisionPlanes = normalizedCollisionPlanes.length > 0;
2497
2718
  const lifetime = now - creationTime;
2498
2719
  const normalizedLifetime = lifetime % (duration * 1e3);
2499
2720
  generalData.normalizedLifetimePercentage = Math.max(
@@ -2547,8 +2768,8 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2547
2768
  dst = {
2548
2769
  isActive: true,
2549
2770
  type: "POINT" /* POINT */,
2550
- position: new THREE4.Vector3(),
2551
- direction: new THREE4.Vector3(),
2771
+ position: new THREE5.Vector3(),
2772
+ direction: new THREE5.Vector3(),
2552
2773
  strength: 0,
2553
2774
  range: 0,
2554
2775
  falloff: "LINEAR" /* LINEAR */
@@ -2568,42 +2789,123 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2568
2789
  dst.direction.copy(_localForceFieldDir);
2569
2790
  }
2570
2791
  }
2792
+ if (hasCollisionPlanes) {
2793
+ if (!hasForceFields) _inverseQuat.copy(worldQuaternion).invert();
2794
+ _localCollisionPlanes.length = normalizedCollisionPlanes.length;
2795
+ for (let i = 0; i < normalizedCollisionPlanes.length; i++) {
2796
+ const src = normalizedCollisionPlanes[i];
2797
+ let dst = _localCollisionPlanes[i];
2798
+ if (!dst) {
2799
+ dst = {
2800
+ isActive: true,
2801
+ position: new THREE5.Vector3(),
2802
+ normal: new THREE5.Vector3(),
2803
+ mode: "KILL" /* KILL */,
2804
+ dampen: 0.5,
2805
+ lifetimeLoss: 0
2806
+ };
2807
+ _localCollisionPlanes[i] = dst;
2808
+ }
2809
+ dst.isActive = src.isActive;
2810
+ dst.mode = src.mode;
2811
+ dst.dampen = src.dampen;
2812
+ dst.lifetimeLoss = src.lifetimeLoss;
2813
+ _localCollisionPlanePos.copy(src.position);
2814
+ particleSystem.worldToLocal(_localCollisionPlanePos);
2815
+ dst.position.copy(_localCollisionPlanePos);
2816
+ _localCollisionPlaneNormal.copy(src.normal);
2817
+ _localCollisionPlaneNormal.applyQuaternion(_inverseQuat);
2818
+ dst.normal.copy(_localCollisionPlaneNormal);
2819
+ }
2820
+ }
2571
2821
  const creationTimes = generalData.creationTimes;
2572
- const isActiveArr = ma.isActive.array;
2573
- const startLifetimeArr = ma.startLifetime.array;
2822
+ const scalarArr = props.scalarArray;
2574
2823
  const positionArr = ma.position.array;
2575
- const lifetimeArr = ma.lifetime.array;
2576
2824
  const creationTimesLength = creationTimes.length;
2577
- let positionNeedsUpdate = false;
2578
- let lifetimeNeedsUpdate = false;
2579
- _modifierParams.delta = delta;
2580
- _modifierParams.generalData = generalData;
2581
- _modifierParams.normalizedConfig = normalizedConfig;
2582
- _modifierParams.attributes = ma;
2583
- for (let index = 0; index < creationTimesLength; index++) {
2584
- if (isActiveArr[index]) {
2585
- const particleLifetime = now - creationTimes[index];
2586
- if (particleLifetime > startLifetimeArr[index]) {
2587
- if (onParticleDeath)
2588
- onParticleDeath(index, positionArr, velocities[index], now);
2589
- deactivateParticle(index);
2590
- } else {
2591
- const velocity = velocities[index];
2592
- velocity.x -= gravityVelocity.x * delta;
2593
- velocity.y -= gravityVelocity.y * delta;
2594
- velocity.z -= gravityVelocity.z * delta;
2595
- if (hasForceFields) {
2596
- applyForceFields({
2597
- particleSystemId: generalData.particleSystemId,
2598
- forceFields: _localForceFields,
2599
- velocity,
2600
- positionArr,
2601
- positionIndex: index * 3,
2602
- delta,
2603
- systemLifetimePercentage: generalData.normalizedLifetimePercentage
2604
- });
2605
- }
2606
- if (gravity !== 0 || velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0 || worldPositionChange.x !== 0 || worldPositionChange.y !== 0 || worldPositionChange.z !== 0) {
2825
+ if (useGPUCompute && computePipeline) {
2826
+ const cp = computePipeline;
2827
+ setUniformFloat(cp.uniforms.delta, delta);
2828
+ setUniformFloat(cp.uniforms.deltaMs, delta * 1e3);
2829
+ setUniformVec3(
2830
+ cp.uniforms.gravityVelocity,
2831
+ gravityVelocity.x,
2832
+ gravityVelocity.y,
2833
+ gravityVelocity.z
2834
+ );
2835
+ setUniformVec3(
2836
+ cp.uniforms.worldPositionChange,
2837
+ generalData.worldPositionChange.x,
2838
+ generalData.worldPositionChange.y,
2839
+ generalData.worldPositionChange.z
2840
+ );
2841
+ setUniformFloat(
2842
+ cp.uniforms.simulationSpaceWorld,
2843
+ simulationSpace === "WORLD" /* WORLD */ ? 1 : 0
2844
+ );
2845
+ const noiseData = generalData.noise;
2846
+ setUniformFloat(cp.uniforms.noiseStrength, noiseData.strength);
2847
+ setUniformFloat(
2848
+ cp.uniforms.noisePower,
2849
+ noiseData.noisePower / noiseData.fbmMax
2850
+ );
2851
+ setUniformFloat(cp.uniforms.noiseFrequency, noiseData.frequency);
2852
+ setUniformFloat(cp.uniforms.noisePositionAmount, noiseData.positionAmount);
2853
+ setUniformFloat(cp.uniforms.noiseRotationAmount, noiseData.rotationAmount);
2854
+ setUniformFloat(cp.uniforms.noiseSizeAmount, noiseData.sizeAmount);
2855
+ if (cp.forceFieldInfo && hasForceFields && _tslMaterialFactory?.encodeForceFieldsForGPU) {
2856
+ const encodedFF = _tslMaterialFactory.encodeForceFieldsForGPU(
2857
+ _localForceFields,
2858
+ generalData.particleSystemId,
2859
+ generalData.normalizedLifetimePercentage
2860
+ );
2861
+ const curveArr = cp.buffers.curveData.array;
2862
+ curveArr.set(encodedFF, cp.forceFieldInfo.offset);
2863
+ cp.buffers.curveData.needsUpdate = true;
2864
+ setUniformFloat(
2865
+ cp.forceFieldInfo.countUniform,
2866
+ normalizedForceFields.length
2867
+ );
2868
+ }
2869
+ if (cp.collisionPlaneInfo && hasCollisionPlanes && _tslMaterialFactory?.encodeCollisionPlanesForGPU) {
2870
+ const encodedCP = _tslMaterialFactory.encodeCollisionPlanesForGPU(
2871
+ _localCollisionPlanes
2872
+ );
2873
+ const curveArr = cp.buffers.curveData.array;
2874
+ curveArr.set(encodedCP, cp.collisionPlaneInfo.offset);
2875
+ cp.buffers.curveData.needsUpdate = true;
2876
+ setUniformFloat(
2877
+ cp.collisionPlaneInfo.countUniform,
2878
+ normalizedCollisionPlanes.length
2879
+ );
2880
+ }
2881
+ if (_tslMaterialFactory?.flushEmitQueue) {
2882
+ _tslMaterialFactory.flushEmitQueue(cp.buffers);
2883
+ }
2884
+ props.computeDispatchReady = true;
2885
+ for (let index = 0; index < creationTimesLength; index++) {
2886
+ const base = index * SCALAR_STRIDE;
2887
+ if (scalarArr[base + S_IS_ACTIVE]) {
2888
+ const particleLifetime = now - creationTimes[index];
2889
+ if (particleLifetime > scalarArr[base + S_START_LIFETIME]) {
2890
+ if (onParticleDeath)
2891
+ onParticleDeath(index, positionArr, velocities[index], now);
2892
+ deactivateParticle(index);
2893
+ } else if (onParticleDeath) {
2894
+ const velocity = velocities[index];
2895
+ velocity.x -= gravityVelocity.x * delta;
2896
+ velocity.y -= gravityVelocity.y * delta;
2897
+ velocity.z -= gravityVelocity.z * delta;
2898
+ if (hasForceFields) {
2899
+ applyForceFields({
2900
+ particleSystemId: generalData.particleSystemId,
2901
+ forceFields: _localForceFields,
2902
+ velocity,
2903
+ positionArr,
2904
+ positionIndex: index * 3,
2905
+ delta,
2906
+ systemLifetimePercentage: generalData.normalizedLifetimePercentage
2907
+ });
2908
+ }
2607
2909
  const positionIndex = index * 3;
2608
2910
  if (simulationSpace === "WORLD" /* WORLD */) {
2609
2911
  positionArr[positionIndex] -= worldPositionChange.x;
@@ -2613,18 +2915,116 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2613
2915
  positionArr[positionIndex] += velocity.x * delta;
2614
2916
  positionArr[positionIndex + 1] += velocity.y * delta;
2615
2917
  positionArr[positionIndex + 2] += velocity.z * delta;
2616
- positionNeedsUpdate = true;
2918
+ if (generalData.orbitalVelocityData) {
2919
+ const orbData = generalData.orbitalVelocityData[index];
2920
+ const { speed, positionOffset, valueModifiers } = orbData;
2921
+ const pctLife = particleLifetime / scalarArr[base + S_START_LIFETIME];
2922
+ positionArr[positionIndex] -= positionOffset.x;
2923
+ positionArr[positionIndex + 1] -= positionOffset.y;
2924
+ positionArr[positionIndex + 2] -= positionOffset.z;
2925
+ const sx = valueModifiers.x ? valueModifiers.x(pctLife) : speed.x;
2926
+ const sy = valueModifiers.y ? valueModifiers.y(pctLife) : speed.y;
2927
+ const sz = valueModifiers.z ? valueModifiers.z(pctLife) : speed.z;
2928
+ _shadowOrbitalEuler.set(sx * delta, sz * delta, sy * delta);
2929
+ positionOffset.applyEuler(_shadowOrbitalEuler);
2930
+ positionArr[positionIndex] += positionOffset.x;
2931
+ positionArr[positionIndex + 1] += positionOffset.y;
2932
+ positionArr[positionIndex + 2] += positionOffset.z;
2933
+ }
2934
+ if (hasCollisionPlanes) {
2935
+ applyCollisionPlanes({
2936
+ collisionPlanes: _localCollisionPlanes,
2937
+ velocity,
2938
+ positionArr,
2939
+ positionIndex,
2940
+ scalarArr,
2941
+ scalarBase: base,
2942
+ deactivateParticle: (pi) => {
2943
+ if (onParticleDeath)
2944
+ onParticleDeath(pi, positionArr, velocities[pi], now);
2945
+ deactivateParticle(pi);
2946
+ },
2947
+ particleIndex: index
2948
+ });
2949
+ }
2950
+ }
2951
+ }
2952
+ }
2953
+ } else {
2954
+ let positionNeedsUpdate = false;
2955
+ let scalarNeedsUpdate = false;
2956
+ _modifierParams.delta = delta;
2957
+ _modifierParams.generalData = generalData;
2958
+ _modifierParams.normalizedConfig = normalizedConfig;
2959
+ _modifierParams.attributes = ma;
2960
+ _modifierParams.scalarArray = scalarArr;
2961
+ for (let index = 0; index < creationTimesLength; index++) {
2962
+ const base = index * SCALAR_STRIDE;
2963
+ if (scalarArr[base + S_IS_ACTIVE]) {
2964
+ const particleLifetime = now - creationTimes[index];
2965
+ if (particleLifetime > scalarArr[base + S_START_LIFETIME]) {
2966
+ if (onParticleDeath)
2967
+ onParticleDeath(index, positionArr, velocities[index], now);
2968
+ deactivateParticle(index);
2969
+ } else {
2970
+ const velocity = velocities[index];
2971
+ velocity.x -= gravityVelocity.x * delta;
2972
+ velocity.y -= gravityVelocity.y * delta;
2973
+ velocity.z -= gravityVelocity.z * delta;
2974
+ if (hasForceFields) {
2975
+ applyForceFields({
2976
+ particleSystemId: generalData.particleSystemId,
2977
+ forceFields: _localForceFields,
2978
+ velocity,
2979
+ positionArr,
2980
+ positionIndex: index * 3,
2981
+ delta,
2982
+ systemLifetimePercentage: generalData.normalizedLifetimePercentage
2983
+ });
2984
+ }
2985
+ if (gravity !== 0 || velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0 || worldPositionChange.x !== 0 || worldPositionChange.y !== 0 || worldPositionChange.z !== 0) {
2986
+ const positionIndex = index * 3;
2987
+ if (simulationSpace === "WORLD" /* WORLD */) {
2988
+ positionArr[positionIndex] -= worldPositionChange.x;
2989
+ positionArr[positionIndex + 1] -= worldPositionChange.y;
2990
+ positionArr[positionIndex + 2] -= worldPositionChange.z;
2991
+ }
2992
+ positionArr[positionIndex] += velocity.x * delta;
2993
+ positionArr[positionIndex + 1] += velocity.y * delta;
2994
+ positionArr[positionIndex + 2] += velocity.z * delta;
2995
+ positionNeedsUpdate = true;
2996
+ }
2997
+ if (hasCollisionPlanes) {
2998
+ const killed = applyCollisionPlanes({
2999
+ collisionPlanes: _localCollisionPlanes,
3000
+ velocity,
3001
+ positionArr,
3002
+ positionIndex: index * 3,
3003
+ scalarArr,
3004
+ scalarBase: base,
3005
+ deactivateParticle: (pi) => {
3006
+ if (onParticleDeath)
3007
+ onParticleDeath(pi, positionArr, velocities[pi], now);
3008
+ deactivateParticle(pi);
3009
+ },
3010
+ particleIndex: index
3011
+ });
3012
+ if (killed) {
3013
+ positionNeedsUpdate = true;
3014
+ continue;
3015
+ }
3016
+ }
3017
+ scalarArr[base + S_LIFETIME] = particleLifetime;
3018
+ scalarNeedsUpdate = true;
3019
+ _modifierParams.particleLifetimePercentage = particleLifetime / scalarArr[base + S_START_LIFETIME];
3020
+ _modifierParams.particleIndex = index;
3021
+ applyModifiers(_modifierParams);
2617
3022
  }
2618
- lifetimeArr[index] = particleLifetime;
2619
- lifetimeNeedsUpdate = true;
2620
- _modifierParams.particleLifetimePercentage = particleLifetime / startLifetimeArr[index];
2621
- _modifierParams.particleIndex = index;
2622
- applyModifiers(_modifierParams);
2623
3023
  }
2624
3024
  }
3025
+ if (positionNeedsUpdate) ma.position.needsUpdate = true;
3026
+ if (scalarNeedsUpdate) props.scalarInterleavedBuffer.needsUpdate = true;
2625
3027
  }
2626
- if (positionNeedsUpdate) ma.position.needsUpdate = true;
2627
- if (lifetimeNeedsUpdate) ma.lifetime.needsUpdate = true;
2628
3028
  if (isEnabled && (looping || lifetime < duration * 1e3)) {
2629
3029
  const emissionDelta = now - lastEmissionTime;
2630
3030
  const neededParticlesByTime = emission.rateOverTime ? Math.floor(
@@ -2841,12 +3241,8 @@ var updateTrailGeometry = (props, now) => {
2841
3241
  const subdivisions = trailConfig.smoothingSubdivisions;
2842
3242
  const useTwistPrevention = trailConfig.twistPrevention;
2843
3243
  const ribbonId = trailConfig.ribbonId;
2844
- const isActiveArr = ma.isActive.array;
3244
+ const trailScalarArr = props.scalarArray;
2845
3245
  const positionArr = ma.position.array;
2846
- const colorRArr = ma.colorR.array;
2847
- const colorGArr = ma.colorG.array;
2848
- const colorBArr = ma.colorB.array;
2849
- const colorAArr = ma.colorA.array;
2850
3246
  const trailPosArr = trailPositionAttr.array;
2851
3247
  const trailAlphaArr = trailAlphaAttr.array;
2852
3248
  const trailColorArr = trailColorAttr.array;
@@ -2865,7 +3261,8 @@ var updateTrailGeometry = (props, now) => {
2865
3261
  }
2866
3262
  _ribbonCount = 0;
2867
3263
  for (let i = 0; i < creationTimesLength; i++) {
2868
- if (isActiveArr[i]) _ribbonIndices[_ribbonCount++] = i;
3264
+ if (trailScalarArr[i * SCALAR_STRIDE + S_IS_ACTIVE])
3265
+ _ribbonIndices[_ribbonCount++] = i;
2869
3266
  }
2870
3267
  for (let i = 1; i < _ribbonCount; i++) {
2871
3268
  const key = _ribbonIndices[i];
@@ -2881,7 +3278,7 @@ var updateTrailGeometry = (props, now) => {
2881
3278
  }
2882
3279
  for (let index = 0; index < creationTimesLength; index++) {
2883
3280
  const vertBase = index * verticesPerParticle;
2884
- if (isActiveArr[index]) {
3281
+ if (trailScalarArr[index * SCALAR_STRIDE + S_IS_ACTIVE]) {
2885
3282
  if (useRibbon && _ribbonCount >= 2 && index !== ribbonLeader) {
2886
3283
  const posIdx2 = index * 3;
2887
3284
  const px2 = positionArr[posIdx2];
@@ -2947,10 +3344,11 @@ var updateTrailGeometry = (props, now) => {
2947
3344
  }
2948
3345
  const count = effectiveCount;
2949
3346
  const ribbonWidth = trailConfig.width;
2950
- const cr = colorRArr[index];
2951
- const cg = colorGArr[index];
2952
- const cb = colorBArr[index];
2953
- const ca = colorAArr[index];
3347
+ const trailBase = index * SCALAR_STRIDE;
3348
+ const cr = trailScalarArr[trailBase + S_COLOR_R];
3349
+ const cg = trailScalarArr[trailBase + S_COLOR_G];
3350
+ const cb = trailScalarArr[trailBase + S_COLOR_B];
3351
+ const ca = trailScalarArr[trailBase + S_COLOR_A];
2954
3352
  const ringOff = index * trailLength * 3;
2955
3353
  const rawPtsSize = count * 3;
2956
3354
  if (!_rawPoints || _rawPointsSize < rawPtsSize) {
@@ -3290,10 +3688,11 @@ var updateTrailGeometry = (props, now) => {
3290
3688
  _rawPoints[(wi - 1) * 3 + 2] = positionArr[lastPIdx + 2];
3291
3689
  }
3292
3690
  }
3293
- const leaderCr = colorRArr[leader];
3294
- const leaderCg = colorGArr[leader];
3295
- const leaderCb = colorBArr[leader];
3296
- const leaderCa = colorAArr[leader];
3691
+ const leaderBase = leader * SCALAR_STRIDE;
3692
+ const leaderCr = trailScalarArr[leaderBase + S_COLOR_R];
3693
+ const leaderCg = trailScalarArr[leaderBase + S_COLOR_G];
3694
+ const leaderCb = trailScalarArr[leaderBase + S_COLOR_B];
3695
+ const leaderCa = trailScalarArr[leaderBase + S_COLOR_A];
3297
3696
  for (let s = 0; s < trailLength; s++) {
3298
3697
  const vIdx = (leaderVertBase + s * 2) * 3;
3299
3698
  const cIdx = (leaderVertBase + s * 2) * 4;
@@ -3493,6 +3892,253 @@ var updateParticleSystems = (cycleData) => {
3493
3892
  );
3494
3893
  };
3495
3894
 
3496
- export { CurveFunctionId, EmitFrom, ForceFieldFalloff, ForceFieldType, LifeTimeCurve, RendererType, Shape, SimulationSpace, SubEmitterTrigger, TimeMode, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultMeshTexture, createDefaultParticleTexture, createParticleSystem, curveFunctionIdMap, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
3895
+ // src/js/effects/three-particles/three-particles-serialization.ts
3896
+ var SERIALIZATION_VERSION = 1;
3897
+ var reverseBlendingMap = new Map(
3898
+ Object.entries(blendingMap).map(([k, v]) => [
3899
+ v,
3900
+ k
3901
+ ])
3902
+ );
3903
+ var reverseCurveFunctionMap = /* @__PURE__ */ new Map();
3904
+ for (const [id, fn] of Object.entries(curveFunctionIdMap)) {
3905
+ if (fn) reverseCurveFunctionMap.set(fn, id);
3906
+ }
3907
+ function serializeAny(value, key) {
3908
+ if (value === null || value === void 0) return value;
3909
+ if (value instanceof THREE5.Vector3)
3910
+ return { x: value.x, y: value.y, z: value.z };
3911
+ if (value instanceof THREE5.Vector2) return { x: value.x, y: value.y };
3912
+ if (value instanceof THREE5.Texture) return void 0;
3913
+ if (typeof value === "function") return void 0;
3914
+ if (Array.isArray(value)) return value.map((item) => serializeAny(item));
3915
+ if (typeof value === "object") {
3916
+ const obj = value;
3917
+ if (obj["type"] === "EASING" /* EASING */ && typeof obj["curveFunction"] === "function") {
3918
+ const id = reverseCurveFunctionMap.get(
3919
+ obj["curveFunction"]
3920
+ );
3921
+ if (!id) {
3922
+ throw new Error(
3923
+ "Cannot serialize a custom curveFunction. Use a predefined CurveFunctionId instead."
3924
+ );
3925
+ }
3926
+ return {
3927
+ type: "EASING" /* EASING */,
3928
+ curveFunctionId: id,
3929
+ ...obj["scale"] !== void 0 ? { scale: obj["scale"] } : {}
3930
+ };
3931
+ }
3932
+ const result = {};
3933
+ for (const [k, v] of Object.entries(obj)) {
3934
+ if (k === "onUpdate" || k === "onComplete") continue;
3935
+ if (k === "blending" && typeof v === "number") {
3936
+ result[k] = reverseBlendingMap.get(v) ?? v;
3937
+ continue;
3938
+ }
3939
+ const serialized = serializeAny(v);
3940
+ if (serialized !== void 0) result[k] = serialized;
3941
+ }
3942
+ return result;
3943
+ }
3944
+ return value;
3945
+ }
3946
+ function serializeParticleSystem(config) {
3947
+ const serialized = serializeAny(config);
3948
+ return JSON.stringify({ _version: SERIALIZATION_VERSION, ...serialized });
3949
+ }
3950
+ function deserializeCurve(raw) {
3951
+ const obj = raw;
3952
+ if (Array.isArray(obj["bezierPoints"]) && !obj["type"]) {
3953
+ return { type: "BEZIER" /* BEZIER */, ...obj };
3954
+ }
3955
+ if (obj["type"] === "EASING" /* EASING */) {
3956
+ const id = obj["curveFunctionId"];
3957
+ const fn = getCurveFunction(id);
3958
+ if (!fn) {
3959
+ throw new Error(
3960
+ `Unknown curveFunctionId: "${id}". Use a value from CurveFunctionId.`
3961
+ );
3962
+ }
3963
+ const curve = {
3964
+ type: "EASING" /* EASING */,
3965
+ curveFunction: fn
3966
+ };
3967
+ if (obj["scale"] !== void 0) curve.scale = obj["scale"];
3968
+ return curve;
3969
+ }
3970
+ return obj;
3971
+ }
3972
+ function deserializeCurveOrValue(value) {
3973
+ if (typeof value === "number") return value;
3974
+ if (!value || typeof value !== "object") return value;
3975
+ const obj = value;
3976
+ const looksLikeCurve = Array.isArray(obj["bezierPoints"]) || obj["type"] === "BEZIER" /* BEZIER */ || obj["type"] === "EASING" /* EASING */ || typeof obj["curveFunctionId"] === "string";
3977
+ if (looksLikeCurve) return deserializeCurve(obj);
3978
+ return obj;
3979
+ }
3980
+ function deserializeVector3(raw) {
3981
+ if (!raw || typeof raw !== "object") return void 0;
3982
+ const { x = 0, y = 0, z = 0 } = raw;
3983
+ return new THREE5.Vector3(x, y, z);
3984
+ }
3985
+ function deserializeVector2(raw) {
3986
+ if (!raw || typeof raw !== "object") return void 0;
3987
+ const { x = 1, y = 1 } = raw;
3988
+ return new THREE5.Vector2(x, y);
3989
+ }
3990
+ function deserializeConfig(raw) {
3991
+ const config = {};
3992
+ if (raw["transform"] && typeof raw["transform"] === "object") {
3993
+ const t = raw["transform"];
3994
+ config.transform = {
3995
+ position: deserializeVector3(t["position"]),
3996
+ rotation: deserializeVector3(t["rotation"]),
3997
+ scale: deserializeVector3(t["scale"])
3998
+ };
3999
+ }
4000
+ for (const field of [
4001
+ "duration",
4002
+ "looping",
4003
+ "gravity",
4004
+ "simulationSpace",
4005
+ "simulationBackend",
4006
+ "maxParticles"
4007
+ ]) {
4008
+ if (field in raw) config[field] = raw[field];
4009
+ }
4010
+ for (const field of [
4011
+ "startDelay",
4012
+ "startLifetime",
4013
+ "startSpeed",
4014
+ "startSize",
4015
+ "startOpacity",
4016
+ "startRotation"
4017
+ ]) {
4018
+ if (field in raw)
4019
+ config[field] = deserializeCurveOrValue(raw[field]);
4020
+ }
4021
+ if ("startColor" in raw)
4022
+ config.startColor = raw["startColor"];
4023
+ if (raw["emission"] && typeof raw["emission"] === "object") {
4024
+ const e = raw["emission"];
4025
+ config.emission = {
4026
+ rateOverTime: deserializeCurveOrValue(e["rateOverTime"]),
4027
+ rateOverDistance: deserializeCurveOrValue(
4028
+ e["rateOverDistance"]
4029
+ ),
4030
+ bursts: Array.isArray(e["bursts"]) ? e["bursts"] : []
4031
+ };
4032
+ }
4033
+ if ("shape" in raw)
4034
+ config.shape = raw["shape"];
4035
+ if (raw["renderer"] && typeof raw["renderer"] === "object") {
4036
+ const r = raw["renderer"];
4037
+ const blending = typeof r["blending"] === "string" ? blendingMap[r["blending"]] ?? THREE5.NormalBlending : r["blending"] ?? THREE5.NormalBlending;
4038
+ config.renderer = { ...r, blending };
4039
+ }
4040
+ if (raw["velocityOverLifetime"] && typeof raw["velocityOverLifetime"] === "object") {
4041
+ const vol = raw["velocityOverLifetime"];
4042
+ const deserializeAxis = (axis) => {
4043
+ if (!axis || typeof axis !== "object") return {};
4044
+ const a = axis;
4045
+ return {
4046
+ ...a["x"] !== void 0 ? { x: deserializeCurveOrValue(a["x"]) } : {},
4047
+ ...a["y"] !== void 0 ? { y: deserializeCurveOrValue(a["y"]) } : {},
4048
+ ...a["z"] !== void 0 ? { z: deserializeCurveOrValue(a["z"]) } : {}
4049
+ };
4050
+ };
4051
+ config.velocityOverLifetime = {
4052
+ isActive: vol["isActive"] ?? false,
4053
+ linear: deserializeAxis(vol["linear"]),
4054
+ orbital: deserializeAxis(vol["orbital"])
4055
+ };
4056
+ }
4057
+ for (const field of ["sizeOverLifetime", "opacityOverLifetime"]) {
4058
+ if (raw[field] && typeof raw[field] === "object") {
4059
+ const m = raw[field];
4060
+ config[field] = {
4061
+ isActive: m["isActive"] ?? false,
4062
+ lifetimeCurve: deserializeCurve(m["lifetimeCurve"])
4063
+ };
4064
+ }
4065
+ }
4066
+ if (raw["colorOverLifetime"] && typeof raw["colorOverLifetime"] === "object") {
4067
+ const col = raw["colorOverLifetime"];
4068
+ config.colorOverLifetime = {
4069
+ isActive: col["isActive"] ?? true,
4070
+ r: deserializeCurve(col["r"]),
4071
+ g: deserializeCurve(col["g"]),
4072
+ b: deserializeCurve(col["b"])
4073
+ };
4074
+ }
4075
+ if (raw["rotationOverLifetime"] && typeof raw["rotationOverLifetime"] === "object") {
4076
+ config.rotationOverLifetime = raw["rotationOverLifetime"];
4077
+ }
4078
+ if (raw["noise"] && typeof raw["noise"] === "object") {
4079
+ config.noise = raw["noise"];
4080
+ }
4081
+ if (raw["textureSheetAnimation"] && typeof raw["textureSheetAnimation"] === "object") {
4082
+ const tsa = raw["textureSheetAnimation"];
4083
+ config.textureSheetAnimation = {
4084
+ ...tsa,
4085
+ tiles: deserializeVector2(tsa["tiles"]),
4086
+ startFrame: deserializeCurveOrValue(tsa["startFrame"])
4087
+ };
4088
+ }
4089
+ if (Array.isArray(raw["subEmitters"])) {
4090
+ config.subEmitters = raw["subEmitters"].map((se) => ({
4091
+ ...se,
4092
+ config: deserializeConfig(se["config"])
4093
+ }));
4094
+ }
4095
+ if (Array.isArray(raw["forceFields"])) {
4096
+ config.forceFields = raw["forceFields"].map((ff) => {
4097
+ const result = {};
4098
+ if ("isActive" in ff) result.isActive = ff["isActive"];
4099
+ if ("type" in ff) result.type = ff["type"];
4100
+ if (ff["position"]) result.position = deserializeVector3(ff["position"]);
4101
+ if (ff["direction"])
4102
+ result.direction = deserializeVector3(ff["direction"]);
4103
+ if ("strength" in ff)
4104
+ result.strength = deserializeCurveOrValue(
4105
+ ff["strength"]
4106
+ );
4107
+ if ("range" in ff)
4108
+ result.range = ff["range"] === null ? Infinity : ff["range"];
4109
+ if ("falloff" in ff) result.falloff = ff["falloff"];
4110
+ return result;
4111
+ });
4112
+ }
4113
+ if (Array.isArray(raw["collisionPlanes"])) {
4114
+ config.collisionPlanes = raw["collisionPlanes"].map(
4115
+ (cp) => {
4116
+ const result = {};
4117
+ if ("isActive" in cp) result.isActive = cp["isActive"];
4118
+ if ("mode" in cp) result.mode = cp["mode"];
4119
+ if (cp["position"])
4120
+ result.position = deserializeVector3(cp["position"]);
4121
+ if (cp["normal"]) result.normal = deserializeVector3(cp["normal"]);
4122
+ if ("dampen" in cp) result.dampen = cp["dampen"];
4123
+ if ("lifetimeLoss" in cp)
4124
+ result.lifetimeLoss = cp["lifetimeLoss"];
4125
+ return result;
4126
+ }
4127
+ );
4128
+ }
4129
+ for (const key of Object.keys(raw)) {
4130
+ if (!(key in config) && key !== "_version" && raw[key] !== null) {
4131
+ config[key] = raw[key];
4132
+ }
4133
+ }
4134
+ return config;
4135
+ }
4136
+ function deserializeParticleSystem(json) {
4137
+ const parsed = JSON.parse(json);
4138
+ const { _version: _, ...raw } = parsed;
4139
+ return deserializeConfig(raw);
4140
+ }
4141
+
4142
+ export { CollisionPlaneMode, CurveFunctionId, EmitFrom, ForceFieldFalloff, ForceFieldType, LifeTimeCurve, RendererType, SCALAR_STRIDE, S_COLOR_A, S_COLOR_B, S_COLOR_G, S_COLOR_R, S_IS_ACTIVE, S_LIFETIME, S_ROTATION, S_SIZE, S_START_FRAME, S_START_LIFETIME, Shape, SimulationBackend, SimulationSpace, SubEmitterTrigger, TimeMode, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultMeshTexture, createDefaultParticleTexture, createParticleSystem, curveFunctionIdMap, deserializeParticleSystem, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isComputeCapableRenderer, isLifeTimeCurve, registerTSLMaterialFactory, removeBezierCurveFunction, resolveSimulationBackend, serializeParticleSystem, updateParticleSystems };
3497
4143
  //# sourceMappingURL=index.js.map
3498
4144
  //# sourceMappingURL=index.js.map