@newkrok/three-particles 2.15.0 → 2.15.2

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.d.ts CHANGED
@@ -1801,6 +1801,12 @@ type ParticleSystemInstance = {
1801
1801
  trailAlphaAttr?: THREE.BufferAttribute;
1802
1802
  /** Trail geometry color attribute */
1803
1803
  trailColorAttr?: THREE.BufferAttribute;
1804
+ /** Trail geometry next-position attribute (cached to avoid repeated getAttribute) */
1805
+ trailNextAttr?: THREE.BufferAttribute;
1806
+ /** Trail geometry half-width attribute (cached) */
1807
+ trailHalfWidthAttr?: THREE.BufferAttribute;
1808
+ /** Trail geometry UV attribute (cached) */
1809
+ trailUVAttr?: THREE.BufferAttribute;
1804
1810
  /** Trail width curve function */
1805
1811
  trailWidthCurveFn?: CurveFunction;
1806
1812
  /** Trail opacity curve function */
package/dist/index.js CHANGED
@@ -1171,6 +1171,10 @@ var _particleSystemId = 0;
1171
1171
  var createdParticleSystems = [];
1172
1172
  var _subEmitterPosition = new THREE4.Vector3();
1173
1173
  var _lastWorldPositionSnapshot = new THREE4.Vector3();
1174
+ var _localForceFieldPos = new THREE4.Vector3();
1175
+ var _localForceFieldDir = new THREE4.Vector3();
1176
+ var _inverseQuat = new THREE4.Quaternion();
1177
+ var _localForceFields = [];
1174
1178
  new THREE4.Vector3();
1175
1179
  new THREE4.Vector3();
1176
1180
  new THREE4.Vector3();
@@ -2148,6 +2152,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2148
2152
  let trailPositionAttr;
2149
2153
  let trailAlphaAttr;
2150
2154
  let trailColorAttr;
2155
+ let trailNextAttr;
2156
+ let trailHalfWidthAttr;
2157
+ let trailUVAttr;
2151
2158
  let trailIndexAttr;
2152
2159
  let trailWidthCurveFn;
2153
2160
  let trailOpacityCurveFn;
@@ -2187,15 +2194,15 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2187
2194
  }
2188
2195
  trailPositionAttr = new THREE4.BufferAttribute(trailPositions, 3);
2189
2196
  trailPositionAttr.setUsage(THREE4.DynamicDrawUsage);
2190
- const trailNextAttr = new THREE4.BufferAttribute(trailNextPositions, 3);
2197
+ trailNextAttr = new THREE4.BufferAttribute(trailNextPositions, 3);
2191
2198
  trailNextAttr.setUsage(THREE4.DynamicDrawUsage);
2192
2199
  trailAlphaAttr = new THREE4.BufferAttribute(trailAlphas, 1);
2193
2200
  trailAlphaAttr.setUsage(THREE4.DynamicDrawUsage);
2194
2201
  trailColorAttr = new THREE4.BufferAttribute(trailColors, 4);
2195
2202
  trailColorAttr.setUsage(THREE4.DynamicDrawUsage);
2196
- const trailHalfWidthAttr = new THREE4.BufferAttribute(trailHalfWidths, 1);
2203
+ trailHalfWidthAttr = new THREE4.BufferAttribute(trailHalfWidths, 1);
2197
2204
  trailHalfWidthAttr.setUsage(THREE4.DynamicDrawUsage);
2198
- const trailUVAttr = new THREE4.BufferAttribute(trailUVs, 2);
2205
+ trailUVAttr = new THREE4.BufferAttribute(trailUVs, 2);
2199
2206
  trailUVAttr.setUsage(THREE4.DynamicDrawUsage);
2200
2207
  trailIndexAttr = new THREE4.BufferAttribute(trailIndices, 1);
2201
2208
  trailGeometry.setAttribute("position", trailPositionAttr);
@@ -2377,6 +2384,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
2377
2384
  trailPositionAttr,
2378
2385
  trailAlphaAttr,
2379
2386
  trailColorAttr,
2387
+ trailNextAttr,
2388
+ trailHalfWidthAttr,
2389
+ trailUVAttr,
2380
2390
  trailWidthCurveFn,
2381
2391
  trailOpacityCurveFn,
2382
2392
  trailColorOverTrailFns,
@@ -2527,6 +2537,37 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2527
2537
  );
2528
2538
  particleSystem.worldToLocal(gravityVelocity);
2529
2539
  }
