@newkrok/three-particles 2.15.1 → 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 ?? {},
@@ -2152,6 +2352,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2152
2352
  let trailPositionAttr;
2153
2353
  let trailAlphaAttr;
2154
2354
  let trailColorAttr;
2355
+ let trailNextAttr;
2356
+ let trailHalfWidthAttr;
2357
+ let trailUVAttr;
2155
2358
  let trailIndexAttr;
2156
2359
  let trailWidthCurveFn;
2157
2360
  let trailOpacityCurveFn;
@@ -2162,7 +2365,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2162
2365
  const totalVertices = maxParticles * verticesPerParticle;
2163
2366
  const indicesPerParticle = (trailLength - 1) * 6;
2164
2367
  const totalIndices = maxParticles * indicesPerParticle;
2165
- trailGeometry = new THREE4.BufferGeometry();
2368
+ trailGeometry = new THREE5.BufferGeometry();
2166
2369
  const trailPositions = new Float32Array(totalVertices * 3);
2167
2370
  const trailNextPositions = new Float32Array(totalVertices * 3);
2168
2371
  const trailAlphas = new Float32Array(totalVertices);
@@ -2189,63 +2392,63 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2189
2392
  trailIndices[i + 5] = v + 2;
2190
2393
  }
2191
2394
  }
2192
- trailPositionAttr = new THREE4.BufferAttribute(trailPositions, 3);
2193
- trailPositionAttr.setUsage(THREE4.DynamicDrawUsage);
2194
- const trailNextAttr = new THREE4.BufferAttribute(trailNextPositions, 3);
2195
- trailNextAttr.setUsage(THREE4.DynamicDrawUsage);
2196
- trailAlphaAttr = new THREE4.BufferAttribute(trailAlphas, 1);
2197
- trailAlphaAttr.setUsage(THREE4.DynamicDrawUsage);
2198
- trailColorAttr = new THREE4.BufferAttribute(trailColors, 4);
2199
- trailColorAttr.setUsage(THREE4.DynamicDrawUsage);
2200
- const trailHalfWidthAttr = new THREE4.BufferAttribute(trailHalfWidths, 1);
2201
- trailHalfWidthAttr.setUsage(THREE4.DynamicDrawUsage);
2202
- const trailUVAttr = new THREE4.BufferAttribute(trailUVs, 2);
2203
- trailUVAttr.setUsage(THREE4.DynamicDrawUsage);
2204
- 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);
2205
2408
  trailGeometry.setAttribute("position", trailPositionAttr);
2206
2409
  trailGeometry.setAttribute("trailNext", trailNextAttr);
2207
2410
  trailGeometry.setAttribute("trailAlpha", trailAlphaAttr);
2208
2411
  trailGeometry.setAttribute("trailColor", trailColorAttr);
2209
2412
  trailGeometry.setAttribute(
2210
2413
  "trailOffset",
2211
- new THREE4.BufferAttribute(trailOffsets, 1)
2414
+ new THREE5.BufferAttribute(trailOffsets, 1)
2212
2415
  );
2213
2416
  trailGeometry.setAttribute("trailHalfWidth", trailHalfWidthAttr);
2214
2417
  trailGeometry.setAttribute("trailUV", trailUVAttr);
2215
2418
  trailGeometry.setIndex(trailIndexAttr);
