@newkrok/three-particles 2.12.0 → 2.13.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/README.md CHANGED
@@ -21,6 +21,7 @@ Particle system for ThreeJS.
21
21
  * Serialization support for saving and loading particle system configs.
22
22
  * GPU instancing renderer (`RendererType.INSTANCED`) — removes `gl_PointSize` hardware limit, ideal for large particles or high particle counts.
23
23
  * Trail / Ribbon renderer (`RendererType.TRAIL`) — continuous ribbon trails behind particles with configurable width, opacity, and color tapering.
24
+ * Mesh particle renderer (`RendererType.MESH`) — render each particle as a 3D mesh (debris, gems, coins) using GPU instancing with full 3D rotation and simple directional lighting.
24
25
  * TypeDoc API documentation available.
25
26
 
26
27
  # Live Demo & Examples
package/dist/index.d.ts CHANGED
@@ -180,7 +180,23 @@ declare const enum RendererType {
180
180
  *
181
181
  * Configure trail-specific properties via {@link TrailConfig} on the renderer.
182
182
  */
183
- TRAIL = "TRAIL"
183
+ TRAIL = "TRAIL",
184
+ /**
185
+ * Render each particle as a 3D mesh using GPU instancing (`InstancedBufferGeometry`).
186
+ * Instead of flat billboard sprites, particles are rendered as real 3D geometry
187
+ * (e.g., cubes, spheres, tori, or any custom `THREE.BufferGeometry`).
188
+ *
189
+ * Key differences from billboard renderers:
190
+ * - **3D rotation**: Particles rotate in all three axes (quaternion-based).
191
+ * - **Normals**: Mesh geometry retains normals, enabling basic lighting.
192
+ * - **Arbitrary geometry**: Any `THREE.BufferGeometry` can be used per particle.
193
+ *
194
+ * All existing modifiers (sizeOverLifetime, colorOverLifetime, noise, force fields,
195
+ * sub-emitters) work with mesh particles.
196
+ *
197
+ * Configure mesh-specific properties via {@link MeshConfig} on the renderer.
198
+ */
199
+ MESH = "MESH"
184
200
  }
185
201
  /**
186
202
  * Defines how force diminishes with distance from a POINT force field center.
@@ -682,6 +698,31 @@ type TrailConfig = {
682
698
  b: LifetimeCurve;
683
699
  };
684
700
  };
701
+ /**
702
+ * Configuration for the mesh particle renderer.
703
+ * Controls which 3D geometry is used when `rendererType` is `RendererType.MESH`.
704
+ *
705
+ * @property geometry - A `THREE.BufferGeometry` to render for each particle.
706
+ * Built-in Three.js primitives like `BoxGeometry`, `SphereGeometry`, `TorusGeometry`
707
+ * all work. The geometry's own normals and UVs are preserved.
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * // Cube mesh particles
712
+ * mesh: {
713
+ * geometry: new THREE.BoxGeometry(1, 1, 1),
714
+ * }
715
+ *
716
+ * // Icosahedron mesh particles
717
+ * mesh: {
718
+ * geometry: new THREE.IcosahedronGeometry(0.5, 0),
719
+ * }
720
+ * ```
721
+ */
722
+ type MeshConfig = {
723
+ /** The geometry to render for each particle. */
724
+ geometry: THREE.BufferGeometry;
725
+ };
685
726
  /**
686
727
  * Configuration for the particle system renderer, controlling blending, transparency, depth, and background color behavior.
687
728
  *
@@ -742,6 +783,13 @@ type Renderer = {
742
783
  * @see TrailConfig
743
784
  */
744
785
  trail?: TrailConfig;
786
+ /**
787
+ * Mesh particle renderer configuration.
788
+ * Only used when `rendererType` is `RendererType.MESH`.
789
+ *
790
+ * @see MeshConfig
791
+ */
792
+ mesh?: MeshConfig;
745
793
  };
746
794
  /**
747
795
  * Configuration for noise effects applied to particles in a particle system.
@@ -1585,6 +1633,8 @@ type MappedAttributes = {
1585
1633
  colorG: AnyBufferAttribute;
1586
1634
  colorB: AnyBufferAttribute;
1587
1635
  colorA: AnyBufferAttribute;
1636
+ /** Packed quaternion vec4 for 3D mesh rotation (only present for RendererType.MESH). */
1637
+ quat?: AnyBufferAttribute;
1588
1638
  };