2540
+ if (hasForceFields) {
2541
+ _inverseQuat.copy(worldQuaternion).invert();
2542
+ _localForceFields.length = normalizedForceFields.length;
2543
+ for (let i = 0; i < normalizedForceFields.length; i++) {
2544
+ const src = normalizedForceFields[i];
2545
+ let dst = _localForceFields[i];
2546
+ if (!dst) {
2547
+ dst = {
2548
+ isActive: true,
2549
+ type: "POINT" /* POINT */,
2550
+ position: new THREE4.Vector3(),
2551
+ direction: new THREE4.Vector3(),
2552
+ strength: 0,
2553
+ range: 0,
2554
+ falloff: "LINEAR" /* LINEAR */
2555
+ };
2556
+ _localForceFields[i] = dst;
2557
+ }
2558
+ dst.isActive = src.isActive;
2559
+ dst.type = src.type;
2560
+ dst.strength = src.strength;
2561
+ dst.range = src.range;
2562
+ dst.falloff = src.falloff;
2563
+ _localForceFieldPos.copy(src.position);
2564
+ particleSystem.worldToLocal(_localForceFieldPos);
2565
+ dst.position.copy(_localForceFieldPos);
2566
+ _localForceFieldDir.copy(src.direction);
2567
+ _localForceFieldDir.applyQuaternion(_inverseQuat);
2568
+ dst.direction.copy(_localForceFieldDir);
2569
+ }
2570
+ }
2530
2571
  const creationTimes = generalData.creationTimes;
2531
2572
  const isActiveArr = ma.isActive.array;
2532
2573
  const startLifetimeArr = ma.startLifetime.array;
