r3f-vfx 0.2.0 → 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 +68 -13
- package/dist/index.d.ts +0 -4
- package/dist/index.js +16 -64
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ✨ Three VFX ✨
|
|
2
2
|
|
|
3
|
-
High-performance GPU-accelerated particle system for Three.js WebGPU
|
|
3
|
+
High-performance GPU-accelerated particle system for Three.js WebGPU.
|
|
4
|
+
|
|
5
|
+
Available for React Three Fiber (R3F), and experimentally for vanilla Three.js, TresJS (Vue), and Threlte (Svelte).
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
@@ -11,25 +13,21 @@ High-performance GPU-accelerated particle system for Three.js WebGPU with React
|
|
|
11
13
|
- 📊 **Curve-based Control** - Bezier curves for size, opacity, velocity, and rotation over lifetime
|
|
12
14
|
- 🔗 **Emitter System** - Decoupled emitters that can share particle systems
|
|
13
15
|
- ⚡ **WebGPU Native** - Built specifically for Three.js WebGPU renderer
|
|
16
|
+
- 🐢 **WebGL fallback** – Three VFX targets WebGPU ([79% global support](https://caniuse.com/webgpu)) but provides a CPU fallback
|
|
14
17
|
|
|
15
|
-
##
|
|
18
|
+
## Quick Start
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
npm install r3f-vfx
|
|
19
|
-
```
|
|
20
|
+
### React Three Fiber
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Add it to your React Three Fiber project with:
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
|
-
npm install
|
|
25
|
+
npm install r3f-vfx
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
## Quick Start
|
|
28
|
-
|
|
29
28
|
```tsx
|
|
30
29
|
import { Canvas } from '@react-three/fiber'
|
|
31
|
-
import { VFXParticles
|
|
32
|
-
import * as THREE from 'three/webgpu'
|
|
30
|
+
import { VFXParticles } from 'r3f-vfx'
|
|
33
31
|
|
|
34
32
|
function App() {
|
|
35
33
|
return (
|
|
@@ -40,7 +38,64 @@ function App() {
|
|
|
40
38
|
}
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
### Vanilla Three.js (Experimental)
|
|
42
|
+
|
|
43
|
+
Add it to your vanilla Three.js project with:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install vanilla-vfx
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { VFXParticles } from 'vanilla-vfx'
|
|
51
|
+
|
|
52
|
+
const particles = new VFXParticles(renderer, { debug: true })
|
|
53
|
+
scene.add(particles.renderObject)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### TresJS / Vue (Experimental)
|
|
57
|
+
|
|
58
|
+
Add it to your TresJS project with:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install tres-vfx
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```vue
|
|
65
|
+
<script setup>
|
|
66
|
+
import { TresCanvas } from '@tresjs/core'
|
|
67
|
+
import { VFXParticles } from 'tres-vfx'
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<TresCanvas>
|
|
72
|
+
<VFXParticles debug />
|
|
73
|
+
</TresCanvas>
|
|
74
|
+
</template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Threlte / Svelte (Experimental)
|
|
78
|
+
|
|
79
|
+
Add it to your Threlte project with:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install threlte-vfx
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```svelte
|
|
86
|
+
<script>
|
|
87
|
+
import { Canvas } from '@threlte/core'
|
|
88
|
+
import VFXParticles from 'threlte-vfx/VFXParticles.svelte'
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<Canvas>
|
|
92
|
+
<VFXParticles debug />
|
|
93
|
+
</Canvas>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## How to use
|
|
97
|
+
|
|
98
|
+
Use the debug panel to design your effect, then copy the generated code and replace it in your code.
|
|
44
99
|
|
|
45
100
|
## API Reference
|
|
46
101
|
|
package/dist/index.d.ts
CHANGED
|
@@ -9,8 +9,6 @@ type VFXParticlesProps = VFXParticleSystemOptions & {
|
|
|
9
9
|
name?: string;
|
|
10
10
|
/** Show debug control panel */
|
|
11
11
|
debug?: boolean;
|
|
12
|
-
/** Optional fallback content to render when WebGPU is not available */
|
|
13
|
-
fallback?: React.ReactNode;
|
|
14
12
|
};
|
|
15
13
|
declare const VFXParticles: react.ForwardRefExoticComponent<core_vfx.BaseParticleProps & {
|
|
16
14
|
backdropNode?: any | ((data: core_vfx.ParticleData) => any) | null;
|
|
@@ -26,8 +24,6 @@ declare const VFXParticles: react.ForwardRefExoticComponent<core_vfx.BaseParticl
|
|
|
26
24
|
name?: string;
|
|
27
25
|
/** Show debug control panel */
|
|
28
26
|
debug?: boolean;
|
|
29
|
-
/** Optional fallback content to render when WebGPU is not available */
|
|
30
|
-
fallback?: React.ReactNode;
|
|
31
27
|
} & react.RefAttributes<unknown>>;
|
|
32
28
|
|
|
33
29
|
interface VFXEmitterProps extends EmitterControllerOptions {
|
package/dist/index.js
CHANGED
|
@@ -17,18 +17,6 @@ var __spreadValues = (a, b) => {
|
|
|
17
17
|
return a;
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __objRest = (source, exclude) => {
|
|
21
|
-
var target = {};
|
|
22
|
-
for (var prop in source)
|
|
23
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
-
target[prop] = source[prop];
|
|
25
|
-
if (source != null && __getOwnPropSymbols)
|
|
26
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
-
target[prop] = source[prop];
|
|
29
|
-
}
|
|
30
|
-
return target;
|
|
31
|
-
};
|
|
32
20
|
|
|
33
21
|
// src/VFXParticles.tsx
|
|
34
22
|
import {
|
|
@@ -62,7 +50,6 @@ import {
|
|
|
62
50
|
EmitterShape,
|
|
63
51
|
Lighting,
|
|
64
52
|
VFXParticleSystem,
|
|
65
|
-
isWebGPUBackend,
|
|
66
53
|
isNonDefaultRotation,
|
|
67
54
|
normalizeProps,
|
|
68
55
|
updateUniforms,
|
|
@@ -81,9 +68,9 @@ import {
|
|
|
81
68
|
buildCurveTextureBin,
|
|
82
69
|
CurveChannel
|
|
83
70
|
} from "core-vfx";
|
|
84
|
-
import {
|
|
85
|
-
var
|
|
86
|
-
function
|
|
71
|
+
import { jsx } from "react/jsx-runtime";
|
|
72
|
+
var VFXParticles = forwardRef(
|
|
73
|
+
function VFXParticles2({
|
|
87
74
|
name,
|
|
88
75
|
maxParticles = 1e4,
|
|
89
76
|
size = [0.1, 0.3],
|
|
@@ -658,40 +645,18 @@ var VFXParticlesImpl = forwardRef(
|
|
|
658
645
|
return /* @__PURE__ */ jsx("primitive", { ref: spriteRef, object: system.renderObject });
|
|
659
646
|
}
|
|
660
647
|
);
|
|
661
|
-
var warnedWebGL = false;
|
|
662
|
-
var VFXParticles = forwardRef(
|
|
663
|
-
function VFXParticles2(_a, ref) {
|
|
664
|
-
var _b = _a, { fallback } = _b, props = __objRest(_b, ["fallback"]);
|
|
665
|
-
const { gl } = useThree();
|
|
666
|
-
const isWebGPU = useMemo(() => isWebGPUBackend(gl), [gl]);
|
|
667
|
-
if (!isWebGPU) {
|
|
668
|
-
if (!warnedWebGL) {
|
|
669
|
-
warnedWebGL = true;
|
|
670
|
-
console.warn(
|
|
671
|
-
"r3f-vfx: WebGPU backend not detected. Particle system disabled."
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
return /* @__PURE__ */ jsx(Fragment, { children: fallback != null ? fallback : null });
|
|
675
|
-
}
|
|
676
|
-
return /* @__PURE__ */ jsx(VFXParticlesImpl, __spreadValues({ ref }, props));
|
|
677
|
-
}
|
|
678
|
-
);
|
|
679
648
|
|
|
680
649
|
// src/VFXEmitter.tsx
|
|
681
650
|
import {
|
|
682
651
|
useRef as useRef2,
|
|
683
652
|
useEffect as useEffect2,
|
|
684
653
|
useCallback as useCallback2,
|
|
685
|
-
useMemo as useMemo2,
|
|
686
654
|
forwardRef as forwardRef2,
|
|
687
655
|
useImperativeHandle as useImperativeHandle2
|
|
688
656
|
} from "react";
|
|
689
|
-
import { useFrame as useFrame2
|
|
657
|
+
import { useFrame as useFrame2 } from "@react-three/fiber";
|
|
690
658
|
import { Vector3, Quaternion } from "three/webgpu";
|
|
691
|
-
import {
|
|
692
|
-
EmitterController,
|
|
693
|
-
isWebGPUBackend as isWebGPUBackend2
|
|
694
|
-
} from "core-vfx";
|
|
659
|
+
import { EmitterController } from "core-vfx";
|
|
695
660
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
696
661
|
var worldPos = new Vector3();
|
|
697
662
|
var worldQuat = new Quaternion();
|
|
@@ -709,8 +674,6 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
|
|
|
709
674
|
onEmit,
|
|
710
675
|
children
|
|
711
676
|
}, ref) {
|
|
712
|
-
const { gl } = useThree2();
|
|
713
|
-
const isWebGPU = useMemo2(() => isWebGPUBackend2(gl), [gl]);
|
|
714
677
|
const groupRef = useRef2(null);
|
|
715
678
|
const controllerRef = useRef2(null);
|
|
716
679
|
if (!controllerRef.current) {
|
|
@@ -733,10 +696,9 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
|
|
|
733
696
|
return useVFXStore.getState().getParticles(name);
|
|
734
697
|
}, [name, particlesRef]);
|
|
735
698
|
useEffect2(() => {
|
|
736
|
-
if (!isWebGPU) return;
|
|
737
699
|
const system = getParticleSystem();
|
|
738
700
|
controller.setSystem(system);
|
|
739
|
-
}, [
|
|
701
|
+
}, [getParticleSystem, controller]);
|
|
740
702
|
useEffect2(() => {
|
|
741
703
|
controller.updateOptions({
|
|
742
704
|
emitCount,
|
|
@@ -760,7 +722,6 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
|
|
|
760
722
|
onEmit
|
|
761
723
|
]);
|
|
762
724
|
useFrame2((_, delta) => {
|
|
763
|
-
if (!isWebGPU) return;
|
|
764
725
|
if (!controller.getSystem()) {
|
|
765
726
|
const system = getParticleSystem();
|
|
766
727
|
if (system) controller.setSystem(system);
|
|
@@ -830,8 +791,6 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
|
|
|
830
791
|
);
|
|
831
792
|
});
|
|
832
793
|
function useVFXEmitter(name) {
|
|
833
|
-
const { gl } = useThree2();
|
|
834
|
-
const isWebGPU = useMemo2(() => isWebGPUBackend2(gl), [gl]);
|
|
835
794
|
const getParticles = useVFXStore((s) => s.getParticles);
|
|
836
795
|
const storeEmit = useVFXStore((s) => s.emit);
|
|
837
796
|
const storeStart = useVFXStore((s) => s.start);
|
|
@@ -839,44 +798,37 @@ function useVFXEmitter(name) {
|
|
|
839
798
|
const storeClear = useVFXStore((s) => s.clear);
|
|
840
799
|
const emit = useCallback2(
|
|
841
800
|
(position = [0, 0, 0], count = 20, overrides = null) => {
|
|
842
|
-
if (!isWebGPU) return false;
|
|
843
801
|
const [x, y, z] = position;
|
|
844
802
|
return storeEmit(name, { x, y, z, count, overrides });
|
|
845
803
|
},
|
|
846
|
-
[
|
|
804
|
+
[name, storeEmit]
|
|
847
805
|
);
|
|
848
806
|
const burst = useCallback2(
|
|
849
807
|
(position = [0, 0, 0], count = 50, overrides = null) => {
|
|
850
|
-
if (!isWebGPU) return false;
|
|
851
808
|
const [x, y, z] = position;
|
|
852
809
|
return storeEmit(name, { x, y, z, count, overrides });
|
|
853
810
|
},
|
|
854
|
-
[
|
|
811
|
+
[name, storeEmit]
|
|
855
812
|
);
|
|
856
813
|
const start = useCallback2(() => {
|
|
857
|
-
if (!isWebGPU) return false;
|
|
858
814
|
return storeStart(name);
|
|
859
|
-
}, [
|
|
815
|
+
}, [name, storeStart]);
|
|
860
816
|
const stop = useCallback2(() => {
|
|
861
|
-
if (!isWebGPU) return false;
|
|
862
817
|
return storeStop(name);
|
|
863
|
-
}, [
|
|
818
|
+
}, [name, storeStop]);
|
|
864
819
|
const clear = useCallback2(() => {
|
|
865
|
-
if (!isWebGPU) return false;
|
|
866
820
|
return storeClear(name);
|
|
867
|
-
}, [
|
|
821
|
+
}, [name, storeClear]);
|
|
868
822
|
const isEmitting = useCallback2(() => {
|
|
869
823
|
var _a;
|
|
870
|
-
if (!isWebGPU) return false;
|
|
871
824
|
const particles = getParticles(name);
|
|
872
825
|
return (_a = particles == null ? void 0 : particles.isEmitting) != null ? _a : false;
|
|
873
|
-
}, [
|
|
826
|
+
}, [name, getParticles]);
|
|
874
827
|
const getUniforms = useCallback2(() => {
|
|
875
828
|
var _a;
|
|
876
|
-
if (!isWebGPU) return null;
|
|
877
829
|
const particles = getParticles(name);
|
|
878
830
|
return (_a = particles == null ? void 0 : particles.uniforms) != null ? _a : null;
|
|
879
|
-
}, [
|
|
831
|
+
}, [name, getParticles]);
|
|
880
832
|
return {
|
|
881
833
|
emit,
|
|
882
834
|
burst,
|
|
@@ -885,7 +837,7 @@ function useVFXEmitter(name) {
|
|
|
885
837
|
clear,
|
|
886
838
|
isEmitting,
|
|
887
839
|
getUniforms,
|
|
888
|
-
getParticles: () =>
|
|
840
|
+
getParticles: () => getParticles(name)
|
|
889
841
|
};
|
|
890
842
|
}
|
|
891
843
|
|
|
@@ -893,7 +845,7 @@ function useVFXEmitter(name) {
|
|
|
893
845
|
import {
|
|
894
846
|
VFXParticleSystem as VFXParticleSystem2,
|
|
895
847
|
EmitterController as EmitterController2,
|
|
896
|
-
isWebGPUBackend
|
|
848
|
+
isWebGPUBackend,
|
|
897
849
|
isNonDefaultRotation as isNonDefaultRotation2,
|
|
898
850
|
normalizeProps as normalizeProps2,
|
|
899
851
|
resolveCurveTexture
|
|
@@ -914,7 +866,7 @@ export {
|
|
|
914
866
|
buildCurveTextureBin,
|
|
915
867
|
createCombinedCurveTexture,
|
|
916
868
|
isNonDefaultRotation2 as isNonDefaultRotation,
|
|
917
|
-
|
|
869
|
+
isWebGPUBackend,
|
|
918
870
|
normalizeProps2 as normalizeProps,
|
|
919
871
|
resolveCurveTexture,
|
|
920
872
|
useVFXEmitter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "r3f-vfx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"prepublishOnly": "bun run copy-readme"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"core-vfx": "0.
|
|
28
|
-
"debug-vfx": "0.1.
|
|
27
|
+
"core-vfx": "0.2.0",
|
|
28
|
+
"debug-vfx": "0.1.2",
|
|
29
29
|
"zustand": "5.0.10"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|