1589
1639
  type ParticleSystemInstance = {
1590
1640
  particleSystem: THREE.Points | THREE.Mesh;
@@ -2028,6 +2078,13 @@ declare const calculateRandomPositionAndVelocityOnRectangle: (position: THREE.Ve
2028
2078
  rotation: Point3D;
2029
2079
  scale: Point3D;
2030
2080
  }) => void;
2081
+ /**
2082
+ * Creates a solid white 1x1 texture for mesh particles.
2083
+ * Unlike the circle texture used by point/billboard renderers, mesh particles
2084
+ * need a neutral texture so the geometry shape is visible.
2085
+ * @returns {THREE.CanvasTexture | null} The generated texture or null if context fails.
2086
+ */
2087
+ declare const createDefaultMeshTexture: () => THREE.CanvasTexture | null;
2031
2088
  /**
2032
2089
  * Creates a default white circle texture using CanvasTexture.
2033
2090
  * @returns {THREE.CanvasTexture | null} The generated texture or null if context fails.
@@ -2142,4 +2199,4 @@ declare const getDefaultParticleSystemConfig: () => any;
2142
2199
  declare const createParticleSystem: (config?: ParticleSystemConfig, externalNow?: number) => ParticleSystem;
2143
2200
  declare const updateParticleSystems: (cycleData: CycleData) => void;
2144
2201
 
2145
- 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 ForceFieldConfig, ForceFieldFalloff, ForceFieldType, type GeneralData, LifeTimeCurve, type LifetimeCurve, type MappedAttributes, type MinMaxColor, type Noise, type NoiseConfig, type NormalizedForceFieldConfig, type NormalizedParticleSystemConfig, type ParticleSystem, type ParticleSystemConfig, type ParticleSystemInstance, type Point3D, type RandomBetweenTwoConstants, type Rectangle, type Renderer, RendererType, type Rgb, Shape, type ShapeConfig, SimulationSpace, type Sphere, type SubEmitterConfig, SubEmitterTrigger, type TextureSheetAnimation, TimeMode, type TrailConfig, type Transform, type VelocityOverLifetime, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, curveFunctionIdMap, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
2202
+ 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 ForceFieldConfig, ForceFieldFalloff, ForceFieldType, type GeneralData, LifeTimeCurve, type LifetimeCurve, type MappedAttributes, type MeshConfig, type MinMaxColor, type Noise, type NoiseConfig, type NormalizedForceFieldConfig, type NormalizedParticleSystemConfig, type ParticleSystem, type ParticleSystemConfig, type ParticleSystemInstance, type Point3D, type RandomBetweenTwoConstants, type Rectangle, type Renderer, RendererType, type Rgb, Shape, type ShapeConfig, SimulationSpace, type Sphere, type SubEmitterConfig, SubEmitterTrigger, type TextureSheetAnimation, TimeMode, type TrailConfig, type Transform, type VelocityOverLifetime, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultMeshTexture, createDefaultParticleTexture, createParticleSystem, curveFunctionIdMap, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
package/dist/index.js CHANGED
@@ -174,6 +174,7 @@ var RendererType = /* @__PURE__ */ ((RendererType2) => {
174
174
  RendererType2["POINTS"] = "POINTS";
175
175
  RendererType2["INSTANCED"] = "INSTANCED";
176
176
  RendererType2["TRAIL"] = "TRAIL";
177
+ RendererType2["MESH"] = "MESH";
177
178
  return RendererType2;
178
179
  })(RendererType || {});