@@ -2554,7 +2595,7 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
2554
2595
  if (hasForceFields) {
2555
2596
  applyForceFields({
2556
2597
  particleSystemId: generalData.particleSystemId,
2557
- forceFields: normalizedForceFields,
2598
+ forceFields: _localForceFields,
2558
2599
  velocity,
2559
2600
  positionArr,
2560
2601
  positionIndex: index * 3,
@@ -2774,13 +2815,16 @@ var updateTrailGeometry = (props, now) => {
2774
2815
  trailPositionAttr,
2775
2816
  trailAlphaAttr,
2776
2817
  trailColorAttr,
2818
+ trailNextAttr: trailNextAttrCached,
2819
+ trailHalfWidthAttr: trailHalfWidthAttrCached,
2820
+ trailUVAttr: trailUVAttrCached,
2777
2821
  trailWidthCurveFn,
2778
2822
  trailOpacityCurveFn,
2779
2823
  trailColorOverTrailFns,
2780
2824
  trailConfig,
2781
2825
  mappedAttributes: ma
2782
2826
  } = props;
2783
- if (!trailPositionAttr || !trailAlphaAttr || !trailColorAttr || !trailWidthCurveFn || !trailOpacityCurveFn || !trailConfig || !generalData.positionHistory || !generalData.positionHistoryIndex || !generalData.positionHistoryCount)
2827
+ if (!trailPositionAttr || !trailAlphaAttr || !trailColorAttr || !trailNextAttrCached || !trailHalfWidthAttrCached || !trailUVAttrCached || !trailWidthCurveFn || !trailOpacityCurveFn || !trailConfig || !generalData.positionHistory || !generalData.positionHistoryIndex || !generalData.positionHistoryCount)
2784
2828
  return;
2785
2829
  const trailLength = trailConfig.length;
2786
2830
  const positionHistory = generalData.positionHistory;
@@ -2806,10 +2850,9 @@ var updateTrailGeometry = (props, now) => {
2806
2850
  const trailPosArr = trailPositionAttr.array;
2807
2851
  const trailAlphaArr = trailAlphaAttr.array;
2808
2852
  const trailColorArr = trailColorAttr.array;
2809
- const trailMesh = props.trailMesh;
2810
- const trailNextArr = trailMesh.geometry.getAttribute("trailNext").array;
2811
- const trailUVArr = trailMesh.geometry.getAttribute("trailUV").array;
2812
- const trailHalfWidthArr = trailMesh.geometry.getAttribute("trailHalfWidth").array;
2853
+ const trailNextArr = trailNextAttrCached.array;
2854
+ const trailUVArr = trailUVAttrCached.array;
2855
+ const trailHalfWidthArr = trailHalfWidthAttrCached.array;
2813
2856
  const verticesPerParticle = trailLength * 2;
2814
2857
  const creationTimesLength = generalData.creationTimes.length;
2815
2858
  let hasUpdates = false;
@@ -3038,6 +3081,13 @@ var updateTrailGeometry = (props, now) => {
3038
3081
  nx = finalPts[(s + 1) * 3];
3039
3082
  ny = finalPts[(s + 1) * 3 + 1];
3040
3083
  nz = finalPts[(s + 1) * 3 + 2];
3084
+ } else if (finalCount >= 2) {
3085
+ const prevX = finalPts[(s - 1) * 3];
3086
+ const prevY = finalPts[(s - 1) * 3 + 1];
3087
+ const prevZ = finalPts[(s - 1) * 3 + 2];
3088
+ nx = hx + (hx - prevX);
3089
+ ny = hy + (hy - prevY);
3090
+ nz = hz + (hz - prevZ);
3041
3091
  } else {
3042
3092
  nx = hx;
3043
3093
  ny = hy + 1e-3;
@@ -3046,14 +3096,24 @@ var updateTrailGeometry = (props, now) => {
3046
3096
  const t = finalCount > 1 ? s / (finalCount - 1) : 0;
3047
3097
  let timeFade = 1;
3048
3098
  if (maxTime > 0 && sampleTimes && effectiveCount > 0) {
3049
- const rawS = useSmoothing ? Math.min(
3050
- Math.floor(s / Math.max(finalCount - 1, 1) * (rawCount - 1)),
3051
- rawCount - 1
3052
- ) : Math.min(s, rawCount - 1);
3053
- const sampleSlot = (historyIndex[index] - 1 - rawS + trailLength * 2) % trailLength;
3054
3099
  const sampleBase = index * trailLength;
3055
- const age = now - sampleTimes[sampleBase + sampleSlot];
3056
- timeFade = 1 - Math.min(age / maxTimeMs, 1);
3100
+ if (useSmoothing && rawCount >= 2) {
3101
+ const rawF = s / Math.max(finalCount - 1, 1) * (rawCount - 1);
3102
+ const rawLo = Math.min(Math.floor(rawF), rawCount - 1);
3103
+ const rawHi = Math.min(rawLo + 1, rawCount - 1);
3104
+ const frac = rawF - rawLo;
3105
+ const slotLo = (historyIndex[index] - 1 - rawLo + trailLength * 2) % trailLength;
3106
+ const slotHi = (historyIndex[index] - 1 - rawHi + trailLength * 2) % trailLength;
3107
+ const ageLo = now - sampleTimes[sampleBase + slotLo];
3108
+ const ageHi = now - sampleTimes[sampleBase + slotHi];
3109
+ const age = ageLo + (ageHi - ageLo) * frac;
3110
+ timeFade = 1 - Math.min(age / maxTimeMs, 1);
3111
+ } else {
3112
+ const rawS = Math.min(s, rawCount - 1);
3113
+ const sampleSlot = (historyIndex[index] - 1 - rawS + trailLength * 2) % trailLength;
3114
+ const age = now - sampleTimes[sampleBase + sampleSlot];
3115
+ timeFade = 1 - Math.min(age / maxTimeMs, 1);
3116
+ }
3057
3117
  }
3058
3118
  const widthScale = trailWidthCurveFn(t);
3059
3119
  const opacityScale = trailOpacityCurveFn(t);
@@ -3286,16 +3346,34 @@ var updateTrailGeometry = (props, now) => {
3286
3346
  nx = _rawPoints[(s + 1) * 3];
3287
3347
  ny = _rawPoints[(s + 1) * 3 + 1];
3288
3348
  nz = _rawPoints[(s + 1) * 3 + 2];
3349
+ } else if (filledCount >= 2) {
3350
+ const prevX = _rawPoints[(s - 1) * 3];
3351
+ const prevY = _rawPoints[(s - 1) * 3 + 1];
3352
+ const prevZ = _rawPoints[(s - 1) * 3 + 2];
3353
+ nx = ptx + (ptx - prevX);
3354
+ ny = pty + (pty - prevY);
3355
+ nz = ptz + (ptz - prevZ);
3289
3356
  } else {
3290
3357
  nx = ptx;
3291
3358
  ny = pty + 1e-3;
3292
3359
  nz = ptz;
3293
3360
  }
3294
3361
  const t = filledCount > 1 ? s / (filledCount - 1) : 0;
3362
+ let ribbonTimeFade = 1;
3363
+ if (maxTime > 0 && controlCount >= 2) {
3364
+ const ctrlF = t * (controlCount - 1);
3365
+ const ctrlLo = Math.min(Math.floor(ctrlF), controlCount - 1);
3366
+ const ctrlHi = Math.min(ctrlLo + 1, controlCount - 1);
3367
+ const frac = ctrlF - ctrlLo;
3368
+ const ageLo = now - generalData.creationTimes[_ribbonIndices[ctrlLo]];
3369
+ const ageHi = now - generalData.creationTimes[_ribbonIndices[ctrlHi]];
3370
+ const age = ageLo + (ageHi - ageLo) * frac;
3371
+ ribbonTimeFade = 1 - Math.min(age / maxTimeMs, 1);
3372
+ }
3295
3373
  const widthScale = trailWidthCurveFn(t);
3296
3374
  const opacityScale = trailOpacityCurveFn(t);
3297
3375
  const halfWidth = trailConfig.width * widthScale * 0.5;
3298
- const alpha = leaderCa * opacityScale;
3376
+ const alpha = leaderCa * opacityScale * ribbonTimeFade;
3299
3377
  const fr = trailColorOverTrailFns ? leaderCr * trailColorOverTrailFns.r(t) : leaderCr;
3300
3378
  const fg = trailColorOverTrailFns ? leaderCg * trailColorOverTrailFns.g(t) : leaderCg;
3301
3379
  const fb = trailColorOverTrailFns ? leaderCb * trailColorOverTrailFns.b(t) : leaderCb;
@@ -3325,6 +3403,55 @@ var updateTrailGeometry = (props, now) => {
3325
3403
  trailColorArr
3326
3404
  );
3327
3405
  }
3406
+ if (useTwistPrevention && prevNormal && filledCount >= 2) {
3407
+ const nIdx = leader * 3;
3408
+ const tx = _rawPoints[3] - _rawPoints[0];
3409
+ const ty = _rawPoints[4] - _rawPoints[1];
3410
+ const tz = _rawPoints[5] - _rawPoints[2];
3411
+ const tLen = Math.sqrt(tx * tx + ty * ty + tz * tz);
3412
+ if (tLen > 1e-4) {
3413
+ const ntx = tx / tLen;
3414
+ const nty = ty / tLen;
3415
+ const ntz = tz / tLen;
3416
+ let upx = 0, upy = 1, upz = 0;
3417
+ const dot = ntx * upx + nty * upy + ntz * upz;
3418
+ if (Math.abs(dot) > 0.999) {
3419
+ upx = 1;
3420
+ upy = 0;
3421
+ upz = 0;
3422
+ }
3423
+ let cnx = nty * upz - ntz * upy;
3424
+ let cny = ntz * upx - ntx * upz;
3425
+ let cnz = ntx * upy - nty * upx;
3426
+ const cnLen = Math.sqrt(cnx * cnx + cny * cny + cnz * cnz);
3427
+ if (cnLen > 1e-4) {
3428
+ cnx /= cnLen;
3429
+ cny /= cnLen;
3430
+ cnz /= cnLen;
3431
+ }
3432
+ const prevNx = prevNormal[nIdx];
3433
+ const prevNy = prevNormal[nIdx + 1];
3434
+ const prevNz = prevNormal[nIdx + 2];
3435
+ const hasPrev = prevNx !== 0 || prevNy !== 0 || prevNz !== 0;
3436
+ if (hasPrev) {
3437
+ const normalDot = cnx * prevNx + cny * prevNy + cnz * prevNz;
3438
+ if (normalDot < 0) {
3439
+ for (let s = 0; s < Math.min(filledCount, trailLength); s++) {
3440
+ const aIdx = leaderVertBase + s * 2;
3441
+ const hw = trailHalfWidthArr[aIdx];
3442
+ trailHalfWidthArr[aIdx] = -hw;
3443
+ trailHalfWidthArr[aIdx + 1] = -hw;
3444
+ }
3445
+ cnx = -cnx;
3446
+ cny = -cny;
3447
+ cnz = -cnz;
3448
+ }
3449
+ }
3450
+ prevNormal[nIdx] = cnx;
3451
+ prevNormal[nIdx + 1] = cny;
3452
+ prevNormal[nIdx + 2] = cnz;
3453
+ }
3454
+ }
3328
3455
  for (let ri = 1; ri < _ribbonCount; ri++) {
3329
3456
  const pIdx = _ribbonIndices[ri];
3330
3457
  const pVertBase = pIdx * verticesPerParticle;
@@ -3355,18 +3482,9 @@ var updateTrailGeometry = (props, now) => {
3355
3482
  trailPositionAttr.needsUpdate = true;
3356
3483
  trailAlphaAttr.needsUpdate = true;
3357
3484
  trailColorAttr.needsUpdate = true;
3358
- const nextAttr = trailMesh.geometry.getAttribute(
3359
- "trailNext"
3360
- );
3361
- const hwAttr = trailMesh.geometry.getAttribute(
3362
- "trailHalfWidth"
3363
- );
3364
- nextAttr.needsUpdate = true;
3365
- hwAttr.needsUpdate = true;
3366
- const uvAttr = trailMesh.geometry.getAttribute(
3367
- "trailUV"
3368
- );
3369
- uvAttr.needsUpdate = true;
3485
+ trailNextAttrCached.needsUpdate = true;
3486
+ trailHalfWidthAttrCached.needsUpdate = true;
3487
+ trailUVAttrCached.needsUpdate = true;
3370
3488
  }
3371
3489
  };
3372
3490
  var updateParticleSystems = (cycleData) => {