@newkrok/three-particles 2.6.6 → 2.7.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.d.ts CHANGED
@@ -116,6 +116,23 @@ declare const enum LifeTimeCurve {
116
116
  */
117
117
  EASING = "EASING"
118
118
  }
119
+ /**
120
+ * Defines when a sub-emitter is triggered relative to a particle's lifecycle.
121
+ *
122
+ * @enum {string}
123
+ */
124
+ declare const enum SubEmitterTrigger {
125
+ /**
126
+ * Trigger the sub-emitter when a particle is born (activated).
127
+ * Useful for trail effects that start immediately with each particle.
128
+ */
129
+ BIRTH = "BIRTH",
130
+ /**
131
+ * Trigger the sub-emitter when a particle dies (reaches end of lifetime).
132
+ * Useful for cascading effects like explosions spawning smoke.
133
+ */
134
+ DEATH = "DEATH"
135
+ }
119
136
 
120
137
  /**
121
138
  * A fixed numerical value.
@@ -701,6 +718,41 @@ type VelocityOverLifetime = {
701
718
  z?: Constant | RandomBetweenTwoConstants | LifetimeCurve;
702
719
  };
703
720
  };
721
+ /**
722
+ * Configuration for a sub-emitter that spawns child particle systems
723
+ * based on parent particle lifecycle events.
724
+ *
725
+ * @example
726
+ * ```typescript
727
+ * const subEmitter: SubEmitterConfig = {
728
+ * trigger: SubEmitterTrigger.DEATH,
729
+ * config: { startLifeTime: 0.5, startSpeed: 2, emission: { rateOverTime: 20 } },
730
+ * inheritVelocity: 0.5,
731
+ * maxInstances: 16,
732
+ * };
733
+ * ```
734
+ */
735
+ type SubEmitterConfig = {
736
+ /** The particle system configuration used when spawning the sub-emitter. */
737
+ config: ParticleSystemConfig;
738
+ /**
739
+ * When to trigger the sub-emitter.
740
+ * @default SubEmitterTrigger.DEATH
741
+ */
742
+ trigger?: SubEmitterTrigger;
743
+ /**
744
+ * Multiplier (0–1) for inheriting the parent particle's velocity.
745
+ * 0 = no inheritance, 1 = full velocity inheritance.
746
+ * @default 0
747
+ */
748
+ inheritVelocity?: number;
749
+ /**
750
+ * Maximum number of concurrent sub-emitter instances for this configuration.
751
+ * Older completed instances are cleaned up to make room for new ones.
752
+ * @default 32
753
+ */
754
+ maxInstances?: number;
755
+ };
704
756
  /**
705
757
  * Configuration object for the particle system.
706
758
  * Defines all aspects of the particle system, including its appearance, behavior, and runtime events.
@@ -1188,6 +1240,22 @@ type ParticleSystemConfig = {
1188
1240
  * }
1189
1241
  */
1190
1242
  textureSheetAnimation?: TextureSheetAnimation;
1243
+ /**
1244
+ * Sub-emitters that spawn child particle systems on particle lifecycle events.
1245
+ * Each sub-emitter is triggered when a particle is born or dies, creating a new
1246
+ * particle system at the parent particle's position.
1247
+ *
1248
+ * @example
1249
+ * ```typescript
1250
+ * subEmitters: [
1251
+ * {
1252
+ * trigger: SubEmitterTrigger.DEATH,
1253
+ * config: { startLifeTime: 0.5, startSpeed: 1, emission: { rateOverTime: 10 } },
1254
+ * },
1255
+ * ]
1256
+ * ```
1257
+ */
1258
+ subEmitters?: Array<SubEmitterConfig>;
1191
1259
  /**
1192
1260
  * Called on every update frame with particle system data.
1193
1261
  */
@@ -1288,6 +1356,10 @@ type ParticleSystemInstance = {
1288
1356
  activationTime: number;
1289
1357
  position: Required<Point3D>;
1290
1358
  }) => void;