179
180
  var ForceFieldFalloff = /* @__PURE__ */ ((ForceFieldFalloff2) => {
@@ -310,6 +311,24 @@ var calculateRandomPositionAndVelocityOnRectangle = (position, quaternion, veloc
310
311
  velocity.set(0, 0, speed);
311
312
  velocity.applyQuaternion(quaternion);
312
313
  };
314
+ var createDefaultMeshTexture = () => {
315
+ try {
316
+ const canvas = document.createElement("canvas");
317
+ canvas.width = 1;
318
+ canvas.height = 1;
319
+ const context = canvas.getContext("2d");
320
+ if (context) {
321
+ context.fillStyle = "white";
322
+ context.fillRect(0, 0, 1, 1);
323
+ const texture = new THREE4.CanvasTexture(canvas);
324
+ texture.needsUpdate = true;
325
+ return texture;
326
+ }
327
+ return null;
328
+ } catch {
329
+ return null;
330
+ }
331
+ };
313
332
  var createDefaultParticleTexture = () => {
314
333
  try {
315
334
  const canvas = document.createElement("canvas");
@@ -494,6 +513,16 @@ var applyModifiers = ({
494
513
  positionArr[positionIndex + 2] += noiseOnPosition * noisePower * positionAmount;
495
514
  attributes.position.needsUpdate = true;
496
515
  }
516
+ if (attributes.quat) {
517
+ const rotZ = attributes.rotation.array[particleIndex];
518
+ const halfZ = rotZ * 0.5;
519
+ const qi = particleIndex * 4;
520
+ attributes.quat.array[qi] = 0;
521
+ attributes.quat.array[qi + 1] = 0;
522
+ attributes.quat.array[qi + 2] = Math.sin(halfZ);
523
+ attributes.quat.array[qi + 3] = Math.cos(halfZ);
524
+ attributes.quat.needsUpdate = true;
525
+ }
497
526
  };
498
527
 
499
528
  // src/js/effects/three-particles/shaders/instanced-particle-fragment-shader.glsl.ts
@@ -630,6 +659,129 @@ var InstancedParticleVertexShader = `
630
659
  `;
631
660
  var instanced_particle_vertex_shader_glsl_default = InstancedParticleVertexShader;
632
661
 
662
+ // src/js/effects/three-particles/shaders/mesh-particle-fragment-shader.glsl.ts
663
+ var MeshParticleFragmentShader = `
664
+ uniform sampler2D map;
665
+ uniform float elapsed;
666
+ uniform float fps;
667
+ uniform bool useFPSForFrameIndex;
668
+ uniform vec2 tiles;
669
+ uniform bool discardBackgroundColor;
670
+ uniform vec3 backgroundColor;
671
+ uniform float backgroundColorTolerance;
672
+
673
+ varying vec4 vColor;
674
+ varying float vLifetime;
675
+ varying float vStartLifetime;
676
+ varying float vStartFrame;
677
+ varying float vRotation;
678
+ varying vec3 vNormal;
679
+ varying vec2 vUv;
680
+
681
+ #include <common>
682
+ #include <logdepthbuf_pars_fragment>
683
+
684
+ void main()
685
+ {
686
+ gl_FragColor = vColor;
687
+
688
+ // Use mesh UVs directly for texture sampling
689
+ vec2 uvPoint = vUv;
690
+
691
+ // Apply texture sheet animation if tiles > 1x1
692
+ if (tiles.x > 1.0 || tiles.y > 1.0) {
693
+ float frameIndex = round(vStartFrame) + (
694
+ useFPSForFrameIndex == true
695
+ ? fps == 0.0
696
+ ? 0.0
697
+ : max((vLifetime / 1000.0) * fps, 0.0)
698
+ : max(min(floor(min(vLifetime / vStartLifetime, 1.0) * (tiles.x * tiles.y)), tiles.x * tiles.y - 1.0), 0.0)
699
+ );
700
+
701
+ float spriteXIndex = floor(mod(frameIndex, tiles.x));
702
+ float spriteYIndex = floor(mod(frameIndex / tiles.x, tiles.y));
703
+
704
+ uvPoint = vec2(
705
+ vUv.x / tiles.x + spriteXIndex / tiles.x,
706
+ vUv.y / tiles.y + spriteYIndex / tiles.y
707
+ );
708
+ }
709
+
710
+ vec4 texColor = texture2D(map, uvPoint);
711
+ gl_FragColor = gl_FragColor * texColor;
712
+
713
+ if (discardBackgroundColor && abs(length(texColor.rgb - backgroundColor.rgb)) < backgroundColorTolerance) discard;
714
+
715
+ // Simple directional lighting from camera direction
716
+ float lightIntensity = 0.5 + 0.5 * max(dot(vNormal, vec3(0.0, 0.0, 1.0)), 0.0);
717
+ gl_FragColor.rgb *= lightIntensity;
718
+
719
+ #include <logdepthbuf_fragment>
720
+ }
721
+ `;
722
+ var mesh_particle_fragment_shader_glsl_default = MeshParticleFragmentShader;
723
+
724
+ // src/js/effects/three-particles/shaders/mesh-particle-vertex-shader.glsl.ts
725
+ var MeshParticleVertexShader = `
726
+ attribute float instanceSize;
727
+ attribute float instanceColorR;
728
+ attribute float instanceColorG;
729
+ attribute float instanceColorB;
730
+ attribute float instanceColorA;
731
+ attribute float instanceLifetime;
732
+ attribute float instanceStartLifetime;
733
+ attribute float instanceRotation;
734
+ attribute float instanceStartFrame;
735
+ attribute vec3 instanceOffset;
736
+ attribute vec4 instanceQuat;
737
+
738
+ varying vec4 vColor;
739
+ varying float vLifetime;
740
+ varying float vStartLifetime;
741
+ varying float vStartFrame;
742
+ varying float vRotation;
743
+ varying vec3 vNormal;
744
+ varying vec2 vUv;
745
+
746
+ #include <common>
747
+ #include <logdepthbuf_pars_vertex>
748
+
749
+ vec3 applyQuaternion(vec3 v, vec4 q) {
750
+ vec3 t = 2.0 * cross(q.xyz, v);
751
+ return v + q.w * t + cross(q.xyz, t);
752
+ }
753
+
754
+ void main()
755
+ {
756
+ vColor = vec4(instanceColorR, instanceColorG, instanceColorB, instanceColorA);
757
+ vLifetime = instanceLifetime;
758
+ vStartLifetime = instanceStartLifetime;
759
+ vStartFrame = instanceStartFrame;
760
+ vRotation = instanceRotation;
761
+
762
+ // Apply quaternion rotation to the mesh vertex position
763
+ vec3 rotatedPosition = applyQuaternion(position, instanceQuat);
764
+
765
+ // Scale mesh by particle size
766
+ vec3 scaledPosition = rotatedPosition * instanceSize;
767
+
768
+ // Apply instance offset (particle world position)
769
+ vec3 worldPos = scaledPosition + instanceOffset;
770
+
771
+ vec4 mvPosition = modelViewMatrix * vec4(worldPos, 1.0);
772
+ gl_Position = projectionMatrix * mvPosition;
773
+
774
+ // Transform normal by quaternion for lighting
775
+ vNormal = normalize((modelViewMatrix * vec4(applyQuaternion(normal, instanceQuat), 0.0)).xyz);
776
+
777
+ // Pass through UVs from the mesh geometry
778
+ vUv = uv;
779
+
780
+ #include <logdepthbuf_vertex>
781
+ }
782
+ `;
783
+ var mesh_particle_vertex_shader_glsl_default = MeshParticleVertexShader;
784
+
633
785
  // src/js/effects/three-particles/shaders/particle-system-fragment-shader.glsl.ts
634
786
  var ParticleSystemFragmentShader = `
635
787
  uniform sampler2D map;
@@ -1239,7 +1391,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1239
1391
  config,
1240
1392
  { applyToFirstObject: false, skippedProperties: [] }
1241
1393
  );
1242
- let particleMap = normalizedConfig.map || createDefaultParticleTexture();
1394
+ let particleMap = normalizedConfig.map || (normalizedConfig.renderer.rendererType === "MESH" /* MESH */ ? createDefaultMeshTexture() : createDefaultParticleTexture());
1243
1395
  const {
1244
1396
  transform,
1245
1397
  duration,
@@ -1424,7 +1576,9 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1424
1576
  }));
1425
1577
  }
1426
1578
  const useTrail = renderer.rendererType === "TRAIL" /* TRAIL */;
1427
- const useInstancing = !useTrail && renderer.rendererType === "INSTANCED" /* INSTANCED */;
1579
+ const useMesh = renderer.rendererType === "MESH" /* MESH */;
1580
+ const useInstancing = !useTrail && !useMesh && renderer.rendererType === "INSTANCED" /* INSTANCED */;
1581
+ const useInstancedAttributes = useInstancing || useMesh;
1428
1582
  const defaultTrailCurve = {
1429
1583
  type: "BEZIER" /* BEZIER */,
1430
1584
  scale: 1,
@@ -1455,8 +1609,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1455
1609
  generalData.positionHistoryIndex = new Uint16Array(maxParticles);
1456
1610
  generalData.positionHistoryCount = new Uint16Array(maxParticles);
1457
1611
  }
1458
- const attr = (name) => useInstancing ? `instance${name.charAt(0).toUpperCase()}${name.slice(1)}` : name;
1459
- const posAttr = useInstancing ? "instanceOffset" : "position";
1612
+ const attr = (name) => useInstancedAttributes ? `instance${name.charAt(0).toUpperCase()}${name.slice(1)}` : name;
1613
+ const posAttr = useInstancedAttributes ? "instanceOffset" : "position";
1460
1614
  const sharedUniforms = {
1461
1615
  elapsed: { value: 0 },
1462
1616
  map: { value: particleMap },
@@ -1470,17 +1624,46 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1470
1624
  backgroundColorTolerance: { value: renderer.backgroundColorTolerance },
1471
1625
  ...useInstancing ? { viewportHeight: { value: 1 } } : {}
1472
1626
  };
1627
+ const getVertexShader = () => {
1628
+ if (useMesh) return mesh_particle_vertex_shader_glsl_default;
1629
+ if (useInstancing) return instanced_particle_vertex_shader_glsl_default;
1630
+ return particle_system_vertex_shader_glsl_default;
1631
+ };
1632
+ const getFragmentShader = () => {
1633
+ if (useMesh) return mesh_particle_fragment_shader_glsl_default;
1634
+ if (useInstancing) return instanced_particle_fragment_shader_glsl_default;
1635
+ return particle_system_fragment_shader_glsl_default;
1636
+ };
1473
1637
  const material = new THREE4.ShaderMaterial({
1474
1638
  uniforms: sharedUniforms,
1475
- vertexShader: useInstancing ? instanced_particle_vertex_shader_glsl_default : particle_system_vertex_shader_glsl_default,
1476
- fragmentShader: useInstancing ? instanced_particle_fragment_shader_glsl_default : particle_system_fragment_shader_glsl_default,
1639
+ vertexShader: getVertexShader(),
1640
+ fragmentShader: getFragmentShader(),
1477
1641
  transparent: renderer.transparent,
1478
1642
  blending: renderer.blending,
1479
1643
  depthTest: renderer.depthTest,
1480
1644
  depthWrite: renderer.depthWrite
1481
1645
  });
1482
1646
  let geometry;
1483
- if (useInstancing) {
1647
+ if (useMesh) {
1648
+ const meshConfig = renderer.mesh;
1649
+ if (!meshConfig?.geometry) {
1650
+ throw new Error(
1651
+ "RendererType.MESH requires a mesh configuration with a geometry. Set renderer.mesh.geometry to a THREE.BufferGeometry instance."
1652
+ );
1653
+ }
1654
+ const instancedGeometry = new THREE4.InstancedBufferGeometry();
1655
+ const sourceGeom = meshConfig.geometry;
1656
+ const srcPos = sourceGeom.getAttribute("position");
1657
+ if (srcPos) instancedGeometry.setAttribute("position", srcPos);
1658
+ const srcNormal = sourceGeom.getAttribute("normal");
1659
+ if (srcNormal) instancedGeometry.setAttribute("normal", srcNormal);
1660
+ const srcUv = sourceGeom.getAttribute("uv");
1661
+ if (srcUv) instancedGeometry.setAttribute("uv", srcUv);
1662
+ const srcIndex = sourceGeom.getIndex();
1663
+ if (srcIndex) instancedGeometry.setIndex(srcIndex);
1664
+ instancedGeometry.instanceCount = maxParticles;
1665
+ geometry = instancedGeometry;
1666
+ } else if (useInstancing) {
1484
1667
  const instancedGeometry = new THREE4.InstancedBufferGeometry();
1485
1668
  const quadPositions = new Float32Array([
1486
1669
  -0.5,
@@ -1521,28 +1704,28 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1521
1704
  positionArray[i * 3 + 1] = startPositions[i].y;
1522
1705
  positionArray[i * 3 + 2] = startPositions[i].z;
1523
1706
  }
1524
- const positionAttribute = useInstancing ? new THREE4.InstancedBufferAttribute(positionArray, 3) : new THREE4.BufferAttribute(positionArray, 3);
1707
+ const positionAttribute = useInstancedAttributes ? new THREE4.InstancedBufferAttribute(positionArray, 3) : new THREE4.BufferAttribute(positionArray, 3);
1525
1708
  geometry.setAttribute(posAttr, positionAttribute);
1526
1709
  createFloat32Attributes({
1527
1710
  geometry,
1528
1711
  propertyName: attr("isActive"),
1529
1712
  maxParticles,
1530
1713
  factory: 0,
1531
- instanced: useInstancing
1714
+ instanced: useInstancedAttributes
1532
1715
  });
1533
1716
  createFloat32Attributes({
1534
1717
  geometry,
1535
1718
  propertyName: attr("lifetime"),
1536
1719
  maxParticles,
1537
1720
  factory: 0,
1538
- instanced: useInstancing
1721
+ instanced: useInstancedAttributes
1539
1722
  });
1540
1723
  createFloat32Attributes({
1541
1724
  geometry,
1542
1725
  propertyName: attr("startLifetime"),
1543
1726
  maxParticles,
1544
1727
  factory: () => calculateValue(generalData.particleSystemId, startLifetime, 0) * 1e3,
1545
- instanced: useInstancing
1728
+ instanced: useInstancedAttributes
1546
1729
  });
1547
1730
  createFloat32Attributes({
1548
1731
  geometry,
@@ -1553,21 +1736,21 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1553
1736
  textureSheetAnimation.startFrame,
1554
1737
  0
1555
1738
  ) : 0,
1556
- instanced: useInstancing
1739
+ instanced: useInstancedAttributes
1557
1740
  });
1558
1741
  createFloat32Attributes({
1559
1742
  geometry,
1560
1743
  propertyName: attr("size"),
1561
1744
  maxParticles,
1562
1745
  factory: (_, index) => generalData.startValues.startSize[index],
1563
- instanced: useInstancing
1746
+ instanced: useInstancedAttributes
1564
1747
  });
1565
1748
  createFloat32Attributes({
1566
1749
  geometry,
1567
1750
  propertyName: attr("rotation"),
1568
1751
  maxParticles,
1569
1752
  factory: 0,
1570
- instanced: useInstancing
1753
+ instanced: useInstancedAttributes
1571
1754
  });
1572
1755
  const colorRandomRatio = Math.random();
1573
1756
  createFloat32Attributes({
@@ -1575,29 +1758,39 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1575
1758
  propertyName: attr("colorR"),
1576
1759
  maxParticles,
1577
1760
  factory: () => startColor.min.r + colorRandomRatio * (startColor.max.r - startColor.min.r),
1578
- instanced: useInstancing
1761
+ instanced: useInstancedAttributes
1579
1762
  });
1580
1763
  createFloat32Attributes({
1581
1764
  geometry,
1582
1765
  propertyName: attr("colorG"),
1583
1766
  maxParticles,
1584
1767
  factory: () => startColor.min.g + colorRandomRatio * (startColor.max.g - startColor.min.g),
1585
- instanced: useInstancing
1768
+ instanced: useInstancedAttributes
1586
1769
  });
1587
1770
  createFloat32Attributes({
1588
1771
  geometry,
1589
1772
  propertyName: attr("colorB"),
1590
1773
  maxParticles,
1591
1774
  factory: () => startColor.min.b + colorRandomRatio * (startColor.max.b - startColor.min.b),
1592
- instanced: useInstancing
1775
+ instanced: useInstancedAttributes
1593
1776
  });
1594
1777
  createFloat32Attributes({
1595
1778
  geometry,
1596
1779
  propertyName: attr("colorA"),
1597
1780
  maxParticles,
1598
1781
  factory: 0,
1599
- instanced: useInstancing
1782
+ instanced: useInstancedAttributes
1600
1783
  });
1784
+ if (useMesh) {
1785
+ const quatArray = new Float32Array(maxParticles * 4);
1786
+ for (let i = 0; i < maxParticles; i++) {
1787
+ quatArray[i * 4 + 3] = 1;
1788
+ }
1789
+ geometry.setAttribute(
1790
+ attr("quat"),
1791
+ new THREE4.InstancedBufferAttribute(quatArray, 4)
1792
+ );
1793
+ }
1601
1794
  const a = geometry.attributes;
1602
1795
  const aIsActive = a[attr("isActive")];
1603
1796
  const aColorR = a[attr("colorR")];
@@ -1610,6 +1803,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1610
1803
  const aRotation = a[attr("rotation")];
1611
1804
  const aLifetime = a[attr("lifetime")];
1612
1805
  const aPosition = a[posAttr];
1806
+ const aQuat = useMesh ? a[attr("quat")] : void 0;
1613
1807
  const deactivateParticle = (particleIndex) => {
1614
1808
  aIsActive.array[particleIndex] = 0;
1615
1809
  aColorA.array[particleIndex] = 0;
@@ -1671,6 +1865,16 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1671
1865
  generalData.normalizedLifetimePercentage
1672
1866
  );
1673
1867
  aRotation.needsUpdate = true;
1868
+ if (aQuat) {
1869
+ const rotZ = aRotation.array[particleIndex];
1870
+ const halfZ = rotZ * 0.5;
1871
+ const qi = particleIndex * 4;
1872
+ aQuat.array[qi] = 0;
1873
+ aQuat.array[qi + 1] = 0;
1874
+ aQuat.array[qi + 2] = Math.sin(halfZ);
1875
+ aQuat.array[qi + 3] = Math.cos(halfZ);
1876
+ aQuat.needsUpdate = true;
1877
+ }
1674
1878
  if (normalizedConfig.rotationOverLifetime.isActive)
1675
1879
  generalData.lifetimeValues.rotationOverLifetime[particleIndex] = THREE4.MathUtils.randFloat(
1676
1880
  normalizedConfig.rotationOverLifetime.min,
@@ -1796,7 +2000,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1796
2000
  },
1797
2001
  renderer: {
1798
2002
  ...subConfig.config.renderer ?? {},
1799
- rendererType: renderer.rendererType
2003
+ ...subConfig.config.renderer?.rendererType ? {} : renderer.rendererType === "MESH" /* MESH */ || renderer.rendererType === "TRAIL" /* TRAIL */ ? {} : { rendererType: renderer.rendererType }
1800
2004
  },
1801
2005
  ...inheritVelocity > 0 ? {
1802
2006
  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
@@ -1923,7 +2127,7 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1923
2127
  };
1924
2128
  }
1925
2129
  }
1926
- let particleSystem = useInstancing ? new THREE4.Mesh(geometry, material) : new THREE4.Points(geometry, material);
2130
+ let particleSystem = useInstancing || useMesh ? new THREE4.Mesh(geometry, material) : new THREE4.Points(geometry, material);
1927
2131
  if (useInstancing) {
1928
2132
  particleSystem.onBeforeRender = (glRenderer) => {
1929
2133
  const size = glRenderer.getSize(new THREE4.Vector2());
@@ -1950,7 +2154,8 @@ var createParticleSystem = (config = DEFAULT_PARTICLE_SYSTEM_CONFIG, externalNow
1950
2154
  colorR: aColorR,
1951
2155
  colorG: aColorG,
1952
2156
  colorB: aColorB,
1953
- colorA: aColorA
2157
+ colorA: aColorA,
2158
+ ...useMesh ? { quat: aQuat } : {}
1954
2159
  };
1955
2160
  const calculatedCreationTime = now + calculateValue(generalData.particleSystemId, startDelay) * 1e3;
1956
2161
  let wrapper;
@@ -2491,6 +2696,6 @@ var updateParticleSystems = (cycleData) => {
2491
2696
  );
2492
2697
  };
2493
2698
 
2494
- export { CurveFunctionId, EmitFrom, ForceFieldFalloff, ForceFieldType, LifeTimeCurve, RendererType, Shape, SimulationSpace, SubEmitterTrigger, TimeMode, applyModifiers, blendingMap, calculateRandomPositionAndVelocityOnBox, calculateRandomPositionAndVelocityOnCircle, calculateRandomPositionAndVelocityOnCone, calculateRandomPositionAndVelocityOnRectangle, calculateRandomPositionAndVelocityOnSphere, calculateValue, createBezierCurveFunction, createDefaultParticleTexture, createParticleSystem, curveFunctionIdMap, getBezierCacheSize, getCurveFunction, getCurveFunctionFromConfig, getDefaultParticleSystemConfig, isLifeTimeCurve, removeBezierCurveFunction, updateParticleSystems };
2699
+ 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 };
2495
2700
  //# sourceMappingURL=index.js.map
2496
2701
  //# sourceMappingURL=index.js.map