threlte-vfx 0.2.1 → 0.3.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 +3 -5
- package/dist/VFXEmitter.svelte +2 -13
- package/dist/VFXParticles.svelte +1 -22
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8 -38
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,9 +13,7 @@ Available for React Three Fiber (R3F), and experimentally for vanilla Three.js,
|
|
|
13
13
|
- 📊 **Curve-based Control** - Bezier curves for size, opacity, velocity, and rotation over lifetime
|
|
14
14
|
- 🔗 **Emitter System** - Decoupled emitters that can share particle systems
|
|
15
15
|
- ⚡ **WebGPU Native** - Built specifically for Three.js WebGPU renderer
|
|
16
|
-
|
|
17
|
-
⚠️ Three VFX only supports WebGPU at the moment ([79% global support](https://caniuse.com/webgpu)). A `fallback` option is available to replace the particle systems by your own fallback objects.
|
|
18
|
-
|
|
16
|
+
- 🐢 **WebGL fallback** – Three VFX targets WebGPU ([79% global support](https://caniuse.com/webgpu)) but provides a CPU fallback
|
|
19
17
|
|
|
20
18
|
## Quick Start
|
|
21
19
|
|
|
@@ -65,8 +63,8 @@ npm install tres-vfx
|
|
|
65
63
|
|
|
66
64
|
```vue
|
|
67
65
|
<script setup>
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
import { TresCanvas } from '@tresjs/core'
|
|
67
|
+
import { VFXParticles } from 'tres-vfx'
|
|
70
68
|
</script>
|
|
71
69
|
|
|
72
70
|
<template>
|
package/dist/VFXEmitter.svelte
CHANGED
|
@@ -4,7 +4,6 @@ import { onMount } from 'svelte'
|
|
|
4
4
|
import { Vector3, Quaternion, Group } from 'three/webgpu'
|
|
5
5
|
import {
|
|
6
6
|
EmitterController,
|
|
7
|
-
isWebGPUBackend,
|
|
8
7
|
coreStore,
|
|
9
8
|
type EmitterControllerOptions,
|
|
10
9
|
} from 'core-vfx'
|
|
@@ -44,7 +43,6 @@ let {
|
|
|
44
43
|
const { renderer } = useThrelte()
|
|
45
44
|
|
|
46
45
|
let groupRef: Group | null = $state(null)
|
|
47
|
-
let isWebGPU = $state(false)
|
|
48
46
|
|
|
49
47
|
const controller = new EmitterController({
|
|
50
48
|
emitCount,
|
|
@@ -89,22 +87,13 @@ $effect(() => {
|
|
|
89
87
|
})
|
|
90
88
|
})
|
|
91
89
|
|
|
92
|
-
function checkWebGPU() {
|
|
93
|
-
if (renderer && isWebGPUBackend(renderer)) {
|
|
94
|
-
isWebGPU = true
|
|
95
|
-
const system = getParticleSystem()
|
|
96
|
-
if (system) controller.setSystem(system)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
90
|
onMount(() => {
|
|
101
|
-
|
|
91
|
+
const system = getParticleSystem()
|
|
92
|
+
if (system) controller.setSystem(system)
|
|
102
93
|
})
|
|
103
94
|
|
|
104
95
|
// Frame loop
|
|
105
96
|
useTask((delta) => {
|
|
106
|
-
if (!isWebGPU) return
|
|
107
|
-
|
|
108
97
|
if (!controller.getSystem()) {
|
|
109
98
|
const system = getParticleSystem()
|
|
110
99
|
if (system) controller.setSystem(system)
|
package/dist/VFXParticles.svelte
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
EmitterShape,
|
|
10
10
|
Lighting,
|
|
11
11
|
VFXParticleSystem,
|
|
12
|
-
isWebGPUBackend,
|
|
13
12
|
isNonDefaultRotation,
|
|
14
13
|
normalizeProps,
|
|
15
14
|
updateUniforms,
|
|
@@ -144,18 +143,15 @@ let {
|
|
|
144
143
|
|
|
145
144
|
const { renderer } = useThrelte()
|
|
146
145
|
|
|
147
|
-
let warnedWebGL = false
|
|
148
146
|
let mounted = false
|
|
149
147
|
|
|
150
148
|
// Internal state — NOT tracked by effects (plain variables)
|
|
151
149
|
let _system: VFXParticleSystem | null = null
|
|
152
150
|
let _renderObject: THREE.Object3D | null = null
|
|
153
|
-
let _isWebGPU = false
|
|
154
151
|
let _emitting = autoStart
|
|
155
152
|
|
|
156
153
|
// Reactive state for the template only
|
|
157
154
|
let renderObjectForTemplate: THREE.Object3D | null = $state(null)
|
|
158
|
-
let isWebGPUForTemplate = $state(false)
|
|
159
155
|
|
|
160
156
|
let debugValues: Record<string, unknown> | null = null
|
|
161
157
|
|
|
@@ -266,7 +262,6 @@ function destroySystem() {
|
|
|
266
262
|
_system = null
|
|
267
263
|
_renderObject = null
|
|
268
264
|
renderObjectForTemplate = null
|
|
269
|
-
isWebGPUForTemplate = false
|
|
270
265
|
}
|
|
271
266
|
|
|
272
267
|
function initSystem() {
|
|
@@ -285,20 +280,6 @@ function initSystem() {
|
|
|
285
280
|
return
|
|
286
281
|
}
|
|
287
282
|
|
|
288
|
-
if (!isWebGPUBackend(renderer)) {
|
|
289
|
-
if (!warnedWebGL) {
|
|
290
|
-
warnedWebGL = true
|
|
291
|
-
console.warn(
|
|
292
|
-
'threlte-vfx: WebGPU backend not detected. Particle system disabled.'
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
_isWebGPU = false
|
|
296
|
-
isWebGPUForTemplate = false
|
|
297
|
-
renderObjectForTemplate = null
|
|
298
|
-
return
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
_isWebGPU = true
|
|
302
283
|
const newSystem = createSystem()
|
|
303
284
|
if (!newSystem) return
|
|
304
285
|
|
|
@@ -334,7 +315,6 @@ function initSystem() {
|
|
|
334
315
|
}
|
|
335
316
|
|
|
336
317
|
// Update template-facing reactive state last
|
|
337
|
-
isWebGPUForTemplate = true
|
|
338
318
|
renderObjectForTemplate = newSystem.renderObject
|
|
339
319
|
}
|
|
340
320
|
|
|
@@ -627,7 +607,6 @@ $effect(() => {
|
|
|
627
607
|
// Use untrack for the actual init to avoid reading/writing
|
|
628
608
|
// reactive state that would re-trigger this effect
|
|
629
609
|
untrack(() => {
|
|
630
|
-
if (!_isWebGPU) return
|
|
631
610
|
initSystem()
|
|
632
611
|
})
|
|
633
612
|
})
|
|
@@ -768,6 +747,6 @@ export function clear() {
|
|
|
768
747
|
}
|
|
769
748
|
</script>
|
|
770
749
|
|
|
771
|
-
{#if
|
|
750
|
+
{#if renderObjectForTemplate}
|
|
772
751
|
<T is={renderObjectForTemplate} />
|
|
773
752
|
{/if}
|
package/dist/index.d.ts
CHANGED
|
@@ -65,7 +65,13 @@ interface VFXParticlesProps {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
declare class VFXParticles extends SvelteComponent<VFXParticlesProps> {
|
|
68
|
-
spawn(
|
|
68
|
+
spawn(
|
|
69
|
+
x?: number,
|
|
70
|
+
y?: number,
|
|
71
|
+
z?: number,
|
|
72
|
+
count?: number,
|
|
73
|
+
overrides?: Record<string, unknown> | null
|
|
74
|
+
): void
|
|
69
75
|
start(): void
|
|
70
76
|
stop(): void
|
|
71
77
|
clear(): void
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
EmitterShape,
|
|
32
32
|
Lighting,
|
|
33
33
|
VFXParticleSystem,
|
|
34
|
-
isWebGPUBackend,
|
|
35
34
|
isNonDefaultRotation,
|
|
36
35
|
normalizeProps,
|
|
37
36
|
updateUniforms,
|
|
@@ -43,14 +42,11 @@ function VFXParticles($$anchor, $$props) {
|
|
|
43
42
|
$.push($$props, true);
|
|
44
43
|
let name = $.prop($$props, "name", 19, () => void 0), debug = $.prop($$props, "debug", 3, false), maxParticles = $.prop($$props, "maxParticles", 3, 1e4), size = $.prop($$props, "size", 19, () => [0.1, 0.3]), colorStart = $.prop($$props, "colorStart", 19, () => ["#ffffff"]), colorEnd = $.prop($$props, "colorEnd", 3, null), fadeSize = $.prop($$props, "fadeSize", 19, () => [1, 0]), fadeSizeCurve = $.prop($$props, "fadeSizeCurve", 3, null), fadeOpacity = $.prop($$props, "fadeOpacity", 19, () => [1, 0]), fadeOpacityCurve = $.prop($$props, "fadeOpacityCurve", 3, null), velocityCurve = $.prop($$props, "velocityCurve", 3, null), gravity = $.prop($$props, "gravity", 19, () => [0, 0, 0]), lifetime = $.prop($$props, "lifetime", 19, () => [1, 2]), direction = $.prop($$props, "direction", 19, () => [[-1, 1], [0, 1], [-1, 1]]), startPosition = $.prop($$props, "startPosition", 19, () => [[0, 0], [0, 0], [0, 0]]), speed = $.prop($$props, "speed", 19, () => [0.1, 0.1]), friction = $.prop($$props, "friction", 19, () => ({ intensity: 0, easing: "linear" })), appearance = $.prop($$props, "appearance", 19, () => Appearance.GRADIENT), alphaMap = $.prop($$props, "alphaMap", 3, null), flipbook = $.prop($$props, "flipbook", 3, null), rotation = $.prop($$props, "rotation", 19, () => [0, 0]), rotationSpeed = $.prop($$props, "rotationSpeed", 19, () => [0, 0]), rotationSpeedCurve = $.prop($$props, "rotationSpeedCurve", 3, null), geometry = $.prop($$props, "geometry", 3, null), orientToDirection = $.prop($$props, "orientToDirection", 3, false), orientAxis = $.prop($$props, "orientAxis", 3, "z"), stretchBySpeed = $.prop($$props, "stretchBySpeed", 3, null), lighting = $.prop($$props, "lighting", 19, () => Lighting.STANDARD), shadow = $.prop($$props, "shadow", 3, false), blending = $.prop($$props, "blending", 19, () => Blending.NORMAL), intensity = $.prop($$props, "intensity", 3, 1), position = $.prop($$props, "position", 19, () => [0, 0, 0]), autoStart = $.prop($$props, "autoStart", 3, true), delay = $.prop($$props, "delay", 3, 0), backdropNode = $.prop($$props, "backdropNode", 3, null), opacityNode = $.prop($$props, "opacityNode", 3, null), colorNode = $.prop($$props, "colorNode", 3, null), alphaTestNode = $.prop($$props, "alphaTestNode", 3, null), castShadowNode = $.prop($$props, "castShadowNode", 3, null), emitCount = $.prop($$props, "emitCount", 3, 1), emitterShape = $.prop($$props, "emitterShape", 19, () => EmitterShape.BOX), emitterRadius = $.prop($$props, "emitterRadius", 19, () => [0, 1]), emitterAngle = $.prop($$props, "emitterAngle", 19, () => Math.PI / 4), emitterHeight = $.prop($$props, "emitterHeight", 19, () => [0, 1]), emitterSurfaceOnly = $.prop($$props, "emitterSurfaceOnly", 3, false), emitterDirection = $.prop($$props, "emitterDirection", 19, () => [0, 1, 0]), turbulence = $.prop($$props, "turbulence", 3, null), attractors = $.prop($$props, "attractors", 3, null), attractToCenter = $.prop($$props, "attractToCenter", 3, false), startPositionAsDirection = $.prop($$props, "startPositionAsDirection", 3, false), softParticles = $.prop($$props, "softParticles", 3, false), softDistance = $.prop($$props, "softDistance", 3, 0.5), collision = $.prop($$props, "collision", 3, null), curveTexturePath = $.prop($$props, "curveTexturePath", 3, null), depthTest = $.prop($$props, "depthTest", 3, true), renderOrder = $.prop($$props, "renderOrder", 3, 0);
|
|
45
44
|
const { renderer } = useThrelte();
|
|
46
|
-
let warnedWebGL = false;
|
|
47
45
|
let mounted = false;
|
|
48
46
|
let _system = null;
|
|
49
47
|
let _renderObject = null;
|
|
50
|
-
let _isWebGPU = false;
|
|
51
48
|
let _emitting = autoStart();
|
|
52
49
|
let renderObjectForTemplate = $.state(null);
|
|
53
|
-
let isWebGPUForTemplate = $.state(false);
|
|
54
50
|
let debugValues = null;
|
|
55
51
|
let activeMaxParticles = $.state($.proxy(maxParticles()));
|
|
56
52
|
let activeLighting = $.state($.proxy(lighting()));
|
|
@@ -143,7 +139,6 @@ function VFXParticles($$anchor, $$props) {
|
|
|
143
139
|
_system = null;
|
|
144
140
|
_renderObject = null;
|
|
145
141
|
$.set(renderObjectForTemplate, null);
|
|
146
|
-
$.set(isWebGPUForTemplate, false);
|
|
147
142
|
}
|
|
148
143
|
function initSystem() {
|
|
149
144
|
const oldSystem = _system;
|
|
@@ -159,17 +154,6 @@ function VFXParticles($$anchor, $$props) {
|
|
|
159
154
|
console.warn("threlte-vfx: No renderer instance available");
|
|
160
155
|
return;
|
|
161
156
|
}
|
|
162
|
-
if (!isWebGPUBackend(renderer)) {
|
|
163
|
-
if (!warnedWebGL) {
|
|
164
|
-
warnedWebGL = true;
|
|
165
|
-
console.warn("threlte-vfx: WebGPU backend not detected. Particle system disabled.");
|
|
166
|
-
}
|
|
167
|
-
_isWebGPU = false;
|
|
168
|
-
$.set(isWebGPUForTemplate, false);
|
|
169
|
-
$.set(renderObjectForTemplate, null);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
_isWebGPU = true;
|
|
173
157
|
const newSystem = createSystem();
|
|
174
158
|
if (!newSystem) return;
|
|
175
159
|
_system = newSystem;
|
|
@@ -199,7 +183,6 @@ function VFXParticles($$anchor, $$props) {
|
|
|
199
183
|
if (debug()) {
|
|
200
184
|
initDebugPanel();
|
|
201
185
|
}
|
|
202
|
-
$.set(isWebGPUForTemplate, true);
|
|
203
186
|
$.set(renderObjectForTemplate, newSystem.renderObject, true);
|
|
204
187
|
}
|
|
205
188
|
function handleDebugUpdate(newValues) {
|
|
@@ -423,7 +406,6 @@ function VFXParticles($$anchor, $$props) {
|
|
|
423
406
|
];
|
|
424
407
|
if (!mounted) return;
|
|
425
408
|
untrack(() => {
|
|
426
|
-
if (!_isWebGPU) return;
|
|
427
409
|
initSystem();
|
|
428
410
|
});
|
|
429
411
|
});
|
|
@@ -553,7 +535,7 @@ function VFXParticles($$anchor, $$props) {
|
|
|
553
535
|
});
|
|
554
536
|
};
|
|
555
537
|
$.if(node, ($$render) => {
|
|
556
|
-
if ($.get(
|
|
538
|
+
if ($.get(renderObjectForTemplate)) $$render(consequent);
|
|
557
539
|
});
|
|
558
540
|
}
|
|
559
541
|
$.append($$anchor, fragment);
|
|
@@ -566,11 +548,7 @@ import * as $2 from "svelte/internal/client";
|
|
|
566
548
|
import { T as T2, useThrelte as useThrelte2, useTask as useTask2 } from "@threlte/core";
|
|
567
549
|
import { onMount as onMount2 } from "svelte";
|
|
568
550
|
import { Vector3, Quaternion, Group } from "three/webgpu";
|
|
569
|
-
import {
|
|
570
|
-
EmitterController,
|
|
571
|
-
isWebGPUBackend as isWebGPUBackend2,
|
|
572
|
-
coreStore as coreStore2
|
|
573
|
-
} from "core-vfx";
|
|
551
|
+
import { EmitterController, coreStore as coreStore2 } from "core-vfx";
|
|
574
552
|
function VFXEmitter($$anchor, $$props) {
|
|
575
553
|
$2.push($$props, true);
|
|
576
554
|
const worldPos = new Vector3();
|
|
@@ -578,7 +556,6 @@ function VFXEmitter($$anchor, $$props) {
|
|
|
578
556
|
let name = $2.prop($$props, "name", 19, () => void 0), particlesRef = $2.prop($$props, "particlesRef", 19, () => void 0), position = $2.prop($$props, "position", 19, () => [0, 0, 0]), emitCount = $2.prop($$props, "emitCount", 3, 10), delay = $2.prop($$props, "delay", 3, 0), autoStart = $2.prop($$props, "autoStart", 3, true), loop = $2.prop($$props, "loop", 3, true), localDirection = $2.prop($$props, "localDirection", 3, false), direction = $2.prop($$props, "direction", 19, () => void 0), overrides = $2.prop($$props, "overrides", 3, null), onEmit = $2.prop($$props, "onEmit", 19, () => void 0);
|
|
579
557
|
const { renderer } = useThrelte2();
|
|
580
558
|
let groupRef = $2.state(null);
|
|
581
|
-
let isWebGPU = $2.state(false);
|
|
582
559
|
const controller = new EmitterController({
|
|
583
560
|
emitCount: emitCount(),
|
|
584
561
|
delay: delay(),
|
|
@@ -617,18 +594,11 @@ function VFXEmitter($$anchor, $$props) {
|
|
|
617
594
|
onEmit: onEmit()
|
|
618
595
|
});
|
|
619
596
|
});
|
|
620
|
-
function checkWebGPU() {
|
|
621
|
-
if (renderer && isWebGPUBackend2(renderer)) {
|
|
622
|
-
$2.set(isWebGPU, true);
|
|
623
|
-
const system = getParticleSystem();
|
|
624
|
-
if (system) controller.setSystem(system);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
597
|
onMount2(() => {
|
|
628
|
-
|
|
598
|
+
const system = getParticleSystem();
|
|
599
|
+
if (system) controller.setSystem(system);
|
|
629
600
|
});
|
|
630
601
|
useTask2((delta) => {
|
|
631
|
-
if (!$2.get(isWebGPU)) return;
|
|
632
602
|
if (!controller.getSystem()) {
|
|
633
603
|
const system = getParticleSystem();
|
|
634
604
|
if (system) controller.setSystem(system);
|
|
@@ -709,11 +679,11 @@ function VFXEmitter($$anchor, $$props) {
|
|
|
709
679
|
|
|
710
680
|
// src/useVFXEmitter.ts
|
|
711
681
|
import { useThrelte as useThrelte3 } from "@threlte/core";
|
|
712
|
-
import { coreStore as coreStore3, isWebGPUBackend
|
|
682
|
+
import { coreStore as coreStore3, isWebGPUBackend } from "core-vfx";
|
|
713
683
|
function useVFXEmitter(name) {
|
|
714
684
|
const { renderer } = useThrelte3();
|
|
715
685
|
const checkWebGPU = () => {
|
|
716
|
-
return renderer &&
|
|
686
|
+
return renderer && isWebGPUBackend(renderer);
|
|
717
687
|
};
|
|
718
688
|
const getParticles = () => coreStore3.getState().getParticles(name);
|
|
719
689
|
const emit = (position = [0, 0, 0], count = 20, overrides = null) => {
|
|
@@ -796,7 +766,7 @@ import {
|
|
|
796
766
|
import {
|
|
797
767
|
VFXParticleSystem as VFXParticleSystem2,
|
|
798
768
|
EmitterController as EmitterController2,
|
|
799
|
-
isWebGPUBackend as
|
|
769
|
+
isWebGPUBackend as isWebGPUBackend2,
|
|
800
770
|
isNonDefaultRotation as isNonDefaultRotation2,
|
|
801
771
|
normalizeProps as normalizeProps2,
|
|
802
772
|
resolveCurveTexture
|
|
@@ -817,7 +787,7 @@ export {
|
|
|
817
787
|
buildCurveTextureBin,
|
|
818
788
|
createCombinedCurveTexture,
|
|
819
789
|
isNonDefaultRotation2 as isNonDefaultRotation,
|
|
820
|
-
|
|
790
|
+
isWebGPUBackend2 as isWebGPUBackend,
|
|
821
791
|
normalizeProps2 as normalizeProps,
|
|
822
792
|
resolveCurveTexture,
|
|
823
793
|
useVFXEmitter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "threlte-vfx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"prepublishOnly": "bun run typecheck && bun run build && bun run copy-readme"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"core-vfx": "0.
|
|
38
|
-
"debug-vfx": "0.1.
|
|
37
|
+
"core-vfx": "0.2.0",
|
|
38
|
+
"debug-vfx": "0.1.2"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@threlte/core": ">=8.0.0",
|