hz-particles 1.0.11 → 1.0.13

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
@@ -4,7 +4,7 @@
4
4
  [![license](https://img.shields.io/npm/l/hz-particles.svg)](https://github.com/jguyet/particle-system/blob/main/LICENSE)
5
5
  [![WebGPU](https://img.shields.io/badge/WebGPU-required-blue.svg)](https://caniuse.com/webgpu)
6
6
 
7
- A high-performance WebGPU particle engine for the web.
7
+ A high-performance WebGPU particle engine for the web. Use it as a standalone WebGPU renderer or as a plug-and-play React Three Fiber component.
8
8
 
9
9
  ## Features
10
10
 
@@ -15,6 +15,7 @@ A high-performance WebGPU particle engine for the web.
15
15
  - **Scene Serialization** — Save/load particle configurations as JSON
16
16
  - **Multiple Emitter Shapes** — Sphere, cube, cylinder, circle, square emission patterns
17
17
  - **Real-time Physics** — Gravity, attractors, drag, velocity, and lifetime management
18
+ - **React Three Fiber Component** — Drop-in `HZParticlesFX` component with preset support
18
19
  - **3D Object Support** — Static 3D objects alongside particle systems
19
20
 
20
21
  ## Requirements
@@ -28,18 +29,36 @@ Check browser support: [caniuse.com/webgpu](https://caniuse.com/webgpu)
28
29
 
29
30
  ## Installation
30
31
 
32
+ ### WebGPU
33
+
31
34
  ```bash
32
35
  npm install hz-particles
33
36
  ```
34
37
 
38
+ ```javascript
39
+ import { initWebGPU, ParticleSystemManager } from 'hz-particles';
40
+ ```
41
+
42
+ ### React Three Fiber
43
+
44
+ ```bash
45
+ npm install hz-particles
46
+ ```
47
+
48
+ ```javascript
49
+ import { HZParticlesFX } from 'hz-particles/r3f';
50
+ ```
51
+
52
+ > **Important:** Your R3F `Canvas` must use `WebGPURenderer`. Pass `gl={{ powerPreference: 'high-performance' }}` and configure Three.js to use its WebGPU backend, or use `@react-three/fiber` with a WebGPU-capable renderer setup.
53
+
35
54
  ## Quick Start
36
55
 
37
- ### Option 1: Using the built-in WebGPU helper
56
+ ### WebGPU
38
57
 
39
58
  ```javascript
40
59
  import { initWebGPU, ParticleSystemManager } from 'hz-particles';
41
60
 
42
- // 1. Setup WebGPU context with the convenience helper
61
+ // 1. Setup WebGPU context
43
62
  const canvas = document.getElementById('webgpu-canvas');
44
63
  canvas.width = 800;
45
64
  canvas.height = 600;
@@ -54,16 +73,10 @@ const systemId = manager.createParticleSystem({
54
73
  particleCount: 1000,
55
74
  });
56
75
 
57
- // 4. Get active system and initialize
76
+ // 4. Initialize compute pipeline
58
77
  const system = manager.getActiveSystem();
59
78
  await system.initComputePipeline(device);
60
79
 
61
- // Optional: Load a texture
62
- const response = await fetch('particle-texture.png');
63
- const blob = await response.blob();
64
- const imageBitmap = await createImageBitmap(blob);
65
- await system.setTexture(imageBitmap);
66
-
67
80
  // 5. Render loop
68
81
  let lastTime = performance.now();
69
82
 
@@ -72,10 +85,8 @@ function render() {
72
85
  const deltaTime = (currentTime - lastTime) / 1000;
73
86
  lastTime = currentTime;
74
87
 
75
- // Update physics
76
88
  manager.updateAllSystems(deltaTime);
77
89
 
78
- // Render
79
90
  const commandEncoder = device.createCommandEncoder();
80
91
  const renderPass = commandEncoder.beginRenderPass({
81
92
  colorAttachments: [{
@@ -96,144 +107,99 @@ function render() {
96
107
  render();
97
108
  ```
98
109
 
99
- ### Option 2: Using an existing WebGPU setup
100
-
101
- If you already have a WebGPU device and context (e.g., from an existing project or framework), you can skip `initWebGPU()` and use the library directly:
102
-
103
- ```javascript
104
- import { ParticleSystemManager, createRenderTextures, createDepthTexture } from 'hz-particles';
105
-
106
- // Assume you already have these from your existing WebGPU setup
107
- const device = yourExistingDevice;
108
- const context = yourExistingContext;
109
- const format = navigator.gpu.getPreferredCanvasFormat();
110
- const canvas = yourExistingCanvas;
111
-
112
- // Create particle system manager with your device
113
- const manager = new ParticleSystemManager(device);
114
-
115
- // Create a particle system
116
- const systemId = manager.createParticleSystem({
117
- maxParticles: 10000,
118
- particleCount: 1000,
119
- });
120
-
121
- // Initialize and use as shown in Option 1
122
- const system = manager.getActiveSystem();
123
- await system.initComputePipeline(device);
124
-
125
- // Your render loop...
126
- ```
127
-
128
- This approach is ideal for integrating into existing WebGPU applications, frameworks, or when you need custom WebGPU initialization.
129
-
130
- ## React Three Fiber Integration
110
+ ### React Three Fiber
131
111
 
132
- While `hz-particles` uses native WebGPU (not Three.js internally), you can integrate it into React Three Fiber applications:
112
+ **Minimal example with a preset file:**
133
113
 
134
114
  ```jsx
135
- import { useEffect, useRef } from 'react';
136
- import { Canvas } from '@react-three/fiber';
137
- import { initWebGPU, ParticleSystemManager } from 'hz-particles';
138
-
139
- function ParticleLayer() {
140
- const canvasRef = useRef();
141
- const engineRef = useRef();
142
-
143
- useEffect(() => {
144
- let animationId;
145
-
146
- async function init() {
147
- // Create dedicated WebGPU canvas
148
- const canvas = canvasRef.current;
149
- canvas.width = window.innerWidth;
150
- canvas.height = window.innerHeight;
151
- const { device, context, format } = await initWebGPU(canvas);
152
-
153
- // Setup particle system
154
- const manager = new ParticleSystemManager(device);
155
- manager.createParticleSystem({ particleCount: 1000 });
156
- const system = manager.getActiveSystem();
157
- await system.initComputePipeline(device);
158
-
159
- engineRef.current = { device, context, manager, system };
160
-
161
- // Render loop
162
- let lastTime = performance.now();
163
- function render() {
164
- const { device, context, manager, system } = engineRef.current;
165
- const currentTime = performance.now();
166
- const deltaTime = (currentTime - lastTime) / 1000;
167
- lastTime = currentTime;
168
-
169
- manager.updateAllSystems(deltaTime);
170
-
171
- const commandEncoder = device.createCommandEncoder();
172
- const renderPass = commandEncoder.beginRenderPass({
173
- colorAttachments: [{
174
- view: context.getCurrentTexture().createView(),
175
- clearValue: { r: 0, g: 0, b: 0, a: 0 }, // Transparent
176
- loadOp: 'clear',
177
- storeOp: 'store',
178
- }],
179
- });
180
- system.render(renderPass);
181
- renderPass.end();
182
- device.queue.submit([commandEncoder.finish()]);
183
-
184
- animationId = requestAnimationFrame(render);
185
- }
186
- render();
187
- }
188
-
189
- init();
190
-
191
- return () => {
192
- if (animationId) cancelAnimationFrame(animationId);
193
- };
194
- }, []);
115
+ import { HZParticlesFX } from 'hz-particles/r3f';
116
+ import explosionPreset from './presets/explosion.json';
195
117
 
118
+ function Scene() {
196
119
  return (
197
- <canvas
198
- ref={canvasRef}
199
- id="webgpu-canvas"
200
- style={{
201
- position: 'absolute',
202
- top: 0,
203
- left: 0,
204
- width: '100%',
205
- height: '100%',
206
- pointerEvents: 'none',
207
- }}
120
+ <HZParticlesFX
121
+ preset={explosionPreset}
122
+ position={[0, 1, 0]}
208
123
  />
209
124
  );
210
125
  }
126
+ ```
127
+
128
+ **Complete example with all key props:**
129
+
130
+ ```jsx
131
+ import { useRef, useState } from 'react';
132
+ import { HZParticlesFX } from 'hz-particles/r3f';
133
+ import explosionPreset from './presets/explosion.json';
134
+
135
+ function Scene() {
136
+ const targetRef = useRef();
137
+ const [resetKey, setResetKey] = useState(0);
211
138
 
212
- // Usage in App
213
- function App() {
214
139
  return (
215
- <div style={{ position: 'relative', width: '100vw', height: '100vh' }}>
216
- {/* R3F scene */}
217
- <Canvas>
218
- <ambientLight />
219
- <mesh>
220
- <boxGeometry />
221
- <meshStandardMaterial />
222
- </mesh>
223
- </Canvas>
224
-
225
- {/* WebGPU particles overlay */}
226
- <ParticleLayer />
227
- </div>
140
+ <>
141
+ <mesh ref={targetRef} position={[0, 1, 0]}>
142
+ <boxGeometry />
143
+ <meshStandardMaterial />
144
+ </mesh>
145
+
146
+ <HZParticlesFX
147
+ preset={explosionPreset}
148
+ positionRef={targetRef}
149
+ scale={1.5}
150
+ resetKey={resetKey}
151
+ onComplete={() => console.log('effect finished')}
152
+ />
153
+
154
+ <button onClick={() => setResetKey(k => k + 1)}>
155
+ Replay
156
+ </button>
157
+ </>
228
158
  );
229
159
  }
230
160
  ```
231
161
 
162
+ ## Props Reference
163
+
164
+ The `HZParticlesFX` component accepts the following props:
165
+
166
+ | Prop | Type | Default | Description |
167
+ |------|------|---------|-------------|
168
+ | `preset` | `SceneData \| string` | — | Particle preset — pass a JSON object or a URL/path to a JSON file |
169
+ | `position` | `[number, number, number]` | `[0, 0, 0]` | Static world-space position of the effect |
170
+ | `positionRef` | `React.RefObject<Object3D>` | — | Attach the effect to a scene object; position tracks the ref each frame |
171
+ | `autoPlay` | `boolean` | `true` | Start playing immediately when mounted |
172
+ | `visible` | `boolean` | `true` | Show or hide the effect without unmounting |
173
+ | `scale` | `number` | `1` | Uniform scale applied to the entire effect |
174
+ | `resetKey` | `number` | `0` | Increment to reset and respawn the particle system |
175
+ | `onComplete` | `() => void` | — | Callback fired when the effect finishes playing |
176
+
177
+ ## Preset Configuration
178
+
179
+ Presets are plain JSON files that describe a particle scene. Key fields:
180
+
181
+ | Field | Type | Description |
182
+ |-------|------|-------------|
183
+ | `particleCount` | `number` | Number of active particles |
184
+ | `lifetime` | `number` | Particle lifetime in seconds |
185
+ | `emissionRate` | `number` | Particles emitted per second |
186
+ | `emissionShape` | `string` | Emitter shape: `sphere`, `cube`, `cylinder`, `circle`, `square` |
187
+ | `particleSize` | `number` | Base size of each particle |
188
+ | `colors` | `string[]` | Array of hex color values interpolated over lifetime |
189
+ | `fadeIn` | `number` | Opacity fade-in duration (0–1, fraction of lifetime) |
190
+ | `fadeOut` | `number` | Opacity fade-out start (0–1, fraction of lifetime) |
191
+ | `bloom` | `boolean` | Enable bloom glow on particles |
192
+ | `gravity` | `number` | Gravity strength applied to particles |
193
+ | `damping` | `number` | Velocity damping factor (0 = no drag, 1 = full stop) |
194
+ | `emissionTrailEnabled` | `boolean` | Enable particle trail rendering |
195
+ | `emissionTrailDuration` | `number` | How long trail segments persist in seconds |
196
+ | `emissionTrailWidth` | `number` | Width of the emission trail |
197
+
232
198
  ## API Reference
233
199
 
234
200
  ### `initWebGPU(canvas)`
235
201
 
236
- Convenience helper to initialize WebGPU context and device. **Library consumers with their own WebGPU setup do NOT need to use this function** — you can pass your existing `device`, `context`, `format`, and `canvas` directly to the library's components.
202
+ Convenience helper to initialize a WebGPU context and device.
237
203
 
238
204
  ```typescript
239
205
  async function initWebGPU(canvas: HTMLCanvasElement): Promise<{
@@ -245,21 +211,17 @@ async function initWebGPU(canvas: HTMLCanvasElement): Promise<{
245
211
  ```
246
212
 
247
213
  **Parameters:**
248
- - `canvas` (required) — Canvas element for WebGPU rendering. You must size the canvas before calling this function.
214
+ - `canvas` (required) — Canvas element. Set `canvas.width` and `canvas.height` before calling.
249
215
 
250
- **Returns:** Object with WebGPU device, canvas context, texture format, and canvas element
216
+ **Returns:** Object with WebGPU device, canvas context, texture format, and canvas element.
251
217
 
252
- **Throws:**
253
- - Error if `canvas` is not provided
254
- - Error if WebGPU is not supported
255
-
256
- **Note:** This function does NOT handle canvas sizing. You should set `canvas.width` and `canvas.height` before calling `initWebGPU()`.
218
+ **Throws:** Error if `canvas` is missing or WebGPU is not supported.
257
219
 
258
220
  ---
259
221
 
260
222
  ### `ParticleSystem`
261
223
 
262
- Core particle system class managing particle simulation and rendering.
224
+ Core particle system class managing simulation and rendering.
263
225
 
264
226
  ```typescript
265
227
  class ParticleSystem {
@@ -267,29 +229,22 @@ class ParticleSystem {
267
229
  }
268
230
  ```
269
231
 
270
- **Constructor Parameters:**
271
- - `device` — GPUDevice instance
272
- - `config` (optional) — Configuration object:
273
- - `maxParticles` (number, default 10000) — Maximum particle buffer size
274
- - `particleCount` (number, default 100) — Initial active particle count
232
+ **Config options:**
233
+ - `maxParticles` (number, default `10000`) Maximum particle buffer size
234
+ - `particleCount` (number, default `100`) — Initial active particle count
275
235
 
276
236
  **Key Methods:**
277
237
 
278
- - `async initComputePipeline(device: GPUDevice)` — Initialize GPU compute and render pipelines. Must be called before rendering.
279
-
280
- - `async setTexture(imageBitmap: ImageBitmap)` Set particle texture from ImageBitmap.
281
-
282
- - `async setGLBModel(arrayBuffer: ArrayBuffer)` Use GLB model geometry as particle shape. Automatically extracts textures.
283
-
284
- - `updateParticles(deltaTime: number)` Execute physics simulation step on GPU.
285
-
286
- - `spawnParticles()` Emit new particles according to emitter configuration.
287
-
288
- - `setGravity(value: number)` — Set gravity strength (default 9.8).
289
-
290
- - `setAttractor(strength: number, position: [number, number, number])` — Set attractor point with strength and 3D position.
291
-
292
- - `render(renderPass: GPURenderPassEncoder)` — Render particles to the current render pass.
238
+ | Method | Description |
239
+ |--------|-------------|
240
+ | `initComputePipeline(device)` | Initialize GPU compute and render pipelines. Must be called before rendering. |
241
+ | `setTexture(imageBitmap)` | Set particle texture from an `ImageBitmap`. |
242
+ | `setGLBModel(arrayBuffer)` | Use GLB model geometry as particle shape. Automatically extracts textures. |
243
+ | `updateParticles(deltaTime)` | Execute a physics simulation step on the GPU. |
244
+ | `spawnParticles()` | Emit new particles according to emitter configuration. |
245
+ | `setGravity(value)` | Set gravity strength (default `9.8`). |
246
+ | `setAttractor(strength, position)` | Set an attractor point with strength and 3D position `[x, y, z]`. |
247
+ | `render(renderPass)` | Render particles to the current render pass. |
293
248
 
294
249
  ---
295
250
 
@@ -305,23 +260,17 @@ class ParticleSystemManager {
305
260
 
306
261
  **Key Methods:**
307
262
 
308
- - `createParticleSystem(config?: object): number` — Create new particle system. Returns system ID.
309
-
310
- - `getActiveSystem(): ParticleSystem | null` Get currently active particle system instance.
311
-
312
- - `getActiveConfig(): object | null` Get active system configuration.
313
-
314
- - `setActiveSystem(index: number): boolean` Switch active system by index. Returns success status.
315
-
316
- - `removeSystem(index: number): boolean` Remove system by index. Returns success status.
317
-
318
- - `updateAllSystems(deltaTime: number)` Update physics for all systems.
319
-
320
- - `getSystemsList(): Array<{name: string, id: number, index: number, isActive: boolean}>` — Get list of all systems with metadata.
321
-
322
- - `duplicateActiveSystem(): number` — Clone active system. Returns new system ID.
323
-
324
- - `async replaceSystems(sceneData: object): boolean` — Load scene from serialized data. Returns success status.
263
+ | Method | Description |
264
+ |--------|-------------|
265
+ | `createParticleSystem(config?)` | Create a new particle system. Returns system ID. |
266
+ | `getActiveSystem()` | Get the currently active `ParticleSystem` instance. |
267
+ | `getActiveConfig()` | Get the active system configuration object. |
268
+ | `setActiveSystem(index)` | Switch active system by index. Returns success status. |
269
+ | `removeSystem(index)` | Remove a system by index. Returns success status. |
270
+ | `updateAllSystems(deltaTime)` | Update physics for all systems. |
271
+ | `getSystemsList()` | Get list of all systems with `name`, `id`, `index`, `isActive`. |
272
+ | `duplicateActiveSystem()` | Clone the active system. Returns new system ID. |
273
+ | `replaceSystems(sceneData)` | Load a scene from serialized data. Returns success status. |
325
274
 
326
275
  ---
327
276
 
@@ -342,11 +291,6 @@ async function parseGLB(arrayBuffer: ArrayBuffer): Promise<{
342
291
  }>
343
292
  ```
344
293
 
345
- **Parameters:**
346
- - `arrayBuffer` — GLB file as ArrayBuffer
347
-
348
- **Returns:** Parsed geometry and animation data
349
-
350
294
  ---
351
295
 
352
296
  ### `GLBAnimator`
@@ -359,42 +303,22 @@ class GLBAnimator {
359
303
 
360
304
  currentTime: number
361
305
  playing: boolean
362
- speed: number // default 1.0
363
- loop: boolean // default true
306
+ speed: number // default 1.0
307
+ loop: boolean // default true
364
308
  }
365
309
  ```
366
310
 
367
311
  **Key Methods:**
368
312
 
369
- - `setRestPose(positions: Float32Array, normals: Float32Array)` — Set T-pose/bind pose for animation.
370
-
371
- - `update(deltaTime: number): { positions: Float32Array, normals: Float32Array, changed: boolean }` Advance animation by deltaTime. Returns deformed geometry and change status.
372
-
373
- - `setAnimation(index: number)` Switch to animation clip by index.
374
-
375
- - `getAnimationNames(): string[]` — Get list of available animation clip names.
376
-
377
- ---
378
-
379
- ### Secondary Exports
380
-
381
- The following modules are also exported for advanced usage:
382
-
383
- - **`ParticleEmitter`** — Emission shape configuration (sphere, cube, cylinder, circle, square)
384
- - **`ParticlePhysics`** — Physics simulation parameter management
385
- - **`ParticleTextureManager`** — Texture loading utilities
386
- - **`Objects3DManager`** — 3D object scene management
387
- - **`saveScene(manager)`** — Export scene as JSON file download
388
- - **`loadScene(event)`** — Load scene from file input event
389
- - **`extractGLBTexture(arrayBuffer)`** — Extract base color texture from GLB file
390
- - **Shader exports** — WGSL shader code for compute and rendering pipelines
391
- - **Geometry helpers** — Primitive shape generators (cube, sphere, etc.)
392
- - **Render pipeline creators** — Low-level WebGPU pipeline construction
313
+ | Method | Description |
314
+ |--------|-------------|
315
+ | `setRestPose(positions, normals)` | Set the T-pose/bind pose for animation. |
316
+ | `update(deltaTime)` | Advance animation. Returns `{ positions, normals, changed }`. |
317
+ | `setAnimation(index)` | Switch to animation clip by index. |
318
+ | `getAnimationNames()` | Get list of available animation clip names. |
393
319
 
394
320
  ## GLB Models & Animation
395
321
 
396
- Load and animate 3D models:
397
-
398
322
  ```javascript
399
323
  import { parseGLB, GLBAnimator } from 'hz-particles';
400
324
 
@@ -425,20 +349,16 @@ if (glbData.animationData) {
425
349
 
426
350
  ## Scene Save/Load
427
351
 
428
- Serialize and restore particle configurations:
429
-
430
352
  ```javascript
431
353
  import { saveScene, loadScene } from 'hz-particles';
432
354
 
433
355
  // Save current scene
434
- const button = document.getElementById('save-button');
435
- button.addEventListener('click', () => {
356
+ document.getElementById('save-button').addEventListener('click', () => {
436
357
  saveScene(manager); // Downloads scene.json
437
358
  });
438
359
 
439
360
  // Load scene from file input
440
- const input = document.getElementById('file-input');
441
- input.addEventListener('change', async (event) => {
361
+ document.getElementById('file-input').addEventListener('change', async (event) => {
442
362
  const success = await loadScene(event, manager);
443
363
  if (success) {
444
364
  console.log('Scene loaded successfully');
@@ -446,28 +366,45 @@ input.addEventListener('change', async (event) => {
446
366
  });
447
367
  ```
448
368
 
449
- ## Online Editor
450
-
451
- Try the interactive particle editor at `http://localhost:8110/editor` when running the Docker container (`docker-compose up` from the repository root)
452
-
453
- The editor provides a visual interface for:
454
- - Real-time particle system configuration
455
- - Emitter shape selection and tuning
456
- - GLB model import and animation control
457
- - Scene preset library
458
- - Export/import scene JSON
459
-
460
369
  ## TypeScript
461
370
 
462
- Type declarations are not yet included in this package. For TypeScript projects, you can suppress the import error:
371
+ Type declarations are not yet included in this package. For TypeScript projects, suppress the import error with:
463
372
 
464
373
  ```typescript
465
374
  // @ts-ignore
466
375
  import { ParticleSystem, initWebGPU } from 'hz-particles';
376
+ // @ts-ignore
377
+ import { HZParticlesFX } from 'hz-particles/r3f';
467
378
  ```
468
379
 
469
380
  Community-contributed type definitions are welcome via PR.
470
381
 
382
+ ## Secondary Exports
383
+
384
+ The following modules are also exported for advanced usage:
385
+
386
+ - **`ParticleEmitter`** — Emission shape configuration (sphere, cube, cylinder, circle, square)
387
+ - **`ParticlePhysics`** — Physics simulation parameter management
388
+ - **`ParticleTextureManager`** — Texture loading utilities
389
+ - **`Objects3DManager`** — 3D object scene management
390
+ - **`saveScene(manager)`** — Export scene as JSON file download
391
+ - **`loadScene(event)`** — Load scene from file input event
392
+ - **`extractGLBTexture(arrayBuffer)`** — Extract base color texture from GLB file
393
+ - **Shader exports** — WGSL shader code for compute and rendering pipelines
394
+ - **Geometry helpers** — Primitive shape generators (cube, sphere, etc.)
395
+ - **Render pipeline creators** — Low-level WebGPU pipeline construction
396
+
397
+ ## Online Editor
398
+
399
+ Try the interactive particle editor at `http://localhost:8110/editor` when running the Docker container (`docker-compose up` from the repository root).
400
+
401
+ The editor provides a visual interface for:
402
+ - Real-time particle system configuration
403
+ - Emitter shape selection and tuning
404
+ - GLB model import and animation control
405
+ - Scene preset library
406
+ - Export/import scene JSON
407
+
471
408
  ## Contributing
472
409
 
473
410
  Contributions are welcome! Please follow these steps:
@@ -1,13 +1,13 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ke=require("react/jsx-runtime"),o=require("react"),Ne=require("@react-three/fiber"),$t=require("three"),xt=require("hz-particles");function Kt(n){const i=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const l in n)if(l!=="default"){const T=Object.getOwnPropertyDescriptor(n,l);Object.defineProperty(i,l,T.get?T:{enumerable:!0,get:()=>n[l]})}}return i.default=n,Object.freeze(i)}const c=Kt($t);function vt(n){return Math.abs(Math.sin(n*12.9898)*43758.5453)%1}function en(n){const i=Math.sin(n*54321.67)*43758.5453%1;return i<0?i+1:i}function gt(n,i,l,T){if(l<=0)return 0;const d=i/l;let X=n.particleSize??.5;if(n.randomSize){const O=n.minSize??.1,Z=n.maxSize??.5;X=O+(Z-O)*en(l)}const Y=Math.max(.01,Math.min(10,n.sizeLifetimeSpeed??1));if(n.fadeSizeEnabled&&(X*=1-Math.pow(d,1/Y)),n.increaseSizeEnabled&&(X*=1+Math.pow(d,1/Y)),n.pulseEnabled){const O=n.pulseAmplitude??.5,Z=n.pulseFrequency??1,re=(n.pulsePhaseRandom??0)*vt(T)*Math.PI*2;X*=1+O*Math.sin(i*Z*Math.PI*2+re)}return Math.max(0,X)}function bt(n,i,l,T){if(l<=0)return 0;let d=n.opacity??1;const X=i/l;if(n.fadeEnabled&&(d*=Math.max(0,1-X)),n.pulseEnabled&&n.pulseOpacity){const Y=n.pulseAmplitude??.5,O=n.pulseFrequency??1,J=(n.pulsePhaseRandom??0)*vt(T)*Math.PI*2;d*=Math.max(0,1+Y*Math.sin(i*O*Math.PI*2+J))}return Math.max(0,Math.min(1,d))}function Ot(n,i){return i===1?n:{...n,systems:n.systems.map(l=>({...l,particleSize:(l.particleSize??.5)*i,minSize:l.minSize!=null?l.minSize*i:void 0,maxSize:l.maxSize!=null?l.maxSize*i:void 0,particleSpeed:(l.particleSpeed??1)*i,minSpeed:l.minSpeed!=null?l.minSpeed*i:void 0,maxSpeed:l.maxSpeed!=null?l.maxSpeed*i:void 0,emissionTrailWidth:l.emissionTrailWidth!=null?l.emissionTrailWidth*i:void 0}))}}function tn(n){let i=0;for(const l of n.systems)i+=l.maxParticles??1e4;return i}function nn({preset:n,position:i,positionRef:l,autoPlay:T=!0,visible:d=!0,scale:X=1,resetKey:Y=0,onComplete:O}){var Ve;const[Z,J]=o.useState(typeof n=="string"?null:n);o.useEffect(()=>{typeof n=="string"?(J(null),xt.fetchPreset(n).then(J).catch(a=>{console.error("[HZParticlesFX] Failed to fetch preset:",a)})):J(n)},[n]);const{camera:re,gl:Qe}=Ne.useThree(),fe=o.useRef(null);if(!fe.current){const a=(Ve=Qe.backend)==null?void 0:Ve.device;a&&(fe.current=a)}const ze=fe.current,Ae=o.useRef(null),ye=o.useRef(null),Re=o.useRef(!1),C=o.useRef(!1),h=o.useMemo(()=>Z?Ot(Z,X):null,[Z,X]),Ye=o.useMemo(()=>h?tn(h):0,[h]),Je=o.useMemo(()=>i?i instanceof c.Vector3?i.clone():new c.Vector3(...i):new c.Vector3(0,0,0),[i]),Pe=o.useMemo(()=>(h==null?void 0:h.systems.some(a=>a.emissionTrailEnabled))??!1,[h]),He=o.useMemo(()=>new c.PlaneGeometry(1,1),[]),_e=o.useMemo(()=>{const a=document.createElement("canvas");a.width=64,a.height=64;const V=a.getContext("2d");V.clearRect(0,0,64,64);const E=V.createRadialGradient(32,32,0,32,32,32);E.addColorStop(0,"rgba(255,255,255,1)"),E.addColorStop(.7,"rgba(255,255,255,1)"),E.addColorStop(1,"rgba(255,255,255,0)"),V.fillStyle=E,V.beginPath(),V.arc(32,32,32,0,Math.PI*2),V.fill();const M=new c.CanvasTexture(a);M.flipY=!1;const _=new c.MeshBasicMaterial({color:16777215,transparent:!0,depthWrite:!1,alphaTest:.01,map:M,vertexColors:!0,side:c.DoubleSide,blending:c.AdditiveBlending});return _.needsUpdate=!0,_},[]),t=64,D=100,Me=o.useRef(null),H=o.useRef(0),p=512,b=o.useRef({buf:new Float32Array(p*4),head:0,count:0,elapsed:0}),{trailGeo:$,trailPosAttr:oe,trailColAttr:y}=o.useMemo(()=>{const a=D*t*6,V=new Float32Array(a*3),E=new Float32Array(a*3),M=new c.BufferGeometry,_=new c.BufferAttribute(V,3);_.setUsage(c.DynamicDrawUsage),M.setAttribute("position",_);const de=new c.BufferAttribute(E,3);return de.setUsage(c.DynamicDrawUsage),M.setAttribute("color",de),M.setDrawRange(0,0),{trailGeo:M,trailPosAttr:_,trailColAttr:de}},[]),U=o.useMemo(()=>new c.MeshBasicMaterial({vertexColors:!0,transparent:!0,depthWrite:!1,side:c.DoubleSide,blending:c.AdditiveBlending}),[]);o.useEffect(()=>{if(!ze||!h)return;const a=new xt.ParticleSystemManager(ze);return ye.current=a,C.current=!1,a.replaceSystems(h).then(()=>{T&&d&&(a.respawnAllSystems(),C.current=!0)}).catch(V=>{console.error("[HZParticlesFX] replaceSystems FAILED:",V)}),()=>{typeof a.destroy=="function"&&a.destroy(),ye.current=null}},[ze,h]),o.useEffect(()=>{const a=ye.current;a&&T&&d&&!C.current&&(a.respawnAllSystems(),C.current=!0,Re.current=!1)},[T,d]);const Se=o.useRef(Y);o.useEffect(()=>{if(Y===Se.current)return;Se.current=Y;const a=b.current;a.head=0,a.count=0,a.elapsed=0,$.setDrawRange(0,0),z.current=null;const V=ye.current;V&&(V.respawnAllSystems(),C.current=!0,Re.current=!1)},[Y]);const xe=o.useMemo(()=>new c.Matrix4,[]),ce=o.useMemo(()=>new c.Vector3,[]),Te=o.useMemo(()=>new c.Vector3,[]),ge=o.useMemo(()=>new c.Quaternion,[]),K=o.useMemo(()=>new c.Vector3,[]),ae=o.useMemo(()=>new c.Vector3(0,0,1),[]),qe=o.useMemo(()=>new c.Color,[]),z=o.useRef(null);return Ne.useFrame((a,V)=>{if(!d||!Ae.current||!ye.current)return;const E=ye.current,M=(l==null?void 0:l.current)??Je,_=V>1?V/1e3:V,de=z.current,$e=de&&_>0,Ht=$e?(M.x-de.x)/_:0,_t=$e?(M.y-de.y)/_:0,qt=$e?(M.z-de.z)/_:0;z.current={x:M.x,y:M.y,z:M.z};for(const{system:s,config:r}of E.particleSystems)r.emissionTrailEnabled?(s.setSimulationTransform({position:[M.x,M.y,M.z],velocity:[Ht,_t,qt]}),r.shapeTranslationX=0,r.shapeTranslationY=0,r.shapeTranslationZ=0):(r.shapeTranslationX=M.x,r.shapeTranslationY=M.y,r.shapeTranslationZ=M.z);E.updateAllSystems(_);for(const{system:s}of E.particleSystems)s.readbackAndProcessParticles();if(Pe){const s=b.current;s.elapsed+=_;const r=s.head*4;s.buf[r]=M.x,s.buf[r+1]=M.y,s.buf[r+2]=M.z,s.buf[r+3]=s.elapsed,s.head=(s.head+1)%p,s.count<p&&s.count++}const Ce=Ae.current;let je=0;for(let s=0;s<E.particleSystems.length;s++){const{system:r,config:x}=E.particleSystems[s];if(x.emissionTrailEnabled)continue;const G=r.particleData,pe=r.activeParticles;if(!G||pe<=0)continue;const B=r._simPosition,A=x.emissionTrailEnabled&&B?B[0]:0,N=x.emissionTrailEnabled&&B?B[1]:0,De=x.emissionTrailEnabled&&B?B[2]:0;for(let ie=0;ie<pe;ie++){const ee=ie*8,Ke=G[ee+0]+A,me=G[ee+1]+N,et=G[ee+2]+De;let be=G[ee+3],ve=G[ee+4],L=G[ee+5];const Ee=G[ee+6],we=G[ee+7];if(Ee>=we||we<=0)continue;const We=Ee/we;if(x.colorTransitionEnabled){const I=x.startColor??[1,0,0],k=x.endColor??[0,0,1];be=I[0]+(k[0]-I[0])*We,ve=I[1]+(k[1]-I[1])*We,L=I[2]+(k[2]-I[2])*We}const he=x.bloomIntensity??1;be=Math.min(1,be*he),ve=Math.min(1,ve*he),L=Math.min(1,L*he);const v=bt(x,Ee,we,ie);be*=v,ve*=v,L*=v;const q=gt(x,Ee,we,ie);if(!(q<=0)){if(ce.set(Ke,me,et),Te.subVectors(re.position,ce).normalize(),ge.setFromUnitVectors(ae,Te),x.velocityStretchEnabled&&r.particleVelocities){const I=ie*4,k=r.particleVelocities[I],Ie=r.particleVelocities[I+1],Fe=r.particleVelocities[I+2],Xe=Math.sqrt(k*k+Ie*Ie+Fe*Fe);if(Xe>.001){const te=x.velocityStretchFactor??1,Q=q*(1+Xe*te);K.set(q,Q,1),Te.set(k,Ie,Fe).normalize(),ge.setFromUnitVectors(ae,Te);const tt=(Q-q)*.5;ce.addScaledVector(Te,tt)}else K.set(q,q,1)}else K.set(q,q,1);xe.compose(ce,ge,K),Ce.setMatrixAt(je,xe),qe.setRGB(be,ve,L),Ce.setColorAt(je,qe),je++}}}Ce.count=je,Ce.instanceMatrix.needsUpdate=!0,Ce.instanceColor&&(Ce.instanceColor.needsUpdate=!0);const wt=je>0||E.particleSystems.some(({system:s})=>s.emitting||s.activeParticles>0);if(Re.current&&!wt&&(C.current=!1,O==null||O()),Re.current=wt,Pe&&Me.current){const s=oe.array,r=y.array;let x=0;for(let pe=0;pe<E.particleSystems.length;pe++){const{system:B,config:A}=E.particleSystems[pe];if(!A.emissionTrailEnabled||A.glbModelEnabled||A.textureEnabled)continue;const N=B.particleData,De=B.particleVelocities,ie=B.activeParticles;if(!N||!De||ie<=0)continue;const ee=A.emissionTrailDuration??1,Ke=A.emissionTrailWidth??.3,me=B._simPosition,et=me?me[0]:0,be=me?me[1]:0,ve=me?me[2]:0,L=B._simVelocity,Ee=L?L[0]:0,we=L?L[1]:0,We=L?L[2]:0,he=b.current,v=he.buf,q=he.count,I=he.head,k=he.elapsed,Ie=q>=2;let Fe=k;if(Ie){const te=((I-q)%p+p)%p;Fe=k-v[te*4+3]}const Xe=k;for(let te=0;te<ie&&!(x+t*6>D*t*6);te++){const Q=te*8,tt=N[Q+6],Ue=N[Q+7];if(tt>=Ue||Ue<=0)continue;const nt=Math.min(Xe,Ue),st=nt/Ue;let Ze=N[Q+3],Ge=N[Q+4],Le=N[Q+5];if(A.colorTransitionEnabled){const e=A.startColor??[1,0,0],f=A.endColor??[0,0,1];Ze=e[0]+(f[0]-e[0])*st,Ge=e[1]+(f[1]-e[1])*st,Le=e[2]+(f[2]-e[2])*st}const rt=A.bloomIntensity??1;if(Ze=Math.min(1,Ze*rt),Ge=Math.min(1,Ge*rt),Le=Math.min(1,Le*rt),gt(A,nt,Ue,te)<=0)continue;const zt=Ke*.5,ot=bt(A,nt,Ue,te);if(ot<.01)continue;const ct=te*4,at=De[ct],it=De[ct+1],lt=De[ct+2],jt=Math.sqrt(at*at+it*it+lt*lt)>.05,At=Math.min(ee,Xe,Fe);if(At<.001)continue;const Wt=(e,f)=>{if(!Ie){f[0]=et-Ee*e,f[1]=be-we*e,f[2]=ve-We*e;return}const S=k-e;for(let j=0;j<q-1;j++){const ne=((I-1-j)%p+p)%p,W=((I-2-j)%p+p)%p,ue=v[ne*4+3],g=v[W*4+3];if(S>=g&&S<=ue){const w=ue!==g?(S-g)/(ue-g):0;f[0]=v[W*4]+(v[ne*4]-v[W*4])*w,f[1]=v[W*4+1]+(v[ne*4+1]-v[W*4+1])*w,f[2]=v[W*4+2]+(v[ne*4+2]-v[W*4+2])*w;return}}const m=((I-q)%p+p)%p;f[0]=v[m*4],f[1]=v[m*4+1],f[2]=v[m*4+2]},Xt=re.position.x,Zt=re.position.y,Gt=re.position.z,u=new Array((t+1)*3);for(let e=0;e<=t;e++){const S=e/t*At,m=[0,0,0];Wt(S,m),jt?(u[e*3]=N[Q]-at*S+m[0],u[e*3+1]=N[Q+1]-it*S+m[1],u[e*3+2]=N[Q+2]-lt*S+m[2]):(u[e*3]=m[0],u[e*3+1]=m[1],u[e*3+2]=m[2])}const le=new Array((t+1)*3);let Rt=0,Pt=0,Tt=0;for(let e=0;e<=t;e++){let f,S,m;e===0?(f=u[3]-u[0],S=u[4]-u[1],m=u[5]-u[2]):e===t?(f=u[e*3]-u[(e-1)*3],S=u[e*3+1]-u[(e-1)*3+1],m=u[e*3+2]-u[(e-1)*3+2]):(f=u[(e+1)*3]-u[(e-1)*3],S=u[(e+1)*3+1]-u[(e-1)*3+1],m=u[(e+1)*3+2]-u[(e-1)*3+2]);let j=Math.sqrt(f*f+S*S+m*m);j<1e-8&&(f=0,S=0,m=1,j=1),f/=j,S/=j,m/=j;const ne=Xt-u[e*3],W=Zt-u[e*3+1],ue=Gt-u[e*3+2];let g=S*ue-m*W,w=m*ne-f*ue,F=f*W-S*ne,se=Math.sqrt(g*g+w*w+F*F);se<1e-8&&(g=-m,w=0,F=f,se=Math.sqrt(g*g+w*w+F*F)),se<1e-8&&(g=0,w=1,F=0,se=1),g/=se,w/=se,F/=se,e>0&&g*Rt+w*Pt+F*Tt<0&&(g=-g,w=-w,F=-F),Rt=g,Pt=w,Tt=F,le[e*3]=g,le[e*3+1]=w,le[e*3+2]=F}for(let e=0;e<t;e++){const f=e/t,S=(e+1)/t,m=u[e*3],j=u[e*3+1],ne=u[e*3+2],W=u[(e+1)*3],ue=u[(e+1)*3+1],g=u[(e+1)*3+2],w=le[e*3],F=le[e*3+1],se=le[e*3+2],ut=le[(e+1)*3],Vt=le[(e+1)*3+1],Ct=le[(e+1)*3+2],Be=zt*(1-f),Oe=zt*(1-S),ft=ot*(1-f)*(1-f),dt=ot*(1-S)*(1-S),Lt=m+w*Be,kt=j+F*Be,Yt=ne+se*Be,Dt=m-w*Be,Et=j-F*Be,It=ne-se*Be,Ft=W+ut*Oe,Ut=ue+Vt*Oe,Bt=g+Ct*Oe,Nt=W-ut*Oe,Qt=ue-Vt*Oe,Jt=g-Ct*Oe,pt=Ze*ft,mt=Ge*ft,ht=Le*ft,yt=Ze*dt,Mt=Ge*dt,St=Le*dt,R=x*3,P=x*3;s[R]=Lt,s[R+1]=kt,s[R+2]=Yt,r[P]=pt,r[P+1]=mt,r[P+2]=ht,s[R+3]=Dt,s[R+4]=Et,s[R+5]=It,r[P+3]=pt,r[P+4]=mt,r[P+5]=ht,s[R+6]=Ft,s[R+7]=Ut,s[R+8]=Bt,r[P+6]=yt,r[P+7]=Mt,r[P+8]=St,s[R+9]=Dt,s[R+10]=Et,s[R+11]=It,r[P+9]=pt,r[P+10]=mt,r[P+11]=ht,s[R+12]=Nt,s[R+13]=Qt,s[R+14]=Jt,r[P+12]=yt,r[P+13]=Mt,r[P+14]=St,s[R+15]=Ft,s[R+16]=Ut,s[R+17]=Bt,r[P+15]=yt,r[P+16]=Mt,r[P+17]=St,x+=6}}}const G=H.current;if(x<G){const pe=x*3,B=G*3;for(let A=pe;A<B;A++)s[A]=0,r[A]=0}H.current=x,$.setDrawRange(0,x),oe.needsUpdate=!0,y.needsUpdate=!0}}),Z?ke.jsxs(ke.Fragment,{children:[ke.jsx("instancedMesh",{ref:a=>{Ae.current=a,a&&(a.count=0)},args:[He,_e,Ye],frustumCulled:!1,renderOrder:100,visible:d}),Pe&&ke.jsx("mesh",{ref:Me,geometry:$,material:U,frustumCulled:!1,renderOrder:99,visible:d})]}):null}const sn=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Oe=require("react/jsx-runtime"),r=require("react"),je=require("@react-three/fiber"),Vt=require("three"),Xe=require("hz-particles");function Dt(e){const u=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const f in e)if(f!=="default"){const A=Object.getOwnPropertyDescriptor(e,f);Object.defineProperty(u,f,A.get?A:{enumerable:!0,get:()=>e[f]})}}return u.default=e,Object.freeze(u)}const c=Dt(Vt);function ke(e){return Math.abs(Math.sin(e*12.9898)*43758.5453)%1}function Et(e){const u=Math.sin(e*54321.67)*43758.5453%1;return u<0?u+1:u}function xt(e,u,f,A){if(f<=0)return 0;const p=u/f;let _=e.particleSize??.5;if(e.randomSize){const q=e.minSize??.1,W=e.maxSize??.5;_=q+(W-q)*Et(f)}const Z=Math.max(.01,Math.min(10,e.sizeLifetimeSpeed??1));if(e.fadeSizeEnabled&&(_*=1-Math.pow(p,1/Z)),e.increaseSizeEnabled&&(_*=1+Math.pow(p,1/Z)),e.pulseEnabled){const q=e.pulseAmplitude??.5,W=e.pulseFrequency??1,te=(e.pulsePhaseRandom??0)*ke(A)*Math.PI*2;_*=1+q*Math.sin(u*W*Math.PI*2+te)}return Math.max(0,_)}function St(e,u,f,A){if(f<=0)return 0;let p=e.opacity??1;const _=u/f;if(e.fadeEnabled&&(p*=Math.max(0,1-_)),e.pulseEnabled&&e.pulseOpacity){const Z=e.pulseAmplitude??.5,q=e.pulseFrequency??1,J=(e.pulsePhaseRandom??0)*ke(A)*Math.PI*2;p*=Math.max(0,1+Z*Math.sin(u*q*Math.PI*2+J))}return Math.max(0,Math.min(1,p))}function gt(e,u){return u===1?e:{...e,systems:e.systems.map(f=>({...f,particleSize:(f.particleSize??.5)*u,minSize:f.minSize!=null?f.minSize*u:void 0,maxSize:f.maxSize!=null?f.maxSize*u:void 0,particleSpeed:(f.particleSpeed??1)*u,minSpeed:f.minSpeed!=null?f.minSpeed*u:void 0,maxSpeed:f.maxSpeed!=null?f.maxSpeed*u:void 0,emissionTrailWidth:(f.emissionTrailWidth??.3)*u}))}}function Ft(e){let u=0;for(const f of e.systems)u+=f.maxParticles??1e4;return u}function It({preset:e,position:u,positionRef:f,autoPlay:A=!0,visible:p=!0,scale:_=1,resetKey:Z=0,onComplete:q}){var Ce;const[W,J]=r.useState(typeof e=="string"?null:e);r.useEffect(()=>{typeof e=="string"?(J(null),Xe.fetchPreset(e).then(J).catch(a=>{console.error("[HZParticlesFX] Failed to fetch preset:",a)})):J(e)},[e]);const{camera:te,gl:_e}=je.useThree(),ae=r.useRef(null);if(!ae.current){const a=(Ce=_e.backend)==null?void 0:Ce.device;a&&(ae.current=a)}const Re=ae.current,Pe=r.useRef(null),fe=r.useRef(null),Te=r.useRef(!1),E=r.useRef(!1),d=r.useMemo(()=>W?gt(W,_):null,[W,_]),Fe=r.useMemo(()=>(d==null?void 0:d.systems.every(a=>a.emissionTrailEnabled))??!1,[d]),We=r.useMemo(()=>d?d.systems.every(a=>a.emissionTrailEnabled)?1:Ft(d):0,[d]),He=r.useMemo(()=>u?u instanceof c.Vector3?u.clone():new c.Vector3(...u):new c.Vector3(0,0,0),[u]),de=r.useMemo(()=>(d==null?void 0:d.systems.some(a=>a.emissionTrailEnabled))??!1,[d]),Ie=r.useMemo(()=>new c.PlaneGeometry(1,1),[]),s=r.useMemo(()=>{const a=document.createElement("canvas");a.width=64,a.height=64;const z=a.getContext("2d");z.clearRect(0,0,64,64);const F=z.createRadialGradient(32,32,0,32,32,32);F.addColorStop(0,"rgba(255,255,255,1)"),F.addColorStop(.7,"rgba(255,255,255,1)"),F.addColorStop(1,"rgba(255,255,255,0)"),z.fillStyle=F,z.beginPath(),z.arc(32,32,32,0,Math.PI*2),z.fill();const x=new c.CanvasTexture(a);x.flipY=!1;const B=new c.MeshBasicMaterial({color:16777215,transparent:!0,depthWrite:!1,alphaTest:.01,map:x,vertexColors:!0,side:c.DoubleSide,blending:c.AdditiveBlending});return B.needsUpdate=!0,B},[]),h=8,pe=r.useRef(null),j=r.useRef(0),y=512,b=r.useRef({buf:new Float32Array(y*4),head:0,count:0,elapsed:0}),{trailGeo:$,trailPosAttr:ne,trailColAttr:M}=r.useMemo(()=>{const z=8*h*6,F=new Float32Array(z*3),x=new Float32Array(z*3),B=new c.BufferGeometry,le=new c.BufferAttribute(F,3);le.setUsage(c.DynamicDrawUsage),B.setAttribute("position",le);const ye=new c.BufferAttribute(x,3);return ye.setUsage(c.DynamicDrawUsage),B.setAttribute("color",ye),B.setDrawRange(0,0),{trailGeo:B,trailPosAttr:le,trailColAttr:ye}},[]),U=r.useMemo(()=>new c.MeshBasicMaterial({color:16777215,vertexColors:!0,transparent:!0,depthWrite:!1,side:c.DoubleSide,blending:c.AdditiveBlending}),[]);r.useEffect(()=>{if(!Re||!d)return;const a=new Xe.ParticleSystemManager(Re);return fe.current=a,E.current=!1,a.replaceSystems(d).then(()=>{A&&p&&(a.respawnAllSystems(),E.current=!0)}).catch(z=>{console.error("[HZParticlesFX] replaceSystems FAILED:",z)}),()=>{typeof a.destroy=="function"&&a.destroy(),fe.current=null}},[Re,d]),r.useEffect(()=>{const a=fe.current;a&&A&&p&&!E.current&&(a.respawnAllSystems(),E.current=!0,Te.current=!1)},[A,p]);const ie=r.useRef(null),me=r.useRef(Z);r.useEffect(()=>{if(Z===me.current)return;me.current=Z;const a=b.current;a.head=0,a.count=0,a.elapsed=0,$.setDrawRange(0,0),ie.current=null;const z=fe.current;z&&(z.respawnAllSystems(),E.current=!0,Te.current=!1)},[Z]);const he=r.useMemo(()=>new c.Matrix4,[]),Ue=r.useMemo(()=>new c.Vector3,[]),se=r.useMemo(()=>new c.Vector3,[]),re=r.useMemo(()=>new c.Quaternion,[]),X=r.useMemo(()=>new c.Vector3,[]),Be=r.useMemo(()=>new c.Vector3(0,0,1),[]),R=r.useMemo(()=>new c.Color,[]);return je.useFrame((a,z)=>{if(!p||!Pe.current||!fe.current)return;const F=fe.current,x=(f==null?void 0:f.current)??He,B=z>1?z/1e3:z,le=ie.current,ye=le&&B>0,vt=ye?(x.x-le.x)/B:0,bt=ye?(x.y-le.y)/B:0,wt=ye?(x.z-le.z)/B:0;ie.current={x:x.x,y:x.y,z:x.z};for(const{system:n,config:o}of F.particleSystems)o.emissionTrailEnabled?(n.setSimulationTransform({position:[x.x,x.y,x.z],velocity:[vt,bt,wt]}),o.shapeTranslationX=0,o.shapeTranslationY=0,o.shapeTranslationZ=0):(o.shapeTranslationX=x.x,o.shapeTranslationY=x.y,o.shapeTranslationZ=x.z);F.updateAllSystems(B);for(const{system:n}of F.particleSystems)n.readbackAndProcessParticles();if(de){const n=b.current;n.elapsed+=B;const o=n.head*4;n.buf[o]=x.x,n.buf[o+1]=x.y,n.buf[o+2]=x.z,n.buf[o+3]=n.elapsed,n.head=(n.head+1)%y,n.count<y&&n.count++}const Me=Pe.current;let xe=0;for(let n=0;n<F.particleSystems.length;n++){const{system:o,config:C}=F.particleSystems[n];if(C.emissionTrailEnabled)continue;const k=o.particleData,qe=o.activeParticles;if(!(!k||qe<=0))for(let ue=0;ue<qe;ue++){const L=ue*8,Se=k[L+0],Ve=k[L+1],O=k[L+2];let ge=k[L+3],ve=k[L+4],G=k[L+5];const be=k[L+6],K=k[L+7];if(be>=K||K<=0)continue;const w=be/K;if(C.colorTransitionEnabled){const I=C.startColor??[1,0,0],oe=C.endColor??[0,0,1];ge=I[0]+(oe[0]-I[0])*w,ve=I[1]+(oe[1]-I[1])*w,G=I[2]+(oe[2]-I[2])*w}const we=C.bloomIntensity??1;ge=Math.min(1,ge*we),ve=Math.min(1,ve*we),G=Math.min(1,G*we);const ze=St(C,be,K,ue);ge*=ze,ve*=ze,G*=ze;const Y=xt(C,be,K,ue);if(!(Y<=0)){if(Ue.set(Se,Ve,O),se.subVectors(te.position,Ue).normalize(),re.setFromUnitVectors(Be,se),C.velocityStretchEnabled&&o.particleVelocities){const I=ue*4,oe=o.particleVelocities[I],De=o.particleVelocities[I+1],P=o.particleVelocities[I+2],ce=Math.sqrt(oe*oe+De*De+P*P);if(ce>.001){const Ge=C.velocityStretchFactor??1,i=Y*(1+ce*Ge);X.set(Y,i,1),se.set(oe,De,P).normalize(),re.setFromUnitVectors(Be,se);const Ee=(i-Y)*.5;Ue.addScaledVector(se,Ee)}else X.set(Y,Y,1)}else X.set(Y,Y,1);he.compose(Ue,re,X),Me.setMatrixAt(xe,he),R.setRGB(ge,ve,G),Me.setColorAt(xe,R),xe++}}}Me.count=xe,Me.visible=xe>0,xe>0&&(Me.instanceMatrix.needsUpdate=!0,Me.instanceColor&&(Me.instanceColor.needsUpdate=!0));const Le=xe>0||F.particleSystems.some(({system:n})=>n.emitting||n.activeParticles>0);if(Te.current&&!Le&&(E.current=!1,q==null||q()),Te.current=Le,de&&pe.current){const n=ne.array,o=M.array;let C=0;const k=te.position.x,qe=te.position.y,ue=te.position.z;for(let Se=0;Se<F.particleSystems.length;Se++){const{system:Ve,config:O}=F.particleSystems[Se];if(!O.emissionTrailEnabled||!Ve.emitting&&Ve.activeParticles<=0)continue;const ge=O.emissionTrailDuration??1,ve=O.emissionTrailWidth??.3,G=O.bloomIntensity??1,be=ve*.5,K=b.current,w=K.buf,we=K.count,ze=K.head,Y=K.elapsed;if(we<2)continue;const I=((ze-we)%y+y)%y,oe=Y-w[I*4+3],De=Math.min(ge,oe);if(De<.001)continue;const P=O.startColor??[1,1,1],ce=O.colorTransitionEnabled?O.endColor??[1,1,1]:P,Ge=(t,m)=>{const g=Y-t;for(let S=0;S<we-1;S++){const T=((ze-1-S)%y+y)%y,H=((ze-2-S)%y+y)%y,ee=w[T*4+3],N=w[H*4+3];if(g>=N&&g<=ee){const v=ee!==N?(g-N)/(ee-N):0;m[0]=w[H*4]+(w[T*4]-w[H*4])*v,m[1]=w[H*4+1]+(w[T*4+1]-w[H*4+1])*v,m[2]=w[H*4+2]+(w[T*4+2]-w[H*4+2])*v;return}}m[0]=w[I*4],m[1]=w[I*4+1],m[2]=w[I*4+2]},i=[x.x,x.y,x.z],Ee=[0,0,0];for(let t=1;t<=h;t++){const m=t/h*De;Ge(m,Ee),i.push(Ee[0],Ee[1],Ee[2])}const Ye=i[0]-i[h*3],Ne=i[1]-i[h*3+1],Qe=i[2]-i[h*3+2];if(Ye*Ye+Ne*Ne+Qe*Qe<1e-6)continue;let Je=0,$e=0,Ke=0;const Ae=[];for(let t=0;t<=h;t++){let m,g,S;t===0?(m=i[3]-i[0],g=i[4]-i[1],S=i[5]-i[2]):t===h?(m=i[t*3]-i[(t-1)*3],g=i[t*3+1]-i[(t-1)*3+1],S=i[t*3+2]-i[(t-1)*3+2]):(m=i[(t+1)*3]-i[(t-1)*3],g=i[(t+1)*3+1]-i[(t-1)*3+1],S=i[(t+1)*3+2]-i[(t-1)*3+2]);let T=Math.sqrt(m*m+g*g+S*S);T<1e-8&&(m=0,g=0,S=1,T=1),m/=T,g/=T,S/=T;const H=k-i[t*3],ee=qe-i[t*3+1],N=ue-i[t*3+2];let v=g*N-S*ee,V=S*H-m*N,D=m*ee-g*H,Q=Math.sqrt(v*v+V*V+D*D);Q<1e-8&&(v=-S,V=0,D=m,Q=Math.sqrt(v*v+V*V+D*D)),Q<1e-8&&(v=0,V=1,D=0,Q=1),v/=Q,V/=Q,D/=Q,t>0&&v*Je+V*$e+D*Ke<0&&(v=-v,V=-V,D=-D),Je=v,$e=V,Ke=D,Ae.push(v,V,D)}for(let t=0;t<h;t++){const m=t/h,g=(t+1)/h,S=be*(1-m),T=be*(1-g),H=(1-m)*(1-m),ee=(1-g)*(1-g),N=Math.min(1,(P[0]+(ce[0]-P[0])*m)*G)*H,v=Math.min(1,(P[1]+(ce[1]-P[1])*m)*G)*H,V=Math.min(1,(P[2]+(ce[2]-P[2])*m)*G)*H,D=Math.min(1,(P[0]+(ce[0]-P[0])*g)*G)*ee,Q=Math.min(1,(P[1]+(ce[1]-P[1])*g)*G)*ee,Ze=Math.min(1,(P[2]+(ce[2]-P[2])*g)*G)*ee,et=i[t*3],tt=i[t*3+1],nt=i[t*3+2],st=i[(t+1)*3],rt=i[(t+1)*3+1],ot=i[(t+1)*3+2],ct=Ae[t*3],at=Ae[t*3+1],it=Ae[t*3+2],lt=Ae[(t+1)*3],ut=Ae[(t+1)*3+1],ft=Ae[(t+1)*3+2],zt=et+ct*S,At=tt+at*S,Rt=nt+it*S,dt=et-ct*S,pt=tt-at*S,mt=nt-it*S,ht=st+lt*T,yt=rt+ut*T,Mt=ot+ft*T,Pt=st-lt*T,Tt=rt-ut*T,Ct=ot-ft*T,l=C*3;n[l]=zt,n[l+1]=At,n[l+2]=Rt,o[l]=N,o[l+1]=v,o[l+2]=V,n[l+3]=dt,n[l+4]=pt,n[l+5]=mt,o[l+3]=N,o[l+4]=v,o[l+5]=V,n[l+6]=ht,n[l+7]=yt,n[l+8]=Mt,o[l+6]=D,o[l+7]=Q,o[l+8]=Ze,n[l+9]=dt,n[l+10]=pt,n[l+11]=mt,o[l+9]=N,o[l+10]=v,o[l+11]=V,n[l+12]=Pt,n[l+13]=Tt,n[l+14]=Ct,o[l+12]=D,o[l+13]=Q,o[l+14]=Ze,n[l+15]=ht,n[l+16]=yt,n[l+17]=Mt,o[l+15]=D,o[l+16]=Q,o[l+17]=Ze,C+=6}}const L=j.current;if(C<L){const Se=C*3,Ve=L*3;for(let O=Se;O<Ve;O++)n[O]=0,o[O]=0}j.current=C,$.setDrawRange(0,C),ne.needsUpdate=!0,M.needsUpdate=!0}}),W?Oe.jsxs(Oe.Fragment,{children:[Oe.jsx("instancedMesh",{ref:a=>{Pe.current=a,a&&(a.count=0,a.visible=!Fe)},args:[Ie,s,We],frustumCulled:!1,renderOrder:100,visible:p&&!Fe}),de&&Oe.jsx("mesh",{ref:pe,geometry:$,material:U,frustumCulled:!1,renderOrder:99,visible:p})]}):null}const Ut=`
2
2
  attribute vec4 aColor;
3
3
  varying vec4 vColor;
4
4
  void main() {
5
5
  vColor = aColor;
6
6
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
7
7
  }
8
- `,rn=`
8
+ `,Bt=`
9
9
  varying vec4 vColor;
10
10
  void main() {
11
11
  gl_FragColor = vColor;
12
12
  }
13
- `;function on({positionRef:n,duration:i=1,width:l=.3,color:T=[1,1,1],maxPoints:d=256,minDistance:X=.05,opacity:Y=1,blending:O=c.AdditiveBlending,visible:Z=!0}){const{camera:J}=Ne.useThree(),re=o.useRef(null),Qe=o.useRef({positions:new Float32Array(d*3),times:new Float32Array(d),head:0,count:0,lastPos:new c.Vector3(1/0,1/0,1/0),elapsed:0}),{geometry:fe,posAttr:ze,colorAttr:Ae,indexBuf:ye}=o.useMemo(()=>{const He=d*2,_e=new Float32Array(He*3),t=new Float32Array(He*4),D=new c.BufferGeometry,Me=new c.BufferAttribute(_e,3);Me.setUsage(c.DynamicDrawUsage),D.setAttribute("position",Me);const H=new c.BufferAttribute(t,4);H.setUsage(c.DynamicDrawUsage),D.setAttribute("aColor",H);const p=(d-1)*6,b=new Uint32Array(p);for(let oe=0;oe<d-1;oe++){const y=oe*2,U=oe*6;b[U]=y,b[U+1]=y+1,b[U+2]=y+2,b[U+3]=y+1,b[U+4]=y+3,b[U+5]=y+2}const $=new c.BufferAttribute(b,1);return D.setIndex($),D.setDrawRange(0,0),{geometry:D,posAttr:Me,colorAttr:H,indexBuf:$}},[d]),Re=o.useMemo(()=>new c.ShaderMaterial({vertexShader:sn,fragmentShader:rn,transparent:!0,depthWrite:!1,side:c.DoubleSide,blending:O}),[O]),C=o.useMemo(()=>new c.Vector3,[]),h=o.useMemo(()=>new c.Vector3,[]),Ye=o.useMemo(()=>new c.Vector3,[]),Je=o.useMemo(()=>new c.Vector3(0,1,0),[]),Pe=o.useMemo(()=>new c.Vector3,[]);return Ne.useFrame((He,_e)=>{if(!re.current)return;const t=Qe.current;if(t.elapsed+=_e,!Z){t.count=0,t.head=0,t.lastPos.set(1/0,1/0,1/0),fe.setDrawRange(0,0);return}const D=n.current;if(!D)return;if(t.lastPos.distanceTo(D)>=X){const y=(t.head+t.count)%d;t.positions[y*3]=D.x,t.positions[y*3+1]=D.y,t.positions[y*3+2]=D.z,t.times[y]=t.elapsed,t.count<d?t.count++:t.head=(t.head+1)%d,t.lastPos.copy(D)}for(;t.count>0;){const y=t.head;if(t.elapsed-t.times[y]>i)t.head=(t.head+1)%d,t.count--;else break}if(t.count<2){fe.setDrawRange(0,0);return}const H=ze.array,p=Ae.array;let b=null;const $=t.count;for(let y=0;y<$;y++){const U=(t.head+y)%d,Se=t.positions[U*3],xe=t.positions[U*3+1],ce=t.positions[U*3+2],ge=1-(t.elapsed-t.times[U])/i;if(y<$-1){const Ve=(t.head+y+1)%d;C.set(t.positions[Ve*3]-Se,t.positions[Ve*3+1]-xe,t.positions[Ve*3+2]-ce);const a=C.length();a>1e-6?C.divideScalar(a):b&&C.copy(b)}else b&&C.copy(b);Ye.set(J.position.x-Se,J.position.y-xe,J.position.z-ce),h.crossVectors(C,Ye);let K=h.length();K<1e-6&&(Pe.crossVectors(C,Je),h.copy(Pe),K=h.length(),K<1e-6&&(h.set(1,0,0),K=1)),h.divideScalar(K);const ae=l*ge,qe=Y*ge*ge,z=y*2;H[z*3]=Se+h.x*ae,H[z*3+1]=xe+h.y*ae,H[z*3+2]=ce+h.z*ae,H[(z+1)*3]=Se-h.x*ae,H[(z+1)*3+1]=xe-h.y*ae,H[(z+1)*3+2]=ce-h.z*ae,p[z*4]=T[0],p[z*4+1]=T[1],p[z*4+2]=T[2],p[z*4+3]=qe,p[(z+1)*4]=T[0],p[(z+1)*4+1]=T[1],p[(z+1)*4+2]=T[2],p[(z+1)*4+3]=qe,b=b||new c.Vector3,b.copy(C)}const oe=($-1)*6;fe.setDrawRange(0,oe),ze.needsUpdate=!0,Ae.needsUpdate=!0}),ke.jsx("mesh",{ref:re,geometry:fe,material:Re,frustumCulled:!1,renderOrder:101,visible:Z})}Object.defineProperty(exports,"fetchPreset",{enumerable:!0,get:()=>xt.fetchPreset});exports.HZParticlesFX=nn;exports.HZTrailRibbon=on;exports.computeParticleOpacity=bt;exports.computeParticleSize=gt;exports.particleHash=vt;exports.scalePreset=Ot;
13
+ `;function Ot({positionRef:e,duration:u=1,width:f=.3,color:A=[1,1,1],maxPoints:p=256,minDistance:_=.05,opacity:Z=1,blending:q=c.AdditiveBlending,visible:W=!0}){const{camera:J}=je.useThree(),te=r.useRef(null),_e=r.useRef({positions:new Float32Array(p*3),times:new Float32Array(p),head:0,count:0,lastPos:new c.Vector3(1/0,1/0,1/0),elapsed:0}),{geometry:ae,posAttr:Re,colorAttr:Pe,indexBuf:fe}=r.useMemo(()=>{const de=p*2,Ie=new Float32Array(de*3),s=new Float32Array(de*4),h=new c.BufferGeometry,pe=new c.BufferAttribute(Ie,3);pe.setUsage(c.DynamicDrawUsage),h.setAttribute("position",pe);const j=new c.BufferAttribute(s,4);j.setUsage(c.DynamicDrawUsage),h.setAttribute("aColor",j);const y=(p-1)*6,b=new Uint32Array(y);for(let ne=0;ne<p-1;ne++){const M=ne*2,U=ne*6;b[U]=M,b[U+1]=M+1,b[U+2]=M+2,b[U+3]=M+1,b[U+4]=M+3,b[U+5]=M+2}const $=new c.BufferAttribute(b,1);return h.setIndex($),h.setDrawRange(0,0),{geometry:h,posAttr:pe,colorAttr:j,indexBuf:$}},[p]),Te=r.useMemo(()=>new c.ShaderMaterial({vertexShader:Ut,fragmentShader:Bt,transparent:!0,depthWrite:!1,side:c.DoubleSide,blending:q}),[q]),E=r.useMemo(()=>new c.Vector3,[]),d=r.useMemo(()=>new c.Vector3,[]),Fe=r.useMemo(()=>new c.Vector3,[]),We=r.useMemo(()=>new c.Vector3(0,1,0),[]),He=r.useMemo(()=>new c.Vector3,[]);return je.useFrame((de,Ie)=>{if(!te.current)return;const s=_e.current;if(s.elapsed+=Ie,!W){s.count=0,s.head=0,s.lastPos.set(1/0,1/0,1/0),ae.setDrawRange(0,0);return}const h=e.current;if(!h)return;if(s.lastPos.distanceTo(h)>=_){const M=(s.head+s.count)%p;s.positions[M*3]=h.x,s.positions[M*3+1]=h.y,s.positions[M*3+2]=h.z,s.times[M]=s.elapsed,s.count<p?s.count++:s.head=(s.head+1)%p,s.lastPos.copy(h)}for(;s.count>0;){const M=s.head;if(s.elapsed-s.times[M]>u)s.head=(s.head+1)%p,s.count--;else break}if(s.count<2){ae.setDrawRange(0,0);return}const j=Re.array,y=Pe.array;let b=null;const $=s.count;for(let M=0;M<$;M++){const U=(s.head+M)%p,ie=s.positions[U*3],me=s.positions[U*3+1],he=s.positions[U*3+2],se=1-(s.elapsed-s.times[U])/u;if(M<$-1){const Ce=(s.head+M+1)%p;E.set(s.positions[Ce*3]-ie,s.positions[Ce*3+1]-me,s.positions[Ce*3+2]-he);const a=E.length();a>1e-6?E.divideScalar(a):b&&E.copy(b)}else b&&E.copy(b);Fe.set(J.position.x-ie,J.position.y-me,J.position.z-he),d.crossVectors(E,Fe);let re=d.length();re<1e-6&&(He.crossVectors(E,We),d.copy(He),re=d.length(),re<1e-6&&(d.set(1,0,0),re=1)),d.divideScalar(re);const X=f*se,Be=Z*se*se,R=M*2;j[R*3]=ie+d.x*X,j[R*3+1]=me+d.y*X,j[R*3+2]=he+d.z*X,j[(R+1)*3]=ie-d.x*X,j[(R+1)*3+1]=me-d.y*X,j[(R+1)*3+2]=he-d.z*X,y[R*4]=A[0],y[R*4+1]=A[1],y[R*4+2]=A[2],y[R*4+3]=Be,y[(R+1)*4]=A[0],y[(R+1)*4+1]=A[1],y[(R+1)*4+2]=A[2],y[(R+1)*4+3]=Be,b=b||new c.Vector3,b.copy(E)}const ne=($-1)*6;ae.setDrawRange(0,ne),Re.needsUpdate=!0,Pe.needsUpdate=!0}),Oe.jsx("mesh",{ref:te,geometry:ae,material:Te,frustumCulled:!1,renderOrder:101,visible:W})}Object.defineProperty(exports,"fetchPreset",{enumerable:!0,get:()=>Xe.fetchPreset});exports.HZParticlesFX=It;exports.HZTrailRibbon=Ot;exports.computeParticleOpacity=St;exports.computeParticleSize=xt;exports.particleHash=ke;exports.scalePreset=gt;