1359
+ /** Called when a particle dies to trigger death sub-emitters */
1360
+ onParticleDeath?: (particleIndex: number, positionArr: THREE.TypedArray, velocity: THREE.Vector3, now: number) => void;
1361
+ /** Called when a particle is born to trigger birth sub-emitters */
1362
+ onParticleBirth?: (particleIndex: number, positionArr: THREE.TypedArray, velocity: THREE.Vector3, now: number) => void;
1291
1363
  };
1292
1364
  /**
1293
1365
  * Represents a particle system instance, providing methods to control and manage its lifecycle.
@@ -1779,4 +1851,4 @@ declare const getDefaultParticleSystemConfig: () => any;
1779
1851
  declare const createParticleSystem: (config?: ParticleSystemConfig, externalNow?: number) => ParticleSystem;
1780
1852
  declare const updateParticleSystems: (cycleData: CycleData) => void;
1781
1853
 
1782
- export { type BezierCurve, type BezierPoint, type Box, type Burst, type BurstState, type Circle, type Cone, type Constant, type CurveBase, type CurveFunction, CurveFunctionId, type CycleData, type EasingCurve, type Emission, EmitFrom, type GeneralData, LifeTimeCurve, type LifetimeCurve, type MinMaxColor, type Noise, type NoiseConfig, type NormalizedParticleSystemConfig, type ParticleSystem, type ParticleSystemConfig, type ParticleSystemInstance, type Point3D, type RandomBetweenTwoConstants, type Rectangle, type Renderer, type Rgb, Shape, type ShapeConfig, SimulationSpace, type Sphere, type TextureSheetAnimation, TimeMode, type Transform, type VelocityOverLifetime, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
1854
+ export { type BezierCurve, type BezierPoint, type Box, type Burst, type BurstState, type Circle, type Cone, type Constant, type CurveBase, type CurveFunction, CurveFunctionId, type CycleData, type EasingCurve, type Emission, EmitFrom, type GeneralData, LifeTimeCurve, type LifetimeCurve, type MinMaxColor, type Noise, type NoiseConfig, type NormalizedParticleSystemConfig, type ParticleSystem, type ParticleSystemConfig, type ParticleSystemInstance, type Point3D, type RandomBetweenTwoConstants, type Rectangle, type Renderer, type Rgb, Shape, type ShapeConfig, SimulationSpace, type Sphere, type SubEmitterConfig, SubEmitterTrigger, type TextureSheetAnimation, TimeMode, type Transform, type VelocityOverLifetime, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
package/dist/index.js CHANGED
@@ -160,6 +160,11 @@ var LifeTimeCurve = /* @__PURE__ */ ((LifeTimeCurve2) => {
160
160
  LifeTimeCurve2["EASING"] = "EASING";
161
161
  return LifeTimeCurve2;
162
162
  })(LifeTimeCurve || {});
163
+ var SubEmitterTrigger = /* @__PURE__ */ ((SubEmitterTrigger2) => {
164
+ SubEmitterTrigger2["BIRTH"] = "BIRTH";
165
+ SubEmitterTrigger2["DEATH"] = "DEATH";
166
+ return SubEmitterTrigger2;
167
+ })(SubEmitterTrigger || {});
163
168
  var calculateRandomPositionAndVelocityOnSphere = (position, quaternion, velocity, speed, {
164
169
  radius,
165
170
  radiusThickness,
@@ -589,6 +594,7 @@ var particle_system_vertex_shader_glsl_default = ParticleSystemVertexShader;
589
594
  // src/js/effects/three-particles/three-particles.ts
590
595
  var _particleSystemId = 0;
591
596
  var createdParticleSystems = [];
597
+ var _subEmitterPosition = new THREE3.Vector3();
592
598
  var _lastWorldPositionSnapshot = new THREE3.Vector3();
593
599
  var _distanceStep = { x: 0, y: 0, z: 0 };
594
600
  var _tempPosition = { x: 0, y: 0, z: 0 };
@@ -903,7 +909,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
903
909
  velocityOverLifetime,
904
910
  onUpdate,
905
911
  onComplete,
906
- textureSheetAnimation
912
+ textureSheetAnimation,
913
+ subEmitters
907
914
  } = normalizedConfig;
908
915
  if (typeof renderer?.blending === "string")
909
916
  renderer.blending = blendingMap[renderer.blending];
@@ -1311,6 +1318,68 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1311
1318
  particleIndex
1312
1319
  });
