@newkrok/three-particles 2.11.1 → 2.12.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
@@ -173,6 +173,7 @@ var ForceFieldType = /* @__PURE__ */ ((ForceFieldType2) => {
173
173
  var RendererType = /* @__PURE__ */ ((RendererType2) => {
174
174
  RendererType2["POINTS"] = "POINTS";
175
175
  RendererType2["INSTANCED"] = "INSTANCED";
176
+ RendererType2["TRAIL"] = "TRAIL";
176
177
  return RendererType2;
177
178
  })(RendererType || {});
178
179
  var ForceFieldFalloff = /* @__PURE__ */ ((ForceFieldFalloff2) => {
@@ -740,6 +741,115 @@ var ParticleSystemVertexShader = `
740
741
  }
741
742
  `;
742
743
  var particle_system_vertex_shader_glsl_default = ParticleSystemVertexShader;
744
+
745
+ // src/js/effects/three-particles/shaders/trail-fragment-shader.glsl.ts
746
+ var TrailFragmentShader = `
747
+ uniform sampler2D map;
748
+ uniform bool useMap;
749
+ uniform bool discardBackgroundColor;
750
+ uniform vec3 backgroundColor;
751
+ uniform float backgroundColorTolerance;
752
+
753
+ varying float vAlpha;
754
+ varying vec4 vColor;
755
+ varying vec2 vUv;
756
+
757
+ #include <common>
758
+ #include <logdepthbuf_pars_fragment>
759
+
760
+ void main()
761
+ {
762
+ // Soft edge: always fade near ribbon edges
763
+ float edgeDist = 1.0 - abs(vUv.x * 2.0 - 1.0);
764
+ float softEdge = smoothstep(0.0, 0.4, edgeDist);
765
+
766
+ gl_FragColor = vColor;
767
+
768
+ if (useMap) {
769
+ // Use texture luminance as brightness modulation on top of soft edge
770
+ vec4 texColor = texture2D(map, vUv);
771
+ float texBrightness = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
772
+ gl_FragColor.rgb *= (0.5 + texBrightness * 0.5);
773
+ gl_FragColor.a *= texColor.a;
774
+ }
775
+
776
+ gl_FragColor.a *= vAlpha * softEdge;
777
+
778
+ if (gl_FragColor.a < 0.001) discard;
779
+
780
+ if (discardBackgroundColor && abs(length(gl_FragColor.rgb - backgroundColor.rgb)) < backgroundColorTolerance) discard;
781
+
782
+ #include <logdepthbuf_fragment>
783
+ }
784
+ `;
785
+ var trail_fragment_shader_glsl_default = TrailFragmentShader;
786
+
787
+ // src/js/effects/three-particles/shaders/trail-vertex-shader.glsl.ts
788
+ var TrailVertexShader = `
789
+ attribute float trailAlpha;
790
+ attribute vec4 trailColor;
791
+ attribute float trailOffset;
792
+ attribute float trailHalfWidth;
793
+ attribute vec3 trailNext;
794
+ attribute vec2 trailUV;
795
+
796
+ varying float vAlpha;
797
+ varying vec4 vColor;
798
+ varying vec2 vUv;
799
+
800
+ #include <common>
801
+ #include <logdepthbuf_pars_vertex>
802
+
803
+ void main()
804
+ {
805
+ vAlpha = trailAlpha;
806
+ vColor = trailColor;
807
+ vUv = trailUV;
808
+
809
+ // Compute tangent from current position to next sample
810
+ vec3 tangent = trailNext - position;
811
+ float tangentLen = length(tangent);
812
+ if (tangentLen < 0.0001) {
813
+ tangent = vec3(0.0, 1.0, 0.0);
814
+ } else {
815
+ tangent = tangent / tangentLen;
816
+ }
817
+
818
+ // Billboard: perpendicular = cross(tangent, viewDirection)
819
+ vec3 worldPos = (modelMatrix * vec4(position, 1.0)).xyz;
820
+ vec3 viewDir = normalize(cameraPosition - worldPos);
821
+ vec3 perp = cross(tangent, viewDir);
822
+ float perpLen = length(perp);
823
+
824
+ // When tangent is nearly parallel to view direction, the cross product
825
+ // collapses and the ribbon becomes edge-on (invisible). Use a secondary
826
+ // perpendicular from cross(tangent, up) and blend it in to guarantee
827
+ // a minimum visible width.
828
+ vec3 upPerp = cross(tangent, vec3(0.0, 1.0, 0.0));
829
+ float upPerpLen = length(upPerp);
830
+ if (upPerpLen < 0.0001) {
831
+ upPerp = cross(tangent, vec3(1.0, 0.0, 0.0));
832
+ upPerpLen = length(upPerp);
833
+ }
834
+ upPerp = upPerp / max(upPerpLen, 0.0001);
835
+
836
+ if (perpLen < 0.0001) {
837
+ perp = upPerp;
838
+ } else {
839
+ perp = perp / perpLen;
840
+ // Blend in the secondary perp when billboard perp gets weak
841
+ float blendFactor = smoothstep(0.0, 0.5, perpLen);
842
+ perp = normalize(mix(upPerp, perp, blendFactor));
843
+ }
844
+
845
+ vec3 offsetPos = position + perp * trailOffset * trailHalfWidth;
846
+ vec4 mvPosition = modelViewMatrix * vec4(offsetPos, 1.0);
847
+ gl_Position = projectionMatrix * mvPosition;
848
+
849
+ #include <logdepthbuf_vertex>
850
+ }
851
+ `;
852
+ var trail_vertex_shader_glsl_default = TrailVertexShader;
743
853
  var _forceDirection = new THREE4.Vector3();
744
854
  var applyPointForce = (field, strength, velocity, positionArr, positionIndex, delta) => {
745
855
  _forceDirection.set(
@@ -811,10 +921,21 @@ var applyForceFields = ({
811
921
  };
812
922
 
813
923
  // src/js/effects/three-particles/three-particles.ts
924
+ var normalizeTrailCurve = (curve, defaultCurve) => {
925
+ if (!curve) return defaultCurve;
926
+ const raw = curve;
927
+ if (!raw.type && Array.isArray(raw.bezierPoints)) {
928
+ return { type: "BEZIER" /* BEZIER */, ...raw };
929
+ }
930
+ return curve;
931
+ };
814
932
  var _particleSystemId = 0;
815
933
  var createdParticleSystems = [];
816
934
  var _subEmitterPosition = new THREE4.Vector3();
817
935
  var _lastWorldPositionSnapshot = new THREE4.Vector3();
936
+ new THREE4.Vector3();
937
+ new THREE4.Vector3();
938
+ new THREE4.Vector3();
818
939
  var _distanceStep = { x: 0, y: 0, z: 0 };
819
940
  var _tempPosition = { x: 0, y: 0, z: 0 };
820
941
  var _modifierParams = {
@@ -1059,12 +1180,20 @@ var destroyParticleSystem = (particleSystem) => {
1059
1180
  ({
1060
1181
  particleSystem: savedParticleSystem,
1061
1182
  wrapper,
1183
+ trailMesh,
1062
1184
  generalData: { particleSystemId }
1063
1185
  }) => {
1064
1186
  if (savedParticleSystem !== particleSystem && wrapper !== particleSystem) {
1065
1187
  return true;
1066
1188
  }
1067
1189
  removeBezierCurveFunction(particleSystemId);
1190
+ if (trailMesh) {
1191
+ trailMesh.geometry.dispose();
1192
+ if (Array.isArray(trailMesh.material))
1193
+ trailMesh.material.forEach((m) => m.dispose());
1194
+ else trailMesh.material.dispose();
1195
+ if (trailMesh.parent) trailMesh.parent.remove(trailMesh);
1196
+ }
1068
1197
  savedParticleSystem.geometry.dispose();
1069
1198
  if (Array.isArray(savedParticleSystem.material))
1070
1199
  savedParticleSystem.material.forEach((material) => material.dispose());
@@ -1294,7 +1423,38 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1294
1423
  probabilityPassed: false
1295
1424
  }));
1296
1425
  }
1297
- const useInstancing = renderer.rendererType === "INSTANCED" /* INSTANCED */;
1426
+ const useTrail = renderer.rendererType === "TRAIL" /* TRAIL */;
1427
+ const useInstancing = !useTrail && renderer.rendererType === "INSTANCED" /* INSTANCED */;
1428
+ const defaultTrailCurve = {
1429
+ type: "BEZIER" /* BEZIER */,
1430
+ scale: 1,
1431
+ bezierPoints: [
1432
+ { x: 0, y: 1, percentage: 0 },
1433
+ { x: 1, y: 0, percentage: 1 }
1434
+ ]
1435
+ };
1436
+ const trailConfig = useTrail ? {
1437
+ length: renderer.trail?.length ?? 20,
1438
+ width: renderer.trail?.width ?? 1,
1439
+ widthOverTrail: normalizeTrailCurve(
1440
+ renderer.trail?.widthOverTrail,
1441
+ defaultTrailCurve
1442
+ ),
1443
+ opacityOverTrail: normalizeTrailCurve(
1444
+ renderer.trail?.opacityOverTrail,
1445
+ defaultTrailCurve
1446
+ ),
1447
+ colorOverTrail: renderer.trail?.colorOverTrail
1448
+ } : void 0;
1449
+ if (useTrail && trailConfig) {
1450
+ const trailLength = trailConfig.length;
1451
+ generalData.trailLength = trailLength;
1452
+ generalData.positionHistory = new Float32Array(
1453
+ maxParticles * trailLength * 3
1454
+ );
1455
+ generalData.positionHistoryIndex = new Uint16Array(maxParticles);
1456
+ generalData.positionHistoryCount = new Uint16Array(maxParticles);
1457
+ }
1298
1458
  const attr = (name) => useInstancing ? `instance${name.charAt(0).toUpperCase()}${name.slice(1)}` : name;
1299
1459
  const posAttr = useInstancing ? "instanceOffset" : "position";
1300
1460
  const sharedUniforms = {
@@ -1463,6 +1623,10 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1463
1623
  }) => {
1464
1624
  aIsActive.array[particleIndex] = 1;
1465
1625
  generalData.creationTimes[particleIndex] = activationTime;
1626
+ if (generalData.positionHistoryCount) {
1627
+ generalData.positionHistoryCount[particleIndex] = 0;
1628
+ generalData.positionHistoryIndex[particleIndex] = 0;
1629
+ }
1466
1630
  if (generalData.noise.offsets)
1467
1631
  generalData.noise.offsets[particleIndex] = Math.random() * 100;
1468
1632
  const colorRandomRatio2 = Math.random();
@@ -1645,6 +1809,120 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1645
1809
  instances.push(subSystem);
1646
1810
  }
1647
1811
  };
1812
+ let trailMesh;
1813
+ let trailGeometry;
1814
+ let trailPositionAttr;
1815
+ let trailAlphaAttr;
1816
+ let trailColorAttr;
1817
+ let trailIndexAttr;
1818
+ let trailWidthCurveFn;
1819
+ let trailOpacityCurveFn;
1820
+ let trailColorOverTrailFns;
1821
+ if (useTrail && trailConfig) {
1822
+ const trailLength = trailConfig.length;
1823
+ const verticesPerParticle = trailLength * 2;
1824
+ const totalVertices = maxParticles * verticesPerParticle;
1825
+ const indicesPerParticle = (trailLength - 1) * 6;
1826
+ const totalIndices = maxParticles * indicesPerParticle;
1827
+ trailGeometry = new THREE4.BufferGeometry();
1828
+ const trailPositions = new Float32Array(totalVertices * 3);
1829
+ const trailNextPositions = new Float32Array(totalVertices * 3);
1830
+ const trailAlphas = new Float32Array(totalVertices);
1831
+ const trailColors = new Float32Array(totalVertices * 4);
1832
+ const trailOffsets = new Float32Array(totalVertices);
1833
+ const trailHalfWidths = new Float32Array(totalVertices);
1834
+ const trailUVs = new Float32Array(totalVertices * 2);
1835
+ const trailIndices = new Uint32Array(totalIndices);
1836
+ for (let p = 0; p < maxParticles; p++) {
1837
+ const vertBase = p * verticesPerParticle;
1838
+ const idxBase = p * indicesPerParticle;
1839
+ for (let s = 0; s < trailLength; s++) {
1840
+ trailOffsets[vertBase + s * 2] = -1;
1841
+ trailOffsets[vertBase + s * 2 + 1] = 1;
1842
+ }
1843
+ for (let s = 0; s < trailLength - 1; s++) {
1844
+ const i = idxBase + s * 6;
1845
+ const v = vertBase + s * 2;
1846
+ trailIndices[i] = v;
1847
+ trailIndices[i + 1] = v + 1;
1848
+ trailIndices[i + 2] = v + 2;
1849
+ trailIndices[i + 3] = v + 1;
1850
+ trailIndices[i + 4] = v + 3;
1851
+ trailIndices[i + 5] = v + 2;
1852
+ }
1853
+ }
1854
+ trailPositionAttr = new THREE4.BufferAttribute(trailPositions, 3);
1855
+ trailPositionAttr.setUsage(THREE4.DynamicDrawUsage);
1856
+ const trailNextAttr = new THREE4.BufferAttribute(trailNextPositions, 3);
1857
+ trailNextAttr.setUsage(THREE4.DynamicDrawUsage);
1858
+ trailAlphaAttr = new THREE4.BufferAttribute(trailAlphas, 1);
1859
+ trailAlphaAttr.setUsage(THREE4.DynamicDrawUsage);
1860
+ trailColorAttr = new THREE4.BufferAttribute(trailColors, 4);
1861
+ trailColorAttr.setUsage(THREE4.DynamicDrawUsage);
1862
+ const trailHalfWidthAttr = new THREE4.BufferAttribute(trailHalfWidths, 1);
1863
+ trailHalfWidthAttr.setUsage(THREE4.DynamicDrawUsage);
1864
+ const trailUVAttr = new THREE4.BufferAttribute(trailUVs, 2);
1865
+ trailUVAttr.setUsage(THREE4.DynamicDrawUsage);
1866
+ trailIndexAttr = new THREE4.BufferAttribute(trailIndices, 1);
1867
+ trailGeometry.setAttribute("position", trailPositionAttr);
1868
+ trailGeometry.setAttribute("trailNext", trailNextAttr);
1869
+ trailGeometry.setAttribute("trailAlpha", trailAlphaAttr);
1870
+ trailGeometry.setAttribute("trailColor", trailColorAttr);
1871
+ trailGeometry.setAttribute(
1872
+ "trailOffset",
1873
+ new THREE4.BufferAttribute(trailOffsets, 1)
1874
+ );
1875
+ trailGeometry.setAttribute("trailHalfWidth", trailHalfWidthAttr);
1876
+ trailGeometry.setAttribute("trailUV", trailUVAttr);
1877
+ trailGeometry.setIndex(trailIndexAttr);
1878
+ const trailMaterial = new THREE4.ShaderMaterial({
1879
+ uniforms: {
1880
+ map: { value: particleMap },
1881
+ useMap: { value: !!particleMap },
1882
+ discardBackgroundColor: { value: renderer.discardBackgroundColor },
1883
+ backgroundColor: { value: renderer.backgroundColor },
1884
+ backgroundColorTolerance: { value: renderer.backgroundColorTolerance }
1885
+ },
1886
+ vertexShader: trail_vertex_shader_glsl_default,
1887
+ fragmentShader: trail_fragment_shader_glsl_default,
1888
+ transparent: renderer.transparent,
1889
+ blending: renderer.blending,
1890
+ depthTest: renderer.depthTest,
1891
+ depthWrite: renderer.depthWrite,
1892
+ side: THREE4.DoubleSide
1893
+ });
1894
+ trailMesh = new THREE4.Mesh(trailGeometry, trailMaterial);
1895
+ trailMesh.frustumCulled = false;
1896
+ const trailCameraPos = new THREE4.Vector3();
1897
+ trailMesh.onBeforeRender = (_renderer, _scene, camera) => {
1898
+ camera.getWorldPosition(trailCameraPos);
1899
+ };
1900
+ generalData.trailCameraPosition = trailCameraPos;
1901
+ trailWidthCurveFn = getCurveFunctionFromConfig(
1902
+ generalData.particleSystemId,
1903
+ trailConfig.widthOverTrail
1904
+ );
1905
+ trailOpacityCurveFn = getCurveFunctionFromConfig(
1906
+ generalData.particleSystemId,
1907
+ trailConfig.opacityOverTrail
1908
+ );
1909
+ if (trailConfig.colorOverTrail?.isActive) {
1910
+ trailColorOverTrailFns = {
1911
+ r: getCurveFunctionFromConfig(
1912
+ generalData.particleSystemId,
1913
+ normalizeTrailCurve(trailConfig.colorOverTrail.r, defaultTrailCurve)
1914
+ ),
1915
+ g: getCurveFunctionFromConfig(
1916
+ generalData.particleSystemId,
1917
+ normalizeTrailCurve(trailConfig.colorOverTrail.g, defaultTrailCurve)
1918
+ ),
1919
+ b: getCurveFunctionFromConfig(
1920
+ generalData.particleSystemId,
1921
+ normalizeTrailCurve(trailConfig.colorOverTrail.b, defaultTrailCurve)
1922
+ )
1923
+ };
1924
+ }
1925
+ }
1648
1926
  let particleSystem = useInstancing ? new THREE4.Mesh(geometry, material) : new THREE4.Points(geometry, material);
1649
1927
  if (useInstancing) {
1650
1928
  particleSystem.onBeforeRender = (glRenderer) => {
@@ -1652,6 +1930,10 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1652
1930
  sharedUniforms.viewportHeight.value = size.y * glRenderer.getPixelRatio();
1653
1931
  };
1654
1932
  }
1933
+ if (useTrail && trailMesh) {
1934
+ material.visible = false;
1935
+ particleSystem.add(trailMesh);
1936
+ }
1655
1937
  particleSystem.position.copy(transform.position);
1656
1938
  particleSystem.rotation.x = THREE4.MathUtils.degToRad(transform.rotation.x);
1657
1939
  particleSystem.rotation.y = THREE4.MathUtils.degToRad(transform.rotation.y);
@@ -1729,7 +2011,20 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1729
2011
  deactivateParticle,
1730
2012
  activateParticle,
1731
2013
  onParticleDeath,
1732
- onParticleBirth
2014
+ onParticleBirth,
2015
+ ...useTrail ? {
2016
+ trailMesh,
2017
+ trailPositionAttr,
2018
+ trailAlphaAttr,
2019
+ trailColorAttr,
2020
+ trailWidthCurveFn,
2021
+ trailOpacityCurveFn,
2022
+ trailColorOverTrailFns,
2023
+ trailConfig: {
2024
+ length: trailConfig.length,
2025
+ width: trailConfig.width
2026
+ }
2027
+ } : {}
1733
2028
  };
1734
2029
  createdParticleSystems.push(instanceData);
1735
2030
  const resumeEmitter = () => generalData.isEnabled = true;
@@ -1988,6 +2283,207 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
1988
2283
  onComplete({
1989
2284
  particleSystem
1990
2285
  });
2286
+ if (props.trailMesh) {
2287
+ updateTrailGeometry(props);
2288
+ }
2289
+ };
2290
+ var updateTrailGeometry = (props) => {
2291
+ const {
2292
+ generalData,
2293
+ trailPositionAttr,
2294
+ trailAlphaAttr,
2295
+ trailColorAttr,
2296
+ trailWidthCurveFn,
2297
+ trailOpacityCurveFn,
2298
+ trailColorOverTrailFns,
2299
+ trailConfig,
2300
+ mappedAttributes: ma
2301
+ } = props;
2302
+ if (!trailPositionAttr || !trailAlphaAttr || !trailColorAttr || !trailWidthCurveFn || !trailOpacityCurveFn || !trailConfig || !generalData.positionHistory || !generalData.positionHistoryIndex || !generalData.positionHistoryCount)
2303
+ return;
2304
+ const trailLength = trailConfig.length;
2305
+ const positionHistory = generalData.positionHistory;
2306
+ const historyIndex = generalData.positionHistoryIndex;
2307
+ const historyCount = generalData.positionHistoryCount;
2308
+ const isActiveArr = ma.isActive.array;
2309
+ const positionArr = ma.position.array;
2310
+ const colorRArr = ma.colorR.array;
2311
+ const colorGArr = ma.colorG.array;
2312
+ const colorBArr = ma.colorB.array;
2313
+ const colorAArr = ma.colorA.array;
2314
+ ma.size.array;
2315
+ const trailPosArr = trailPositionAttr.array;
2316
+ const trailAlphaArr = trailAlphaAttr.array;
2317
+ const trailColorArr = trailColorAttr.array;
2318
+ const trailMesh = props.trailMesh;
2319
+ const trailNextArr = trailMesh.geometry.getAttribute("trailNext").array;
2320
+ const trailUVArr = trailMesh.geometry.getAttribute("trailUV").array;
2321
+ const trailHalfWidthArr = trailMesh.geometry.getAttribute("trailHalfWidth").array;
2322
+ const verticesPerParticle = trailLength * 2;
2323
+ const creationTimesLength = generalData.creationTimes.length;
2324
+ let hasUpdates = false;
2325
+ for (let index = 0; index < creationTimesLength; index++) {
2326
+ const vertBase = index * verticesPerParticle;
2327
+ if (isActiveArr[index]) {
2328
+ hasUpdates = true;
2329
+ const posIdx = index * 3;
2330
+ const px = positionArr[posIdx];
2331
+ const py = positionArr[posIdx + 1];
2332
+ const pz = positionArr[posIdx + 2];
2333
+ const histBase = (index * trailLength + historyIndex[index]) * 3;
2334
+ positionHistory[histBase] = px;
2335
+ positionHistory[histBase + 1] = py;
2336
+ positionHistory[histBase + 2] = pz;
2337
+ historyIndex[index] = (historyIndex[index] + 1) % trailLength;
2338
+ if (historyCount[index] < trailLength) historyCount[index]++;
2339
+ const count = historyCount[index];
2340
+ const ribbonWidth = trailConfig.width;
2341
+ const cr = colorRArr[index];
2342
+ const cg = colorGArr[index];
2343
+ const cb = colorBArr[index];
2344
+ const ca = colorAArr[index];
2345
+ const ringOff = index * trailLength * 3;
2346
+ for (let s = 0; s < trailLength; s++) {
2347
+ const vIdx = (vertBase + s * 2) * 3;
2348
+ const cIdx = (vertBase + s * 2) * 4;
2349
+ const aIdx = vertBase + s * 2;
2350
+ if (s >= count) {
2351
+ trailPosArr[vIdx] = px;
2352
+ trailPosArr[vIdx + 1] = py;
2353
+ trailPosArr[vIdx + 2] = pz;
2354
+ trailPosArr[vIdx + 3] = px;
2355
+ trailPosArr[vIdx + 4] = py;
2356
+ trailPosArr[vIdx + 5] = pz;
2357
+ trailNextArr[vIdx] = px;
2358
+ trailNextArr[vIdx + 1] = py;
2359
+ trailNextArr[vIdx + 2] = pz;
2360
+ trailNextArr[vIdx + 3] = px;
2361
+ trailNextArr[vIdx + 4] = py;
2362
+ trailNextArr[vIdx + 5] = pz;
2363
+ trailHalfWidthArr[aIdx] = 0;
2364
+ trailHalfWidthArr[aIdx + 1] = 0;
2365
+ const uvIdx2 = (vertBase + s * 2) * 2;
2366
+ trailUVArr[uvIdx2] = 0;
2367
+ trailUVArr[uvIdx2 + 1] = 0;
2368
+ trailUVArr[uvIdx2 + 2] = 0;
2369
+ trailUVArr[uvIdx2 + 3] = 0;
2370
+ trailAlphaArr[aIdx] = 0;
2371
+ trailAlphaArr[aIdx + 1] = 0;
2372
+ trailColorArr[cIdx] = 0;
2373
+ trailColorArr[cIdx + 1] = 0;
2374
+ trailColorArr[cIdx + 2] = 0;
2375
+ trailColorArr[cIdx + 3] = 0;
2376
+ trailColorArr[cIdx + 4] = 0;
2377
+ trailColorArr[cIdx + 5] = 0;
2378
+ trailColorArr[cIdx + 6] = 0;
2379
+ trailColorArr[cIdx + 7] = 0;
2380
+ continue;
2381
+ }
2382
+ const histIdx = (historyIndex[index] - 1 - s + trailLength * 2) % trailLength * 3 + ringOff;
2383
+ const hx = positionHistory[histIdx];
2384
+ const hy = positionHistory[histIdx + 1];
2385
+ const hz = positionHistory[histIdx + 2];
2386
+ let nx, ny, nz;
2387
+ if (s < count - 1) {
2388
+ const ni = (historyIndex[index] - 2 - s + trailLength * 2) % trailLength * 3 + ringOff;
2389
+ nx = positionHistory[ni];
2390
+ ny = positionHistory[ni + 1];
2391
+ nz = positionHistory[ni + 2];
2392
+ } else {
2393
+ nx = hx;
2394
+ ny = hy + 1e-3;
2395
+ nz = hz;
2396
+ }
2397
+ const t = count > 1 ? s / (count - 1) : 0;
2398
+ const widthScale = trailWidthCurveFn(t);
2399
+ const opacityScale = trailOpacityCurveFn(t);
2400
+ const halfWidth = ribbonWidth * widthScale * 0.5;
2401
+ trailPosArr[vIdx] = hx;
2402
+ trailPosArr[vIdx + 1] = hy;
2403
+ trailPosArr[vIdx + 2] = hz;
2404
+ trailPosArr[vIdx + 3] = hx;
2405
+ trailPosArr[vIdx + 4] = hy;
2406
+ trailPosArr[vIdx + 5] = hz;
2407
+ trailNextArr[vIdx] = nx;
2408
+ trailNextArr[vIdx + 1] = ny;
2409
+ trailNextArr[vIdx + 2] = nz;
2410
+ trailNextArr[vIdx + 3] = nx;
2411
+ trailNextArr[vIdx + 4] = ny;
2412
+ trailNextArr[vIdx + 5] = nz;
2413
+ trailHalfWidthArr[aIdx] = halfWidth;
2414
+ trailHalfWidthArr[aIdx + 1] = halfWidth;
2415
+ const uvIdx = (vertBase + s * 2) * 2;
2416
+ trailUVArr[uvIdx] = 0;
2417
+ trailUVArr[uvIdx + 1] = t;
2418
+ trailUVArr[uvIdx + 2] = 1;
2419
+ trailUVArr[uvIdx + 3] = t;
2420
+ const alpha = ca * opacityScale;
2421
+ trailAlphaArr[aIdx] = alpha;
2422
+ trailAlphaArr[aIdx + 1] = alpha;
2423
+ const fr = trailColorOverTrailFns ? cr * trailColorOverTrailFns.r(t) : cr;
2424
+ const fg = trailColorOverTrailFns ? cg * trailColorOverTrailFns.g(t) : cg;
2425
+ const fb = trailColorOverTrailFns ? cb * trailColorOverTrailFns.b(t) : cb;
2426
+ trailColorArr[cIdx] = fr;
2427
+ trailColorArr[cIdx + 1] = fg;
2428
+ trailColorArr[cIdx + 2] = fb;
2429
+ trailColorArr[cIdx + 3] = ca;
2430
+ trailColorArr[cIdx + 4] = fr;
2431
+ trailColorArr[cIdx + 5] = fg;
2432
+ trailColorArr[cIdx + 6] = fb;
2433
+ trailColorArr[cIdx + 7] = ca;
2434
+ }
2435
+ } else if (historyCount[index] > 0) {
2436
+ hasUpdates = true;
2437
+ historyCount[index] = 0;
2438
+ historyIndex[index] = 0;
2439
+ for (let s = 0; s < trailLength; s++) {
2440
+ const vIdx = (vertBase + s * 2) * 3;
2441
+ const cIdx = (vertBase + s * 2) * 4;
2442
+ const aIdx = vertBase + s * 2;
2443
+ trailPosArr[vIdx] = 0;
2444
+ trailPosArr[vIdx + 1] = 0;
2445
+ trailPosArr[vIdx + 2] = 0;
2446
+ trailPosArr[vIdx + 3] = 0;
2447
+ trailPosArr[vIdx + 4] = 0;
2448
+ trailPosArr[vIdx + 5] = 0;
2449
+ trailNextArr[vIdx] = 0;
2450
+ trailNextArr[vIdx + 1] = 0;
2451
+ trailNextArr[vIdx + 2] = 0;
2452
+ trailNextArr[vIdx + 3] = 0;
2453
+ trailNextArr[vIdx + 4] = 0;
2454
+ trailNextArr[vIdx + 5] = 0;
2455
+ trailHalfWidthArr[aIdx] = 0;
2456
+ trailHalfWidthArr[aIdx + 1] = 0;
2457
+ trailAlphaArr[aIdx] = 0;
2458
+ trailAlphaArr[aIdx + 1] = 0;
2459
+ trailColorArr[cIdx] = 0;
2460
+ trailColorArr[cIdx + 1] = 0;
2461
+ trailColorArr[cIdx + 2] = 0;
2462
+ trailColorArr[cIdx + 3] = 0;
2463
+ trailColorArr[cIdx + 4] = 0;
2464
+ trailColorArr[cIdx + 5] = 0;
2465
+ trailColorArr[cIdx + 6] = 0;
2466
+ trailColorArr[cIdx + 7] = 0;
2467
+ }
2468
+ }
2469
+ }
2470
+ if (hasUpdates) {
2471
+ trailPositionAttr.needsUpdate = true;
2472
+ trailAlphaAttr.needsUpdate = true;
2473
+ trailColorAttr.needsUpdate = true;
2474
+ const nextAttr = trailMesh.geometry.getAttribute(
2475
+ "trailNext"
2476
+ );
2477
+ const hwAttr = trailMesh.geometry.getAttribute(
2478
+ "trailHalfWidth"
2479
+ );
2480
+ nextAttr.needsUpdate = true;
2481
+ hwAttr.needsUpdate = true;
2482
+ const uvAttr = trailMesh.geometry.getAttribute(
2483
+ "trailUV"
2484
+ );
2485
+ uvAttr.needsUpdate = true;
2486
+ }
1991
2487
  };
1992
2488
  var updateParticleSystems = (cycleData) => {
1993
2489
  createdParticleSystems.forEach(