2216
- const trailMaterial = new THREE4.ShaderMaterial({
2217
- uniforms: {
2218
- map: { value: particleMap },
2219
- useMap: { value: !!particleMap },
2220
- discardBackgroundColor: { value: renderer.discardBackgroundColor },
2221
- backgroundColor: { value: renderer.backgroundColor },
2222
- backgroundColorTolerance: { value: renderer.backgroundColorTolerance },
2223
- softParticlesEnabled: { value: softParticlesEnabled },
2224
- softParticlesIntensity: {
2225
- value: Math.max(renderer.softParticles?.intensity ?? 1, 1e-3)
2226
- },
2227
- sceneDepthTexture: {
2228
- value: renderer.softParticles?.depthTexture ?? null
2229
- },
2230
- 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
2231
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,
2232
2439
  vertexShader: trail_vertex_shader_glsl_default,
2233
2440
  fragmentShader: trail_fragment_shader_glsl_default,
2234
- transparent: renderer.transparent,
2235
- blending: renderer.blending,
2236
- depthTest: renderer.depthTest,
2237
- depthWrite: renderer.depthWrite,
2238
- side: THREE4.DoubleSide
2441
+ ...rendererConfig,
2442
+ side: THREE5.DoubleSide
2239
2443
  });
2240
- trailMesh = new THREE4.Mesh(trailGeometry, trailMaterial);
2444
+ trailMesh = new THREE5.Mesh(trailGeometry, trailMaterial);
2241
2445
  trailMesh.frustumCulled = false;
2242
- const trailCameraPos = new THREE4.Vector3();
2446
+ const trailCameraPos = new THREE5.Vector3();
2243
2447
  trailMesh.onBeforeRender = (_renderer, _scene, camera) => {
2244
2448
  camera.getWorldPosition(trailCameraPos);
2245
2449
  if (softParticlesEnabled && camera.isPerspectiveCamera) {
2246
2450
  const perspCam = camera;
2247
- const trailUniforms = trailMaterial.uniforms;
2248
- trailUniforms.cameraNearFar.value.set(
2451
+ trailUniformValues.cameraNearFar.value.set(
2249
2452
  perspCam.near,
2250
2453
  perspCam.far
2251
2454
  );
@@ -2277,11 +2480,11 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2277
2480
  };
2278
2481
  }
2279
2482
  }
2280
- let particleSystem = useInstancing || useMesh ? new THREE4.Mesh(geometry, material) : new THREE4.Points(geometry, material);
2281
- if (useInstancing || softParticlesEnabled) {
2483
+ let particleSystem = useInstancing || useMesh ? new THREE5.Mesh(geometry, material) : new THREE5.Points(geometry, material);
2484
+ if (useInstancing || softParticlesEnabled || useGPUCompute) {
2282
2485
  particleSystem.onBeforeRender = (glRenderer, _scene, camera) => {
2283
2486
  if (useInstancing) {
2284
- const size = glRenderer.getSize(new THREE4.Vector2());
2487
+ const size = glRenderer.getSize(new THREE5.Vector2());
2285
2488
  sharedUniforms.viewportHeight.value = size.y * glRenderer.getPixelRatio();
2286
2489
  }
2287
2490
  if (softParticlesEnabled && camera.isPerspectiveCamera) {
@@ -2298,9 +2501,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2298
2501
  particleSystem.add(trailMesh);
2299
2502
  }
2300
2503
  particleSystem.position.copy(transform.position);
2301
- particleSystem.rotation.x = THREE4.MathUtils.degToRad(transform.rotation.x);
2302
- particleSystem.rotation.y = THREE4.MathUtils.degToRad(transform.rotation.y);
2303
- 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);
2304
2507
  particleSystem.scale.copy(transform.scale);
2305
2508
  const mappedAttributes = {
2306
2509
  position: aPosition,
@@ -2310,10 +2513,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2310
2513
  startFrame: aStartFrame,
2311
2514
  size: aSize,
2312
2515
  rotation: aRotation,
2313
- colorR: aColorR,
2314
- colorG: aColorG,
2315
- colorB: aColorB,
2316
- colorA: aColorA,
2516
+ color: aColor,
2317
2517
  ...useMesh ? { quat: aQuat } : {}
2318
2518
  };
2319
2519
  const calculatedCreationTime = now + calculateValue(generalData.particleSystemId, startDelay) * 1e3;
@@ -2331,6 +2531,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2331
2531
  positionArr[posIdx + 1],
2332
2532
  positionArr[posIdx + 2]
2333
2533
  );
2534
+ if (simulationSpace === "LOCAL" /* LOCAL */) {
2535
+ particleSystem.localToWorld(_subEmitterPosition);
2536
+ }
2334
2537
  spawnSubEmitters(
2335
2538
  deathSubEmitters,
2336
2539
  _subEmitterPosition,
@@ -2345,6 +2548,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2345
2548
  positionArr[posIdx + 1],
2346
2549
  positionArr[posIdx + 2]
2347
2550
  );
2551
+ if (simulationSpace === "LOCAL" /* LOCAL */) {
2552
+ particleSystem.localToWorld(_subEmitterPosition);
2553
+ }
2348
2554
  spawnSubEmitters(
2349
2555
  birthSubEmitters,
2350
2556
  _subEmitterPosition,
@@ -2356,7 +2562,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2356
2562
  particleSystem,
2357
2563
  wrapper,
2358
2564
  mappedAttributes,
2359
- elapsedUniform: material.uniforms.elapsed,
2565
+ scalarArray,
2566
+ scalarInterleavedBuffer,
2567
+ elapsedUniform: sharedUniforms.elapsed,
2360
2568
  generalData,
2361
2569
  onUpdate,
2362
2570
  onComplete,
@@ -2367,6 +2575,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2367
2575
  simulationSpace,
2368
2576
  gravity,
2369
2577
  normalizedForceFields,
2578
+ normalizedCollisionPlanes,
2370
2579
  emission,
2371
2580
  normalizedConfig,
2372
2581
  iterationCount: 0,
@@ -2376,11 +2585,17 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2376
2585
  activateParticle,
2377
2586
  onParticleDeath,
2378
2587
  onParticleBirth,
2588
+ useGPUCompute: useGPUCompute && gpuPipeline !== null,
2589
+ computePipeline: gpuPipeline ?? void 0,
2590
+ computeDispatchReady: false,
2379
2591
  ...useTrail ? {
2380
2592
  trailMesh,
2381
2593
  trailPositionAttr,
2382
2594
  trailAlphaAttr,
2383
2595
  trailColorAttr,
2596
+ trailNextAttr,
2597
+ trailHalfWidthAttr,
2598
+ trailUVAttr,
2384
2599
  trailWidthCurveFn,
2385
2600
  trailOpacityCurveFn,
2386
2601
  trailColorOverTrailFns,
@@ -2434,15 +2649,22 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2434
2649
  cfg.forceFields
2435
2650
  );
2436
2651
  }
2652
+ if (partialConfig.collisionPlanes !== void 0) {
2653
+ instanceData.normalizedCollisionPlanes = normalizeCollisionPlanes(
2654
+ cfg.collisionPlanes
2655
+ );
2656
+ }
2437
2657
  if (partialConfig.noise !== void 0) {
2438
2658
  const n = cfg.noise;
2439
2659
  generalData.noise = {
2440
2660
  isActive: n.isActive,
2441
2661
  strength: n.strength,
2442
2662
  noisePower: 0.15 * n.strength,
2663
+ frequency: n.frequency,
2443
2664
  positionAmount: n.positionAmount,
2444
2665
  rotationAmount: n.rotationAmount,
2445
2666
  sizeAmount: n.sizeAmount,
2667
+ fbmMax: 2 - Math.pow(2, -n.octaves),
2446
2668
  sampler: n.isActive ? new FBM({
2447
2669
  seed: Math.random(),
2448
2670
  scale: n.frequency,
@@ -2458,7 +2680,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2458
2680
  pauseEmitter,
2459
2681
  dispose,
2460
2682
  update,
2461
- updateConfig
2683
+ updateConfig,
2684
+ computeNode: gpuPipeline?.computeNode ?? null
2462
2685
  };
2463
2686
  };
2464
2687
  var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
@@ -2483,11 +2706,15 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2483
2706
  simulationSpace,
2484
2707
  gravity,
2485
2708
  normalizedForceFields,
2709
+ normalizedCollisionPlanes,
2486
2710
  onParticleDeath,
2487
2711
  onParticleBirth,
2488
- mappedAttributes: ma
2712
+ mappedAttributes: ma,
2713
+ useGPUCompute,
2714
+ computePipeline
2489
2715
  } = props;
2490
2716
  const hasForceFields = normalizedForceFields.length > 0;
2717
+ const hasCollisionPlanes = normalizedCollisionPlanes.length > 0;
2491
2718
  const lifetime = now - creationTime;
2492
2719
  const normalizedLifetime = lifetime % (duration * 1e3);
2493
2720
  generalData.normalizedLifetimePercentage = Math.max(
@@ -2541,8 +2768,8 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2541
2768
  dst = {
2542
2769
  isActive: true,
2543
2770
  type: "POINT" /* POINT */,
2544
- position: new THREE4.Vector3(),
2545
- direction: new THREE4.Vector3(),
2771
+ position: new THREE5.Vector3(),
2772
+ direction: new THREE5.Vector3(),
2546
2773
  strength: 0,
2547
2774
  range: 0,
2548
2775
  falloff: "LINEAR" /* LINEAR */
@@ -2562,42 +2789,123 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2562
2789
  dst.direction.copy(_localForceFieldDir);
2563
2790
  }
2564
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
+ }
2565
2821
  const creationTimes = generalData.creationTimes;
2566
- const isActiveArr = ma.isActive.array;
2567
- const startLifetimeArr = ma.startLifetime.array;
2822
+ const scalarArr = props.scalarArray;
2568
2823
  const positionArr = ma.position.array;
2569
- const lifetimeArr = ma.lifetime.array;
2570
2824
  const creationTimesLength = creationTimes.length;
2571
- let positionNeedsUpdate = false;
2572
- let lifetimeNeedsUpdate = false;
2573
- _modifierParams.delta = delta;
2574
- _modifierParams.generalData = generalData;
2575
- _modifierParams.normalizedConfig = normalizedConfig;
2576
- _modifierParams.attributes = ma;
2577
- for (let index = 0; index < creationTimesLength; index++) {
2578
- if (isActiveArr[index]) {
2579
- const particleLifetime = now - creationTimes[index];
2580
- if (particleLifetime > startLifetimeArr[index]) {
2581
- if (onParticleDeath)
2582
- onParticleDeath(index, positionArr, velocities[index], now);
2583
- deactivateParticle(index);
2584
- } else {
2585
- const velocity = velocities[index];
2586
- velocity.x -= gravityVelocity.x * delta;
2587
- velocity.y -= gravityVelocity.y * delta;
2588
- velocity.z -= gravityVelocity.z * delta;
2589
- if (hasForceFields) {
2590
- applyForceFields({
2591
- particleSystemId: generalData.particleSystemId,
2592
- forceFields: _localForceFields,
2593
- velocity,
2594
- positionArr,
2595
- positionIndex: index * 3,
2596
- delta,
2597
- systemLifetimePercentage: generalData.normalizedLifetimePercentage
2598
- });
2599
- }
2600
- 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
+ }
2601
2909
  const positionIndex = index * 3;
2602
2910
  if (simulationSpace === "WORLD" /* WORLD */) {
2603
2911
  positionArr[positionIndex] -= worldPositionChange.x;
@@ -2607,18 +2915,116 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2607
2915
  positionArr[positionIndex] += velocity.x * delta;
2608
2916
  positionArr[positionIndex + 1] += velocity.y * delta;
2609
2917
  positionArr[positionIndex + 2] += velocity.z * delta;
2610
- 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);
2611
3022
  }
2612
- lifetimeArr[index] = particleLifetime;
2613
- lifetimeNeedsUpdate = true;
2614
- _modifierParams.particleLifetimePercentage = particleLifetime / startLifetimeArr[index];
2615
- _modifierParams.particleIndex = index;
2616
- applyModifiers(_modifierParams);
2617
3023
  }
2618
3024
  }
3025
+ if (positionNeedsUpdate) ma.position.needsUpdate = true;
3026
+ if (scalarNeedsUpdate) props.scalarInterleavedBuffer.needsUpdate = true;
2619
3027
  }
2620
- if (positionNeedsUpdate) ma.position.needsUpdate = true;
2621
- if (lifetimeNeedsUpdate) ma.lifetime.needsUpdate = true;
2622
3028
  if (isEnabled && (looping || lifetime < duration * 1e3)) {
2623
3029
  const emissionDelta = now - lastEmissionTime;
2624
3030
  const neededParticlesByTime = emission.rateOverTime ? Math.floor(
@@ -2809,13 +3215,16 @@ var updateTrailGeometry = (props, now) => {
2809
3215
  trailPositionAttr,
2810
3216
  trailAlphaAttr,
2811
3217
  trailColorAttr,
3218
+ trailNextAttr: trailNextAttrCached,
3219
+ trailHalfWidthAttr: trailHalfWidthAttrCached,
3220
+ trailUVAttr: trailUVAttrCached,
2812
3221
  trailWidthCurveFn,
2813
3222
  trailOpacityCurveFn,
2814
3223
  trailColorOverTrailFns,
2815
3224
  trailConfig,
2816
3225
  mappedAttributes: ma
2817
3226
  } = props;
2818
- if (!trailPositionAttr || !trailAlphaAttr || !trailColorAttr || !trailWidthCurveFn || !trailOpacityCurveFn || !trailConfig || !generalData.positionHistory || !generalData.positionHistoryIndex || !generalData.positionHistoryCount)
3227
+ if (!trailPositionAttr || !trailAlphaAttr || !trailColorAttr || !trailNextAttrCached || !trailHalfWidthAttrCached || !trailUVAttrCached || !trailWidthCurveFn || !trailOpacityCurveFn || !trailConfig || !generalData.positionHistory || !generalData.positionHistoryIndex || !generalData.positionHistoryCount)
2819
3228
  return;
2820
3229
  const trailLength = trailConfig.length;
2821
3230
  const positionHistory = generalData.positionHistory;
@@ -2832,19 +3241,14 @@ var updateTrailGeometry = (props, now) => {
2832
3241
  const subdivisions = trailConfig.smoothingSubdivisions;
2833
3242
  const useTwistPrevention = trailConfig.twistPrevention;
2834
3243
  const ribbonId = trailConfig.ribbonId;
2835
- const isActiveArr = ma.isActive.array;
3244
+ const trailScalarArr = props.scalarArray;
2836
3245
  const positionArr = ma.position.array;
2837
- const colorRArr = ma.colorR.array;
2838
- const colorGArr = ma.colorG.array;
2839
- const colorBArr = ma.colorB.array;
2840
- const colorAArr = ma.colorA.array;
2841
3246
  const trailPosArr = trailPositionAttr.array;
2842
3247
  const trailAlphaArr = trailAlphaAttr.array;
2843
3248
  const trailColorArr = trailColorAttr.array;
2844
- const trailMesh = props.trailMesh;
2845
- const trailNextArr = trailMesh.geometry.getAttribute("trailNext").array;
2846
- const trailUVArr = trailMesh.geometry.getAttribute("trailUV").array;
2847
- const trailHalfWidthArr = trailMesh.geometry.getAttribute("trailHalfWidth").array;
3249
+ const trailNextArr = trailNextAttrCached.array;
3250
+ const trailUVArr = trailUVAttrCached.array;
3251
+ const trailHalfWidthArr = trailHalfWidthAttrCached.array;
2848
3252
  const verticesPerParticle = trailLength * 2;
2849
3253
  const creationTimesLength = generalData.creationTimes.length;
2850
3254
  let hasUpdates = false;
@@ -2857,7 +3261,8 @@ var updateTrailGeometry = (props, now) => {
2857
3261
  }
2858
3262
  _ribbonCount = 0;
2859
3263
  for (let i = 0; i < creationTimesLength; i++) {
2860
- if (isActiveArr[i]) _ribbonIndices[_ribbonCount++] = i;
3264
+ if (trailScalarArr[i * SCALAR_STRIDE + S_IS_ACTIVE])
3265
+ _ribbonIndices[_ribbonCount++] = i;
2861
3266
  }
2862
3267
  for (let i = 1; i < _ribbonCount; i++) {
2863
3268
  const key = _ribbonIndices[i];
@@ -2873,7 +3278,7 @@ var updateTrailGeometry = (props, now) => {
2873
3278
  }
2874
3279
  for (let index = 0; index < creationTimesLength; index++) {
2875
3280
  const vertBase = index * verticesPerParticle;
2876
- if (isActiveArr[index]) {
3281
+ if (trailScalarArr[index * SCALAR_STRIDE + S_IS_ACTIVE]) {
2877
3282
  if (useRibbon && _ribbonCount >= 2 && index !== ribbonLeader) {
2878
3283
  const posIdx2 = index * 3;
2879
3284
  const px2 = positionArr[posIdx2];
@@ -2939,10 +3344,11 @@ var updateTrailGeometry = (props, now) => {
2939
3344
  }
2940
3345
  const count = effectiveCount;
2941
3346
  const ribbonWidth = trailConfig.width;
2942
- const cr = colorRArr[index];
2943
- const cg = colorGArr[index];
2944
- const cb = colorBArr[index];
2945
- 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];
2946
3352
  const ringOff = index * trailLength * 3;
2947
3353
  const rawPtsSize = count * 3;
2948
3354
  if (!_rawPoints || _rawPointsSize < rawPtsSize) {
@@ -3073,6 +3479,13 @@ var updateTrailGeometry = (props, now) => {
3073
3479
  nx = finalPts[(s + 1) * 3];
3074
3480
  ny = finalPts[(s + 1) * 3 + 1];
3075
3481
  nz = finalPts[(s + 1) * 3 + 2];
3482
+ } else if (finalCount >= 2) {
3483
+ const prevX = finalPts[(s - 1) * 3];
3484
+ const prevY = finalPts[(s - 1) * 3 + 1];
3485
+ const prevZ = finalPts[(s - 1) * 3 + 2];
3486
+ nx = hx + (hx - prevX);
3487
+ ny = hy + (hy - prevY);
3488
+ nz = hz + (hz - prevZ);
3076
3489
  } else {
3077
3490
  nx = hx;
3078
3491
  ny = hy + 1e-3;
@@ -3081,14 +3494,24 @@ var updateTrailGeometry = (props, now) => {
3081
3494
  const t = finalCount > 1 ? s / (finalCount - 1) : 0;
3082
3495
  let timeFade = 1;
3083
3496
  if (maxTime > 0 && sampleTimes && effectiveCount > 0) {
3084
- const rawS = useSmoothing ? Math.min(
3085
- Math.floor(s / Math.max(finalCount - 1, 1) * (rawCount - 1)),
3086
- rawCount - 1
3087
- ) : Math.min(s, rawCount - 1);
3088
- const sampleSlot = (historyIndex[index] - 1 - rawS + trailLength * 2) % trailLength;
3089
3497
  const sampleBase = index * trailLength;
3090
- const age = now - sampleTimes[sampleBase + sampleSlot];
3091
- timeFade = 1 - Math.min(age / maxTimeMs, 1);
3498
+ if (useSmoothing && rawCount >= 2) {
3499
+ const rawF = s / Math.max(finalCount - 1, 1) * (rawCount - 1);
3500
+ const rawLo = Math.min(Math.floor(rawF), rawCount - 1);
3501
+ const rawHi = Math.min(rawLo + 1, rawCount - 1);
3502
+ const frac = rawF - rawLo;
3503
+ const slotLo = (historyIndex[index] - 1 - rawLo + trailLength * 2) % trailLength;
3504
+ const slotHi = (historyIndex[index] - 1 - rawHi + trailLength * 2) % trailLength;
3505
+ const ageLo = now - sampleTimes[sampleBase + slotLo];
3506
+ const ageHi = now - sampleTimes[sampleBase + slotHi];
3507
+ const age = ageLo + (ageHi - ageLo) * frac;
3508
+ timeFade = 1 - Math.min(age / maxTimeMs, 1);
3509
+ } else {
3510
+ const rawS = Math.min(s, rawCount - 1);
3511
+ const sampleSlot = (historyIndex[index] - 1 - rawS + trailLength * 2) % trailLength;
3512
+ const age = now - sampleTimes[sampleBase + sampleSlot];
3513
+ timeFade = 1 - Math.min(age / maxTimeMs, 1);
3514
+ }
3092
3515
  }
3093
3516
  const widthScale = trailWidthCurveFn(t);
3094
3517
  const opacityScale = trailOpacityCurveFn(t);
@@ -3265,10 +3688,11 @@ var updateTrailGeometry = (props, now) => {
3265
3688
  _rawPoints[(wi - 1) * 3 + 2] = positionArr[lastPIdx + 2];
3266
3689
  }
3267
3690
  }
3268
- const leaderCr = colorRArr[leader];
3269
- const leaderCg = colorGArr[leader];
3270
- const leaderCb = colorBArr[leader];
3271
- 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];
3272
3696
  for (let s = 0; s < trailLength; s++) {
3273
3697
  const vIdx = (leaderVertBase + s * 2) * 3;
3274
3698
  const cIdx = (leaderVertBase + s * 2) * 4;
@@ -3321,16 +3745,34 @@ var updateTrailGeometry = (props, now) => {
3321
3745
  nx = _rawPoints[(s + 1) * 3];
3322
3746
  ny = _rawPoints[(s + 1) * 3 + 1];
3323
3747
  nz = _rawPoints[(s + 1) * 3 + 2];
3748
+ } else if (filledCount >= 2) {
3749
+ const prevX = _rawPoints[(s - 1) * 3];
3750
+ const prevY = _rawPoints[(s - 1) * 3 + 1];
3751
+ const prevZ = _rawPoints[(s - 1) * 3 + 2];
3752
+ nx = ptx + (ptx - prevX);
3753
+ ny = pty + (pty - prevY);
3754
+ nz = ptz + (ptz - prevZ);
3324
3755
  } else {
3325
3756
  nx = ptx;
3326
3757
  ny = pty + 1e-3;
3327
3758
  nz = ptz;
3328
3759
  }
3329
3760
  const t = filledCount > 1 ? s / (filledCount - 1) : 0;
3761
+ let ribbonTimeFade = 1;
3762
+ if (maxTime > 0 && controlCount >= 2) {
3763
+ const ctrlF = t * (controlCount - 1);
3764
+ const ctrlLo = Math.min(Math.floor(ctrlF), controlCount - 1);
3765
+ const ctrlHi = Math.min(ctrlLo + 1, controlCount - 1);
3766
+ const frac = ctrlF - ctrlLo;
3767
+ const ageLo = now - generalData.creationTimes[_ribbonIndices[ctrlLo]];
3768
+ const ageHi = now - generalData.creationTimes[_ribbonIndices[ctrlHi]];
3769
+ const age = ageLo + (ageHi - ageLo) * frac;
3770
+ ribbonTimeFade = 1 - Math.min(age / maxTimeMs, 1);
3771
+ }
3330
3772
  const widthScale = trailWidthCurveFn(t);
3331
3773
  const opacityScale = trailOpacityCurveFn(t);
3332
3774
  const halfWidth = trailConfig.width * widthScale * 0.5;
3333
- const alpha = leaderCa * opacityScale;
3775
+ const alpha = leaderCa * opacityScale * ribbonTimeFade;
3334
3776
  const fr = trailColorOverTrailFns ? leaderCr * trailColorOverTrailFns.r(t) : leaderCr;
3335
3777
  const fg = trailColorOverTrailFns ? leaderCg * trailColorOverTrailFns.g(t) : leaderCg;
3336
3778
  const fb = trailColorOverTrailFns ? leaderCb * trailColorOverTrailFns.b(t) : leaderCb;
@@ -3360,6 +3802,55 @@ var updateTrailGeometry = (props, now) => {
3360
3802
  trailColorArr
3361
3803
  );
3362
3804
  }
3805
+ if (useTwistPrevention && prevNormal && filledCount >= 2) {
3806
+ const nIdx = leader * 3;
3807
+ const tx = _rawPoints[3] - _rawPoints[0];
3808
+ const ty = _rawPoints[4] - _rawPoints[1];
3809
+ const tz = _rawPoints[5] - _rawPoints[2];
3810
+ const tLen = Math.sqrt(tx * tx + ty * ty + tz * tz);
3811
+ if (tLen > 1e-4) {
3812
+ const ntx = tx / tLen;
3813
+ const nty = ty / tLen;
3814
+ const ntz = tz / tLen;
3815
+ let upx = 0, upy = 1, upz = 0;
3816
+ const dot = ntx * upx + nty * upy + ntz * upz;
3817
+ if (Math.abs(dot) > 0.999) {
3818
+ upx = 1;
3819
+ upy = 0;
3820
+ upz = 0;
3821
+ }
3822
+ let cnx = nty * upz - ntz * upy;
3823
+ let cny = ntz * upx - ntx * upz;
3824
+ let cnz = ntx * upy - nty * upx;
3825
+ const cnLen = Math.sqrt(cnx * cnx + cny * cny + cnz * cnz);
3826
+ if (cnLen > 1e-4) {
3827
+ cnx /= cnLen;
3828
+ cny /= cnLen;
3829
+ cnz /= cnLen;
3830
+ }
3831
+ const prevNx = prevNormal[nIdx];
3832
+ const prevNy = prevNormal[nIdx + 1];
3833
+ const prevNz = prevNormal[nIdx + 2];
3834
+ const hasPrev = prevNx !== 0 || prevNy !== 0 || prevNz !== 0;
3835
+ if (hasPrev) {
3836
+ const normalDot = cnx * prevNx + cny * prevNy + cnz * prevNz;
3837
+ if (normalDot < 0) {
3838
+ for (let s = 0; s < Math.min(filledCount, trailLength); s++) {
3839
+ const aIdx = leaderVertBase + s * 2;
3840
+ const hw = trailHalfWidthArr[aIdx];
3841
+ trailHalfWidthArr[aIdx] = -hw;
3842
+ trailHalfWidthArr[aIdx + 1] = -hw;
3843
+ }
3844
+ cnx = -cnx;
3845
+ cny = -cny;
3846
+ cnz = -cnz;
3847
+ }
3848
+ }
3849
+ prevNormal[nIdx] = cnx;
3850
+ prevNormal[nIdx + 1] = cny;
3851
+ prevNormal[nIdx + 2] = cnz;
3852
+ }
3853
+ }
3363
3854
  for (let ri = 1; ri < _ribbonCount; ri++) {
3364
3855
  const pIdx = _ribbonIndices[ri];
3365
3856
  const pVertBase = pIdx * verticesPerParticle;
@@ -3390,18 +3881,9 @@ var updateTrailGeometry = (props, now) => {
3390
3881
  trailPositionAttr.needsUpdate = true;
3391
3882
  trailAlphaAttr.needsUpdate = true;
3392
3883
  trailColorAttr.needsUpdate = true;
3393
- const nextAttr = trailMesh.geometry.getAttribute(
3394
- "trailNext"
3395
- );
3396
- const hwAttr = trailMesh.geometry.getAttribute(
3397
- "trailHalfWidth"
3398
- );
3399
- nextAttr.needsUpdate = true;
3400
- hwAttr.needsUpdate = true;
3401
- const uvAttr = trailMesh.geometry.getAttribute(
3402
- "trailUV"
3403
- );
3404
- uvAttr.needsUpdate = true;
3884
+ trailNextAttrCached.needsUpdate = true;
3885
+ trailHalfWidthAttrCached.needsUpdate = true;
3886
+ trailUVAttrCached.needsUpdate = true;
3405
3887
  }
3406
3888
  };
3407
3889
  var updateParticleSystems = (cycleData) => {
@@ -3410,6 +3892,253 @@ var updateParticleSystems = (cycleData) => {
3410
3892
  );
3411
3893
  };
3412
3894
 
3413
- 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 };
3414
4143
  //# sourceMappingURL=index.js.map
3415
4144
  //# sourceMappingURL=index.js.map