1313
1320
  };
1321
+ const subEmitterArr = subEmitters ?? [];
1322
+ const deathSubEmitters = subEmitterArr.filter(
1323
+ (s) => (s.trigger ?? "DEATH" /* DEATH */) === "DEATH" /* DEATH */
1324
+ );
1325
+ const birthSubEmitters = subEmitterArr.filter(
1326
+ (s) => s.trigger === "BIRTH" /* BIRTH */
1327
+ );
1328
+ const subEmitterInstancesMap = /* @__PURE__ */ new Map();
1329
+ for (const cfg of subEmitterArr) {
1330
+ subEmitterInstancesMap.set(cfg, []);
1331
+ }
1332
+ const cleanupCompletedInstances = (instances) => {
1333
+ for (let i = instances.length - 1; i >= 0; i--) {
1334
+ const sub = instances[i];
1335
+ const points = sub.instance;
1336
+ const isActiveArr = points.geometry?.attributes?.isActive?.array;
1337
+ if (!isActiveArr) {
1338
+ sub.dispose();
1339
+ instances.splice(i, 1);
1340
+ continue;
1341
+ }
1342
+ let hasActive = false;
1343
+ for (let j = 0; j < isActiveArr.length; j++) {
1344
+ if (isActiveArr[j]) {
1345
+ hasActive = true;
1346
+ break;
1347
+ }
1348
+ }
1349
+ if (!hasActive) {
1350
+ sub.dispose();
1351
+ instances.splice(i, 1);
1352
+ }
1353
+ }
1354
+ };
1355
+ const spawnSubEmitters = (configs, position, velocity, spawnNow) => {
1356
+ for (const subConfig of configs) {
1357
+ const instances = subEmitterInstancesMap.get(subConfig);
1358
+ const maxInst = subConfig.maxInstances ?? 32;
1359
+ if (instances.length >= maxInst) {
1360
+ cleanupCompletedInstances(instances);
1361
+ if (instances.length >= maxInst) continue;
1362
+ }
1363
+ const inheritVelocity = subConfig.inheritVelocity ?? 0;
1364
+ const subSystem = createParticleSystem(
1365
+ {
1366
+ ...subConfig.config,
1367
+ looping: false,
1368
+ transform: {
1369
+ ...subConfig.config.transform,
1370
+ position: new THREE3.Vector3(position.x, position.y, position.z)
1371
+ },
1372
+ ...inheritVelocity > 0 ? {
1373
+ startSpeed: (typeof subConfig.config.startSpeed === "number" ? subConfig.config.startSpeed : typeof subConfig.config.startSpeed === "object" && subConfig.config.startSpeed !== null && "min" in subConfig.config.startSpeed ? subConfig.config.startSpeed.min ?? 0 : 0) + velocity.length() * inheritVelocity
1374
+ } : {}
1375
+ },
1376
+ spawnNow
1377
+ );
1378
+ const parentObj = (wrapper || particleSystem).parent;
1379
+ if (parentObj) parentObj.add(subSystem.instance);
1380
+ instances.push(subSystem);
1381
+ }
1382
+ };
1314
1383
  let particleSystem = new THREE3.Points(geometry, material);
1315
1384
  particleSystem.position.copy(transform.position);
1316
1385
  particleSystem.rotation.x = THREE3.MathUtils.degToRad(transform.rotation.x);
@@ -1323,6 +1392,36 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1323
1392
  wrapper = new Gyroscope();
1324
1393
  wrapper.add(particleSystem);
1325
1394
  }
