hz-particles 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 HZ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,444 @@
1
+ # hz-particles
2
+
3
+ [![npm version](https://img.shields.io/npm/v/hz-particles.svg)](https://www.npmjs.com/package/hz-particles)
4
+ [![license](https://img.shields.io/npm/l/hz-particles.svg)](https://github.com/TODO/hz-particles/blob/main/LICENSE)
5
+ [![WebGPU](https://img.shields.io/badge/WebGPU-required-blue.svg)](https://caniuse.com/webgpu)
6
+
7
+ A high-performance WebGPU particle engine for the web.
8
+
9
+ ## Features
10
+
11
+ - **WebGPU Compute Shaders** — GPU-accelerated particle physics simulation
12
+ - **GPU Instancing** — Efficient rendering of thousands of particles
13
+ - **GLB Model Support** — Use 3D models as particle shapes with automatic texture extraction
14
+ - **Skeletal Animations** — Animated GLB models with full animation control
15
+ - **Scene Serialization** — Save/load particle configurations as JSON
16
+ - **Multiple Emitter Shapes** — Sphere, cube, cylinder, circle, square emission patterns
17
+ - **Real-time Physics** — Gravity, attractors, drag, velocity, and lifetime management
18
+ - **3D Object Support** — Static 3D objects alongside particle systems
19
+
20
+ ## Requirements
21
+
22
+ **WebGPU-compatible browser required:**
23
+ - Chrome/Edge 113+ (stable)
24
+ - Firefox Nightly (experimental)
25
+ - Safari Technology Preview (experimental)
26
+
27
+ Check browser support: [caniuse.com/webgpu](https://caniuse.com/webgpu)
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install hz-particles
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```javascript
38
+ import { initWebGPU, ParticleSystemManager } from 'hz-particles';
39
+
40
+ // 1. Setup WebGPU context
41
+ const canvas = document.getElementById('webgpu-canvas');
42
+ const { device, context, format } = await initWebGPU(canvas);
43
+
44
+ // 2. Create particle system manager
45
+ const manager = new ParticleSystemManager(device);
46
+
47
+ // 3. Create a particle system
48
+ const systemId = manager.createParticleSystem({
49
+ maxParticles: 10000,
50
+ particleCount: 1000,
51
+ });
52
+
53
+ // 4. Get active system and initialize
54
+ const system = manager.getActiveSystem();
55
+ await system.initComputePipeline(device);
56
+
57
+ // Optional: Load a texture
58
+ const response = await fetch('particle-texture.png');
59
+ const blob = await response.blob();
60
+ const imageBitmap = await createImageBitmap(blob);
61
+ await system.setTexture(imageBitmap);
62
+
63
+ // 5. Render loop
64
+ let lastTime = performance.now();
65
+
66
+ function render() {
67
+ const currentTime = performance.now();
68
+ const deltaTime = (currentTime - lastTime) / 1000;
69
+ lastTime = currentTime;
70
+
71
+ // Update physics
72
+ manager.updateAllSystems(deltaTime);
73
+
74
+ // Render
75
+ const commandEncoder = device.createCommandEncoder();
76
+ const renderPass = commandEncoder.beginRenderPass({
77
+ colorAttachments: [{
78
+ view: context.getCurrentTexture().createView(),
79
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
80
+ loadOp: 'clear',
81
+ storeOp: 'store',
82
+ }],
83
+ });
84
+
85
+ system.render(renderPass);
86
+ renderPass.end();
87
+ device.queue.submit([commandEncoder.finish()]);
88
+
89
+ requestAnimationFrame(render);
90
+ }
91
+
92
+ render();
93
+ ```
94
+
95
+ ## React Three Fiber Integration
96
+
97
+ While `hz-particles` uses native WebGPU (not Three.js internally), you can integrate it into React Three Fiber applications:
98
+
99
+ ```jsx
100
+ import { useEffect, useRef } from 'react';
101
+ import { Canvas } from '@react-three/fiber';
102
+ import { initWebGPU, ParticleSystemManager } from 'hz-particles';
103
+
104
+ function ParticleLayer() {
105
+ const canvasRef = useRef();
106
+ const engineRef = useRef();
107
+
108
+ useEffect(() => {
109
+ let animationId;
110
+
111
+ async function init() {
112
+ // Create dedicated WebGPU canvas
113
+ const canvas = canvasRef.current;
114
+ const { device, context, format } = await initWebGPU(canvas);
115
+
116
+ // Setup particle system
117
+ const manager = new ParticleSystemManager(device);
118
+ manager.createParticleSystem({ particleCount: 1000 });
119
+ const system = manager.getActiveSystem();
120
+ await system.initComputePipeline(device);
121
+
122
+ engineRef.current = { device, context, manager, system };
123
+
124
+ // Render loop
125
+ let lastTime = performance.now();
126
+ function render() {
127
+ const { device, context, manager, system } = engineRef.current;
128
+ const currentTime = performance.now();
129
+ const deltaTime = (currentTime - lastTime) / 1000;
130
+ lastTime = currentTime;
131
+
132
+ manager.updateAllSystems(deltaTime);
133
+
134
+ const commandEncoder = device.createCommandEncoder();
135
+ const renderPass = commandEncoder.beginRenderPass({
136
+ colorAttachments: [{
137
+ view: context.getCurrentTexture().createView(),
138
+ clearValue: { r: 0, g: 0, b: 0, a: 0 }, // Transparent
139
+ loadOp: 'clear',
140
+ storeOp: 'store',
141
+ }],
142
+ });
143
+ system.render(renderPass);
144
+ renderPass.end();
145
+ device.queue.submit([commandEncoder.finish()]);
146
+
147
+ animationId = requestAnimationFrame(render);
148
+ }
149
+ render();
150
+ }
151
+
152
+ init();
153
+
154
+ return () => {
155
+ if (animationId) cancelAnimationFrame(animationId);
156
+ };
157
+ }, []);
158
+
159
+ return (
160
+ <canvas
161
+ ref={canvasRef}
162
+ id="webgpu-canvas"
163
+ style={{
164
+ position: 'absolute',
165
+ top: 0,
166
+ left: 0,
167
+ width: '100%',
168
+ height: '100%',
169
+ pointerEvents: 'none',
170
+ }}
171
+ />
172
+ );
173
+ }
174
+
175
+ // Usage in App
176
+ function App() {
177
+ return (
178
+ <div style={{ position: 'relative', width: '100vw', height: '100vh' }}>
179
+ {/* R3F scene */}
180
+ <Canvas>
181
+ <ambientLight />
182
+ <mesh>
183
+ <boxGeometry />
184
+ <meshStandardMaterial />
185
+ </mesh>
186
+ </Canvas>
187
+
188
+ {/* WebGPU particles overlay */}
189
+ <ParticleLayer />
190
+ </div>
191
+ );
192
+ }
193
+ ```
194
+
195
+ ## API Reference
196
+
197
+ ### `initWebGPU(canvas?)`
198
+
199
+ Initialize WebGPU context and device.
200
+
201
+ ```typescript
202
+ async function initWebGPU(canvas?: HTMLCanvasElement): Promise<{
203
+ device: GPUDevice,
204
+ context: GPUCanvasContext,
205
+ format: GPUTextureFormat,
206
+ canvas: HTMLCanvasElement
207
+ }>
208
+ ```
209
+
210
+ **Parameters:**
211
+ - `canvas` (optional) — Canvas element. If omitted, looks for `document.getElementById('webgpu-canvas')`
212
+
213
+ **Returns:** Object with WebGPU device, canvas context, texture format, and canvas element
214
+
215
+ **Throws:** Error if WebGPU is not supported
216
+
217
+ ---
218
+
219
+ ### `ParticleSystem`
220
+
221
+ Core particle system class managing particle simulation and rendering.
222
+
223
+ ```typescript
224
+ class ParticleSystem {
225
+ constructor(device: GPUDevice, config?: object)
226
+ }
227
+ ```
228
+
229
+ **Constructor Parameters:**
230
+ - `device` — GPUDevice instance
231
+ - `config` (optional) — Configuration object:
232
+ - `maxParticles` (number, default 10000) — Maximum particle buffer size
233
+ - `particleCount` (number, default 100) — Initial active particle count
234
+
235
+ **Key Methods:**
236
+
237
+ - `async initComputePipeline(device: GPUDevice)` — Initialize GPU compute and render pipelines. Must be called before rendering.
238
+
239
+ - `async setTexture(imageBitmap: ImageBitmap)` — Set particle texture from ImageBitmap.
240
+
241
+ - `async setGLBModel(arrayBuffer: ArrayBuffer)` — Use GLB model geometry as particle shape. Automatically extracts textures.
242
+
243
+ - `updateParticles(deltaTime: number)` — Execute physics simulation step on GPU.
244
+
245
+ - `spawnParticles()` — Emit new particles according to emitter configuration.
246
+
247
+ - `setGravity(value: number)` — Set gravity strength (default 9.8).
248
+
249
+ - `setAttractor(strength: number, position: [number, number, number])` — Set attractor point with strength and 3D position.
250
+
251
+ - `render(renderPass: GPURenderPassEncoder)` — Render particles to the current render pass.
252
+
253
+ ---
254
+
255
+ ### `ParticleSystemManager`
256
+
257
+ Manages multiple particle systems within a single scene.
258
+
259
+ ```typescript
260
+ class ParticleSystemManager {
261
+ constructor(device: GPUDevice)
262
+ }
263
+ ```
264
+
265
+ **Key Methods:**
266
+
267
+ - `createParticleSystem(config?: object): number` — Create new particle system. Returns system ID.
268
+
269
+ - `getActiveSystem(): ParticleSystem | null` — Get currently active particle system instance.
270
+
271
+ - `getActiveConfig(): object | null` — Get active system configuration.
272
+
273
+ - `setActiveSystem(index: number): boolean` — Switch active system by index. Returns success status.
274
+
275
+ - `removeSystem(index: number): boolean` — Remove system by index. Returns success status.
276
+
277
+ - `updateAllSystems(deltaTime: number)` — Update physics for all systems.
278
+
279
+ - `getSystemsList(): Array<{name: string, id: number, index: number, isActive: boolean}>` — Get list of all systems with metadata.
280
+
281
+ - `duplicateActiveSystem(): number` — Clone active system. Returns new system ID.
282
+
283
+ - `async replaceSystems(sceneData: object): boolean` — Load scene from serialized data. Returns success status.
284
+
285
+ ---
286
+
287
+ ### `parseGLB(arrayBuffer)`
288
+
289
+ Parse GLB binary format and extract geometry data.
290
+
291
+ ```typescript
292
+ async function parseGLB(arrayBuffer: ArrayBuffer): Promise<{
293
+ positions: Float32Array,
294
+ normals: Float32Array,
295
+ indices: Uint16Array | Uint32Array,
296
+ texCoords: Float32Array | null,
297
+ vertexCount: number,
298
+ indexCount: number,
299
+ animationData: object | null,
300
+ hasBaseColorTexture: boolean
301
+ }>
302
+ ```
303
+
304
+ **Parameters:**
305
+ - `arrayBuffer` — GLB file as ArrayBuffer
306
+
307
+ **Returns:** Parsed geometry and animation data
308
+
309
+ ---
310
+
311
+ ### `GLBAnimator`
312
+
313
+ Handles skeletal animation playback for animated GLB models.
314
+
315
+ ```typescript
316
+ class GLBAnimator {
317
+ constructor(animationData: object)
318
+
319
+ currentTime: number
320
+ playing: boolean
321
+ speed: number // default 1.0
322
+ loop: boolean // default true
323
+ }
324
+ ```
325
+
326
+ **Key Methods:**
327
+
328
+ - `setRestPose(positions: Float32Array, normals: Float32Array)` — Set T-pose/bind pose for animation.
329
+
330
+ - `update(deltaTime: number): { positions: Float32Array, normals: Float32Array, changed: boolean }` — Advance animation by deltaTime. Returns deformed geometry and change status.
331
+
332
+ - `setAnimation(index: number)` — Switch to animation clip by index.
333
+
334
+ - `getAnimationNames(): string[]` — Get list of available animation clip names.
335
+
336
+ ---
337
+
338
+ ### Secondary Exports
339
+
340
+ The following modules are also exported for advanced usage:
341
+
342
+ - **`ParticleEmitter`** — Emission shape configuration (sphere, cube, cylinder, circle, square)
343
+ - **`ParticlePhysics`** — Physics simulation parameter management
344
+ - **`ParticleTextureManager`** — Texture loading utilities
345
+ - **`Objects3DManager`** — 3D object scene management
346
+ - **`saveScene(manager)`** — Export scene as JSON file download
347
+ - **`loadScene(event)`** — Load scene from file input event
348
+ - **`extractGLBTexture(arrayBuffer)`** — Extract base color texture from GLB file
349
+ - **Shader exports** — WGSL shader code for compute and rendering pipelines
350
+ - **Geometry helpers** — Primitive shape generators (cube, sphere, etc.)
351
+ - **Render pipeline creators** — Low-level WebGPU pipeline construction
352
+
353
+ ## GLB Models & Animation
354
+
355
+ Load and animate 3D models:
356
+
357
+ ```javascript
358
+ import { parseGLB, GLBAnimator } from 'hz-particles';
359
+
360
+ // Load GLB file
361
+ const response = await fetch('model.glb');
362
+ const arrayBuffer = await response.arrayBuffer();
363
+ const glbData = await parseGLB(arrayBuffer);
364
+
365
+ // Use as particle shape
366
+ await system.setGLBModel(arrayBuffer);
367
+
368
+ // Setup animation (if model has animations)
369
+ if (glbData.animationData) {
370
+ const animator = new GLBAnimator(glbData.animationData);
371
+ animator.setRestPose(glbData.positions, glbData.normals);
372
+ animator.playing = true;
373
+ animator.loop = true;
374
+ animator.speed = 1.0;
375
+
376
+ // In render loop:
377
+ const { positions, normals, changed } = animator.update(deltaTime);
378
+ if (changed) {
379
+ // Update particle system with new geometry
380
+ // (See full API documentation for geometry update methods)
381
+ }
382
+ }
383
+ ```
384
+
385
+ ## Scene Save/Load
386
+
387
+ Serialize and restore particle configurations:
388
+
389
+ ```javascript
390
+ import { saveScene, loadScene } from 'hz-particles';
391
+
392
+ // Save current scene
393
+ const button = document.getElementById('save-button');
394
+ button.addEventListener('click', () => {
395
+ saveScene(manager); // Downloads scene.json
396
+ });
397
+
398
+ // Load scene from file input
399
+ const input = document.getElementById('file-input');
400
+ input.addEventListener('change', async (event) => {
401
+ const success = await loadScene(event, manager);
402
+ if (success) {
403
+ console.log('Scene loaded successfully');
404
+ }
405
+ });
406
+ ```
407
+
408
+ ## Online Editor
409
+
410
+ Try the interactive particle editor: [Online Editor](https://your-domain.com/editor) (deployment URL coming soon)
411
+
412
+ The editor provides a visual interface for:
413
+ - Real-time particle system configuration
414
+ - Emitter shape selection and tuning
415
+ - GLB model import and animation control
416
+ - Scene preset library
417
+ - Export/import scene JSON
418
+
419
+ ## TypeScript
420
+
421
+ Type declarations are not yet included in this package. For TypeScript projects, you can suppress the import error:
422
+
423
+ ```typescript
424
+ // @ts-ignore
425
+ import { ParticleSystem, initWebGPU } from 'hz-particles';
426
+ ```
427
+
428
+ Community-contributed type definitions are welcome via PR.
429
+
430
+ ## Contributing
431
+
432
+ Contributions are welcome! Please follow these steps:
433
+
434
+ 1. Fork the repository
435
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
436
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
437
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
438
+ 5. Open a Pull Request
439
+
440
+ ## License
441
+
442
+ MIT License - see [LICENSE](LICENSE) file for details.
443
+
444
+ Copyright (c) 2025 HZ