hz-particles 1.0.15 → 1.2.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 +84 -60
- package/dist-lib/hz-particles-r3f.cjs +3 -3
- package/dist-lib/hz-particles-r3f.d.ts +22 -17
- package/dist-lib/hz-particles-r3f.mjs +196 -432
- package/dist-lib/hz-particles.cjs +113 -15
- package/dist-lib/hz-particles.d.ts +145 -6
- package/dist-lib/hz-particles.mjs +2009 -1104
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ A high-performance WebGPU particle engine for the web. Use it as a standalone We
|
|
|
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** —
|
|
18
|
+
- **React Three Fiber Component** — `HZFaithfulFX` (engine-faithful: the real overlay inline on three's WebGPURenderer), with preset support
|
|
19
19
|
- **3D Object Support** — Static 3D objects alongside particle systems
|
|
20
20
|
|
|
21
21
|
## Requirements
|
|
@@ -46,7 +46,7 @@ npm install hz-particles
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
```javascript
|
|
49
|
-
import {
|
|
49
|
+
import { HZFaithfulFX } from 'hz-particles/r3f';
|
|
50
50
|
```
|
|
51
51
|
|
|
52
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.
|
|
@@ -109,70 +109,45 @@ render();
|
|
|
109
109
|
|
|
110
110
|
### React Three Fiber
|
|
111
111
|
|
|
112
|
-
**
|
|
112
|
+
`<HZFaithfulFX>` runs the **real engine** (the overlay) inline on three's `WebGPURenderer`: shader
|
|
113
|
+
shapes, noise, per-system blending, multi-pass bloom, faithful trails + depth occlusion. What you
|
|
114
|
+
design in the editor is exactly what renders. Pass a `position` for a static FX, or a `positionRef`
|
|
115
|
+
for a moving one (a ball trail) — the overlay pulls the ref each frame.
|
|
113
116
|
|
|
114
117
|
```jsx
|
|
115
|
-
import {
|
|
116
|
-
import
|
|
118
|
+
import { useRef } from 'react';
|
|
119
|
+
import { HZFaithfulFX } from 'hz-particles/r3f';
|
|
120
|
+
import firePreset from './presets/fire.json';
|
|
117
121
|
|
|
118
122
|
function Scene() {
|
|
119
|
-
|
|
120
|
-
<HZParticlesFX
|
|
121
|
-
preset={explosionPreset}
|
|
122
|
-
position={[0, 1, 0]}
|
|
123
|
-
/>
|
|
124
|
-
);
|
|
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);
|
|
138
|
-
|
|
123
|
+
const ballRef = useRef(null); // THREE.Vector3, updated by your app each frame
|
|
139
124
|
return (
|
|
140
125
|
<>
|
|
141
|
-
<
|
|
142
|
-
|
|
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>
|
|
126
|
+
<HZFaithfulFX preset={firePreset} position={[0, 1, 0]} /> {/* static FX */}
|
|
127
|
+
<HZFaithfulFX preset={trailPreset} positionRef={ballRef} scale={1.5} /> {/* ball trail */}
|
|
157
128
|
</>
|
|
158
129
|
);
|
|
159
130
|
}
|
|
160
131
|
```
|
|
161
132
|
|
|
133
|
+
`<HZFaithfulFX>` **drives rendering** (its `useFrame` calls `gl.render` itself, so R3F stops
|
|
134
|
+
auto-rendering). If your host owns its own render loop (custom RAF, post-processing pipeline), use
|
|
135
|
+
`initHzFxOverlay()` + `makeThreeSceneDepth()` directly (see above). Re-trigger an effect by remounting
|
|
136
|
+
it (change its `key`).
|
|
137
|
+
|
|
162
138
|
## Props Reference
|
|
163
139
|
|
|
164
|
-
The `
|
|
140
|
+
The `HZFaithfulFX` component accepts the following props:
|
|
165
141
|
|
|
166
142
|
| Prop | Type | Default | Description |
|
|
167
143
|
|------|------|---------|-------------|
|
|
168
|
-
| `preset` | `SceneData
|
|
169
|
-
| `position` | `[number, number, number]` | `[0, 0, 0]` | Static world
|
|
170
|
-
| `positionRef` | `React.RefObject<
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `onComplete` | `() => void` | — | Callback fired when the effect finishes playing |
|
|
144
|
+
| `preset` | `SceneData` | — | Particle preset (JSON exported from the editor) |
|
|
145
|
+
| `position` | `[number, number, number]` | `[0, 0, 0]` | Static emitter world position (used when `positionRef` is omitted) |
|
|
146
|
+
| `positionRef` | `React.RefObject<Vector3 \| null>` | — | Moving emitter (e.g. a ball trail): the overlay reads this ref every frame |
|
|
147
|
+
| `scale` | `number` | `1` | Uniform scale applied to the preset (sizes, speeds, emission, trail) |
|
|
148
|
+
| `active` | `boolean` | `true` | When false, a moving emitter pauses (no new trail) |
|
|
149
|
+
| `renderPriority` | `number` | `1` | `useFrame` priority. This component drives rendering, so keep it > 0 |
|
|
150
|
+
| `noOcclusion` | `boolean` | `false` | Skip scene-depth occlusion (particles never hidden behind geometry) |
|
|
176
151
|
|
|
177
152
|
## Preset Configuration
|
|
178
153
|
|
|
@@ -347,6 +322,54 @@ if (glbData.animationData) {
|
|
|
347
322
|
}
|
|
348
323
|
```
|
|
349
324
|
|
|
325
|
+
## Engine-faithful Integration (overlay renderer)
|
|
326
|
+
|
|
327
|
+
`initHzFxOverlay()` is **the same render loop the HZ previews and editor use** — shader-drawn
|
|
328
|
+
shapes, per-system blending, noise distortion, GLB mesh particles + animation, multi-pass bloom,
|
|
329
|
+
and faithful trails. It's the recommended way to drop HZ effects into any app (vanilla JS,
|
|
330
|
+
TypeScript, or React — one core, no per-framework reimplementation).
|
|
331
|
+
|
|
332
|
+
```javascript
|
|
333
|
+
import { initHzFxOverlay, makeThreeSceneDepth } from 'hz-particles';
|
|
334
|
+
|
|
335
|
+
// OVERLAY mode: pass a canvas — the overlay owns its WebGPU context and renders on a
|
|
336
|
+
// transparent background. Stack it above your scene with pointer-events: none.
|
|
337
|
+
const fx = await initHzFxOverlay(canvas);
|
|
338
|
+
await fx.loadPreset(preset); // or fx.setEmitters([{ preset, position }])
|
|
339
|
+
|
|
340
|
+
function frame(dt) {
|
|
341
|
+
fx.setCamera(proj, view, [cx, cy, cz]); // column-major matrices + camera world pos
|
|
342
|
+
fx.render(dt);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// INLINE mode (share your own WebGPU renderer, e.g. three's WebGPURenderer):
|
|
346
|
+
const fx2 = await initHzFxOverlay(
|
|
347
|
+
{ device, context, canvas },
|
|
348
|
+
{ getSceneDepth: makeThreeSceneDepth(renderer) } // one-line occlusion behind your geometry
|
|
349
|
+
);
|
|
350
|
+
// each frame, AFTER renderer.render(scene, camera): fx2.setCamera(...); fx2.render(dt);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Coexisting groups & moving emitters** — one overlay hosts many FX at once. `addEmitter(preset, pos)`
|
|
354
|
+
adds a static group; `addMovingEmitter(preset, { getPosition })` adds a moving one (e.g. a ball trail)
|
|
355
|
+
whose comet trail follows the path you supply (the overlay *pulls* `getPosition()` each frame; return
|
|
356
|
+
`null` to pause). Both return `{ remove() }`.
|
|
357
|
+
|
|
358
|
+
| Method | Description |
|
|
359
|
+
| --- | --- |
|
|
360
|
+
| `setCamera(proj, view, pos)` / `setCameraMVP(mvp, pos)` | Drive the camera (column-major). |
|
|
361
|
+
| `loadPreset(preset, pos?)` / `setEmitters([...])` | (Re)build emitters. |
|
|
362
|
+
| `addEmitter(preset, pos?)` → `{ remove }` | Add a STATIC group that coexists with others. |
|
|
363
|
+
| `addMovingEmitter(preset, { getPosition })` → `{ setPosition, remove }` | Add a MOVING group (ball trail). |
|
|
364
|
+
| `render(dt)` | Simulate + render one frame (inline: call after your scene render). |
|
|
365
|
+
| `resize()` | Recreate render textures (also auto-detected from the canvas). |
|
|
366
|
+
| `clearCaches()` | Drop cached bind groups after a `replaceSystems`/`addSystems` that reuses ids. |
|
|
367
|
+
| `trackHistoryGroup(ids, getPosition)` → `{ remove }` | Give existing manager systems a moving trail (history only). |
|
|
368
|
+
| `destroy()` | Release GPU resources. |
|
|
369
|
+
|
|
370
|
+
Options: `{ getSceneDepth, autoRespawn, manager }`. Pass `manager` to **share an existing
|
|
371
|
+
`ParticleSystemManager`** so your app keeps owning it while the overlay is the single render path.
|
|
372
|
+
|
|
350
373
|
## Scene Save/Load
|
|
351
374
|
|
|
352
375
|
```javascript
|
|
@@ -354,7 +377,7 @@ import { saveScene, loadScene } from 'hz-particles';
|
|
|
354
377
|
|
|
355
378
|
// Save current scene
|
|
356
379
|
document.getElementById('save-button').addEventListener('click', () => {
|
|
357
|
-
saveScene(manager); // Downloads
|
|
380
|
+
saveScene(manager); // Downloads a self-contained .hzfx package (or .hzfx → JSON on Alt-click)
|
|
358
381
|
});
|
|
359
382
|
|
|
360
383
|
// Load scene from file input
|
|
@@ -368,17 +391,15 @@ document.getElementById('file-input').addEventListener('change', async (event) =
|
|
|
368
391
|
|
|
369
392
|
## TypeScript
|
|
370
393
|
|
|
371
|
-
Type declarations are
|
|
394
|
+
Type declarations **are shipped** — the package's `types` entry points at `dist-lib/hz-particles.d.ts`
|
|
395
|
+
(and `dist-lib/hz-particles-r3f.d.ts` for the `hz-particles/r3f` subpath), so imports are fully typed
|
|
396
|
+
out of the box:
|
|
372
397
|
|
|
373
398
|
```typescript
|
|
374
|
-
|
|
375
|
-
import {
|
|
376
|
-
// @ts-ignore
|
|
377
|
-
import { HZParticlesFX } from 'hz-particles/r3f';
|
|
399
|
+
import { initHzFxOverlay, ParticleSystemManager, serializeSystemConfig } from 'hz-particles';
|
|
400
|
+
import { HZFaithfulFX } from 'hz-particles/r3f';
|
|
378
401
|
```
|
|
379
402
|
|
|
380
|
-
Community-contributed type definitions are welcome via PR.
|
|
381
|
-
|
|
382
403
|
## Secondary Exports
|
|
383
404
|
|
|
384
405
|
The following modules are also exported for advanced usage:
|
|
@@ -387,8 +408,11 @@ The following modules are also exported for advanced usage:
|
|
|
387
408
|
- **`ParticlePhysics`** — Physics simulation parameter management
|
|
388
409
|
- **`ParticleTextureManager`** — Texture loading utilities
|
|
389
410
|
- **`Objects3DManager`** — 3D object scene management
|
|
390
|
-
- **`saveScene(manager)`** — Export scene as JSON file download
|
|
391
|
-
- **`loadScene(event)`** — Load scene from file input event
|
|
411
|
+
- **`saveScene(manager)`** — Export scene as a self-contained `.hzfx` package (or JSON) file download
|
|
412
|
+
- **`loadScene(event)`** — Load scene from file input event (`.hzfx` or JSON, auto-detected)
|
|
413
|
+
- **`serializeSystemConfig(config)`** — Single source of truth for a system's serializable config fields (shared by `saveScene` + the editor's code export; add binary assets yourself)
|
|
414
|
+
- **`initHzFxOverlay(target, options?)`** — Engine-faithful overlay/inline renderer (see above)
|
|
415
|
+
- **`packHZFX` / `unpackHZFX` / `isHZFX`** — Build/read the `.hzfx` binary package format
|
|
392
416
|
- **`extractGLBTexture(arrayBuffer)`** — Extract base color texture from GLB file
|
|
393
417
|
- **Shader exports** — WGSL shader code for compute and rendering pipelines
|
|
394
418
|
- **Geometry helpers** — Primitive shape generators (cube, sphere, etc.)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const je=require("react/jsx-runtime"),a=require("react"),Ze=require("@react-three/fiber"),Ut=require("three"),Je=require("hz-particles");function Ot(i){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const I in i)if(I!=="default"){const e=Object.getOwnPropertyDescriptor(i,I);Object.defineProperty(n,I,e.get?e:{enumerable:!0,get:()=>i[I]})}}return n.default=i,Object.freeze(n)}const l=Ot(Ut);function $e(i){return Math.abs(Math.sin(i*12.9898)*43758.5453)%1}function Bt(i){const n=Math.sin(i*54321.67)*43758.5453%1;return n<0?n+1:n}function wt(i,n,I,e){if(I<=0)return 0;const y=n/I;let N=i.particleSize??.5;if(i.randomSize){const G=i.minSize??.1,Y=i.maxSize??.5;N=G+(Y-G)*Bt(I)}const $=Math.max(.01,Math.min(10,i.sizeLifetimeSpeed??1));if(i.fadeSizeEnabled&&(N*=1-Math.pow(y,1/$)),i.increaseSizeEnabled&&(N*=1+Math.pow(y,1/$)),i.pulseEnabled){const G=i.pulseAmplitude??.5,Y=i.pulseFrequency??1,ae=(i.pulsePhaseRandom??0)*$e(e)*Math.PI*2;N*=1+G*Math.sin(n*Y*Math.PI*2+ae)}return Math.max(0,N)}function Rt(i,n,I,e){if(I<=0)return 0;let y=i.opacity??1;const N=n/I;if(i.fadeEnabled&&(y*=Math.max(0,1-N)),i.pulseEnabled&&i.pulseOpacity){const $=i.pulseAmplitude??.5,G=i.pulseFrequency??1,ne=(i.pulsePhaseRandom??0)*$e(e)*Math.PI*2;y*=Math.max(0,1+$*Math.sin(n*G*Math.PI*2+ne))}return Math.max(0,Math.min(1,y))}function Tt(i,n){if(n===1)return i;const I=(e,y)=>e!=null?e*n:y*n;return{...i,systems:i.systems.map(e=>({...e,particleSize:I(e.particleSize,.5),minSize:e.minSize!=null?e.minSize*n:void 0,maxSize:e.maxSize!=null?e.maxSize*n:void 0,particleSpeed:(e.particleSpeed??1)*n,minSpeed:e.minSpeed!=null?e.minSpeed*n:void 0,maxSpeed:e.maxSpeed!=null?e.maxSpeed*n:void 0,cubeLength:e.cubeLength!=null?e.cubeLength*n:void 0,outerLength:e.outerLength!=null?e.outerLength*n:void 0,innerLength:e.innerLength!=null?e.innerLength*n:void 0,outerRadius:e.outerRadius!=null?e.outerRadius*n:void 0,innerRadius:e.innerRadius!=null?e.innerRadius*n:void 0,squareSize:e.squareSize!=null?e.squareSize*n:void 0,squareInnerSize:e.squareInnerSize!=null?e.squareInnerSize*n:void 0,circleInnerRadius:e.circleInnerRadius!=null?e.circleInnerRadius*n:void 0,circleOuterRadius:e.circleOuterRadius!=null?e.circleOuterRadius*n:void 0,cylinderInnerRadius:e.cylinderInnerRadius!=null?e.cylinderInnerRadius*n:void 0,cylinderOuterRadius:e.cylinderOuterRadius!=null?e.cylinderOuterRadius*n:void 0,cylinderHeight:e.cylinderHeight!=null?e.cylinderHeight*n:void 0,emissionTrailWidth:(e.emissionTrailWidth??.3)*n,emissionTrailDuration:(e.emissionTrailDuration??1)*n,emissionTrailMinDistance:e.emissionTrailMinDistance!=null?e.emissionTrailMinDistance*n:void 0,emissionTrailShapeAmplitude:e.emissionTrailShapeAmplitude!=null?e.emissionTrailShapeAmplitude*n:void 0}))}}function Ht(i){let n=0;for(const I of i.systems)n+=I.maxParticles??1e4;return n}function Lt({preset:i,position:n,positionRef:I,autoPlay:e=!0,visible:y=!0,scale:N=1,resetKey:$=0,onComplete:G}){var Be;const[Y,ne]=a.useState(typeof i=="string"?null:i);a.useEffect(()=>{typeof i=="string"?(ne(null),Je.fetchPreset(i).then(ne).catch(d=>{console.error("[HZParticlesFX] Failed to fetch preset:",d)})):ne(i)},[i]);const{camera:ae,gl:ke}=Ze.useThree(),de=a.useRef(null);if(!de.current){const d=(Be=ke.backend)==null?void 0:Be.device;d&&(de.current=d)}const Ie=de.current,De=a.useRef(null),he=a.useRef(null),Ce=a.useRef(!1),B=a.useRef(!1),h=a.useMemo(()=>Y?Tt(Y,N):null,[Y,N]),Ue=a.useMemo(()=>(h==null?void 0:h.systems.every(d=>d.emissionTrailEnabled))??!1,[h]),Ne=a.useMemo(()=>h?h.systems.every(d=>d.emissionTrailEnabled)?1:Ht(h):0,[h]),_e=a.useMemo(()=>n?n instanceof l.Vector3?n.clone():new l.Vector3(...n):new l.Vector3(0,0,0),[n]),ge=a.useMemo(()=>(h==null?void 0:h.systems.some(d=>d.emissionTrailEnabled))??!1,[h]),Oe=a.useMemo(()=>new l.PlaneGeometry(1,1),[]),r=a.useMemo(()=>{const d=document.createElement("canvas");d.width=64,d.height=64;const A=d.getContext("2d");A.clearRect(0,0,64,64);const w=A.createRadialGradient(32,32,0,32,32,32);w.addColorStop(0,"rgba(255,255,255,1)"),w.addColorStop(.7,"rgba(255,255,255,1)"),w.addColorStop(1,"rgba(255,255,255,0)"),A.fillStyle=w,A.beginPath(),A.arc(32,32,32,0,Math.PI*2),A.fill();const R=new l.CanvasTexture(d);R.flipY=!1;const _=new l.MeshBasicMaterial({color:16777215,transparent:!0,depthWrite:!1,alphaTest:.01,map:R,vertexColors:!0,side:l.DoubleSide,blending:l.NormalBlending});return _.needsUpdate=!0,_},[]),C=a.useRef(null);a.useEffect(()=>{if(!h)return;const d=h.systems.find(w=>!w.emissionTrailEnabled&&w.textureEnabled&&w.textureImageData);if(!d){C.current&&r.map!==C.current&&(r.map=C.current,r.needsUpdate=!0);return}C.current||(C.current=r.map);const A=new Image;return A.onload=()=>{const w=new l.Texture(A);w.needsUpdate=!0,r.map=w,r.needsUpdate=!0},A.src=d.textureImageData,()=>{r.map&&r.map!==C.current&&r.map.dispose()}},[h,r]);const ye=64,X=a.useRef(null),Z=a.useRef(0),M=512,re=a.useRef({buf:new Float32Array(M*6),head:0,count:0,elapsed:0}),{trailGeo:se,trailPosAttr:z,trailColAttr:H}=a.useMemo(()=>{const A=8*ye*6,w=new Float32Array(A*3),R=new Float32Array(A*3),_=new l.BufferGeometry,pe=new l.BufferAttribute(w,3);pe.setUsage(l.DynamicDrawUsage),_.setAttribute("position",pe);const xe=new l.BufferAttribute(R,3);return xe.setUsage(l.DynamicDrawUsage),_.setAttribute("color",xe),_.setDrawRange(0,0),{trailGeo:_,trailPosAttr:pe,trailColAttr:xe}},[]),Ee=a.useMemo(()=>new l.MeshBasicMaterial({color:16777215,vertexColors:!0,transparent:!0,depthWrite:!1,side:l.DoubleSide,blending:l.NormalBlending}),[]);a.useEffect(()=>{if(!Ie||!h)return;const d=new Je.ParticleSystemManager(Ie);return he.current=d,B.current=!1,d.replaceSystems(h).then(()=>{e&&y&&(d.respawnAllSystems(),B.current=!0)}).catch(A=>{console.error("[HZParticlesFX] replaceSystems FAILED:",A)}),()=>{typeof d.destroy=="function"&&d.destroy(),he.current=null}},[Ie,h]),a.useEffect(()=>{const d=he.current;d&&e&&y&&!B.current&&(d.respawnAllSystems(),B.current=!0,Ce.current=!1)},[e,y]);const fe=a.useRef(null),Me=a.useRef($);a.useEffect(()=>{if($===Me.current)return;Me.current=$;const d=re.current;d.head=0,d.count=0,d.elapsed=0,se.setDrawRange(0,0),fe.current=null;const A=he.current;A&&(A.respawnAllSystems(),B.current=!0,Ce.current=!1)},[$]);const Ye=a.useMemo(()=>new l.Matrix4,[]),me=a.useMemo(()=>new l.Vector3,[]),K=a.useMemo(()=>new l.Vector3,[]),ie=a.useMemo(()=>new l.Quaternion,[]),Se=a.useMemo(()=>new l.Vector3,[]),V=a.useMemo(()=>new l.Vector3(0,0,1),[]),Ve=a.useMemo(()=>new l.Color,[]);return Ze.useFrame((d,A)=>{var et;if(!y||!De.current||!he.current)return;const w=he.current,R=(I==null?void 0:I.current)??_e,_=A>1?A/1e3:A,pe=fe.current,xe=pe&&_>0,At=xe?(R.x-pe.x)/_:0,Pt=xe?(R.y-pe.y)/_:0,It=xe?(R.z-pe.z)/_:0;fe.current={x:R.x,y:R.y,z:R.z};for(const{system:s,config:u}of w.particleSystems)u.emissionTrailEnabled?(s.setSimulationTransform({position:[R.x,R.y,R.z],velocity:[At,Pt,It]}),u.emissionTranslationX=0,u.emissionTranslationY=0,u.emissionTranslationZ=0):(u.emissionTranslationX=R.x,u.emissionTranslationY=R.y,u.emissionTranslationZ=R.z);w.updateAllSystems(_);for(const{system:s}of w.particleSystems)s.readbackAndProcessParticles();if(ge){const s=re.current;s.elapsed+=_;let u=0,E=0;const U=(et=w.particleSystems.find(({config:L})=>L.emissionTrailEnabled))==null?void 0:et.config;if(U){const L=U.emissionTrailShape??"straight";if(L!=="straight"){const O=U.emissionTrailShapeAmplitude??.1,ce=U.emissionTrailShapeFrequency??4,te=2*Math.PI*ce*s.elapsed;L==="zigzag"?u=O*Math.sign(Math.sin(te)):L==="sine"?u=O*Math.sin(te):L==="spiral"&&(u=O*Math.sin(te),E=O*Math.cos(te))}}const ee=s.head*6;s.buf[ee]=R.x,s.buf[ee+1]=R.y,s.buf[ee+2]=R.z,s.buf[ee+3]=s.elapsed,s.buf[ee+4]=u,s.buf[ee+5]=E,s.head=(s.head+1)%M,s.count<M&&s.count++}const be=De.current;let ve=0;for(let s=0;s<w.particleSystems.length;s++){const{system:u,config:E}=w.particleSystems[s];if(E.emissionTrailEnabled)continue;const U=u.particleData,ee=u.activeParticles;if(!(!U||ee<=0))for(let L=0;L<ee;L++){const O=L*8,ce=U[O+0],te=U[O+1],D=U[O+2];let ze=U[O+3],we=U[O+4],Q=U[O+5];const Re=U[O+6],oe=U[O+7];if(Re>=oe||oe<=0)continue;const x=Re/oe;if(E.colorTransitionEnabled){const F=E.startColor??[1,0,0],le=E.endColor??[0,0,1];ze=F[0]+(le[0]-F[0])*x,we=F[1]+(le[1]-F[1])*x,Q=F[2]+(le[2]-F[2])*x}const Te=E.bloomIntensity??1;ze=Math.min(1,ze*Te),we=Math.min(1,we*Te),Q=Math.min(1,Q*Te);const Ae=Rt(E,Re,oe,L);ze*=Ae,we*=Ae,Q*=Ae;const J=wt(E,Re,oe,L);if(!(J<=0)){if(me.set(ce,te,D),K.subVectors(ae.position,me).normalize(),ie.setFromUnitVectors(V,K),E.velocityStretchEnabled&&u.particleVelocities){const F=L*4,le=u.particleVelocities[F],Fe=u.particleVelocities[F+1],q=u.particleVelocities[F+2],ue=Math.sqrt(le*le+Fe*Fe+q*q);if(ue>.001){const Qe=E.velocityStretchFactor??1,W=J*(1+ue*Qe);Se.set(J,W,1),K.set(le,Fe,q).normalize(),ie.setFromUnitVectors(V,K);const o=(W-J)*.5;me.addScaledVector(K,o)}else Se.set(J,J,1)}else Se.set(J,J,1);Ye.compose(me,ie,Se),be.setMatrixAt(ve,Ye),Ve.setRGB(ze,we,Q),be.setColorAt(ve,Ve),ve++}}}be.count=ve,be.visible=ve>0,ve>0&&(be.instanceMatrix.needsUpdate=!0,be.instanceColor&&(be.instanceColor.needsUpdate=!0));const Ke=ve>0||w.particleSystems.some(({system:s})=>s.emitting||s.activeParticles>0);if(Ce.current&&!Ke&&(B.current=!1,G==null||G()),Ce.current=Ke,ge&&X.current){const s=z.array,u=H.array;let E=0;const U=ae.position.x,ee=ae.position.y,L=ae.position.z;for(let ce=0;ce<w.particleSystems.length;ce++){const{system:te,config:D}=w.particleSystems[ce];if(!D.emissionTrailEnabled||!te.emitting&&te.activeParticles<=0)continue;const ze=D.emissionTrailDuration??1,we=D.emissionTrailWidth??.3,Q=D.bloomIntensity??1,Re=we*.5,oe=re.current,x=oe.buf,Te=oe.count,Ae=oe.head,J=oe.elapsed;if(Te<2)continue;const F=((Ae-Te)%M+M)%M,le=J-x[F*6+3],Fe=Math.min(ze,le);if(Fe<.001)continue;const q=D.colorTransitionEnabled?D.startColor??[1,1,1]:D.particleColor??[1,1,1],ue=D.colorTransitionEnabled?D.endColor??[1,1,1]:q,Qe=(t,c)=>{const g=J-t;for(let f=0;f<Te-1;f++){const b=((Ae-1-f)%M+M)%M,P=((Ae-2-f)%M+M)%M,j=x[b*6+3],T=x[P*6+3];if(g>=T&&g<=j){const p=j!==T?(g-T)/(j-T):0;c[0]=x[P*6]+(x[b*6]-x[P*6])*p,c[1]=x[P*6+1]+(x[b*6+1]-x[P*6+1])*p,c[2]=x[P*6+2]+(x[b*6+2]-x[P*6+2])*p,c[3]=x[P*6+4]+(x[b*6+4]-x[P*6+4])*p,c[4]=x[P*6+5]+(x[b*6+5]-x[P*6+5])*p;return}}c[0]=x[F*6],c[1]=x[F*6+1],c[2]=x[F*6+2],c[3]=x[F*6+4],c[4]=x[F*6+5]},W=Math.max(1,Math.min(ye,D.emissionTrailSegments??8)),o=[R.x,R.y,R.z],He=D.emissionTrailShape??"straight",We=[];{const t=D.emissionTrailShapeAmplitude??.1,c=D.emissionTrailShapeFrequency??4,g=2*Math.PI*c*J;let f=0,b=0;He==="zigzag"?f=t*Math.sign(Math.sin(g)):He==="sine"?f=t*Math.sin(g):He==="spiral"&&(f=t*Math.sin(g),b=t*Math.cos(g)),We.push(f,b)}const qe=[0,0,0,0,0];for(let t=1;t<=W;t++){const c=t/W*Fe;Qe(c,qe),o.push(qe[0],qe[1],qe[2]),We.push(qe[3],qe[4])}const tt=o[0]-o[W*3],nt=o[1]-o[W*3+1],rt=o[2]-o[W*3+2];if(tt*tt+nt*nt+rt*rt<1e-6)continue;let st=0,it=0,ot=0;const Pe=[],Ge=[];for(let t=0;t<=W;t++){let c,g,f;t===0?(c=o[3]-o[0],g=o[4]-o[1],f=o[5]-o[2]):t===W?(c=o[t*3]-o[(t-1)*3],g=o[t*3+1]-o[(t-1)*3+1],f=o[t*3+2]-o[(t-1)*3+2]):(c=o[(t+1)*3]-o[(t-1)*3],g=o[(t+1)*3+1]-o[(t-1)*3+1],f=o[(t+1)*3+2]-o[(t-1)*3+2]);let b=Math.sqrt(c*c+g*g+f*f);b<1e-8&&(c=0,g=0,f=1,b=1),c/=b,g/=b,f/=b,Ge.push(c,g,f);const P=U-o[t*3],j=ee-o[t*3+1],T=L-o[t*3+2];let p=g*T-f*j,S=f*P-c*T,v=c*j-g*P,k=Math.sqrt(p*p+S*S+v*v);k<1e-8&&(p=-f,S=0,v=c,k=Math.sqrt(p*p+S*S+v*v)),k<1e-8&&(p=0,S=1,v=0,k=1),p/=k,S/=k,v/=k,t>0&&p*st+S*it+v*ot<0&&(p=-p,S=-S,v=-v),st=p,it=S,ot=v,Pe.push(p,S,v)}if(He!=="straight")for(let t=1;t<=W;t++){const c=We[t*2],g=We[t*2+1];if(Math.abs(c)<1e-8&&Math.abs(g)<1e-8)continue;const f=t*3,b=Ge[f],P=Ge[f+1],j=Ge[f+2];let T=-j,p=0,S=b,v=Math.sqrt(T*T+S*S);if(v<1e-6&&(T=0,p=j,S=-P,v=Math.sqrt(T*T+p*p+S*S),v<1e-6&&(T=1,p=0,S=0,v=1)),T/=v,p/=v,S/=v,He==="spiral"){const k=P*S-j*p,Le=j*T-b*S,Xe=b*p-P*T;o[f]+=T*c+k*g,o[f+1]+=p*c+Le*g,o[f+2]+=S*c+Xe*g}else o[f]+=T*c,o[f+1]+=p*c,o[f+2]+=S*c}for(let t=0;t<W;t++){const c=t/W,g=(t+1)/W,f=Re*(1-c),b=Re*(1-g),P=(1-c)*(1-c),j=(1-g)*(1-g),T=Math.min(1,(q[0]+(ue[0]-q[0])*c)*Q)*P,p=Math.min(1,(q[1]+(ue[1]-q[1])*c)*Q)*P,S=Math.min(1,(q[2]+(ue[2]-q[2])*c)*Q)*P,v=Math.min(1,(q[0]+(ue[0]-q[0])*g)*Q)*j,k=Math.min(1,(q[1]+(ue[1]-q[1])*g)*Q)*j,Le=Math.min(1,(q[2]+(ue[2]-q[2])*g)*Q)*j,Xe=o[t*3],at=o[t*3+1],ct=o[t*3+2],lt=o[(t+1)*3],ut=o[(t+1)*3+1],dt=o[(t+1)*3+2],ft=Pe[t*3],mt=Pe[t*3+1],pt=Pe[t*3+2],ht=Pe[(t+1)*3],gt=Pe[(t+1)*3+1],yt=Pe[(t+1)*3+2],Dt=Xe+ft*f,Ct=at+mt*f,Et=ct+pt*f,Mt=Xe-ft*f,St=at-mt*f,xt=ct-pt*f,bt=lt+ht*b,vt=ut+gt*b,zt=dt+yt*b,Vt=lt-ht*b,Ft=ut-gt*b,qt=dt-yt*b,m=E*3;s[m]=Dt,s[m+1]=Ct,s[m+2]=Et,u[m]=T,u[m+1]=p,u[m+2]=S,s[m+3]=Mt,s[m+4]=St,s[m+5]=xt,u[m+3]=T,u[m+4]=p,u[m+5]=S,s[m+6]=bt,s[m+7]=vt,s[m+8]=zt,u[m+6]=v,u[m+7]=k,u[m+8]=Le,s[m+9]=Mt,s[m+10]=St,s[m+11]=xt,u[m+9]=T,u[m+10]=p,u[m+11]=S,s[m+12]=Vt,s[m+13]=Ft,s[m+14]=qt,u[m+12]=v,u[m+13]=k,u[m+14]=Le,s[m+15]=bt,s[m+16]=vt,s[m+17]=zt,u[m+15]=v,u[m+16]=k,u[m+17]=Le,E+=6}}const O=Z.current;if(E<O){const ce=E*3,te=O*3;for(let D=ce;D<te;D++)s[D]=0,u[D]=0}Z.current=E,se.setDrawRange(0,E),z.needsUpdate=!0,H.needsUpdate=!0}}),Y?je.jsxs(je.Fragment,{children:[je.jsx("instancedMesh",{ref:d=>{De.current=d,d&&(d.count=0,d.visible=!Ue)},args:[Oe,r,Ne],frustumCulled:!1,renderOrder:100,visible:y&&!Ue}),ge&&je.jsx("mesh",{ref:X,geometry:se,material:Ee,frustumCulled:!1,renderOrder:99,visible:y})]}):null}const jt=`
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react"),Z=require("@react-three/fiber"),X=require("hz-particles"),K=require("react/jsx-runtime"),Q=require("three");function Y(n){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const a in n)if(a!=="default"){const e=Object.getOwnPropertyDescriptor(n,a);Object.defineProperty(t,a,e.get?e:{enumerable:!0,get:()=>n[a]})}}return t.default=n,Object.freeze(t)}const p=Y(Q);function J(n,t){if(t===1)return n;const a=(e,i)=>e!=null?e*t:i*t;return{...n,systems:n.systems.map(e=>({...e,particleSize:a(e.particleSize,.5),minSize:e.minSize!=null?e.minSize*t:void 0,maxSize:e.maxSize!=null?e.maxSize*t:void 0,particleSpeed:(e.particleSpeed??1)*t,minSpeed:e.minSpeed!=null?e.minSpeed*t:void 0,maxSpeed:e.maxSpeed!=null?e.maxSpeed*t:void 0,cubeLength:e.cubeLength!=null?e.cubeLength*t:void 0,outerLength:e.outerLength!=null?e.outerLength*t:void 0,innerLength:e.innerLength!=null?e.innerLength*t:void 0,outerRadius:e.outerRadius!=null?e.outerRadius*t:void 0,innerRadius:e.innerRadius!=null?e.innerRadius*t:void 0,squareSize:e.squareSize!=null?e.squareSize*t:void 0,squareInnerSize:e.squareInnerSize!=null?e.squareInnerSize*t:void 0,circleInnerRadius:e.circleInnerRadius!=null?e.circleInnerRadius*t:void 0,circleOuterRadius:e.circleOuterRadius!=null?e.circleOuterRadius*t:void 0,cylinderInnerRadius:e.cylinderInnerRadius!=null?e.cylinderInnerRadius*t:void 0,cylinderOuterRadius:e.cylinderOuterRadius!=null?e.cylinderOuterRadius*t:void 0,cylinderHeight:e.cylinderHeight!=null?e.cylinderHeight*t:void 0,emissionTrailWidth:(e.emissionTrailWidth??.3)*t,emissionTrailDuration:(e.emissionTrailDuration??1)*t,emissionTrailMinDistance:e.emissionTrailMinDistance!=null?e.emissionTrailMinDistance*t:void 0,emissionTrailShapeAmplitude:e.emissionTrailShapeAmplitude!=null?e.emissionTrailShapeAmplitude*t:void 0}))}}function $({preset:n,positionRef:t,position:a=[0,0,0],scale:e=1,active:i=!0,renderPriority:z=1,noOcclusion:A=!1}){const d=s.useRef(null),v=s.useRef(null),x=s.useRef(!1),I=s.useRef(0),C=s.useRef(i);C.current=i;const y=s.useRef(a);y.current=a;const D=s.useMemo(()=>e&&e!==1?J(n,e):n,[n,e]),E=l=>t?l.addMovingEmitter(D,{getPosition:()=>{const m=t.current;return C.current&&m?[m.x,m.y,m.z]:null}}):l.addEmitter(D,y.current);return s.useEffect(()=>{var f,u;const l=d.current;if(!l)return;let m=!1;return(u=(f=v.current)==null?void 0:f.remove)==null||u.call(f),v.current=null,E(l).then(c=>{var T;m?(T=c==null?void 0:c.remove)==null||T.call(c):v.current=c}),()=>{m=!0}},[D]),s.useEffect(()=>()=>{var l,m;(m=(l=d.current)==null?void 0:l.destroy)==null||m.call(l),d.current=null,v.current=null,x.current=!1},[]),Z.useFrame(l=>{l.gl.render(l.scene,l.camera);const m=performance.now(),f=I.current?Math.min(.05,(m-I.current)/1e3):.016;I.current=m;const u=l.gl,c=u.backend;!d.current&&!x.current&&(c!=null&&c.device)&&(c!=null&&c.context)&&(x.current=!0,X.initHzFxOverlay({device:c.device,context:c.context,canvas:u.domElement},{getSceneDepth:A?()=>null:X.makeThreeSceneDepth(u)}).then(R=>(d.current=R,E(R))).then(R=>{v.current=R}).catch(R=>console.error("[HZFaithfulFX] init failed:",R)));const T=d.current;if(T){const R=l.camera;T.setCamera(R.projectionMatrix.elements,R.matrixWorldInverse.elements,R.position),T.render(f)}},z),null}const ee=`
|
|
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
|
-
`,
|
|
8
|
+
`,te=`
|
|
9
9
|
varying vec4 vColor;
|
|
10
10
|
void main() {
|
|
11
11
|
gl_FragColor = vColor;
|
|
12
12
|
}
|
|
13
|
-
`;function
|
|
13
|
+
`;function ne({positionRef:n,duration:t=1,width:a=.3,color:e=[1,1,1],maxPoints:i=256,minDistance:z=.05,opacity:A=1,blending:d=p.AdditiveBlending,visible:v=!0}){const{camera:x}=Z.useThree(),I=s.useRef(null),C=s.useRef({positions:new Float32Array(i*3),times:new Float32Array(i),head:0,count:0,lastPos:new p.Vector3(1/0,1/0,1/0),elapsed:0}),{geometry:y,posAttr:D,colorAttr:E,indexBuf:l}=s.useMemo(()=>{const B=i*2,U=new Float32Array(B*3),r=new Float32Array(B*4),M=new p.BufferGeometry,H=new p.BufferAttribute(U,3);H.setUsage(p.DynamicDrawUsage),M.setAttribute("position",H);const w=new p.BufferAttribute(r,4);w.setUsage(p.DynamicDrawUsage),M.setAttribute("aColor",w);const b=(i-1)*6,h=new Uint32Array(b);for(let F=0;F<i-1;F++){const o=F*2,g=F*6;h[g]=o,h[g+1]=o+1,h[g+2]=o+2,h[g+3]=o+1,h[g+4]=o+3,h[g+5]=o+2}const O=new p.BufferAttribute(h,1);return M.setIndex(O),M.setDrawRange(0,0),{geometry:M,posAttr:H,colorAttr:w,indexBuf:O}},[i]),m=s.useMemo(()=>new p.ShaderMaterial({vertexShader:ee,fragmentShader:te,transparent:!0,depthWrite:!1,side:p.DoubleSide,blending:d}),[d]),f=s.useMemo(()=>new p.Vector3,[]),u=s.useMemo(()=>new p.Vector3,[]),c=s.useMemo(()=>new p.Vector3,[]),T=s.useMemo(()=>new p.Vector3(0,1,0),[]),R=s.useMemo(()=>new p.Vector3,[]);return Z.useFrame((B,U)=>{if(!I.current)return;const r=C.current;if(r.elapsed+=U,!v){r.count=0,r.head=0,r.lastPos.set(1/0,1/0,1/0),y.setDrawRange(0,0);return}const M=n.current;if(!M)return;if(r.lastPos.distanceTo(M)>=z){const o=(r.head+r.count)%i;r.positions[o*3]=M.x,r.positions[o*3+1]=M.y,r.positions[o*3+2]=M.z,r.times[o]=r.elapsed,r.count<i?r.count++:r.head=(r.head+1)%i,r.lastPos.copy(M)}for(;r.count>0;){const o=r.head;if(r.elapsed-r.times[o]>t)r.head=(r.head+1)%i,r.count--;else break}if(r.count<2){y.setDrawRange(0,0);return}const w=D.array,b=E.array;let h=null;const O=r.count;for(let o=0;o<O;o++){const g=(r.head+o)%i,L=r.positions[g*3],V=r.positions[g*3+1],j=r.positions[g*3+2],_=1-(r.elapsed-r.times[g])/t;if(o<O-1){const W=(r.head+o+1)%i;f.set(r.positions[W*3]-L,r.positions[W*3+1]-V,r.positions[W*3+2]-j);const N=f.length();N>1e-6?f.divideScalar(N):h&&f.copy(h)}else h&&f.copy(h);c.set(x.position.x-L,x.position.y-V,x.position.z-j),u.crossVectors(f,c);let P=u.length();P<1e-6&&(R.crossVectors(f,T),u.copy(R),P=u.length(),P<1e-6&&(u.set(1,0,0),P=1)),u.divideScalar(P);const q=a*_,G=A*_*_,S=o*2;w[S*3]=L+u.x*q,w[S*3+1]=V+u.y*q,w[S*3+2]=j+u.z*q,w[(S+1)*3]=L-u.x*q,w[(S+1)*3+1]=V-u.y*q,w[(S+1)*3+2]=j-u.z*q,b[S*4]=e[0],b[S*4+1]=e[1],b[S*4+2]=e[2],b[S*4+3]=G,b[(S+1)*4]=e[0],b[(S+1)*4+1]=e[1],b[(S+1)*4+2]=e[2],b[(S+1)*4+3]=G,h=h||new p.Vector3,h.copy(f)}const F=(O-1)*6;y.setDrawRange(0,F),D.needsUpdate=!0,E.needsUpdate=!0}),K.jsx("mesh",{ref:I,geometry:y,material:m,frustumCulled:!1,renderOrder:101,visible:v})}function k(n){return Math.abs(Math.sin(n*12.9898)*43758.5453)%1}function re(n){const t=Math.sin(n*54321.67)*43758.5453%1;return t<0?t+1:t}function ie(n,t,a,e){if(a<=0)return 0;const i=t/a;let z=n.particleSize??.5;if(n.randomSize){const d=n.minSize??.1,v=n.maxSize??.5;z=d+(v-d)*re(a)}const A=Math.max(.01,Math.min(10,n.sizeLifetimeSpeed??1));if(n.fadeSizeEnabled&&(z*=1-Math.pow(i,1/A)),n.increaseSizeEnabled&&(z*=1+Math.pow(i,1/A)),n.pulseEnabled){const d=n.pulseAmplitude??.5,v=n.pulseFrequency??1,I=(n.pulsePhaseRandom??0)*k(e)*Math.PI*2;z*=1+d*Math.sin(t*v*Math.PI*2+I)}return Math.max(0,z)}function oe(n,t,a,e){if(a<=0)return 0;let i=n.opacity??1;const z=t/a;if(n.fadeEnabled&&(i*=Math.max(0,1-z)),n.pulseEnabled&&n.pulseOpacity){const A=n.pulseAmplitude??.5,d=n.pulseFrequency??1,x=(n.pulsePhaseRandom??0)*k(e)*Math.PI*2;i*=Math.max(0,1+A*Math.sin(t*d*Math.PI*2+x))}return Math.max(0,Math.min(1,i))}Object.defineProperty(exports,"fetchPreset",{enumerable:!0,get:()=>X.fetchPreset});exports.HZFaithfulFX=$;exports.HZTrailRibbon=ne;exports.computeParticleOpacity=oe;exports.computeParticleSize=ie;exports.particleHash=k;exports.scalePreset=J;
|
|
@@ -2,26 +2,31 @@ import type { SceneData, ParticleSystemConfig } from 'hz-particles';
|
|
|
2
2
|
import type { RefObject } from 'react';
|
|
3
3
|
import type { Vector3 } from 'three';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
6
|
-
/** Preset SceneData (loaded from JSON or inline)
|
|
7
|
-
preset: SceneData
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
autoPlay?: boolean;
|
|
14
|
-
/** Toggle visibility (default true). */
|
|
15
|
-
visible?: boolean;
|
|
16
|
-
/** Scale multiplier for particle size and speed (default 1). */
|
|
5
|
+
export interface HZFaithfulFXProps {
|
|
6
|
+
/** Preset SceneData (loaded from JSON or inline). */
|
|
7
|
+
preset: SceneData;
|
|
8
|
+
/** Moving emitter (e.g. a ball trail): the overlay reads this ref each frame. */
|
|
9
|
+
positionRef?: RefObject<Vector3 | null>;
|
|
10
|
+
/** Static emitter world position (used when `positionRef` is omitted). */
|
|
11
|
+
position?: [number, number, number];
|
|
12
|
+
/** Uniform scale applied to the preset (sizes, speeds, emission, trail). */
|
|
17
13
|
scale?: number;
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
|
|
14
|
+
/** When false, a moving emitter pauses (no new trail). Default true. */
|
|
15
|
+
active?: boolean;
|
|
16
|
+
/** `useFrame` priority. This component DRIVES rendering, so keep it > 0. Default 1. */
|
|
17
|
+
renderPriority?: number;
|
|
18
|
+
/** Skip scene-depth occlusion (particles never hidden behind geometry). */
|
|
19
|
+
noOcclusion?: boolean;
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Engine-FAITHFUL R3F component: runs the real hz-particles overlay inline on three's
|
|
24
|
+
* WebGPURenderer (shader shapes, noise, per-system blending, multi-pass bloom, faithful
|
|
25
|
+
* trails) — what you design in the editor is exactly what renders. It DRIVES rendering
|
|
26
|
+
* (its `useFrame` calls `gl.render` itself). If your host owns its own render loop, use
|
|
27
|
+
* `initHzFxOverlay()` + `makeThreeSceneDepth()` directly instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare function HZFaithfulFX(props: HZFaithfulFXProps): JSX.Element;
|
|
25
30
|
|
|
26
31
|
export declare function scalePreset(preset: SceneData, scale: number): SceneData;
|
|
27
32
|
|