1395
+ const hasDeathSubEmitters = deathSubEmitters.length > 0;
1396
+ const hasBirthSubEmitters = birthSubEmitters.length > 0;
1397
+ const onParticleDeath = hasDeathSubEmitters ? (particleIndex, positionArr, velocity, deathNow) => {
1398
+ const posIdx = particleIndex * 3;
1399
+ _subEmitterPosition.set(
1400
+ positionArr[posIdx],
1401
+ positionArr[posIdx + 1],
1402
+ positionArr[posIdx + 2]
1403
+ );
1404
+ spawnSubEmitters(
1405
+ deathSubEmitters,
1406
+ _subEmitterPosition,
1407
+ velocity,
1408
+ deathNow
1409
+ );
1410
+ } : void 0;
1411
+ const onParticleBirth = hasBirthSubEmitters ? (particleIndex, positionArr, velocity, birthNow) => {
1412
+ const posIdx = particleIndex * 3;
1413
+ _subEmitterPosition.set(
1414
+ positionArr[posIdx],
1415
+ positionArr[posIdx + 1],
1416
+ positionArr[posIdx + 2]
1417
+ );
1418
+ spawnSubEmitters(
1419
+ birthSubEmitters,
1420
+ _subEmitterPosition,
1421
+ velocity,
1422
+ birthNow
1423
+ );
1424
+ } : void 0;
1326
1425
  const instanceData = {
1327
1426
  particleSystem,
1328
1427
  wrapper,
@@ -1342,12 +1441,20 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1342
1441
  velocities,
1343
1442
  freeList,
1344
1443
  deactivateParticle,
1345
- activateParticle
1444
+ activateParticle,
1445
+ onParticleDeath,
1446
+ onParticleBirth
1346
1447
  };
1347
1448
  createdParticleSystems.push(instanceData);
1348
1449
  const resumeEmitter = () => generalData.isEnabled = true;
1349
1450
  const pauseEmitter = () => generalData.isEnabled = false;
1350
- const dispose = () => destroyParticleSystem(particleSystem);
1451
+ const dispose = () => {
1452
+ for (const instances of subEmitterInstancesMap.values()) {
1453
+ for (const sub of instances) sub.dispose();
1454
+ instances.length = 0;
1455
+ }
1456
+ destroyParticleSystem(particleSystem);
1457
+ };
1351
1458
  const update = (cycleData) => updateParticleSystemInstance(instanceData, cycleData);
1352
1459
  return {
1353
1460
  instance: wrapper || particleSystem,
@@ -1377,7 +1484,9 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
1377
1484
  deactivateParticle,
1378
1485
  activateParticle,
1379
1486
  simulationSpace,
1380
- gravity
1487
+ gravity,
1488
+ onParticleDeath,
1489
+ onParticleBirth
1381
1490
  } = props;
1382
1491
  const lifetime = now - creationTime;
1383
1492
  const normalizedLifetime = lifetime % (duration * 1e3);
@@ -1439,6 +1548,8 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
1439
1548
  if (isActiveArr[index]) {
1440
1549
  const particleLifetime = now - creationTimes[index];
1441
1550
  if (particleLifetime > startLifetimeArr[index]) {
1551
+ if (onParticleDeath)
1552
+ onParticleDeath(index, positionArr, velocities[index], now);
1442
1553
  deactivateParticle(index);
1443
1554
  } else {
1444
1555
  const velocity = velocities[index];
@@ -1550,6 +1661,13 @@ var updateParticleSystemInstance = (props, { now, delta, elapsed }) => {
1550
1661
  activationTime: now,
1551
1662
  position: _tempPosition
1552
1663
  });
1664
+ if (onParticleBirth)
1665
+ onParticleBirth(
1666
+ particleIndex,
1667
+ attributes.position.array,
1668
+ velocities[particleIndex],
1669
+ now
1670
+ );
1553
1671
  props.lastEmissionTime = now;
1554
1672
  }
1555
1673
  }
@@ -1573,6 +1691,6 @@ var updateParticleSystems = (cycleData) => {
1573
1691
  );
1574
1692
  };
1575
1693
 
1576
- export { CurveFunctionId, EmitFrom, LifeTimeCurve, Shape, SimulationSpace, TimeMode, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
1694
+ export { CurveFunctionId, EmitFrom, LifeTimeCurve, Shape, SimulationSpace, SubEmitterTrigger, TimeMode, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
1577
1695
  //# sourceMappingURL=index.js.map
1578
1696
  //# sourceMappingURL=index.js.map