r3f-vfx 0.0.4 → 0.0.6

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +213 -49
  2. package/dist/index.js +68 -181
  3. package/package.json +6 -2
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- import * as zustand from 'zustand';
1
+ import * as react from 'react';
2
+ import { RefObject, ReactNode } from 'react';
3
+ import * as THREE from 'three/webgpu';
4
+ import { CoreState } from 'core-vfx';
2
5
 
3
6
  declare const Appearance: Readonly<{
4
7
  DEFAULT: "default";
@@ -6,10 +9,10 @@ declare const Appearance: Readonly<{
6
9
  CIRCULAR: "circular";
7
10
  }>;
8
11
  declare const Blending: Readonly<{
9
- NORMAL: any;
10
- ADDITIVE: any;
11
- MULTIPLY: any;
12
- SUBTRACTIVE: any;
12
+ NORMAL: 1;
13
+ ADDITIVE: 2;
14
+ MULTIPLY: 4;
15
+ SUBTRACTIVE: 3;
13
16
  }>;
14
17
  declare const EmitterShape: Readonly<{
15
18
  POINT: 0;
@@ -34,32 +37,185 @@ declare const Lighting: Readonly<{
34
37
  STANDARD: "standard";
35
38
  PHYSICAL: "physical";
36
39
  }>;
37
- declare function bakeCurveToArray(curveData: any, resolution?: number): Float32Array<ArrayBuffer>;
38
- declare function createCombinedCurveTexture(sizeCurve: any, opacityCurve: any, velocityCurve: any, rotationSpeedCurve: any): any;
39
- declare const VFXParticles: any;
40
-
41
- /**
42
- * Higher-order hook for programmatic emitter control
43
- *
44
- * Usage:
45
- * const { emit, burst, start, stop } = useVFXEmitter("sparks");
46
- *
47
- * // Emit at a position
48
- * emit([1, 2, 3], 50);
49
- *
50
- * // Burst with overrides
51
- * burst([0, 0, 0], 100, { colorStart: ["#ff0000"] });
52
- */
53
- declare function useVFXEmitter(name: any): {
54
- emit: any;
55
- burst: any;
56
- start: any;
57
- stop: any;
58
- clear: any;
59
- isEmitting: any;
60
- getUniforms: any;
61
- getParticles: () => any;
40
+ type CurvePoint = {
41
+ pos: [number, number];
42
+ handleIn?: [number, number];
43
+ handleOut?: [number, number];
44
+ };
45
+ type CurveData = {
46
+ points: CurvePoint[];
47
+ } | null;
48
+ declare const bakeCurveToArray: (curveData: CurveData, resolution?: number) => Float32Array;
49
+ declare const createCombinedCurveTexture: (sizeCurve: CurveData, opacityCurve: CurveData, velocityCurve: CurveData, rotationSpeedCurve: CurveData) => THREE.DataTexture;
50
+ type Rotation3DInput = number | [number, number] | [[number, number], [number, number], [number, number]] | null | undefined;
51
+ type ParticleData = Record<string, any>;
52
+ type VFXParticlesProps = {
53
+ /** Optional name for registering with useVFXStore (enables VFXEmitter linking) */
54
+ name?: string;
55
+ /** Maximum number of particles */
56
+ maxParticles?: number;
57
+ /** Particle size [min, max] or single value */
58
+ size?: number | [number, number];
59
+ /** Array of hex color strings for start color */
60
+ colorStart?: string[];
61
+ /** Array of hex color strings for end color (null = use colorStart) */
62
+ colorEnd?: string[] | null;
63
+ /** Fade size [start, end] multiplier over lifetime */
64
+ fadeSize?: number | [number, number];
65
+ /** Curve data for size over lifetime */
66
+ fadeSizeCurve?: CurveData;
67
+ /** Fade opacity [start, end] multiplier over lifetime */
68
+ fadeOpacity?: number | [number, number];
69
+ /** Curve data for opacity over lifetime */
70
+ fadeOpacityCurve?: CurveData;
71
+ /** Curve data for velocity over lifetime */
72
+ velocityCurve?: CurveData;
73
+ /** Gravity vector [x, y, z] */
74
+ gravity?: [number, number, number];
75
+ /** Particle lifetime in seconds [min, max] or single value */
76
+ lifetime?: number | [number, number];
77
+ /** Direction ranges for velocity */
78
+ direction?: Rotation3DInput;
79
+ /** Start position offset ranges */
80
+ startPosition?: Rotation3DInput;
81
+ /** Speed [min, max] or single value */
82
+ speed?: number | [number, number];
83
+ /** Friction settings */
84
+ friction?: {
85
+ intensity?: number | [number, number];
86
+ easing?: string;
87
+ };
88
+ /** Particle appearance type */
89
+ appearance?: (typeof Appearance)[keyof typeof Appearance];
90
+ /** Alpha map texture */
91
+ alphaMap?: THREE.Texture | null;
92
+ /** Flipbook animation settings */
93
+ flipbook?: {
94
+ rows: number;
95
+ columns: number;
96
+ } | null;
97
+ /** Rotation [min, max] in radians or 3D rotation ranges */
98
+ rotation?: Rotation3DInput;
99
+ /** Rotation speed [min, max] in radians/second or 3D ranges */
100
+ rotationSpeed?: Rotation3DInput;
101
+ /** Curve data for rotation speed over lifetime */
102
+ rotationSpeedCurve?: CurveData;
103
+ /** Custom geometry for 3D particles */
104
+ geometry?: THREE.BufferGeometry | null;
105
+ /** Rotate geometry to face velocity direction */
106
+ orientToDirection?: boolean;
107
+ /** Which local axis aligns with velocity */
108
+ orientAxis?: string;
109
+ /** Stretch particles based on speed */
110
+ stretchBySpeed?: {
111
+ factor: number;
112
+ maxStretch: number;
113
+ } | null;
114
+ /** Material lighting type for geometry mode */
115
+ lighting?: (typeof Lighting)[keyof typeof Lighting];
116
+ /** Enable shadows on geometry instances */
117
+ shadow?: boolean;
118
+ /** Blending mode */
119
+ blending?: THREE.Blending;
120
+ /** Color intensity multiplier */
121
+ intensity?: number;
122
+ /** Emitter position [x, y, z] */
123
+ position?: [number, number, number];
124
+ /** Start emitting automatically */
125
+ autoStart?: boolean;
126
+ /** Delay between emissions in seconds */
127
+ delay?: number;
128
+ /** TSL node or function for backdrop sampling */
129
+ backdropNode?: any | ((data: ParticleData) => any) | null;
130
+ /** TSL node or function for custom opacity */
131
+ opacityNode?: any | ((data: ParticleData) => any) | null;
132
+ /** TSL node or function to override color */
133
+ colorNode?: any | ((data: ParticleData, defaultColor: any) => any) | null;
134
+ /** TSL node or function for alpha test/discard */
135
+ alphaTestNode?: any | ((data: ParticleData) => any) | null;
136
+ /** TSL node or function for shadow map output */
137
+ castShadowNode?: any | ((data: ParticleData) => any) | null;
138
+ /** Number of particles to emit per frame */
139
+ emitCount?: number;
140
+ /** Emitter shape type */
141
+ emitterShape?: (typeof EmitterShape)[keyof typeof EmitterShape];
142
+ /** Emitter radius [inner, outer] */
143
+ emitterRadius?: number | [number, number];
144
+ /** Cone angle in radians */
145
+ emitterAngle?: number;
146
+ /** Cone height [min, max] */
147
+ emitterHeight?: number | [number, number];
148
+ /** Emit from surface only */
149
+ emitterSurfaceOnly?: boolean;
150
+ /** Direction for cone/disk normal */
151
+ emitterDirection?: [number, number, number];
152
+ /** Turbulence settings */
153
+ turbulence?: {
154
+ intensity: number;
155
+ frequency?: number;
156
+ speed?: number;
157
+ } | null;
158
+ /** Array of attractors (max 4) */
159
+ attractors?: Array<{
160
+ position?: [number, number, number];
161
+ strength?: number;
162
+ radius?: number;
163
+ type?: 'point' | 'vortex';
164
+ axis?: [number, number, number];
165
+ }> | null;
166
+ /** Particles move from spawn position to center over lifetime */
167
+ attractToCenter?: boolean;
168
+ /** Use start position offset as direction */
169
+ startPositionAsDirection?: boolean;
170
+ /** Fade particles when intersecting scene geometry */
171
+ softParticles?: boolean;
172
+ /** Distance over which to fade soft particles */
173
+ softDistance?: number;
174
+ /** Plane collision settings */
175
+ collision?: {
176
+ plane?: {
177
+ y: number;
178
+ };
179
+ bounce?: number;
180
+ friction?: number;
181
+ die?: boolean;
182
+ sizeBasedGravity?: number;
183
+ } | null;
184
+ /** Show debug control panel */
185
+ debug?: boolean;
62
186
  };
187
+ declare const VFXParticles: react.ForwardRefExoticComponent<VFXParticlesProps & react.RefAttributes<unknown>>;
188
+
189
+ interface VFXEmitterProps {
190
+ /** Name of the registered VFXParticles system */
191
+ name?: string;
192
+ /** Direct ref to VFXParticles (alternative to name) */
193
+ particlesRef?: RefObject<any> | any;
194
+ /** Local position offset */
195
+ position?: [number, number, number];
196
+ /** Particles to emit per burst */
197
+ emitCount?: number;
198
+ /** Seconds between emissions (0 = every frame) */
199
+ delay?: number;
200
+ /** Start emitting automatically */
201
+ autoStart?: boolean;
202
+ /** Keep emitting (false = emit once) */
203
+ loop?: boolean;
204
+ /** Transform direction by parent's world rotation */
205
+ localDirection?: boolean;
206
+ /** Direction override [[minX,maxX],[minY,maxY],[minZ,maxZ]] */
207
+ direction?: [[number, number], [number, number], [number, number]];
208
+ /** Per-spawn overrides (size, speed, colors, etc.) */
209
+ overrides?: Record<string, any> | null;
210
+ /** Callback fired after each emission */
211
+ onEmit?: (params: {
212
+ position: [number, number, number] | number[];
213
+ count: number;
214
+ direction: any;
215
+ }) => void;
216
+ /** Children elements */
217
+ children?: ReactNode;
218
+ }
63
219
  /**
64
220
  * VFXEmitter - A reusable emitter component that links to a VFXParticles system
65
221
  *
@@ -104,30 +260,38 @@ declare function useVFXEmitter(name: any): {
104
260
  * @param {object} [props.overrides] - Per-spawn overrides (size, speed, colors, etc.)
105
261
  * @param {function} [props.onEmit] - Callback fired after each emission
106
262
  */
107
- declare const VFXEmitter: any;
108
-
263
+ declare const VFXEmitter: react.ForwardRefExoticComponent<VFXEmitterProps & react.RefAttributes<unknown>>;
109
264
  /**
110
- * VFX Store - Centralized management for VFX particle systems
111
- *
112
- * Allows multiple VFXEmitter components to share a single VFXParticles instance,
113
- * avoiding extra draw calls while enabling emission from multiple positions.
265
+ * Higher-order hook for programmatic emitter control
114
266
  *
115
267
  * Usage:
268
+ * const { emit, burst, start, stop } = useVFXEmitter("sparks");
116
269
  *
117
- * // Register a particle system
118
- * <VFXParticles ref={(ref) => registerParticles("sparks", ref)} ... />
119
- *
120
- * // Or use the VFXParticles name prop with auto-registration
121
- * <VFXParticles name="sparks" ... />
122
- *
123
- * // Emit from anywhere using VFXEmitter (no extra draw calls!)
124
- * <VFXEmitter name="sparks" position={[1, 0, 0]} emitCount={10} />
125
- * <VFXEmitter name="sparks" position={[-1, 0, 0]} emitCount={5} />
270
+ * // Emit at a position
271
+ * emit([1, 2, 3], 50);
126
272
  *
127
- * // Or emit programmatically
128
- * const emit = useVFXStore(s => s.emit);
129
- * emit("sparks", { x: 0, y: 1, z: 0, count: 20 });
273
+ * // Burst with overrides
274
+ * burst([0, 0, 0], 100, { colorStart: ["#ff0000"] });
130
275
  */
131
- declare const useVFXStore: zustand.UseBoundStore<zustand.StoreApi<any>>;
276
+ declare function useVFXEmitter(name: string): {
277
+ emit: (position?: number[], count?: number, overrides?: null) => boolean;
278
+ burst: (position?: number[], count?: number, overrides?: null) => boolean;
279
+ start: () => boolean;
280
+ stop: () => boolean;
281
+ clear: () => boolean;
282
+ isEmitting: () => boolean;
283
+ getUniforms: () => Record<string, unknown> | null;
284
+ getParticles: () => {
285
+ spawn: (x: number, y: number, z: number, count: number, overrides?: Record<string, unknown> | null) => void;
286
+ start: () => void;
287
+ stop: () => void;
288
+ clear: () => void;
289
+ isEmitting: boolean;
290
+ uniforms: Record<string, unknown>;
291
+ } | null;
292
+ };
293
+
294
+ declare function useVFXStore(): CoreState;
295
+ declare function useVFXStore<T>(selector: (state: CoreState) => T): T;
132
296
 
133
297
  export { Appearance, AttractorType, Blending, Easing, EmitterShape, Lighting, VFXEmitter, VFXParticles, bakeCurveToArray, createCombinedCurveTexture, useVFXEmitter, useVFXStore };
package/dist/index.js CHANGED
@@ -18,19 +18,6 @@ var __spreadValues = (a, b) => {
18
18
  return a;
19
19
  };
20
20
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
- var __restKey = (key) => typeof key === "symbol" ? key : key + "";
22
- var __objRest = (source, exclude) => {
23
- var target = {};
24
- for (var prop in source)
25
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
26
- target[prop] = source[prop];
27
- if (source != null && __getOwnPropSymbols)
28
- for (var prop of __getOwnPropSymbols(source)) {
29
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
30
- target[prop] = source[prop];
31
- }
32
- return target;
33
- };
34
21
  var __esm = (fn, res) => function __init() {
35
22
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
36
23
  };
@@ -39,134 +26,15 @@ var __export = (target, all) => {
39
26
  __defProp(target, name, { get: all[name], enumerable: true });
40
27
  };
41
28
 
42
- // src/useVFXStore.js
43
- import { create } from "zustand";
44
- var useVFXStore;
45
- var init_useVFXStore = __esm({
46
- "src/useVFXStore.js"() {
29
+ // src/react-store.ts
30
+ import { coreStore } from "core-vfx";
31
+ import { useStore } from "zustand";
32
+ function useVFXStore(selector) {
33
+ return useStore(coreStore, selector);
34
+ }
35
+ var init_react_store = __esm({
36
+ "src/react-store.ts"() {
47
37
  "use strict";
48
- useVFXStore = create((set, get) => ({
49
- // Registered particle systems: { name: ref }
50
- particles: {},
51
- /**
52
- * Register a VFXParticles instance by name
53
- * @param {string} name - Unique identifier for this particle system
54
- * @param {object} ref - The ref object from VFXParticles (with spawn, start, stop methods)
55
- */
56
- registerParticles: (name, ref) => {
57
- if (!name || !ref) return;
58
- set((state) => ({
59
- particles: __spreadProps(__spreadValues({}, state.particles), { [name]: ref })
60
- }));
61
- },
62
- /**
63
- * Unregister a VFXParticles instance
64
- * @param {string} name - Name of the particle system to unregister
65
- */
66
- unregisterParticles: (name) => {
67
- set((state) => {
68
- const _a = state.particles, { [name]: _ } = _a, rest = __objRest(_a, [__restKey(name)]);
69
- return { particles: rest };
70
- });
71
- },
72
- /**
73
- * Get a registered particle system by name
74
- * @param {string} name - Name of the particle system
75
- * @returns {object|null} The particle system ref or null
76
- */
77
- getParticles: (name) => {
78
- return get().particles[name] || null;
79
- },
80
- /**
81
- * Emit particles from a registered system
82
- * @param {string} name - Name of the particle system
83
- * @param {object} options - Emission options
84
- * @param {number} [options.x=0] - X position offset
85
- * @param {number} [options.y=0] - Y position offset
86
- * @param {number} [options.z=0] - Z position offset
87
- * @param {number} [options.count=20] - Number of particles to emit
88
- * @param {object} [options.overrides] - Spawn parameter overrides
89
- * @returns {boolean} True if emission was successful
90
- */
91
- emit: (name, { x = 0, y = 0, z = 0, count = 20, overrides = null } = {}) => {
92
- const particles = get().particles[name];
93
- if (!(particles == null ? void 0 : particles.spawn)) {
94
- console.warn(
95
- `VFXStore: No particle system registered with name "${name}"`
96
- );
97
- return false;
98
- }
99
- particles.spawn(x, y, z, count, overrides);
100
- return true;
101
- },
102
- /**
103
- * Start auto-emission on a registered particle system
104
- * @param {string} name - Name of the particle system
105
- * @returns {boolean} True if successful
106
- */
107
- start: (name) => {
108
- const particles = get().particles[name];
109
- if (!(particles == null ? void 0 : particles.start)) {
110
- console.warn(
111
- `VFXStore: No particle system registered with name "${name}"`
112
- );
113
- return false;
114
- }
115
- particles.start();
116
- return true;
117
- },
118
- /**
119
- * Stop auto-emission on a registered particle system
120
- * @param {string} name - Name of the particle system
121
- * @returns {boolean} True if successful
122
- */
123
- stop: (name) => {
124
- const particles = get().particles[name];
125
- if (!(particles == null ? void 0 : particles.stop)) {
126
- console.warn(
127
- `VFXStore: No particle system registered with name "${name}"`
128
- );
129
- return false;
130
- }
131
- particles.stop();
132
- return true;
133
- },
134
- /**
135
- * Clear all particles from a registered system
136
- * @param {string} name - Name of the particle system
137
- * @returns {boolean} True if successful
138
- */
139
- clear: (name) => {
140
- const particles = get().particles[name];
141
- if (!(particles == null ? void 0 : particles.clear)) {
142
- console.warn(
143
- `VFXStore: No particle system registered with name "${name}"`
144
- );
145
- return false;
146
- }
147
- particles.clear();
148
- return true;
149
- },
150
- /**
151
- * Check if a particle system is currently emitting
152
- * @param {string} name - Name of the particle system
153
- * @returns {boolean} True if emitting
154
- */
155
- isEmitting: (name) => {
156
- var _a;
157
- const particles = get().particles[name];
158
- return (_a = particles == null ? void 0 : particles.isEmitting) != null ? _a : false;
159
- },
160
- /**
161
- * Get the uniforms object for direct manipulation
162
- * @param {string} name - Name of the particle system
163
- * @returns {object|null} The uniforms object or null
164
- */
165
- getUniforms: (name) => {
166
- const particles = get().particles[name];
167
- return (particles == null ? void 0 : particles.uniforms) || null;
168
- }
169
- }));
170
38
  }
171
39
  });
172
40
 
@@ -184,7 +52,7 @@ __export(VFXParticlesDebugPanel_exports, {
184
52
  import { createRoot } from "react-dom/client";
185
53
  import { useState, useCallback, useRef, useEffect } from "react";
186
54
  import * as THREE from "three";
187
- import { create as create2 } from "zustand";
55
+ import { create } from "zustand";
188
56
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
189
57
  function renderDebugPanel(values, onChange) {
190
58
  currentValues = values;
@@ -355,7 +223,7 @@ var init_VFXParticlesDebugPanel = __esm({
355
223
  "src/VFXParticlesDebugPanel.jsx"() {
356
224
  "use strict";
357
225
  init_VFXParticles();
358
- useDebugPanelStore = create2(() => ({
226
+ useDebugPanelStore = create(() => ({
359
227
  flushChangesRef: { current: null }
360
228
  }));
361
229
  getFlushChanges = () => useDebugPanelStore.getState().flushChangesRef.current;
@@ -5249,7 +5117,7 @@ ${" ".repeat(indent - 2)}}`;
5249
5117
  }
5250
5118
  });
5251
5119
 
5252
- // src/VFXParticles.jsx
5120
+ // src/VFXParticles.tsx
5253
5121
  import {
5254
5122
  forwardRef,
5255
5123
  useImperativeHandle,
@@ -5281,7 +5149,6 @@ import {
5281
5149
  positionLocal,
5282
5150
  cos,
5283
5151
  sin,
5284
- atan,
5285
5152
  sqrt,
5286
5153
  acos,
5287
5154
  PI,
@@ -5296,9 +5163,9 @@ import {
5296
5163
  import { jsx as jsx2 } from "react/jsx-runtime";
5297
5164
  var Appearance, Blending, EmitterShape, AttractorType, Easing, Lighting, MAX_ATTRACTORS, hexToRgb, toRange, easingToType, axisToNumber, CURVE_RESOLUTION, evaluateBezierSegment, sampleCurveAtX, bakeCurveToArray, createCombinedCurveTexture, toRotation3D, VFXParticles;
5298
5165
  var init_VFXParticles = __esm({
5299
- "src/VFXParticles.jsx"() {
5166
+ "src/VFXParticles.tsx"() {
5300
5167
  "use strict";
5301
- init_useVFXStore();
5168
+ init_react_store();
5302
5169
  Appearance = Object.freeze({
5303
5170
  DEFAULT: "default",
5304
5171
  GRADIENT: "gradient",
@@ -5520,10 +5387,11 @@ var init_VFXParticles = __esm({
5520
5387
  ];
5521
5388
  if (Array.isArray(value)) {
5522
5389
  if (Array.isArray(value[0])) {
5390
+ const nested = value;
5523
5391
  return [
5524
- toRange(value[0], [0, 0]),
5525
- toRange(value[1], [0, 0]),
5526
- toRange(value[2], [0, 0])
5392
+ toRange(nested[0], [0, 0]),
5393
+ toRange(nested[1], [0, 0]),
5394
+ toRange(nested[2], [0, 0])
5527
5395
  ];
5528
5396
  }
5529
5397
  const range = toRange(value, [0, 0]);
@@ -5642,7 +5510,7 @@ var init_VFXParticles = __esm({
5642
5510
  debug = false
5643
5511
  }, ref) {
5644
5512
  const { gl: renderer } = useThree();
5645
- const spriteRef = useRef2();
5513
+ const spriteRef = useRef2(null);
5646
5514
  const initialized = useRef2(false);
5647
5515
  const nextIndex = useRef2(0);
5648
5516
  const [emitting, setEmitting] = useState2(autoStart);
@@ -5750,8 +5618,9 @@ var init_VFXParticles = __esm({
5750
5618
  return [0, 0];
5751
5619
  }, [friction]);
5752
5620
  const frictionEasingType = useMemo(() => {
5621
+ var _a;
5753
5622
  if (typeof friction === "object" && friction !== null && "easing" in friction) {
5754
- return easingToType(friction.easing);
5623
+ return easingToType((_a = friction.easing) != null ? _a : "linear");
5755
5624
  }
5756
5625
  return 0;
5757
5626
  }, [friction]);
@@ -5982,14 +5851,15 @@ var init_VFXParticles = __esm({
5982
5851
  );
5983
5852
  for (let i = 0; i < MAX_ATTRACTORS; i++) {
5984
5853
  const a = attractorList[i];
5854
+ const u = uniforms;
5985
5855
  if (a) {
5986
- uniforms[`attractor${i}Pos`].value.set(...(_d = a.position) != null ? _d : [0, 0, 0]);
5987
- uniforms[`attractor${i}Strength`].value = (_e = a.strength) != null ? _e : 1;
5988
- uniforms[`attractor${i}Radius`].value = (_f = a.radius) != null ? _f : 0;
5989
- uniforms[`attractor${i}Type`].value = a.type === "vortex" ? 1 : 0;
5990
- uniforms[`attractor${i}Axis`].value.set(...(_g = a.axis) != null ? _g : [0, 1, 0]).normalize();
5856
+ u[`attractor${i}Pos`].value.set(...(_d = a.position) != null ? _d : [0, 0, 0]);
5857
+ u[`attractor${i}Strength`].value = (_e = a.strength) != null ? _e : 1;
5858
+ u[`attractor${i}Radius`].value = (_f = a.radius) != null ? _f : 0;
5859
+ u[`attractor${i}Type`].value = a.type === "vortex" ? 1 : 0;
5860
+ u[`attractor${i}Axis`].value.set(...(_g = a.axis) != null ? _g : [0, 1, 0]).normalize();
5991
5861
  } else {
5992
- uniforms[`attractor${i}Strength`].value = 0;
5862
+ u[`attractor${i}Strength`].value = 0;
5993
5863
  }
5994
5864
  }
5995
5865
  uniforms.attractToCenter.value = attractToCenter ? 1 : 0;
@@ -6861,6 +6731,7 @@ var init_VFXParticles = __esm({
6861
6731
  });
6862
6732
  }, [renderer, computeInit]);
6863
6733
  const applySpawnOverrides = useCallback2(
6734
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6864
6735
  (overrides) => {
6865
6736
  if (!overrides) return null;
6866
6737
  const saved = {};
@@ -6907,15 +6778,16 @@ var init_VFXParticles = __esm({
6907
6778
  saved.gravity = uniforms.gravity.value.clone();
6908
6779
  uniforms.gravity.value.set(...overrides.gravity);
6909
6780
  }
6781
+ const u = uniforms;
6910
6782
  if (overrides.colorStart !== void 0) {
6911
6783
  const colors = overrides.colorStart.slice(0, 8).map(hexToRgb);
6912
6784
  while (colors.length < 8)
6913
6785
  colors.push(colors[colors.length - 1] || [1, 1, 1]);
6914
6786
  setUniform("colorStartCount", overrides.colorStart.length);
6915
6787
  colors.forEach((c, i) => {
6916
- if (uniforms[`colorStart${i}`]) {
6917
- saved[`colorStart${i}`] = uniforms[`colorStart${i}`].value.clone();
6918
- uniforms[`colorStart${i}`].value.setRGB(...c);
6788
+ if (u[`colorStart${i}`]) {
6789
+ saved[`colorStart${i}`] = u[`colorStart${i}`].value.clone();
6790
+ u[`colorStart${i}`].value.setRGB(...c);
6919
6791
  }
6920
6792
  });
6921
6793
  }
@@ -6925,9 +6797,9 @@ var init_VFXParticles = __esm({
6925
6797
  colors.push(colors[colors.length - 1] || [1, 1, 1]);
6926
6798
  setUniform("colorEndCount", overrides.colorEnd.length);
6927
6799
  colors.forEach((c, i) => {
6928
- if (uniforms[`colorEnd${i}`]) {
6929
- saved[`colorEnd${i}`] = uniforms[`colorEnd${i}`].value.clone();
6930
- uniforms[`colorEnd${i}`].value.setRGB(...c);
6800
+ if (u[`colorEnd${i}`]) {
6801
+ saved[`colorEnd${i}`] = u[`colorEnd${i}`].value.clone();
6802
+ u[`colorEnd${i}`].value.setRGB(...c);
6931
6803
  }
6932
6804
  });
6933
6805
  }
@@ -6968,7 +6840,8 @@ var init_VFXParticles = __esm({
6968
6840
  );
6969
6841
  const spawn = useCallback2(
6970
6842
  (x = 0, y = 0, z = 0, count = 20, overrides = null) => {
6971
- const [px, py, pz] = positionRef.current;
6843
+ var _a;
6844
+ const [px, py, pz] = (_a = positionRef.current) != null ? _a : [0, 0, 0];
6972
6845
  spawnInternal(px + x, py + y, pz + z, count, overrides);
6973
6846
  },
6974
6847
  [spawnInternal]
@@ -7067,6 +6940,7 @@ var init_VFXParticles = __esm({
7067
6940
  const prevGeometryTypeRef = useRef2(null);
7068
6941
  const prevGeometryArgsRef = useRef2(null);
7069
6942
  const handleDebugUpdate = useCallback2(
6943
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7070
6944
  (newValues) => {
7071
6945
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
7072
6946
  debugValuesRef.current = __spreadValues(__spreadValues({}, debugValuesRef.current), newValues);
@@ -7539,8 +7413,8 @@ var init_VFXParticles = __esm({
7539
7413
  // src/index.ts
7540
7414
  init_VFXParticles();
7541
7415
 
7542
- // src/VFXEmitter.jsx
7543
- init_useVFXStore();
7416
+ // src/VFXEmitter.tsx
7417
+ init_react_store();
7544
7418
  import {
7545
7419
  useRef as useRef3,
7546
7420
  useEffect as useEffect3,
@@ -7568,7 +7442,7 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
7568
7442
  onEmit,
7569
7443
  children
7570
7444
  }, ref) {
7571
- const groupRef = useRef3();
7445
+ const groupRef = useRef3(null);
7572
7446
  const emitAccumulator = useRef3(0);
7573
7447
  const emitting = useRef3(autoStart);
7574
7448
  const hasEmittedOnce = useRef3(false);
@@ -7578,18 +7452,28 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
7578
7452
  }
7579
7453
  return useVFXStore.getState().getParticles(name);
7580
7454
  }, [name, particlesRef]);
7581
- const transformDirectionByQuat = useCallback3((dirRange, quat) => {
7582
- if (!dirRange) return null;
7583
- const minDir = _tempVec.set(dirRange[0][0], dirRange[1][0], dirRange[2][0]);
7584
- minDir.applyQuaternion(quat);
7585
- const maxDir = new Vector32(dirRange[0][1], dirRange[1][1], dirRange[2][1]);
7586
- maxDir.applyQuaternion(quat);
7587
- return [
7588
- [Math.min(minDir.x, maxDir.x), Math.max(minDir.x, maxDir.x)],
7589
- [Math.min(minDir.y, maxDir.y), Math.max(minDir.y, maxDir.y)],
7590
- [Math.min(minDir.z, maxDir.z), Math.max(minDir.z, maxDir.z)]
7591
- ];
7592
- }, []);
7455
+ const transformDirectionByQuat = useCallback3(
7456
+ (dirRange, quat) => {
7457
+ const minDir = _tempVec.set(
7458
+ dirRange[0][0],
7459
+ dirRange[1][0],
7460
+ dirRange[2][0]
7461
+ );
7462
+ minDir.applyQuaternion(quat);
7463
+ const maxDir = new Vector32(
7464
+ dirRange[0][1],
7465
+ dirRange[1][1],
7466
+ dirRange[2][1]
7467
+ );
7468
+ maxDir.applyQuaternion(quat);
7469
+ return [
7470
+ [Math.min(minDir.x, maxDir.x), Math.max(minDir.x, maxDir.x)],
7471
+ [Math.min(minDir.y, maxDir.y), Math.max(minDir.y, maxDir.y)],
7472
+ [Math.min(minDir.z, maxDir.z), Math.max(minDir.z, maxDir.z)]
7473
+ ];
7474
+ },
7475
+ []
7476
+ );
7593
7477
  const getEmitParams = useCallback3(() => {
7594
7478
  if (!groupRef.current) {
7595
7479
  return { position, direction };
@@ -7695,7 +7579,10 @@ var VFXEmitter = forwardRef2(function VFXEmitter2({
7695
7579
  }),
7696
7580
  [emit, burst, start, stop, getParticleSystem]
7697
7581
  );
7698
- return /* @__PURE__ */ jsx3("group", { ref: groupRef, position, children });
7582
+ return (
7583
+ // @ts-expect-error
7584
+ /* @__PURE__ */ jsx3("group", { ref: groupRef, position, children })
7585
+ );
7699
7586
  });
7700
7587
  function useVFXEmitter(name) {
7701
7588
  const getParticles = useVFXStore((s) => s.getParticles);
@@ -7743,7 +7630,7 @@ function useVFXEmitter(name) {
7743
7630
  }
7744
7631
 
7745
7632
  // src/index.ts
7746
- init_useVFXStore();
7633
+ init_react_store();
7747
7634
  export {
7748
7635
  Appearance,
7749
7636
  AttractorType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "r3f-vfx",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -24,10 +24,14 @@
24
24
  "prepublishOnly": "bun run typecheck && bun run build && bun run copy-readme"
25
25
  },
26
26
  "dependencies": {
27
- "core-vfx": "0.0.1",
27
+ "core-vfx": "0.0.2",
28
28
  "zustand": "5.0.10"
29
29
  },
30
30
  "devDependencies": {
31
+ "@react-three/fiber": "9.5.0",
32
+ "@types/react": "19.2.10",
33
+ "@types/three": "0.182.0",
34
+ "three": "0.182.0",
31
35
  "tsup": "8.5.1",
32
36
  "typescript": "5.9.3"
33
37
  },