@react-three/fiber 10.0.0-alpha.1 → 10.0.0-canary.1b98c17

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.
@@ -1,11 +1,17 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, WebGPURenderer, Color, Vector4, PostProcessing } from 'three/webgpu';
2
+ import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, Vector4, PostProcessing } from 'three/webgpu';
3
3
  import { Inspector } from 'three/addons/inspector/Inspector.js';
4
- import { jsx, Fragment } from 'react/jsx-runtime';
4
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
  import * as React from 'react';
6
- import React__default, { useMemo, useLayoutEffect, useEffect, useContext, useRef, useImperativeHandle, useCallback, useState } from 'react';
6
+ import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
7
7
  import useMeasure from 'react-use-measure';
8
8
  import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
9
+ import { useThree as useThree$1, useLoader as useLoader$1, useFrame as useFrame$1, createPortal as createPortal$1, applyProps as applyProps$1, extend as extend$1 } from '@react-three/fiber';
10
+ import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
11
+ import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
12
+ import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
13
+ import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
14
+ import { GainMapLoader } from '@monogrid/gainmap-js';
9
15
  import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
10
16
  import { createWithEqualityFn } from 'zustand/traditional';
11
17
  import { suspend, preload, clear } from 'suspend-react';
@@ -46,9 +52,392 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
46
52
  WebGLRenderer: WebGLRenderer
47
53
  }, [webgpu]);
48
54
 
49
- var __defProp$2 = Object.defineProperty;
50
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
51
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
55
+ const primaryRegistry = /* @__PURE__ */ new Map();
56
+ const pendingSubscribers = /* @__PURE__ */ new Map();
57
+ function registerPrimary(id, renderer, store) {
58
+ if (primaryRegistry.has(id)) {
59
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
60
+ }
61
+ const entry = { renderer, store };
62
+ primaryRegistry.set(id, entry);
63
+ const subscribers = pendingSubscribers.get(id);
64
+ if (subscribers) {
65
+ subscribers.forEach((callback) => callback(entry));
66
+ pendingSubscribers.delete(id);
67
+ }
68
+ return () => {
69
+ const currentEntry = primaryRegistry.get(id);
70
+ if (currentEntry?.renderer === renderer) {
71
+ primaryRegistry.delete(id);
72
+ }
73
+ };
74
+ }
75
+ function getPrimary(id) {
76
+ return primaryRegistry.get(id);
77
+ }
78
+ function waitForPrimary(id, timeout = 5e3) {
79
+ const existing = primaryRegistry.get(id);
80
+ if (existing) {
81
+ return Promise.resolve(existing);
82
+ }
83
+ return new Promise((resolve, reject) => {
84
+ const timeoutId = setTimeout(() => {
85
+ const subscribers = pendingSubscribers.get(id);
86
+ if (subscribers) {
87
+ const index = subscribers.indexOf(callback);
88
+ if (index !== -1) subscribers.splice(index, 1);
89
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
90
+ }
91
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
92
+ }, timeout);
93
+ const callback = (entry) => {
94
+ clearTimeout(timeoutId);
95
+ resolve(entry);
96
+ };
97
+ if (!pendingSubscribers.has(id)) {
98
+ pendingSubscribers.set(id, []);
99
+ }
100
+ pendingSubscribers.get(id).push(callback);
101
+ });
102
+ }
103
+ function hasPrimary(id) {
104
+ return primaryRegistry.has(id);
105
+ }
106
+ function unregisterPrimary(id) {
107
+ primaryRegistry.delete(id);
108
+ }
109
+ function getPrimaryIds() {
110
+ return Array.from(primaryRegistry.keys());
111
+ }
112
+
113
+ const presetsObj = {
114
+ apartment: "lebombo_1k.hdr",
115
+ city: "potsdamer_platz_1k.hdr",
116
+ dawn: "kiara_1_dawn_1k.hdr",
117
+ forest: "forest_slope_1k.hdr",
118
+ lobby: "st_fagans_interior_1k.hdr",
119
+ night: "dikhololo_night_1k.hdr",
120
+ park: "rooitou_park_1k.hdr",
121
+ studio: "studio_small_03_1k.hdr",
122
+ sunset: "venice_sunset_1k.hdr",
123
+ warehouse: "empty_warehouse_01_1k.hdr"
124
+ };
125
+
126
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
127
+ const isArray = (arr) => Array.isArray(arr);
128
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
129
+ function useEnvironment({
130
+ files = defaultFiles,
131
+ path = "",
132
+ preset = void 0,
133
+ colorSpace = void 0,
134
+ extensions
135
+ } = {}) {
136
+ if (preset) {
137
+ validatePreset(preset);
138
+ files = presetsObj[preset];
139
+ path = CUBEMAP_ROOT;
140
+ }
141
+ const multiFile = isArray(files);
142
+ const { extension, isCubemap } = getExtension(files);
143
+ const loader = getLoader$1(extension);
144
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
145
+ const renderer = useThree$1((state) => state.renderer);
146
+ useLayoutEffect(() => {
147
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
148
+ function clearGainmapTexture() {
149
+ useLoader$1.clear(loader, multiFile ? [files] : files);
150
+ }
151
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
152
+ }, [extension, files, loader, multiFile, renderer.domElement]);
153
+ const loaderResult = useLoader$1(
154
+ loader,
155
+ multiFile ? [files] : files,
156
+ (loader2) => {
157
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
158
+ loader2.setRenderer?.(renderer);
159
+ }
160
+ loader2.setPath?.(path);
161
+ if (extensions) extensions(loader2);
162
+ }
163
+ );
164
+ let texture = multiFile ? (
165
+ // @ts-ignore
166
+ loaderResult[0]
167
+ ) : loaderResult;
168
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
169
+ texture = texture.renderTarget?.texture;
170
+ }
171
+ texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
172
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
173
+ return texture;
174
+ }
175
+ const preloadDefaultOptions = {
176
+ files: defaultFiles,
177
+ path: "",
178
+ preset: void 0,
179
+ extensions: void 0
180
+ };
181
+ useEnvironment.preload = (preloadOptions) => {
182
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
183
+ let { files, path = "" } = options;
184
+ const { preset, extensions } = options;
185
+ if (preset) {
186
+ validatePreset(preset);
187
+ files = presetsObj[preset];
188
+ path = CUBEMAP_ROOT;
189
+ }
190
+ const { extension } = getExtension(files);
191
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
192
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
193
+ }
194
+ const loader = getLoader$1(extension);
195
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
196
+ useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
197
+ loader2.setPath?.(path);
198
+ if (extensions) extensions(loader2);
199
+ });
200
+ };
201
+ const clearDefaultOptins = {
202
+ files: defaultFiles,
203
+ preset: void 0
204
+ };
205
+ useEnvironment.clear = (clearOptions) => {
206
+ const options = { ...clearDefaultOptins, ...clearOptions };
207
+ let { files } = options;
208
+ const { preset } = options;
209
+ if (preset) {
210
+ validatePreset(preset);
211
+ files = presetsObj[preset];
212
+ }
213
+ const { extension } = getExtension(files);
214
+ const loader = getLoader$1(extension);
215
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
216
+ useLoader$1.clear(loader, isArray(files) ? [files] : files);
217
+ };
218
+ function validatePreset(preset) {
219
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
220
+ }
221
+ function getExtension(files) {
222
+ const isCubemap = isArray(files) && files.length === 6;
223
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
224
+ const firstEntry = isArray(files) ? files[0] : files;
225
+ const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
226
+ return { extension, isCubemap, isGainmap };
227
+ }
228
+ function getLoader$1(extension) {
229
+ const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
230
+ return loader;
231
+ }
232
+
233
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
234
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
235
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
236
+ sceneProps = {
237
+ backgroundBlurriness: 0,
238
+ backgroundIntensity: 1,
239
+ backgroundRotation: [0, 0, 0],
240
+ environmentIntensity: 1,
241
+ environmentRotation: [0, 0, 0],
242
+ ...sceneProps
243
+ };
244
+ const target = resolveScene(scene || defaultScene);
245
+ const oldbg = target.background;
246
+ const oldenv = target.environment;
247
+ const oldSceneProps = {
248
+ // @ts-ignore
249
+ backgroundBlurriness: target.backgroundBlurriness,
250
+ // @ts-ignore
251
+ backgroundIntensity: target.backgroundIntensity,
252
+ // @ts-ignore
253
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
254
+ // @ts-ignore
255
+ environmentIntensity: target.environmentIntensity,
256
+ // @ts-ignore
257
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
258
+ };
259
+ if (background !== "only") target.environment = texture;
260
+ if (background) target.background = texture;
261
+ applyProps$1(target, sceneProps);
262
+ return () => {
263
+ if (background !== "only") target.environment = oldenv;
264
+ if (background) target.background = oldbg;
265
+ applyProps$1(target, oldSceneProps);
266
+ };
267
+ }
268
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
269
+ const defaultScene = useThree$1((state) => state.scene);
270
+ React.useLayoutEffect(() => {
271
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
272
+ });
273
+ return null;
274
+ }
275
+ function EnvironmentCube({
276
+ background = false,
277
+ scene,
278
+ blur,
279
+ backgroundBlurriness,
280
+ backgroundIntensity,
281
+ backgroundRotation,
282
+ environmentIntensity,
283
+ environmentRotation,
284
+ ...rest
285
+ }) {
286
+ const texture = useEnvironment(rest);
287
+ const defaultScene = useThree$1((state) => state.scene);
288
+ React.useLayoutEffect(() => {
289
+ return setEnvProps(background, scene, defaultScene, texture, {
290
+ backgroundBlurriness: blur ?? backgroundBlurriness,
291
+ backgroundIntensity,
292
+ backgroundRotation,
293
+ environmentIntensity,
294
+ environmentRotation
295
+ });
296
+ });
297
+ React.useEffect(() => {
298
+ return () => {
299
+ texture.dispose();
300
+ };
301
+ }, [texture]);
302
+ return null;
303
+ }
304
+ function EnvironmentPortal({
305
+ children,
306
+ near = 0.1,
307
+ far = 1e3,
308
+ resolution = 256,
309
+ frames = 1,
310
+ map,
311
+ background = false,
312
+ blur,
313
+ backgroundBlurriness,
314
+ backgroundIntensity,
315
+ backgroundRotation,
316
+ environmentIntensity,
317
+ environmentRotation,
318
+ scene,
319
+ files,
320
+ path,
321
+ preset = void 0,
322
+ extensions
323
+ }) {
324
+ const gl = useThree$1((state) => state.gl);
325
+ const defaultScene = useThree$1((state) => state.scene);
326
+ const camera = React.useRef(null);
327
+ const [virtualScene] = React.useState(() => new Scene());
328
+ const fbo = React.useMemo(() => {
329
+ const fbo2 = new WebGLCubeRenderTarget(resolution);
330
+ fbo2.texture.type = HalfFloatType;
331
+ return fbo2;
332
+ }, [resolution]);
333
+ React.useEffect(() => {
334
+ return () => {
335
+ fbo.dispose();
336
+ };
337
+ }, [fbo]);
338
+ React.useLayoutEffect(() => {
339
+ if (frames === 1) {
340
+ const autoClear = gl.autoClear;
341
+ gl.autoClear = true;
342
+ camera.current.update(gl, virtualScene);
343
+ gl.autoClear = autoClear;
344
+ }
345
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
346
+ backgroundBlurriness: blur ?? backgroundBlurriness,
347
+ backgroundIntensity,
348
+ backgroundRotation,
349
+ environmentIntensity,
350
+ environmentRotation
351
+ });
352
+ }, [
353
+ children,
354
+ virtualScene,
355
+ fbo.texture,
356
+ scene,
357
+ defaultScene,
358
+ background,
359
+ frames,
360
+ gl,
361
+ blur,
362
+ backgroundBlurriness,
363
+ backgroundIntensity,
364
+ backgroundRotation,
365
+ environmentIntensity,
366
+ environmentRotation
367
+ ]);
368
+ let count = 1;
369
+ useFrame$1(() => {
370
+ if (frames === Infinity || count < frames) {
371
+ const autoClear = gl.autoClear;
372
+ gl.autoClear = true;
373
+ camera.current.update(gl, virtualScene);
374
+ gl.autoClear = autoClear;
375
+ count++;
376
+ }
377
+ });
378
+ return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
379
+ /* @__PURE__ */ jsxs(Fragment, { children: [
380
+ children,
381
+ /* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
382
+ files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
383
+ ] }),
384
+ virtualScene
385
+ ) });
386
+ }
387
+ function EnvironmentGround(props) {
388
+ const textureDefault = useEnvironment(props);
389
+ const texture = props.map || textureDefault;
390
+ React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
391
+ React.useEffect(() => {
392
+ return () => {
393
+ textureDefault.dispose();
394
+ };
395
+ }, [textureDefault]);
396
+ const height = props.ground?.height ?? 15;
397
+ const radius = props.ground?.radius ?? 60;
398
+ const scale = props.ground?.scale ?? 1e3;
399
+ const args = React.useMemo(
400
+ () => [texture, height, radius],
401
+ [texture, height, radius]
402
+ );
403
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
404
+ /* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
405
+ /* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
406
+ ] });
407
+ }
408
+ function EnvironmentColor({ color, scene }) {
409
+ const defaultScene = useThree$1((state) => state.scene);
410
+ React.useLayoutEffect(() => {
411
+ if (color === void 0) return;
412
+ const target = resolveScene(scene || defaultScene);
413
+ const oldBg = target.background;
414
+ target.background = new Color(color);
415
+ return () => {
416
+ target.background = oldBg;
417
+ };
418
+ });
419
+ return null;
420
+ }
421
+ function EnvironmentDualSource(props) {
422
+ const { backgroundFiles, ...envProps } = props;
423
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
425
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
426
+ ] });
427
+ }
428
+ function Environment(props) {
429
+ if (props.color && !props.files && !props.preset && !props.map) {
430
+ return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
431
+ }
432
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
433
+ return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
434
+ }
435
+ return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
436
+ }
437
+
438
+ var __defProp$3 = Object.defineProperty;
439
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
440
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
52
441
  const act = React["act"];
53
442
  const useIsomorphicLayoutEffect = /* @__PURE__ */ (() => typeof window !== "undefined" && (window.document?.createElement || window.navigator?.product === "ReactNative"))() ? React.useLayoutEffect : React.useEffect;
54
443
  function useMutableCallback(fn) {
@@ -80,7 +469,7 @@ const ErrorBoundary = /* @__PURE__ */ (() => {
80
469
  return _a = class extends React.Component {
81
470
  constructor() {
82
471
  super(...arguments);
83
- __publicField$2(this, "state", { error: false });
472
+ __publicField$3(this, "state", { error: false });
84
473
  }
85
474
  componentDidCatch(err) {
86
475
  this.props.set(err);
@@ -88,7 +477,7 @@ const ErrorBoundary = /* @__PURE__ */ (() => {
88
477
  render() {
89
478
  return this.state.error ? null : this.props.children;
90
479
  }
91
- }, __publicField$2(_a, "getDerivedStateFromError", () => ({ error: true })), _a;
480
+ }, __publicField$3(_a, "getDerivedStateFromError", () => ({ error: true })), _a;
92
481
  })();
93
482
 
94
483
  const is = {
@@ -225,7 +614,8 @@ function prepare(target, root, type, props) {
225
614
  object,
226
615
  eventCount: 0,
227
616
  handlers: {},
228
- isHidden: false
617
+ isHidden: false,
618
+ deferredRefs: []
229
619
  };
230
620
  if (object) object.__r3f = instance;
231
621
  }
@@ -274,7 +664,7 @@ function createOcclusionObserverNode(store, uniform) {
274
664
  let occlusionSetupPromise = null;
275
665
  function enableOcclusion(store) {
276
666
  const state = store.getState();
277
- const { internal, renderer, rootScene } = state;
667
+ const { internal, renderer } = state;
278
668
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
279
669
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
280
670
  if (!hasOcclusionSupport) {
@@ -437,6 +827,22 @@ function hasVisibilityHandlers(handlers) {
437
827
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
438
828
  }
439
829
 
830
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
831
+ function fromRef(ref) {
832
+ return { [FROM_REF]: ref };
833
+ }
834
+ function isFromRef(value) {
835
+ return value !== null && typeof value === "object" && FROM_REF in value;
836
+ }
837
+
838
+ const ONCE = Symbol.for("@react-three/fiber.once");
839
+ function once(...args) {
840
+ return { [ONCE]: args.length ? args : true };
841
+ }
842
+ function isOnce(value) {
843
+ return value !== null && typeof value === "object" && ONCE in value;
844
+ }
845
+
440
846
  const RESERVED_PROPS = [
441
847
  "children",
442
848
  "key",
@@ -507,7 +913,7 @@ function getMemoizedPrototype(root) {
507
913
  ctor = new root.constructor();
508
914
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
509
915
  }
510
- } catch (e) {
916
+ } catch {
511
917
  }
512
918
  return ctor;
513
919
  }
@@ -538,7 +944,7 @@ function applyProps(object, props) {
538
944
  const rootState = instance && findInitialRoot(instance).getState();
539
945
  const prevHandlers = instance?.eventCount;
540
946
  for (const prop in props) {
541
- let value = props[prop];
947
+ const value = props[prop];
542
948
  if (RESERVED_PROPS.includes(prop)) continue;
543
949
  if (instance && EVENT_REGEX.test(prop)) {
544
950
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -553,6 +959,25 @@ function applyProps(object, props) {
553
959
  continue;
554
960
  }
555
961
  if (value === void 0) continue;
962
+ if (isFromRef(value)) {
963
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
964
+ continue;
965
+ }
966
+ if (isOnce(value)) {
967
+ if (instance?.appliedOnce?.has(prop)) continue;
968
+ if (instance) {
969
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
970
+ instance.appliedOnce.add(prop);
971
+ }
972
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
973
+ const args = value[ONCE];
974
+ if (typeof targetRoot[targetKey] === "function") {
975
+ targetRoot[targetKey](...args === true ? [] : args);
976
+ } else if (args !== true && args.length > 0) {
977
+ targetRoot[targetKey] = args[0];
978
+ }
979
+ continue;
980
+ }
556
981
  let { root, key, target } = resolve(object, prop);
557
982
  if (target === void 0 && (typeof root !== "object" || root === null)) {
558
983
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -575,7 +1000,10 @@ function applyProps(object, props) {
575
1000
  else target.set(value);
576
1001
  } else {
577
1002
  root[key] = value;
578
- if (rootState && !rootState.linear && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
1003
+ if (key.endsWith("Node") && root.isMaterial) {
1004
+ root.needsUpdate = true;
1005
+ }
1006
+ if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
579
1007
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
580
1008
  root[key].colorSpace = rootState.textureColorSpace;
581
1009
  }
@@ -608,38 +1036,60 @@ function applyProps(object, props) {
608
1036
  return object;
609
1037
  }
610
1038
 
1039
+ const DEFAULT_POINTER_ID = 0;
1040
+ const XR_POINTER_ID_START = 1e3;
1041
+ function getPointerState(internal, pointerId) {
1042
+ let state = internal.pointerMap.get(pointerId);
1043
+ if (!state) {
1044
+ state = {
1045
+ hovered: /* @__PURE__ */ new Map(),
1046
+ captured: /* @__PURE__ */ new Map(),
1047
+ initialClick: [0, 0],
1048
+ initialHits: []
1049
+ };
1050
+ internal.pointerMap.set(pointerId, state);
1051
+ }
1052
+ return state;
1053
+ }
1054
+ function getPointerId(event) {
1055
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1056
+ }
611
1057
  function makeId(event) {
612
1058
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
613
1059
  }
614
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
615
- const captureData = captures.get(obj);
1060
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1061
+ const pointerState = internal.pointerMap.get(pointerId);
1062
+ if (!pointerState) return;
1063
+ const captureData = pointerState.captured.get(obj);
616
1064
  if (captureData) {
617
- captures.delete(obj);
618
- if (captures.size === 0) {
619
- capturedMap.delete(pointerId);
620
- captureData.target.releasePointerCapture(pointerId);
621
- }
1065
+ pointerState.captured.delete(obj);
1066
+ captureData.target.releasePointerCapture(pointerId);
622
1067
  }
623
1068
  }
624
1069
  function removeInteractivity(store, object) {
625
1070
  const { internal } = store.getState();
626
1071
  internal.interaction = internal.interaction.filter((o) => o !== object);
627
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
628
- internal.hovered.forEach((value, key) => {
629
- if (value.eventObject === object || value.object === object) {
630
- internal.hovered.delete(key);
1072
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1073
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1074
+ pointerState.hovered.forEach((value, key) => {
1075
+ if (value.eventObject === object || value.object === object) {
1076
+ pointerState.hovered.delete(key);
1077
+ }
1078
+ });
1079
+ if (pointerState.captured.has(object)) {
1080
+ releaseInternalPointerCapture(internal, object, pointerId);
631
1081
  }
632
- });
633
- internal.capturedMap.forEach((captures, pointerId) => {
634
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
635
- });
1082
+ }
636
1083
  unregisterVisibility(store, object);
637
1084
  }
638
1085
  function createEvents(store) {
639
- function calculateDistance(event) {
1086
+ function calculateDistance(event, pointerId) {
640
1087
  const { internal } = store.getState();
641
- const dx = event.offsetX - internal.initialClick[0];
642
- const dy = event.offsetY - internal.initialClick[1];
1088
+ const pointerState = internal.pointerMap.get(pointerId);
1089
+ if (!pointerState) return 0;
1090
+ const [initialX, initialY] = pointerState.initialClick;
1091
+ const dx = event.offsetX - initialX;
1092
+ const dy = event.offsetY - initialY;
643
1093
  return Math.round(Math.sqrt(dx * dx + dy * dy));
644
1094
  }
645
1095
  function filterPointerEvents(objects) {
@@ -675,6 +1125,15 @@ function createEvents(store) {
675
1125
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
676
1126
  }
677
1127
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1128
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1129
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1130
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1131
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1132
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1133
+ if (aInteractivePriority !== bInteractivePriority) {
1134
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1135
+ }
1136
+ }
678
1137
  const aState = getRootState(a.object);
679
1138
  const bState = getRootState(b.object);
680
1139
  const aPriority = aState?.events?.priority ?? 1;
@@ -690,14 +1149,19 @@ function createEvents(store) {
690
1149
  for (const hit of hits) {
691
1150
  let eventObject = hit.object;
692
1151
  while (eventObject) {
693
- if (eventObject.__r3f?.eventCount)
1152
+ if (eventObject.__r3f?.eventCount) {
694
1153
  intersections.push({ ...hit, eventObject });
1154
+ }
695
1155
  eventObject = eventObject.parent;
696
1156
  }
697
1157
  }
698
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
699
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
700
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1158
+ if ("pointerId" in event) {
1159
+ const pointerId = event.pointerId;
1160
+ const pointerState = state.internal.pointerMap.get(pointerId);
1161
+ if (pointerState?.captured.size) {
1162
+ for (const captureData of pointerState.captured.values()) {
1163
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1164
+ }
701
1165
  }
702
1166
  }
703
1167
  return intersections;
@@ -710,28 +1174,26 @@ function createEvents(store) {
710
1174
  if (state) {
711
1175
  const { raycaster, pointer, camera, internal } = state;
712
1176
  const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
713
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1177
+ const hasPointerCapture = (id) => {
1178
+ const pointerState = internal.pointerMap.get(id);
1179
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1180
+ };
714
1181
  const setPointerCapture = (id) => {
715
1182
  const captureData = { intersection: hit, target: event.target };
716
- if (internal.capturedMap.has(id)) {
717
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
718
- } else {
719
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
720
- }
1183
+ const pointerState = getPointerState(internal, id);
1184
+ pointerState.captured.set(hit.eventObject, captureData);
721
1185
  event.target.setPointerCapture(id);
722
1186
  };
723
1187
  const releasePointerCapture = (id) => {
724
- const captures = internal.capturedMap.get(id);
725
- if (captures) {
726
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
727
- }
1188
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
728
1189
  };
729
- let extractEventProps = {};
730
- for (let prop in event) {
731
- let property = event[prop];
1190
+ const extractEventProps = {};
1191
+ for (const prop in event) {
1192
+ const property = event[prop];
732
1193
  if (typeof property !== "function") extractEventProps[prop] = property;
733
1194
  }
734
- let raycastEvent = {
1195
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1196
+ const raycastEvent = {
735
1197
  ...hit,
736
1198
  ...extractEventProps,
737
1199
  pointer,
@@ -741,18 +1203,19 @@ function createEvents(store) {
741
1203
  unprojectedPoint,
742
1204
  ray: raycaster.ray,
743
1205
  camera,
1206
+ pointerId: eventPointerId,
744
1207
  // Hijack stopPropagation, which just sets a flag
745
1208
  stopPropagation() {
746
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1209
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
747
1210
  if (
748
1211
  // ...if this pointer hasn't been captured
749
- !capturesForPointer || // ... or if the hit object is capturing the pointer
750
- capturesForPointer.has(hit.eventObject)
1212
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1213
+ pointerState.captured.has(hit.eventObject)
751
1214
  ) {
752
1215
  raycastEvent.stopped = localState.stopped = true;
753
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1216
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
754
1217
  const higher = intersections.slice(0, intersections.indexOf(hit));
755
- cancelPointer([...higher, hit]);
1218
+ cancelPointer([...higher, hit], eventPointerId);
756
1219
  }
757
1220
  }
758
1221
  },
@@ -768,15 +1231,18 @@ function createEvents(store) {
768
1231
  }
769
1232
  return intersections;
770
1233
  }
771
- function cancelPointer(intersections) {
1234
+ function cancelPointer(intersections, pointerId) {
772
1235
  const { internal } = store.getState();
773
- for (const hoveredObj of internal.hovered.values()) {
1236
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1237
+ const pointerState = internal.pointerMap.get(pid);
1238
+ if (!pointerState) return;
1239
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
774
1240
  if (!intersections.length || !intersections.find(
775
1241
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
776
1242
  )) {
777
1243
  const eventObject = hoveredObj.eventObject;
778
1244
  const instance = eventObject.__r3f;
779
- internal.hovered.delete(makeId(hoveredObj));
1245
+ pointerState.hovered.delete(hoveredId);
780
1246
  if (instance?.eventCount) {
781
1247
  const handlers = instance.handlers;
782
1248
  const data = { ...hoveredObj, intersections };
@@ -805,41 +1271,118 @@ function createEvents(store) {
805
1271
  instance?.handlers.onDropMissed?.(event);
806
1272
  }
807
1273
  }
1274
+ function cleanupPointer(pointerId) {
1275
+ const { internal } = store.getState();
1276
+ const pointerState = internal.pointerMap.get(pointerId);
1277
+ if (pointerState) {
1278
+ for (const [, hoveredObj] of pointerState.hovered) {
1279
+ const eventObject = hoveredObj.eventObject;
1280
+ const instance = eventObject.__r3f;
1281
+ if (instance?.eventCount) {
1282
+ const handlers = instance.handlers;
1283
+ const data = { ...hoveredObj, intersections: [] };
1284
+ handlers.onPointerOut?.(data);
1285
+ handlers.onPointerLeave?.(data);
1286
+ }
1287
+ }
1288
+ internal.pointerMap.delete(pointerId);
1289
+ }
1290
+ internal.pointerDirty.delete(pointerId);
1291
+ }
1292
+ function processDeferredPointer(event, pointerId) {
1293
+ const state = store.getState();
1294
+ const { internal } = state;
1295
+ if (!state.events.enabled) return;
1296
+ const filter = filterPointerEvents;
1297
+ const hits = intersect(event, filter);
1298
+ cancelPointer(hits, pointerId);
1299
+ function onIntersect(data) {
1300
+ const eventObject = data.eventObject;
1301
+ const instance = eventObject.__r3f;
1302
+ if (!instance?.eventCount) return;
1303
+ const handlers = instance.handlers;
1304
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1305
+ const id = makeId(data);
1306
+ const pointerState = getPointerState(internal, pointerId);
1307
+ const hoveredItem = pointerState.hovered.get(id);
1308
+ if (!hoveredItem) {
1309
+ pointerState.hovered.set(id, data);
1310
+ handlers.onPointerOver?.(data);
1311
+ handlers.onPointerEnter?.(data);
1312
+ } else if (hoveredItem.stopped) {
1313
+ data.stopPropagation();
1314
+ }
1315
+ }
1316
+ handlers.onPointerMove?.(data);
1317
+ }
1318
+ handleIntersects(hits, event, 0, onIntersect);
1319
+ }
808
1320
  function handlePointer(name) {
809
1321
  switch (name) {
810
1322
  case "onPointerLeave":
811
- case "onPointerCancel":
812
1323
  case "onDragLeave":
813
1324
  return () => cancelPointer([]);
1325
+ // Global cancel of these events
1326
+ case "onPointerCancel":
1327
+ return (event) => {
1328
+ const pointerId = getPointerId(event);
1329
+ cleanupPointer(pointerId);
1330
+ };
814
1331
  case "onLostPointerCapture":
815
1332
  return (event) => {
816
1333
  const { internal } = store.getState();
817
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1334
+ const pointerId = getPointerId(event);
1335
+ const pointerState = internal.pointerMap.get(pointerId);
1336
+ if (pointerState?.captured.size) {
818
1337
  requestAnimationFrame(() => {
819
- if (internal.capturedMap.has(event.pointerId)) {
820
- internal.capturedMap.delete(event.pointerId);
821
- cancelPointer([]);
1338
+ const pointerState2 = internal.pointerMap.get(pointerId);
1339
+ if (pointerState2?.captured.size) {
1340
+ pointerState2.captured.clear();
822
1341
  }
1342
+ cancelPointer([], pointerId);
823
1343
  });
824
1344
  }
825
1345
  };
826
1346
  }
827
1347
  return function handleEvent(event) {
828
1348
  const state = store.getState();
829
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1349
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1350
+ const pointerId = getPointerId(event);
830
1351
  internal.lastEvent.current = event;
831
- if (!state.events.enabled) return;
1352
+ if (!events.enabled) return;
832
1353
  const isPointerMove = name === "onPointerMove";
833
1354
  const isDragOver = name === "onDragOver";
834
1355
  const isDrop = name === "onDrop";
835
1356
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1357
+ const isPointerDown = name === "onPointerDown";
1358
+ const isPointerUp = name === "onPointerUp";
1359
+ const isWheel = name === "onWheel";
1360
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1361
+ if (isPointerMove && canDeferRaycasts) {
1362
+ events.compute?.(event, state);
1363
+ internal.pointerDirty.set(pointerId, event);
1364
+ return;
1365
+ }
1366
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1367
+ events.compute?.(event, state);
1368
+ internal.pointerDirty.set(pointerId, event);
1369
+ return;
1370
+ }
1371
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1372
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1373
+ internal.pointerDirty.delete(pointerId);
1374
+ processDeferredPointer(deferredEvent, pointerId);
1375
+ }
836
1376
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
837
1377
  const hits = intersect(event, filter);
838
- const delta = isClickEvent ? calculateDistance(event) : 0;
839
- if (name === "onPointerDown") {
840
- internal.initialClick = [event.offsetX, event.offsetY];
841
- internal.initialHits = hits.map((hit) => hit.eventObject);
842
- }
1378
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1379
+ if (isPointerDown) {
1380
+ const pointerState2 = getPointerState(internal, pointerId);
1381
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1382
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1383
+ }
1384
+ const pointerState = internal.pointerMap.get(pointerId);
1385
+ const initialHits = pointerState?.initialHits ?? [];
843
1386
  if (isClickEvent && !hits.length) {
844
1387
  if (delta <= 2) {
845
1388
  pointerMissed(event, internal.interaction);
@@ -854,7 +1397,9 @@ function createEvents(store) {
854
1397
  dropMissed(event, internal.interaction);
855
1398
  if (onDropMissed) onDropMissed(event);
856
1399
  }
857
- if (isPointerMove || isDragOver) cancelPointer(hits);
1400
+ if (isPointerMove || isDragOver) {
1401
+ cancelPointer(hits, pointerId);
1402
+ }
858
1403
  function onIntersect(data) {
859
1404
  const eventObject = data.eventObject;
860
1405
  const instance = eventObject.__r3f;
@@ -863,9 +1408,10 @@ function createEvents(store) {
863
1408
  if (isPointerMove) {
864
1409
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
865
1410
  const id = makeId(data);
866
- const hoveredItem = internal.hovered.get(id);
1411
+ const pointerState2 = getPointerState(internal, pointerId);
1412
+ const hoveredItem = pointerState2.hovered.get(id);
867
1413
  if (!hoveredItem) {
868
- internal.hovered.set(id, data);
1414
+ pointerState2.hovered.set(id, data);
869
1415
  handlers.onPointerOver?.(data);
870
1416
  handlers.onPointerEnter?.(data);
871
1417
  } else if (hoveredItem.stopped) {
@@ -875,9 +1421,10 @@ function createEvents(store) {
875
1421
  handlers.onPointerMove?.(data);
876
1422
  } else if (isDragOver) {
877
1423
  const id = makeId(data);
878
- const hoveredItem = internal.hovered.get(id);
1424
+ const pointerState2 = getPointerState(internal, pointerId);
1425
+ const hoveredItem = pointerState2.hovered.get(id);
879
1426
  if (!hoveredItem) {
880
- internal.hovered.set(id, data);
1427
+ pointerState2.hovered.set(id, data);
881
1428
  handlers.onDragOverEnter?.(data);
882
1429
  } else if (hoveredItem.stopped) {
883
1430
  data.stopPropagation();
@@ -888,18 +1435,18 @@ function createEvents(store) {
888
1435
  } else {
889
1436
  const handler = handlers[name];
890
1437
  if (handler) {
891
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1438
+ if (!isClickEvent || initialHits.includes(eventObject)) {
892
1439
  pointerMissed(
893
1440
  event,
894
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1441
+ internal.interaction.filter((object) => !initialHits.includes(object))
895
1442
  );
896
1443
  handler(data);
897
1444
  }
898
1445
  } else {
899
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1446
+ if (isClickEvent && initialHits.includes(eventObject)) {
900
1447
  pointerMissed(
901
1448
  event,
902
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1449
+ internal.interaction.filter((object) => !initialHits.includes(object))
903
1450
  );
904
1451
  }
905
1452
  }
@@ -908,7 +1455,15 @@ function createEvents(store) {
908
1455
  handleIntersects(hits, event, delta, onIntersect);
909
1456
  };
910
1457
  }
911
- return { handlePointer };
1458
+ function flushDeferredPointers() {
1459
+ const { internal, events } = store.getState();
1460
+ if (!events.frameTimedRaycasts) return;
1461
+ for (const [pointerId, event] of internal.pointerDirty) {
1462
+ processDeferredPointer(event, pointerId);
1463
+ }
1464
+ internal.pointerDirty.clear();
1465
+ }
1466
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
912
1467
  }
913
1468
  const DOM_EVENTS = {
914
1469
  onClick: ["click", false],
@@ -927,11 +1482,16 @@ const DOM_EVENTS = {
927
1482
  onLostPointerCapture: ["lostpointercapture", true]
928
1483
  };
929
1484
  function createPointerEvents(store) {
930
- const { handlePointer } = createEvents(store);
1485
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1486
+ let nextXRPointerId = XR_POINTER_ID_START;
1487
+ const xrPointers = /* @__PURE__ */ new Map();
931
1488
  return {
932
1489
  priority: 1,
933
1490
  enabled: true,
934
- compute(event, state, previous) {
1491
+ frameTimedRaycasts: true,
1492
+ alwaysFireOnScroll: true,
1493
+ updateOnFrame: false,
1494
+ compute(event, state) {
935
1495
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
936
1496
  state.raycaster.setFromCamera(state.pointer, state.camera);
937
1497
  },
@@ -940,11 +1500,33 @@ function createPointerEvents(store) {
940
1500
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
941
1501
  {}
942
1502
  ),
943
- update: () => {
1503
+ update: (pointerId) => {
1504
+ const { events, internal } = store.getState();
1505
+ if (!events.handlers) return;
1506
+ if (pointerId !== void 0) {
1507
+ const event = internal.pointerDirty.get(pointerId);
1508
+ if (event) {
1509
+ internal.pointerDirty.delete(pointerId);
1510
+ processDeferredPointer(event, pointerId);
1511
+ } else if (internal.lastEvent?.current) {
1512
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1513
+ }
1514
+ } else {
1515
+ flushDeferredPointers();
1516
+ if (internal.lastEvent?.current) {
1517
+ events.handlers.onPointerMove(internal.lastEvent.current);
1518
+ }
1519
+ }
1520
+ },
1521
+ flush: () => {
944
1522
  const { events, internal } = store.getState();
945
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1523
+ flushDeferredPointers();
1524
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1525
+ events.handlers.onPointerMove(internal.lastEvent.current);
1526
+ }
946
1527
  },
947
1528
  connect: (target) => {
1529
+ if (!target) return;
948
1530
  const { set, events } = store.getState();
949
1531
  events.disconnect?.();
950
1532
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -968,6 +1550,32 @@ function createPointerEvents(store) {
968
1550
  }
969
1551
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
970
1552
  }
1553
+ },
1554
+ registerPointer: (config) => {
1555
+ const pointerId = nextXRPointerId++;
1556
+ xrPointers.set(pointerId, config);
1557
+ const { internal } = store.getState();
1558
+ getPointerState(internal, pointerId);
1559
+ return pointerId;
1560
+ },
1561
+ unregisterPointer: (pointerId) => {
1562
+ xrPointers.delete(pointerId);
1563
+ const { internal } = store.getState();
1564
+ const pointerState = internal.pointerMap.get(pointerId);
1565
+ if (pointerState) {
1566
+ for (const [, hoveredObj] of pointerState.hovered) {
1567
+ const eventObject = hoveredObj.eventObject;
1568
+ const instance = eventObject.__r3f;
1569
+ if (instance?.eventCount) {
1570
+ const handlers = instance.handlers;
1571
+ const data = { ...hoveredObj, intersections: [] };
1572
+ handlers.onPointerOut?.(data);
1573
+ handlers.onPointerLeave?.(data);
1574
+ }
1575
+ }
1576
+ internal.pointerMap.delete(pointerId);
1577
+ }
1578
+ internal.pointerDirty.delete(pointerId);
971
1579
  }
972
1580
  };
973
1581
  }
@@ -1029,300 +1637,26 @@ function notifyAlpha({ message, link }) {
1029
1637
  }
1030
1638
  }
1031
1639
 
1032
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1033
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1034
- const createStore = (invalidate, advance) => {
1035
- const rootStore = createWithEqualityFn((set, get) => {
1036
- const position = new Vector3();
1037
- const defaultTarget = new Vector3();
1038
- const tempTarget = new Vector3();
1039
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1040
- const { width, height, top, left } = size;
1041
- const aspect = width / height;
1042
- if (target.isVector3) tempTarget.copy(target);
1043
- else tempTarget.set(...target);
1044
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1045
- if (isOrthographicCamera(camera)) {
1046
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1047
- } else {
1048
- const fov = camera.fov * Math.PI / 180;
1049
- const h = 2 * Math.tan(fov / 2) * distance;
1050
- const w = h * (width / height);
1051
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1052
- }
1053
- }
1054
- let performanceTimeout = void 0;
1055
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1056
- const pointer = new Vector2();
1057
- const rootState = {
1058
- set,
1059
- get,
1060
- // Mock objects that have to be configured
1061
- gl: null,
1062
- renderer: null,
1063
- camera: null,
1064
- frustum: new Frustum(),
1065
- autoUpdateFrustum: true,
1066
- raycaster: null,
1067
- events: { priority: 1, enabled: true, connected: false },
1068
- scene: null,
1069
- rootScene: null,
1070
- xr: null,
1071
- inspector: null,
1072
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1073
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1074
- legacy: false,
1075
- linear: false,
1076
- flat: false,
1077
- textureColorSpace: "srgb",
1078
- isLegacy: false,
1079
- webGPUSupported: false,
1080
- isNative: false,
1081
- controls: null,
1082
- pointer,
1083
- mouse: pointer,
1084
- frameloop: "always",
1085
- onPointerMissed: void 0,
1086
- onDragOverMissed: void 0,
1087
- onDropMissed: void 0,
1088
- performance: {
1089
- current: 1,
1090
- min: 0.5,
1091
- max: 1,
1092
- debounce: 200,
1093
- regress: () => {
1094
- const state2 = get();
1095
- if (performanceTimeout) clearTimeout(performanceTimeout);
1096
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1097
- performanceTimeout = setTimeout(
1098
- () => setPerformanceCurrent(get().performance.max),
1099
- state2.performance.debounce
1100
- );
1101
- }
1102
- },
1103
- size: { width: 0, height: 0, top: 0, left: 0 },
1104
- viewport: {
1105
- initialDpr: 0,
1106
- dpr: 0,
1107
- width: 0,
1108
- height: 0,
1109
- top: 0,
1110
- left: 0,
1111
- aspect: 0,
1112
- distance: 0,
1113
- factor: 0,
1114
- getCurrentViewport
1115
- },
1116
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1117
- setSize: (width, height, top = 0, left = 0) => {
1118
- const camera = get().camera;
1119
- const size = { width, height, top, left };
1120
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1121
- },
1122
- setDpr: (dpr) => set((state2) => {
1123
- const resolved = calculateDpr(dpr);
1124
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1125
- }),
1126
- setFrameloop: (frameloop = "always") => {
1127
- set(() => ({ frameloop }));
1128
- },
1129
- setError: (error) => set(() => ({ error })),
1130
- error: null,
1131
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1132
- uniforms: {},
1133
- nodes: {},
1134
- textures: /* @__PURE__ */ new Map(),
1135
- postProcessing: null,
1136
- passes: {},
1137
- previousRoot: void 0,
1138
- internal: {
1139
- // Events
1140
- interaction: [],
1141
- hovered: /* @__PURE__ */ new Map(),
1142
- subscribers: [],
1143
- initialClick: [0, 0],
1144
- initialHits: [],
1145
- capturedMap: /* @__PURE__ */ new Map(),
1146
- lastEvent: React.createRef(),
1147
- // Visibility tracking (onFramed, onOccluded, onVisible)
1148
- visibilityRegistry: /* @__PURE__ */ new Map(),
1149
- // Occlusion system (WebGPU only)
1150
- occlusionEnabled: false,
1151
- occlusionObserver: null,
1152
- occlusionCache: /* @__PURE__ */ new Map(),
1153
- helperGroup: null,
1154
- // Updates
1155
- active: false,
1156
- frames: 0,
1157
- priority: 0,
1158
- subscribe: (ref, priority, store) => {
1159
- const internal = get().internal;
1160
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1161
- internal.subscribers.push({ ref, priority, store });
1162
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1163
- return () => {
1164
- const internal2 = get().internal;
1165
- if (internal2?.subscribers) {
1166
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1167
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1168
- }
1169
- };
1170
- },
1171
- // Renderer Storage (single source of truth)
1172
- actualRenderer: null,
1173
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1174
- scheduler: null
1175
- }
1176
- };
1177
- return rootState;
1178
- });
1179
- const state = rootStore.getState();
1180
- Object.defineProperty(state, "gl", {
1181
- get() {
1182
- const currentState = rootStore.getState();
1183
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1184
- const stack = new Error().stack || "";
1185
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1186
- if (!isInternalAccess) {
1187
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1188
- notifyDepreciated({
1189
- heading: "Accessing state.gl in WebGPU mode",
1190
- body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
1191
- });
1192
- }
1193
- }
1194
- return currentState.internal.actualRenderer;
1195
- },
1196
- set(value) {
1197
- rootStore.getState().internal.actualRenderer = value;
1198
- },
1199
- enumerable: true,
1200
- configurable: true
1201
- });
1202
- Object.defineProperty(state, "renderer", {
1203
- get() {
1204
- return rootStore.getState().internal.actualRenderer;
1205
- },
1206
- set(value) {
1207
- rootStore.getState().internal.actualRenderer = value;
1208
- },
1209
- enumerable: true,
1210
- configurable: true
1211
- });
1212
- let oldScene = state.scene;
1213
- rootStore.subscribe(() => {
1214
- const currentState = rootStore.getState();
1215
- const { scene, rootScene, set } = currentState;
1216
- if (scene !== oldScene) {
1217
- oldScene = scene;
1218
- if (scene?.isScene && scene !== rootScene) {
1219
- set({ rootScene: scene });
1220
- }
1221
- }
1222
- });
1223
- let oldSize = state.size;
1224
- let oldDpr = state.viewport.dpr;
1225
- let oldCamera = state.camera;
1226
- rootStore.subscribe(() => {
1227
- const { camera, size, viewport, set, internal } = rootStore.getState();
1228
- const actualRenderer = internal.actualRenderer;
1229
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1230
- oldSize = size;
1231
- oldDpr = viewport.dpr;
1232
- updateCamera(camera, size);
1233
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1234
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1235
- actualRenderer.setSize(size.width, size.height, updateStyle);
1236
- }
1237
- if (camera !== oldCamera) {
1238
- oldCamera = camera;
1239
- const { rootScene } = rootStore.getState();
1240
- if (camera && rootScene && !camera.parent) {
1241
- rootScene.add(camera);
1242
- }
1243
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1244
- const currentState = rootStore.getState();
1245
- if (currentState.autoUpdateFrustum && camera) {
1246
- updateFrustum(camera, currentState.frustum);
1247
- }
1248
- }
1249
- });
1250
- rootStore.subscribe((state2) => invalidate(state2));
1251
- return rootStore;
1252
- };
1253
-
1254
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1255
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1256
- function getLoader(Proto) {
1257
- if (isConstructor$1(Proto)) {
1258
- let loader = memoizedLoaders.get(Proto);
1259
- if (!loader) {
1260
- loader = new Proto();
1261
- memoizedLoaders.set(Proto, loader);
1262
- }
1263
- return loader;
1264
- }
1265
- return Proto;
1266
- }
1267
- function loadingFn(extensions, onProgress) {
1268
- return function(Proto, input) {
1269
- const loader = getLoader(Proto);
1270
- if (extensions) extensions(loader);
1271
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1272
- return loader.loadAsync(input, onProgress).then((data) => {
1273
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1274
- return data;
1275
- });
1276
- }
1277
- return new Promise(
1278
- (res, reject) => loader.load(
1279
- input,
1280
- (data) => {
1281
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1282
- res(data);
1283
- },
1284
- onProgress,
1285
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1286
- )
1287
- );
1288
- };
1289
- }
1290
- function useLoader(loader, input, extensions, onProgress) {
1291
- const keys = Array.isArray(input) ? input : [input];
1292
- const fn = loadingFn(extensions, onProgress);
1293
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1294
- return Array.isArray(input) ? results : results[0];
1295
- }
1296
- useLoader.preload = function(loader, input, extensions, onProgress) {
1297
- const keys = Array.isArray(input) ? input : [input];
1298
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
1299
- };
1300
- useLoader.clear = function(loader, input) {
1301
- const keys = Array.isArray(input) ? input : [input];
1302
- keys.forEach((key) => clear([loader, key]));
1303
- };
1304
- useLoader.loader = getLoader;
1305
-
1306
- var __defProp$1 = Object.defineProperty;
1307
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1308
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1309
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1310
- class PhaseGraph {
1311
- constructor() {
1312
- /** Ordered list of phase nodes */
1313
- __publicField$1(this, "phases", []);
1314
- /** Quick lookup by name */
1315
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1316
- /** Cached ordered names (invalidated on changes) */
1317
- __publicField$1(this, "orderedNamesCache", null);
1318
- this.initializeDefaultPhases();
1319
- }
1320
- //* Initialization --------------------------------
1321
- initializeDefaultPhases() {
1322
- for (const name of DEFAULT_PHASES) {
1323
- const node = { name, isAutoGenerated: false };
1324
- this.phases.push(node);
1325
- this.phaseMap.set(name, node);
1640
+ var __defProp$2 = Object.defineProperty;
1641
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1642
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1643
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1644
+ class PhaseGraph {
1645
+ constructor() {
1646
+ /** Ordered list of phase nodes */
1647
+ __publicField$2(this, "phases", []);
1648
+ /** Quick lookup by name */
1649
+ __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1650
+ /** Cached ordered names (invalidated on changes) */
1651
+ __publicField$2(this, "orderedNamesCache", null);
1652
+ this.initializeDefaultPhases();
1653
+ }
1654
+ //* Initialization --------------------------------
1655
+ initializeDefaultPhases() {
1656
+ for (const name of DEFAULT_PHASES) {
1657
+ const node = { name, isAutoGenerated: false };
1658
+ this.phases.push(node);
1659
+ this.phaseMap.set(name, node);
1326
1660
  }
1327
1661
  this.invalidateCache();
1328
1662
  }
@@ -1341,8 +1675,9 @@ class PhaseGraph {
1341
1675
  const node = { name, isAutoGenerated: false };
1342
1676
  let insertIndex = this.phases.length;
1343
1677
  const targetIndex = this.getPhaseIndex(before ?? after);
1344
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1345
- else {
1678
+ if (targetIndex !== -1) {
1679
+ insertIndex = before ? targetIndex : targetIndex + 1;
1680
+ } else {
1346
1681
  const constraintType = before ? "before" : "after";
1347
1682
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1348
1683
  }
@@ -1554,7 +1889,7 @@ function shouldRun(job, now) {
1554
1889
  const minInterval = 1e3 / job.fps;
1555
1890
  const lastRun = job.lastRun ?? 0;
1556
1891
  const elapsed = now - lastRun;
1557
- if (elapsed < minInterval) return false;
1892
+ if (elapsed < minInterval - 1) return false;
1558
1893
  if (job.drop) {
1559
1894
  job.lastRun = now;
1560
1895
  } else {
@@ -1570,9 +1905,9 @@ function resetJobTiming(job) {
1570
1905
  job.lastRun = void 0;
1571
1906
  }
1572
1907
 
1573
- var __defProp = Object.defineProperty;
1574
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1575
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1908
+ var __defProp$1 = Object.defineProperty;
1909
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1910
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1576
1911
  const hmrData = (() => {
1577
1912
  if (typeof process !== "undefined" && process.env.NODE_ENV === "test") return void 0;
1578
1913
  if (typeof import_meta_hot !== "undefined") return import_meta_hot;
@@ -1586,9 +1921,9 @@ const _Scheduler = class _Scheduler {
1586
1921
  //* Constructor ================================
1587
1922
  constructor() {
1588
1923
  //* Critical State ================================
1589
- __publicField(this, "roots", /* @__PURE__ */ new Map());
1590
- __publicField(this, "phaseGraph");
1591
- __publicField(this, "loopState", {
1924
+ __publicField$1(this, "roots", /* @__PURE__ */ new Map());
1925
+ __publicField$1(this, "phaseGraph");
1926
+ __publicField$1(this, "loopState", {
1592
1927
  running: false,
1593
1928
  rafHandle: null,
1594
1929
  lastTime: null,
@@ -1597,21 +1932,21 @@ const _Scheduler = class _Scheduler {
1597
1932
  elapsedTime: 0,
1598
1933
  createdAt: performance.now()
1599
1934
  });
1600
- __publicField(this, "stoppedTime", 0);
1935
+ __publicField$1(this, "stoppedTime", 0);
1601
1936
  //* Private State ================================
1602
- __publicField(this, "nextRootIndex", 0);
1603
- __publicField(this, "globalBeforeJobs", /* @__PURE__ */ new Map());
1604
- __publicField(this, "globalAfterJobs", /* @__PURE__ */ new Map());
1605
- __publicField(this, "nextGlobalIndex", 0);
1606
- __publicField(this, "idleCallbacks", /* @__PURE__ */ new Set());
1607
- __publicField(this, "nextJobIndex", 0);
1608
- __publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
1609
- __publicField(this, "pendingFrames", 0);
1610
- __publicField(this, "_frameloop", "always");
1937
+ __publicField$1(this, "nextRootIndex", 0);
1938
+ __publicField$1(this, "globalBeforeJobs", /* @__PURE__ */ new Map());
1939
+ __publicField$1(this, "globalAfterJobs", /* @__PURE__ */ new Map());
1940
+ __publicField$1(this, "nextGlobalIndex", 0);
1941
+ __publicField$1(this, "idleCallbacks", /* @__PURE__ */ new Set());
1942
+ __publicField$1(this, "nextJobIndex", 0);
1943
+ __publicField$1(this, "jobStateListeners", /* @__PURE__ */ new Map());
1944
+ __publicField$1(this, "pendingFrames", 0);
1945
+ __publicField$1(this, "_frameloop", "always");
1611
1946
  //* Independent Mode & Error Handling State ================================
1612
- __publicField(this, "_independent", false);
1613
- __publicField(this, "errorHandler", null);
1614
- __publicField(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
1947
+ __publicField$1(this, "_independent", false);
1948
+ __publicField$1(this, "errorHandler", null);
1949
+ __publicField$1(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
1615
1950
  //* Core Loop Execution Methods ================================
1616
1951
  /**
1617
1952
  * Main RAF loop callback.
@@ -1620,7 +1955,7 @@ const _Scheduler = class _Scheduler {
1620
1955
  * @returns {void}
1621
1956
  * @private
1622
1957
  */
1623
- __publicField(this, "loop", (timestamp) => {
1958
+ __publicField$1(this, "loop", (timestamp) => {
1624
1959
  if (!this.loopState.running) return;
1625
1960
  this.executeFrame(timestamp);
1626
1961
  if (this._frameloop === "demand") {
@@ -2327,56 +2662,384 @@ const _Scheduler = class _Scheduler {
2327
2662
  //* Cross-Bundle Singleton Key ==============================
2328
2663
  // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2329
2664
  // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2330
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2665
+ __publicField$1(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2331
2666
  let Scheduler = _Scheduler;
2332
2667
  const getScheduler = () => Scheduler.get();
2333
2668
  if (hmrData) {
2334
2669
  hmrData.accept?.();
2335
2670
  }
2336
2671
 
2337
- function useFrame(callback, priorityOrOptions) {
2338
- const store = React.useContext(context);
2339
- const isInsideCanvas = store !== null;
2340
- const scheduler = getScheduler();
2341
- const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
2342
- id: priorityOrOptions.id,
2343
- phase: priorityOrOptions.phase,
2344
- priority: priorityOrOptions.priority,
2345
- fps: priorityOrOptions.fps,
2346
- drop: priorityOrOptions.drop,
2347
- enabled: priorityOrOptions.enabled,
2348
- before: priorityOrOptions.before,
2349
- after: priorityOrOptions.after
2350
- }) : "";
2351
- const options = React.useMemo(() => {
2352
- return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
2353
- }, [optionsKey]);
2354
- const reactId = React.useId();
2355
- const id = options.id ?? reactId;
2356
- const callbackRef = useMutableCallback(callback);
2357
- const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
2358
- useIsomorphicLayoutEffect(() => {
2359
- if (!callback) return;
2360
- if (isInsideCanvas) {
2361
- const state = store.getState();
2362
- const rootId = state.internal.rootId;
2363
- if (isLegacyPriority) {
2364
- state.internal.priority++;
2365
- let parentRoot = state.previousRoot;
2366
- while (parentRoot) {
2367
- const parentState = parentRoot.getState();
2368
- if (parentState?.internal) parentState.internal.priority++;
2369
- parentRoot = parentState?.previousRoot;
2370
- }
2371
- notifyDepreciated({
2372
- heading: "useFrame with numeric priority is deprecated",
2373
- body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
2374
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
2375
- });
2672
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2673
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
2674
+ const createStore = (invalidate, advance) => {
2675
+ const rootStore = createWithEqualityFn((set, get) => {
2676
+ const position = new Vector3();
2677
+ const defaultTarget = new Vector3();
2678
+ const tempTarget = new Vector3();
2679
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2680
+ const { width, height, top, left } = size;
2681
+ const aspect = width / height;
2682
+ if (target.isVector3) tempTarget.copy(target);
2683
+ else tempTarget.set(...target);
2684
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2685
+ if (isOrthographicCamera(camera)) {
2686
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2687
+ } else {
2688
+ const fov = camera.fov * Math.PI / 180;
2689
+ const h = 2 * Math.tan(fov / 2) * distance;
2690
+ const w = h * (width / height);
2691
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2376
2692
  }
2377
- const wrappedCallback = (frameState, delta) => {
2378
- const localState = store.getState();
2379
- const mergedState = {
2693
+ }
2694
+ let performanceTimeout = void 0;
2695
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2696
+ const pointer = new Vector2();
2697
+ const rootState = {
2698
+ set,
2699
+ get,
2700
+ // Mock objects that have to be configured
2701
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2702
+ primaryStore: null,
2703
+ gl: null,
2704
+ renderer: null,
2705
+ camera: null,
2706
+ frustum: new Frustum(),
2707
+ autoUpdateFrustum: true,
2708
+ raycaster: null,
2709
+ events: {
2710
+ priority: 1,
2711
+ enabled: true,
2712
+ connected: false,
2713
+ frameTimedRaycasts: true,
2714
+ alwaysFireOnScroll: true,
2715
+ updateOnFrame: false
2716
+ },
2717
+ scene: null,
2718
+ rootScene: null,
2719
+ xr: null,
2720
+ inspector: null,
2721
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2722
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2723
+ textureColorSpace: SRGBColorSpace,
2724
+ isLegacy: false,
2725
+ webGPUSupported: false,
2726
+ isNative: false,
2727
+ controls: null,
2728
+ pointer,
2729
+ mouse: pointer,
2730
+ frameloop: "always",
2731
+ onPointerMissed: void 0,
2732
+ onDragOverMissed: void 0,
2733
+ onDropMissed: void 0,
2734
+ performance: {
2735
+ current: 1,
2736
+ min: 0.5,
2737
+ max: 1,
2738
+ debounce: 200,
2739
+ regress: () => {
2740
+ const state2 = get();
2741
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2742
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2743
+ performanceTimeout = setTimeout(
2744
+ () => setPerformanceCurrent(get().performance.max),
2745
+ state2.performance.debounce
2746
+ );
2747
+ }
2748
+ },
2749
+ size: { width: 0, height: 0, top: 0, left: 0 },
2750
+ viewport: {
2751
+ initialDpr: 0,
2752
+ dpr: 0,
2753
+ width: 0,
2754
+ height: 0,
2755
+ top: 0,
2756
+ left: 0,
2757
+ aspect: 0,
2758
+ distance: 0,
2759
+ factor: 0,
2760
+ getCurrentViewport
2761
+ },
2762
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2763
+ setSize: (width, height, top, left) => {
2764
+ const state2 = get();
2765
+ if (width === void 0) {
2766
+ set({ _sizeImperative: false });
2767
+ if (state2._sizeProps) {
2768
+ const { width: propW, height: propH } = state2._sizeProps;
2769
+ if (propW !== void 0 || propH !== void 0) {
2770
+ const currentSize = state2.size;
2771
+ const newSize = {
2772
+ width: propW ?? currentSize.width,
2773
+ height: propH ?? currentSize.height,
2774
+ top: currentSize.top,
2775
+ left: currentSize.left
2776
+ };
2777
+ set((s) => ({
2778
+ size: newSize,
2779
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2780
+ }));
2781
+ getScheduler().invalidate();
2782
+ }
2783
+ }
2784
+ return;
2785
+ }
2786
+ const w = width;
2787
+ const h = height ?? width;
2788
+ const t = top ?? state2.size.top;
2789
+ const l = left ?? state2.size.left;
2790
+ const size = { width: w, height: h, top: t, left: l };
2791
+ set((s) => ({
2792
+ size,
2793
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2794
+ _sizeImperative: true
2795
+ }));
2796
+ getScheduler().invalidate();
2797
+ },
2798
+ setDpr: (dpr) => set((state2) => {
2799
+ const resolved = calculateDpr(dpr);
2800
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2801
+ }),
2802
+ setFrameloop: (frameloop = "always") => {
2803
+ set(() => ({ frameloop }));
2804
+ },
2805
+ setError: (error) => set(() => ({ error })),
2806
+ error: null,
2807
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2808
+ uniforms: {},
2809
+ nodes: {},
2810
+ buffers: {},
2811
+ gpuStorage: {},
2812
+ textures: /* @__PURE__ */ new Map(),
2813
+ renderPipeline: null,
2814
+ passes: {},
2815
+ _hmrVersion: 0,
2816
+ _sizeImperative: false,
2817
+ _sizeProps: null,
2818
+ previousRoot: void 0,
2819
+ internal: {
2820
+ // Events
2821
+ interaction: [],
2822
+ subscribers: [],
2823
+ // Per-pointer state (new unified structure)
2824
+ pointerMap: /* @__PURE__ */ new Map(),
2825
+ pointerDirty: /* @__PURE__ */ new Map(),
2826
+ lastEvent: React.createRef(),
2827
+ // Deprecated but kept for backwards compatibility
2828
+ hovered: /* @__PURE__ */ new Map(),
2829
+ initialClick: [0, 0],
2830
+ initialHits: [],
2831
+ capturedMap: /* @__PURE__ */ new Map(),
2832
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2833
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2834
+ // Occlusion system (WebGPU only)
2835
+ occlusionEnabled: false,
2836
+ occlusionObserver: null,
2837
+ occlusionCache: /* @__PURE__ */ new Map(),
2838
+ helperGroup: null,
2839
+ // Updates
2840
+ active: false,
2841
+ frames: 0,
2842
+ priority: 0,
2843
+ subscribe: (ref, priority, store) => {
2844
+ const internal = get().internal;
2845
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2846
+ internal.subscribers.push({ ref, priority, store });
2847
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2848
+ return () => {
2849
+ const internal2 = get().internal;
2850
+ if (internal2?.subscribers) {
2851
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2852
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2853
+ }
2854
+ };
2855
+ },
2856
+ // Renderer Storage (single source of truth)
2857
+ actualRenderer: null,
2858
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2859
+ scheduler: null
2860
+ }
2861
+ };
2862
+ return rootState;
2863
+ });
2864
+ const state = rootStore.getState();
2865
+ Object.defineProperty(state, "gl", {
2866
+ get() {
2867
+ const currentState = rootStore.getState();
2868
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2869
+ const stack = new Error().stack || "";
2870
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2871
+ if (!isInternalAccess) {
2872
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2873
+ notifyDepreciated({
2874
+ heading: "Accessing state.gl in WebGPU mode",
2875
+ body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
2876
+ });
2877
+ }
2878
+ }
2879
+ return currentState.internal.actualRenderer;
2880
+ },
2881
+ set(value) {
2882
+ rootStore.getState().internal.actualRenderer = value;
2883
+ },
2884
+ enumerable: true,
2885
+ configurable: true
2886
+ });
2887
+ Object.defineProperty(state, "renderer", {
2888
+ get() {
2889
+ return rootStore.getState().internal.actualRenderer;
2890
+ },
2891
+ set(value) {
2892
+ rootStore.getState().internal.actualRenderer = value;
2893
+ },
2894
+ enumerable: true,
2895
+ configurable: true
2896
+ });
2897
+ let oldScene = state.scene;
2898
+ rootStore.subscribe(() => {
2899
+ const currentState = rootStore.getState();
2900
+ const { scene, rootScene, set } = currentState;
2901
+ if (scene !== oldScene) {
2902
+ oldScene = scene;
2903
+ if (scene?.isScene && scene !== rootScene) {
2904
+ set({ rootScene: scene });
2905
+ }
2906
+ }
2907
+ });
2908
+ let oldSize = state.size;
2909
+ let oldDpr = state.viewport.dpr;
2910
+ let oldCamera = state.camera;
2911
+ rootStore.subscribe(() => {
2912
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2913
+ const actualRenderer = internal.actualRenderer;
2914
+ const canvasTarget = internal.canvasTarget;
2915
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2916
+ oldSize = size;
2917
+ oldDpr = viewport.dpr;
2918
+ updateCamera(camera, size);
2919
+ if (internal.isSecondary && canvasTarget) {
2920
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2921
+ canvasTarget.setSize(size.width, size.height, false);
2922
+ } else {
2923
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2924
+ actualRenderer.setSize(size.width, size.height, false);
2925
+ if (canvasTarget) {
2926
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2927
+ canvasTarget.setSize(size.width, size.height, false);
2928
+ }
2929
+ }
2930
+ }
2931
+ if (camera !== oldCamera) {
2932
+ oldCamera = camera;
2933
+ const { rootScene } = rootStore.getState();
2934
+ if (camera && rootScene && !camera.parent) {
2935
+ rootScene.add(camera);
2936
+ }
2937
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2938
+ const currentState = rootStore.getState();
2939
+ if (currentState.autoUpdateFrustum && camera) {
2940
+ updateFrustum(camera, currentState.frustum);
2941
+ }
2942
+ }
2943
+ });
2944
+ rootStore.subscribe((state2) => invalidate(state2));
2945
+ return rootStore;
2946
+ };
2947
+
2948
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2949
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2950
+ function getLoader(Proto) {
2951
+ if (isConstructor$1(Proto)) {
2952
+ let loader = memoizedLoaders.get(Proto);
2953
+ if (!loader) {
2954
+ loader = new Proto();
2955
+ memoizedLoaders.set(Proto, loader);
2956
+ }
2957
+ return loader;
2958
+ }
2959
+ return Proto;
2960
+ }
2961
+ function loadingFn(extensions, onProgress) {
2962
+ return function(Proto, input) {
2963
+ const loader = getLoader(Proto);
2964
+ if (extensions) extensions(loader);
2965
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2966
+ return loader.loadAsync(input, onProgress).then((data) => {
2967
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2968
+ return data;
2969
+ });
2970
+ }
2971
+ return new Promise(
2972
+ (res, reject) => loader.load(
2973
+ input,
2974
+ (data) => {
2975
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2976
+ res(data);
2977
+ },
2978
+ onProgress,
2979
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2980
+ )
2981
+ );
2982
+ };
2983
+ }
2984
+ function useLoader(loader, input, extensions, onProgress) {
2985
+ const keys = Array.isArray(input) ? input : [input];
2986
+ const fn = loadingFn(extensions, onProgress);
2987
+ const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
2988
+ return Array.isArray(input) ? results : results[0];
2989
+ }
2990
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2991
+ const keys = Array.isArray(input) ? input : [input];
2992
+ keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
2993
+ };
2994
+ useLoader.clear = function(loader, input) {
2995
+ const keys = Array.isArray(input) ? input : [input];
2996
+ keys.forEach((key) => clear([loader, key]));
2997
+ };
2998
+ useLoader.loader = getLoader;
2999
+
3000
+ function useFrame(callback, priorityOrOptions) {
3001
+ const store = React.useContext(context);
3002
+ const isInsideCanvas = store !== null;
3003
+ const scheduler = getScheduler();
3004
+ const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
3005
+ id: priorityOrOptions.id,
3006
+ phase: priorityOrOptions.phase,
3007
+ priority: priorityOrOptions.priority,
3008
+ fps: priorityOrOptions.fps,
3009
+ drop: priorityOrOptions.drop,
3010
+ enabled: priorityOrOptions.enabled,
3011
+ before: priorityOrOptions.before,
3012
+ after: priorityOrOptions.after
3013
+ }) : "";
3014
+ const options = React.useMemo(() => {
3015
+ return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
3016
+ }, [optionsKey]);
3017
+ const reactId = React.useId();
3018
+ const id = options.id ?? reactId;
3019
+ const callbackRef = useMutableCallback(callback);
3020
+ const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
3021
+ useIsomorphicLayoutEffect(() => {
3022
+ if (!callback) return;
3023
+ if (isInsideCanvas) {
3024
+ const state = store.getState();
3025
+ const rootId = state.internal.rootId;
3026
+ if (isLegacyPriority) {
3027
+ state.internal.priority++;
3028
+ let parentRoot = state.previousRoot;
3029
+ while (parentRoot) {
3030
+ const parentState = parentRoot.getState();
3031
+ if (parentState?.internal) parentState.internal.priority++;
3032
+ parentRoot = parentState?.previousRoot;
3033
+ }
3034
+ notifyDepreciated({
3035
+ heading: "useFrame with numeric priority is deprecated",
3036
+ body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
3037
+ link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
3038
+ });
3039
+ }
3040
+ const wrappedCallback = (frameState, delta) => {
3041
+ const localState = store.getState();
3042
+ const mergedState = {
2380
3043
  ...localState,
2381
3044
  time: frameState.time,
2382
3045
  delta: frameState.delta,
@@ -2513,6 +3176,9 @@ function useTexture(input, optionsOrOnLoad) {
2513
3176
  const textureCache = useThree((state) => state.textures);
2514
3177
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2515
3178
  const { onLoad, cache = false } = options;
3179
+ const onLoadRef = useRef(onLoad);
3180
+ onLoadRef.current = onLoad;
3181
+ const onLoadCalledForRef = useRef(null);
2516
3182
  const urls = useMemo(() => getUrls(input), [input]);
2517
3183
  const cachedResult = useMemo(() => {
2518
3184
  if (!cache) return null;
@@ -2523,9 +3189,13 @@ function useTexture(input, optionsOrOnLoad) {
2523
3189
  TextureLoader,
2524
3190
  IsObject(input) ? Object.values(input) : input
2525
3191
  );
3192
+ const inputKey = urls.join("\0");
2526
3193
  useLayoutEffect(() => {
2527
- if (!cachedResult) onLoad?.(loadedTextures);
2528
- }, [onLoad, cachedResult, loadedTextures]);
3194
+ if (cachedResult) return;
3195
+ if (onLoadCalledForRef.current === inputKey) return;
3196
+ onLoadCalledForRef.current = inputKey;
3197
+ onLoadRef.current?.(loadedTextures);
3198
+ }, [cachedResult, loadedTextures, inputKey]);
2529
3199
  useEffect(() => {
2530
3200
  if (cachedResult) return;
2531
3201
  if ("initTexture" in renderer) {
@@ -2692,14 +3362,31 @@ function useTextures() {
2692
3362
  }, [store]);
2693
3363
  }
2694
3364
 
2695
- function useRenderTarget(width, height, options) {
3365
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2696
3366
  const isLegacy = useThree((s) => s.isLegacy);
2697
3367
  const size = useThree((s) => s.size);
3368
+ let width;
3369
+ let height;
3370
+ let opts;
3371
+ if (typeof widthOrOptions === "object") {
3372
+ opts = widthOrOptions;
3373
+ } else if (typeof widthOrOptions === "number") {
3374
+ width = widthOrOptions;
3375
+ if (typeof heightOrOptions === "object") {
3376
+ height = widthOrOptions;
3377
+ opts = heightOrOptions;
3378
+ } else if (typeof heightOrOptions === "number") {
3379
+ height = heightOrOptions;
3380
+ opts = options;
3381
+ } else {
3382
+ height = widthOrOptions;
3383
+ }
3384
+ }
2698
3385
  return useMemo(() => {
2699
3386
  const w = width ?? size.width;
2700
3387
  const h = height ?? size.height;
2701
- return new RenderTarget(w, h, options);
2702
- }, [width, height, size.width, size.height, options, isLegacy]);
3388
+ return new RenderTarget(w, h, opts);
3389
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2703
3390
  }
2704
3391
 
2705
3392
  function useStore() {
@@ -2749,28 +3436,18 @@ function addTail(callback) {
2749
3436
  function invalidate(state, frames = 1, stackFrames = false) {
2750
3437
  getScheduler().invalidate(frames, stackFrames);
2751
3438
  }
2752
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3439
+ function advance(timestamp) {
2753
3440
  getScheduler().step(timestamp);
2754
3441
  }
2755
3442
 
2756
- const version = "10.0.0-alpha.1";
3443
+ const version = "10.0.0-alpha.2";
2757
3444
  const packageData = {
2758
3445
  version: version};
2759
3446
 
2760
3447
  function Xb(Tt) {
2761
3448
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2762
3449
  }
2763
- var Rm = { exports: {} }, Og = { exports: {} };
2764
- /**
2765
- * @license React
2766
- * react-reconciler.production.js
2767
- *
2768
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2769
- *
2770
- * This source code is licensed under the MIT license found in the
2771
- * LICENSE file in the root directory of this source tree.
2772
- */
2773
- var _b;
3450
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2774
3451
  function Kb() {
2775
3452
  return _b || (_b = 1, (function(Tt) {
2776
3453
  Tt.exports = function(m) {
@@ -3842,7 +4519,6 @@ Error generating stack: ` + l.message + `
3842
4519
  if (J === cl || J === jc) throw J;
3843
4520
  var Ge = Yn(29, J, null, P.mode);
3844
4521
  return Ge.lanes = H, Ge.return = P, Ge;
3845
- } finally {
3846
4522
  }
3847
4523
  };
3848
4524
  }
@@ -4496,7 +5172,6 @@ Error generating stack: ` + l.message + `
4496
5172
  var h = r.lastRenderedState, y = d(h, a);
4497
5173
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4498
5174
  } catch {
4499
- } finally {
4500
5175
  }
4501
5176
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4502
5177
  }
@@ -6917,10 +7592,7 @@ Error generating stack: ` + l.message + `
6917
7592
  function vr(t, r) {
6918
7593
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6919
7594
  }
6920
- var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy");
6921
- var gc = Symbol.for("react.activity");
6922
- var $r = Symbol.for("react.memo_cache_sentinel");
6923
- var Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
7595
+ var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy"), gc = Symbol.for("react.activity"), $r = Symbol.for("react.memo_cache_sentinel"), Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
6924
7596
  m.cloneMutableInstance;
6925
7597
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6926
7598
  m.cloneMutableTextInstance;
@@ -7289,17 +7961,7 @@ No matching component was found for:
7289
7961
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7290
7962
  })(Og)), Og.exports;
7291
7963
  }
7292
- var Mg = { exports: {} };
7293
- /**
7294
- * @license React
7295
- * react-reconciler.development.js
7296
- *
7297
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7298
- *
7299
- * This source code is licensed under the MIT license found in the
7300
- * LICENSE file in the root directory of this source tree.
7301
- */
7302
- var Rb;
7964
+ var Mg = { exports: {} }, Rb;
7303
7965
  function e0() {
7304
7966
  return Rb || (Rb = 1, (function(Tt) {
7305
7967
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13066,10 +13728,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13066
13728
  function Ic() {
13067
13729
  return di;
13068
13730
  }
13069
- var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy");
13070
- var Ds = Symbol.for("react.activity");
13071
- var Bh = Symbol.for("react.memo_cache_sentinel");
13072
- var ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13731
+ var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy"), Ds = Symbol.for("react.activity"), Bh = Symbol.for("react.memo_cache_sentinel"), ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13073
13732
  m.cloneMutableInstance;
13074
13733
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13075
13734
  m.cloneMutableTextInstance;
@@ -14037,15 +14696,6 @@ function n0() {
14037
14696
  var t0 = n0();
14038
14697
  const r0 = Xb(t0);
14039
14698
 
14040
- /**
14041
- * @license React
14042
- * react-reconciler-constants.production.js
14043
- *
14044
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14045
- *
14046
- * This source code is licensed under the MIT license found in the
14047
- * LICENSE file in the root directory of this source tree.
14048
- */
14049
14699
  const t = 1, o = 8, r = 32, e = 2;
14050
14700
 
14051
14701
  function createReconciler(config) {
@@ -14072,10 +14722,11 @@ function extend(objects) {
14072
14722
  function validateInstance(type, props) {
14073
14723
  const name = toPascalCase(type);
14074
14724
  const target = catalogue[name];
14075
- if (type !== "primitive" && !target)
14725
+ if (type !== "primitive" && !target) {
14076
14726
  throw new Error(
14077
14727
  `R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`
14078
14728
  );
14729
+ }
14079
14730
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14080
14731
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14081
14732
  }
@@ -14239,6 +14890,7 @@ function swapInstances() {
14239
14890
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14240
14891
  instance.object.__r3f = instance;
14241
14892
  setFiberRef(fiber, instance.object);
14893
+ delete instance.appliedOnce;
14242
14894
  applyProps(instance.object, instance.props);
14243
14895
  if (instance.props.attach) {
14244
14896
  attach(parent, instance);
@@ -14312,8 +14964,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14312
14964
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14313
14965
  if (isTailSibling) swapInstances();
14314
14966
  },
14315
- finalizeInitialChildren: () => false,
14316
- commitMount() {
14967
+ finalizeInitialChildren: (instance) => {
14968
+ for (const prop in instance.props) {
14969
+ if (isFromRef(instance.props[prop])) return true;
14970
+ }
14971
+ return false;
14972
+ },
14973
+ commitMount(instance) {
14974
+ const resolved = {};
14975
+ for (const prop in instance.props) {
14976
+ const value = instance.props[prop];
14977
+ if (isFromRef(value)) {
14978
+ const ref = value[FROM_REF];
14979
+ if (ref.current != null) resolved[prop] = ref.current;
14980
+ }
14981
+ }
14982
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14317
14983
  },
14318
14984
  getPublicInstance: (instance) => instance?.object,
14319
14985
  prepareForCommit: () => null,
@@ -14526,14 +15192,17 @@ function createRoot(canvas) {
14526
15192
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14527
15193
  let onCreated;
14528
15194
  let lastCamera;
14529
- let lastConfiguredProps = {};
15195
+ const lastConfiguredProps = {};
14530
15196
  let configured = false;
14531
15197
  let pending = null;
14532
15198
  return {
14533
15199
  async configure(props = {}) {
14534
15200
  let resolve;
14535
15201
  pending = new Promise((_resolve) => resolve = _resolve);
14536
- let {
15202
+ const {
15203
+ id: canvasId,
15204
+ primaryCanvas,
15205
+ scheduler: schedulerConfig,
14537
15206
  gl: glConfig,
14538
15207
  renderer: rendererConfig,
14539
15208
  size: propsSize,
@@ -14541,10 +15210,6 @@ function createRoot(canvas) {
14541
15210
  events,
14542
15211
  onCreated: onCreatedCallback,
14543
15212
  shadows = false,
14544
- linear = false,
14545
- flat = false,
14546
- textureColorSpace = SRGBColorSpace,
14547
- legacy = false,
14548
15213
  orthographic = false,
14549
15214
  frameloop = "always",
14550
15215
  dpr = [1, 2],
@@ -14555,11 +15220,15 @@ function createRoot(canvas) {
14555
15220
  onDragOverMissed,
14556
15221
  onDropMissed,
14557
15222
  autoUpdateFrustum = true,
14558
- occlusion = false
15223
+ occlusion = false,
15224
+ _sizeProps,
15225
+ forceEven
14559
15226
  } = props;
14560
- let state = store.getState();
15227
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
15228
+ const state = store.getState();
14561
15229
  const defaultGPUProps = {
14562
- canvas
15230
+ canvas,
15231
+ antialias: true
14563
15232
  };
14564
15233
  if (glConfig && !R3F_BUILD_LEGACY) {
14565
15234
  throw new Error(
@@ -14570,22 +15239,61 @@ function createRoot(canvas) {
14570
15239
  throw new Error("Cannot use both gl and renderer props at the same time");
14571
15240
  }
14572
15241
  let renderer = state.internal.actualRenderer;
14573
- if (!state.internal.actualRenderer) {
15242
+ if (primaryCanvas && !state.internal.actualRenderer) {
15243
+ const primary = await waitForPrimary(primaryCanvas);
15244
+ renderer = primary.renderer;
15245
+ state.internal.actualRenderer = renderer;
15246
+ const canvasTarget = new CanvasTarget(canvas);
15247
+ primary.store.setState((prev) => ({
15248
+ internal: { ...prev.internal, isMultiCanvas: true }
15249
+ }));
15250
+ state.set((prev) => ({
15251
+ webGPUSupported: primary.store.getState().webGPUSupported,
15252
+ renderer,
15253
+ primaryStore: primary.store,
15254
+ internal: {
15255
+ ...prev.internal,
15256
+ canvasTarget,
15257
+ isMultiCanvas: true,
15258
+ isSecondary: true,
15259
+ targetId: primaryCanvas
15260
+ }
15261
+ }));
15262
+ } else if (!state.internal.actualRenderer) {
14574
15263
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14575
15264
  if (!renderer.hasInitialized?.()) {
15265
+ const size2 = computeInitialSize(canvas, propsSize);
15266
+ if (size2.width > 0 && size2.height > 0) {
15267
+ const pixelRatio = calculateDpr(dpr);
15268
+ canvas.width = size2.width * pixelRatio;
15269
+ canvas.height = size2.height * pixelRatio;
15270
+ }
14576
15271
  await renderer.init();
14577
15272
  }
14578
15273
  const backend = renderer.backend;
14579
15274
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14580
15275
  state.internal.actualRenderer = renderer;
14581
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15276
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15277
+ if (canvasId && !state.internal.isSecondary) {
15278
+ const canvasTarget = new CanvasTarget(canvas);
15279
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15280
+ state.set((prev) => ({
15281
+ internal: {
15282
+ ...prev.internal,
15283
+ canvasTarget,
15284
+ unregisterPrimary
15285
+ }
15286
+ }));
15287
+ }
14582
15288
  }
14583
15289
  let raycaster = state.raycaster;
14584
15290
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
14585
15291
  const { params, ...options } = raycastOptions || {};
14586
15292
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14587
- if (!is.equ(params, raycaster.params, shallowLoose))
15293
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14588
15294
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15295
+ }
15296
+ let tempCamera = state.camera;
14589
15297
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14590
15298
  lastCamera = cameraOptions;
14591
15299
  const isCamera = cameraOptions?.isCamera;
@@ -14605,6 +15313,7 @@ function createRoot(canvas) {
14605
15313
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14606
15314
  }
14607
15315
  state.set({ camera });
15316
+ tempCamera = camera;
14608
15317
  raycaster.camera = camera;
14609
15318
  }
14610
15319
  if (!state.scene) {
@@ -14622,7 +15331,7 @@ function createRoot(canvas) {
14622
15331
  rootScene: scene,
14623
15332
  internal: { ...prev.internal, container: scene }
14624
15333
  }));
14625
- const camera = state.camera;
15334
+ const camera = tempCamera;
14626
15335
  if (camera && !camera.parent) scene.add(camera);
14627
15336
  }
14628
15337
  if (events && !state.events.handlers) {
@@ -14636,9 +15345,17 @@ function createRoot(canvas) {
14636
15345
  wasEnabled = enabled;
14637
15346
  });
14638
15347
  }
15348
+ if (_sizeProps !== void 0) {
15349
+ state.set({ _sizeProps });
15350
+ }
15351
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15352
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15353
+ }
14639
15354
  const size = computeInitialSize(canvas, propsSize);
14640
- if (!is.equ(size, state.size, shallowLoose)) {
15355
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15356
+ const wasImperative = state._sizeImperative;
14641
15357
  state.setSize(size.width, size.height, size.top, size.left);
15358
+ if (!wasImperative) state.set({ _sizeImperative: false });
14642
15359
  }
14643
15360
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14644
15361
  state.setDpr(dpr);
@@ -14660,10 +15377,10 @@ function createRoot(canvas) {
14660
15377
  lastConfiguredProps.performance = performance;
14661
15378
  }
14662
15379
  if (!state.xr) {
14663
- const handleXRFrame = (timestamp, frame) => {
15380
+ const handleXRFrame = (timestamp, _frame) => {
14664
15381
  const state2 = store.getState();
14665
15382
  if (state2.frameloop === "never") return;
14666
- advance(timestamp, true);
15383
+ advance(timestamp);
14667
15384
  };
14668
15385
  const actualRenderer = state.internal.actualRenderer;
14669
15386
  const handleSessionChange = () => {
@@ -14675,16 +15392,16 @@ function createRoot(canvas) {
14675
15392
  };
14676
15393
  const xr = {
14677
15394
  connect() {
14678
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14679
- const actualRenderer2 = renderer2 || gl;
14680
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14681
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15395
+ const { gl, renderer: renderer2 } = store.getState();
15396
+ const xrManager = (renderer2 || gl).xr;
15397
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15398
+ xrManager.addEventListener("sessionend", handleSessionChange);
14682
15399
  },
14683
15400
  disconnect() {
14684
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14685
- const actualRenderer2 = renderer2 || gl;
14686
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14687
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15401
+ const { gl, renderer: renderer2 } = store.getState();
15402
+ const xrManager = (renderer2 || gl).xr;
15403
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15404
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14688
15405
  }
14689
15406
  };
14690
15407
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14696,41 +15413,92 @@ function createRoot(canvas) {
14696
15413
  const oldType = renderer.shadowMap.type;
14697
15414
  renderer.shadowMap.enabled = !!shadows;
14698
15415
  if (is.boo(shadows)) {
14699
- renderer.shadowMap.type = PCFSoftShadowMap;
15416
+ renderer.shadowMap.type = PCFShadowMap;
14700
15417
  } else if (is.str(shadows)) {
15418
+ if (shadows === "soft") {
15419
+ notifyDepreciated({
15420
+ heading: 'shadows="soft" is deprecated',
15421
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15422
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15423
+ });
15424
+ }
14701
15425
  const types = {
14702
15426
  basic: BasicShadowMap,
14703
15427
  percentage: PCFShadowMap,
14704
- soft: PCFSoftShadowMap,
15428
+ soft: PCFShadowMap,
14705
15429
  variance: VSMShadowMap
14706
15430
  };
14707
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15431
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
14708
15432
  } else if (is.obj(shadows)) {
14709
15433
  Object.assign(renderer.shadowMap, shadows);
14710
15434
  }
14711
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15435
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14712
15436
  renderer.shadowMap.needsUpdate = true;
15437
+ }
15438
+ }
15439
+ if (!configured) {
15440
+ renderer.outputColorSpace = SRGBColorSpace;
15441
+ renderer.toneMapping = ACESFilmicToneMapping;
14713
15442
  }
14714
15443
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14715
15444
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14716
15445
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14717
15446
  }
14718
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
14719
- applyProps(renderer, glConfig);
15447
+ const r3fProps = ["textureColorSpace"];
15448
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15449
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15450
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15451
+ const glProps = {};
15452
+ for (const key in glConfig) {
15453
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15454
+ }
15455
+ applyProps(renderer, glProps);
15456
+ }
14720
15457
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14721
15458
  const currentRenderer = state.renderer;
14722
15459
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14723
- applyProps(currentRenderer, rendererConfig);
15460
+ const rendererProps = {};
15461
+ for (const key in rendererConfig) {
15462
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15463
+ }
15464
+ applyProps(currentRenderer, rendererProps);
14724
15465
  }
14725
15466
  }
14726
15467
  const scheduler = getScheduler();
14727
15468
  const rootId = state.internal.rootId;
14728
15469
  if (!rootId) {
14729
- const newRootId = scheduler.generateRootId();
15470
+ const newRootId = canvasId || scheduler.generateRootId();
14730
15471
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14731
15472
  getState: () => store.getState(),
14732
15473
  onError: (err) => store.getState().setError(err)
14733
15474
  });
15475
+ const unregisterCanvasTarget = scheduler.register(
15476
+ () => {
15477
+ const state2 = store.getState();
15478
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15479
+ const renderer2 = state2.internal.actualRenderer;
15480
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15481
+ }
15482
+ },
15483
+ {
15484
+ id: `${newRootId}_canvasTarget`,
15485
+ rootId: newRootId,
15486
+ phase: "start",
15487
+ system: true
15488
+ }
15489
+ );
15490
+ const unregisterEventsFlush = scheduler.register(
15491
+ () => {
15492
+ const state2 = store.getState();
15493
+ state2.events.flush?.();
15494
+ },
15495
+ {
15496
+ id: `${newRootId}_events`,
15497
+ rootId: newRootId,
15498
+ phase: "input",
15499
+ system: true
15500
+ }
15501
+ );
14734
15502
  const unregisterFrustum = scheduler.register(
14735
15503
  () => {
14736
15504
  const state2 = store.getState();
@@ -14765,18 +15533,22 @@ function createRoot(canvas) {
14765
15533
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14766
15534
  if (userHandlesRender || state2.internal.priority) return;
14767
15535
  try {
14768
- if (state2.postProcessing?.render) state2.postProcessing.render();
15536
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14769
15537
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14770
15538
  } catch (error) {
14771
15539
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14772
15540
  }
14773
15541
  },
14774
15542
  {
14775
- id: `${newRootId}_render`,
15543
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15544
+ id: canvasId || `${newRootId}_render`,
14776
15545
  rootId: newRootId,
14777
15546
  phase: "render",
14778
- system: true
15547
+ system: true,
14779
15548
  // Internal flag: this is a system job, not user-controlled
15549
+ // Apply scheduler config for render ordering and rate limiting
15550
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15551
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14780
15552
  }
14781
15553
  );
14782
15554
  state.set((state2) => ({
@@ -14785,6 +15557,8 @@ function createRoot(canvas) {
14785
15557
  rootId: newRootId,
14786
15558
  unregisterRoot: () => {
14787
15559
  unregisterRoot();
15560
+ unregisterCanvasTarget();
15561
+ unregisterEventsFlush();
14788
15562
  unregisterFrustum();
14789
15563
  unregisterVisibility();
14790
15564
  unregisterRender();
@@ -14843,15 +15617,24 @@ function unmountComponentAtNode(canvas, callback) {
14843
15617
  const renderer = state.internal.actualRenderer;
14844
15618
  const unregisterRoot = state.internal.unregisterRoot;
14845
15619
  if (unregisterRoot) unregisterRoot();
15620
+ const unregisterPrimary = state.internal.unregisterPrimary;
15621
+ if (unregisterPrimary) unregisterPrimary();
15622
+ const canvasTarget = state.internal.canvasTarget;
15623
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14846
15624
  state.events.disconnect?.();
14847
15625
  cleanupHelperGroup(root.store);
14848
- renderer?.renderLists?.dispose?.();
14849
- renderer?.forceContextLoss?.();
14850
- if (renderer?.xr) state.xr.disconnect();
15626
+ if (state.isLegacy && renderer) {
15627
+ ;
15628
+ renderer.renderLists?.dispose?.();
15629
+ renderer.forceContextLoss?.();
15630
+ }
15631
+ if (!state.internal.isSecondary) {
15632
+ if (renderer?.xr) state.xr.disconnect();
15633
+ }
14851
15634
  dispose(state.scene);
14852
15635
  _roots.delete(canvas);
14853
15636
  if (callback) callback(canvas);
14854
- } catch (e) {
15637
+ } catch {
14855
15638
  }
14856
15639
  }, 500);
14857
15640
  }
@@ -14859,36 +15642,34 @@ function unmountComponentAtNode(canvas, callback) {
14859
15642
  }
14860
15643
  }
14861
15644
  function createPortal(children, container, state) {
14862
- return /* @__PURE__ */ jsx(PortalWrapper, { children, container, state });
15645
+ return /* @__PURE__ */ jsx(Portal, { children, container, state });
14863
15646
  }
14864
- function PortalWrapper({ children, container, state }) {
15647
+ function Portal({ children, container, state }) {
14865
15648
  const isRef = useCallback((obj) => obj && "current" in obj, []);
14866
- const [resolvedContainer, setResolvedContainer] = useState(() => {
15649
+ const [resolvedContainer, _setResolvedContainer] = useState(() => {
14867
15650
  if (isRef(container)) return container.current ?? null;
14868
15651
  return container;
14869
15652
  });
15653
+ const setResolvedContainer = useCallback(
15654
+ (newContainer) => {
15655
+ if (!newContainer || newContainer === resolvedContainer) return;
15656
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15657
+ },
15658
+ [resolvedContainer, _setResolvedContainer, isRef]
15659
+ );
14870
15660
  useMemo(() => {
14871
- if (isRef(container)) {
14872
- const current = container.current;
14873
- if (!current) {
14874
- queueMicrotask(() => {
14875
- const updated = container.current;
14876
- if (updated && updated !== resolvedContainer) {
14877
- setResolvedContainer(updated);
14878
- }
14879
- });
14880
- } else if (current !== resolvedContainer) {
14881
- setResolvedContainer(current);
14882
- }
14883
- } else if (container !== resolvedContainer) {
14884
- setResolvedContainer(container);
15661
+ if (isRef(container) && !container.current) {
15662
+ return queueMicrotask(() => {
15663
+ setResolvedContainer(container.current);
15664
+ });
14885
15665
  }
14886
- }, [container, resolvedContainer, isRef]);
15666
+ setResolvedContainer(container);
15667
+ }, [container, isRef, setResolvedContainer]);
14887
15668
  if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
14888
15669
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14889
- return /* @__PURE__ */ jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15670
+ return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14890
15671
  }
14891
- function Portal({ state = {}, children, container }) {
15672
+ function PortalInner({ state = {}, children, container }) {
14892
15673
  const { events, size, injectScene = true, ...rest } = state;
14893
15674
  const previousRoot = useStore();
14894
15675
  const [raycaster] = useState(() => new Raycaster());
@@ -14909,11 +15690,12 @@ function Portal({ state = {}, children, container }) {
14909
15690
  };
14910
15691
  }, [portalScene, container, injectScene]);
14911
15692
  const inject = useMutableCallback((rootState, injectState) => {
15693
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14912
15694
  let viewport = void 0;
14913
- if (injectState.camera && size) {
15695
+ if (injectState.camera && (size || injectState.size)) {
14914
15696
  const camera = injectState.camera;
14915
- viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), size);
14916
- if (camera !== rootState.camera) updateCamera(camera, size);
15697
+ viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
15698
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14917
15699
  }
14918
15700
  return {
14919
15701
  // The intersect consists of the previous root state
@@ -14930,7 +15712,7 @@ function Portal({ state = {}, children, container }) {
14930
15712
  previousRoot,
14931
15713
  // Events, size and viewport can be overridden by the inject layer
14932
15714
  events: { ...rootState.events, ...injectState.events, ...events },
14933
- size: { ...rootState.size, ...size },
15715
+ size: resolvedSize,
14934
15716
  viewport: { ...rootState.viewport, ...viewport },
14935
15717
  // Layers are allowed to override events
14936
15718
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14942,9 +15724,13 @@ function Portal({ state = {}, children, container }) {
14942
15724
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14943
15725
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14944
15726
  onMutate(previousRoot.getState());
14945
- previousRoot.subscribe(onMutate);
14946
15727
  return store;
14947
15728
  }, [previousRoot, container]);
15729
+ useIsomorphicLayoutEffect(() => {
15730
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15731
+ const unsubscribe = previousRoot.subscribe(onMutate);
15732
+ return unsubscribe;
15733
+ }, [previousRoot, usePortalStore]);
14948
15734
  return (
14949
15735
  // @ts-ignore, reconciler types are not maintained
14950
15736
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -14964,15 +15750,13 @@ function CanvasImpl({
14964
15750
  fallback,
14965
15751
  resize,
14966
15752
  style,
15753
+ id,
14967
15754
  gl,
14968
- renderer,
15755
+ renderer: rendererProp,
14969
15756
  events = createPointerEvents,
14970
15757
  eventSource,
14971
15758
  eventPrefix,
14972
15759
  shadows,
14973
- linear,
14974
- flat,
14975
- legacy,
14976
15760
  orthographic,
14977
15761
  frameloop,
14978
15762
  dpr,
@@ -14984,10 +15768,56 @@ function CanvasImpl({
14984
15768
  onDragOverMissed,
14985
15769
  onDropMissed,
14986
15770
  onCreated,
15771
+ hmr,
15772
+ width,
15773
+ height,
15774
+ background,
15775
+ forceEven,
14987
15776
  ...props
14988
15777
  }) {
15778
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15779
+ let primaryCanvas;
15780
+ let scheduler;
15781
+ let renderer;
15782
+ if (isRendererConfig) {
15783
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15784
+ primaryCanvas = pc;
15785
+ scheduler = sc;
15786
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15787
+ } else {
15788
+ renderer = rendererProp;
15789
+ }
14989
15790
  React.useMemo(() => extend(THREE), []);
14990
15791
  const Bridge = useBridge();
15792
+ const backgroundProps = React.useMemo(() => {
15793
+ if (!background) return null;
15794
+ if (typeof background === "object" && !background.isColor) {
15795
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15796
+ return {
15797
+ ...rest,
15798
+ preset,
15799
+ files: envMap || files,
15800
+ backgroundFiles: backgroundMap,
15801
+ background: true
15802
+ };
15803
+ }
15804
+ if (typeof background === "number") {
15805
+ return { color: background, background: true };
15806
+ }
15807
+ if (typeof background === "string") {
15808
+ if (background in presetsObj) {
15809
+ return { preset: background, background: true };
15810
+ }
15811
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15812
+ return { files: background, background: true };
15813
+ }
15814
+ return { color: background, background: true };
15815
+ }
15816
+ if (background.isColor) {
15817
+ return { color: background, background: true };
15818
+ }
15819
+ return null;
15820
+ }, [background]);
14991
15821
  const hasInitialSizeRef = React.useRef(false);
14992
15822
  const measureConfig = React.useMemo(() => {
14993
15823
  if (!hasInitialSizeRef.current) {
@@ -15004,7 +15834,21 @@ function CanvasImpl({
15004
15834
  };
15005
15835
  }, [resize, hasInitialSizeRef.current]);
15006
15836
  const [containerRef, containerRect] = useMeasure(measureConfig);
15007
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
15837
+ const effectiveSize = React.useMemo(() => {
15838
+ let w = width ?? containerRect.width;
15839
+ let h = height ?? containerRect.height;
15840
+ if (forceEven) {
15841
+ w = Math.ceil(w / 2) * 2;
15842
+ h = Math.ceil(h / 2) * 2;
15843
+ }
15844
+ return {
15845
+ width: w,
15846
+ height: h,
15847
+ top: containerRect.top,
15848
+ left: containerRect.left
15849
+ };
15850
+ }, [width, height, containerRect, forceEven]);
15851
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15008
15852
  hasInitialSizeRef.current = true;
15009
15853
  }
15010
15854
  const canvasRef = React.useRef(null);
@@ -15023,7 +15867,7 @@ function CanvasImpl({
15023
15867
  useIsomorphicLayoutEffect(() => {
15024
15868
  effectActiveRef.current = true;
15025
15869
  const canvas = canvasRef.current;
15026
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15870
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15027
15871
  if (!root.current) {
15028
15872
  root.current = createRoot(canvas);
15029
15873
  notifyAlpha({
@@ -15043,21 +15887,24 @@ function CanvasImpl({
15043
15887
  async function run() {
15044
15888
  if (!effectActiveRef.current || !root.current) return;
15045
15889
  await root.current.configure({
15890
+ id,
15891
+ primaryCanvas,
15892
+ scheduler,
15046
15893
  gl,
15047
15894
  renderer,
15048
15895
  scene,
15049
15896
  events,
15050
15897
  shadows,
15051
- linear,
15052
- flat,
15053
- legacy,
15054
15898
  orthographic,
15055
15899
  frameloop,
15056
15900
  dpr,
15057
15901
  performance,
15058
15902
  raycaster,
15059
15903
  camera,
15060
- size: containerRect,
15904
+ size: effectiveSize,
15905
+ // Store size props for reset functionality
15906
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15907
+ forceEven,
15061
15908
  // Pass mutable reference to onPointerMissed so it's free to update
15062
15909
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15063
15910
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15081,7 +15928,10 @@ function CanvasImpl({
15081
15928
  });
15082
15929
  if (!effectActiveRef.current || !root.current) return;
15083
15930
  root.current.render(
15084
- /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsx(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15931
+ /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
15932
+ backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
15933
+ children ?? null
15934
+ ] }) }) })
15085
15935
  );
15086
15936
  }
15087
15937
  run();
@@ -15102,7 +15952,36 @@ function CanvasImpl({
15102
15952
  root.current = null;
15103
15953
  };
15104
15954
  }
15105
- }, []);
15955
+ }, []);
15956
+ React.useEffect(() => {
15957
+ if (hmr === false) return;
15958
+ const canvas = canvasRef.current;
15959
+ if (!canvas) return;
15960
+ const handleHMR = () => {
15961
+ queueMicrotask(() => {
15962
+ const rootEntry = _roots.get(canvas);
15963
+ if (rootEntry?.store) {
15964
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15965
+ rootEntry.store.setState((state) => ({
15966
+ nodes: {},
15967
+ uniforms: {},
15968
+ _hmrVersion: state._hmrVersion + 1
15969
+ }));
15970
+ }
15971
+ });
15972
+ };
15973
+ if (typeof import.meta !== "undefined" && import.meta.hot) {
15974
+ const hot = import.meta.hot;
15975
+ hot.on("vite:afterUpdate", handleHMR);
15976
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15977
+ }
15978
+ if (typeof module !== "undefined" && module.hot) {
15979
+ const hot = module.hot;
15980
+ hot.addStatusHandler((status) => {
15981
+ if (status === "idle") handleHMR();
15982
+ });
15983
+ }
15984
+ }, [hmr]);
15106
15985
  const pointerEvents = eventSource ? "none" : "auto";
15107
15986
  return /* @__PURE__ */ jsx(
15108
15987
  "div",
@@ -15117,7 +15996,16 @@ function CanvasImpl({
15117
15996
  ...style
15118
15997
  },
15119
15998
  ...props,
15120
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15999
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
16000
+ "canvas",
16001
+ {
16002
+ ref: canvasRef,
16003
+ id,
16004
+ className: "r3f-canvas",
16005
+ style: { display: "block", width: "100%", height: "100%" },
16006
+ children: fallback
16007
+ }
16008
+ ) })
15121
16009
  }
15122
16010
  );
15123
16011
  }
@@ -15125,6 +16013,100 @@ function Canvas(props) {
15125
16013
  return /* @__PURE__ */ jsx(FiberProvider, { children: /* @__PURE__ */ jsx(CanvasImpl, { ...props }) });
15126
16014
  }
15127
16015
 
16016
+ var __defProp = Object.defineProperty;
16017
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16018
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
16019
+ var _a;
16020
+ const INTERNAL_DATA = Symbol("ScopedStore.data");
16021
+ _a = INTERNAL_DATA;
16022
+ const _ScopedStore = class _ScopedStore {
16023
+ constructor(data) {
16024
+ __publicField(this, _a);
16025
+ this[INTERNAL_DATA] = data;
16026
+ return new Proxy(this, {
16027
+ get(target, prop, receiver) {
16028
+ if (typeof prop === "string") {
16029
+ if (prop === "scope" || prop === "has" || prop === "keys") {
16030
+ return Reflect.get(target, prop, receiver);
16031
+ }
16032
+ return target[INTERNAL_DATA][prop];
16033
+ }
16034
+ return Reflect.get(target, prop, receiver);
16035
+ },
16036
+ has(target, prop) {
16037
+ return typeof prop === "string" ? prop in target[INTERNAL_DATA] : Reflect.has(target, prop);
16038
+ },
16039
+ ownKeys(target) {
16040
+ return Reflect.ownKeys(target[INTERNAL_DATA]);
16041
+ },
16042
+ getOwnPropertyDescriptor(target, prop) {
16043
+ if (typeof prop === "string" && prop in target[INTERNAL_DATA]) {
16044
+ return {
16045
+ configurable: true,
16046
+ enumerable: true,
16047
+ value: target[INTERNAL_DATA][prop]
16048
+ };
16049
+ }
16050
+ return void 0;
16051
+ }
16052
+ });
16053
+ }
16054
+ /**
16055
+ * Access a nested scope by key.
16056
+ * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
16057
+ */
16058
+ scope(key) {
16059
+ const data = this[INTERNAL_DATA][key];
16060
+ return new _ScopedStore(
16061
+ data && typeof data === "object" ? data : {}
16062
+ );
16063
+ }
16064
+ /**
16065
+ * Check if a key exists in the store.
16066
+ */
16067
+ has(key) {
16068
+ return key in this[INTERNAL_DATA];
16069
+ }
16070
+ /**
16071
+ * Get all keys in the store.
16072
+ */
16073
+ keys() {
16074
+ return Object.keys(this[INTERNAL_DATA]);
16075
+ }
16076
+ };
16077
+ let ScopedStore = _ScopedStore;
16078
+ function createScopedStore(data) {
16079
+ return new ScopedStore(data);
16080
+ }
16081
+ function createLazyCreatorState(state) {
16082
+ let _uniforms = null;
16083
+ let _nodes = null;
16084
+ let _buffers = null;
16085
+ let _gpuStorage = null;
16086
+ return Object.create(state, {
16087
+ uniforms: {
16088
+ get() {
16089
+ return _uniforms ?? (_uniforms = createScopedStore(state.uniforms));
16090
+ }
16091
+ },
16092
+ nodes: {
16093
+ get() {
16094
+ return _nodes ?? (_nodes = createScopedStore(state.nodes));
16095
+ }
16096
+ },
16097
+ buffers: {
16098
+ get() {
16099
+ return _buffers ?? (_buffers = createScopedStore(state.buffers));
16100
+ }
16101
+ },
16102
+ gpuStorage: {
16103
+ get() {
16104
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
16105
+ }
16106
+ }
16107
+ });
16108
+ }
16109
+
15128
16110
  function addTexture(set, key, value) {
15129
16111
  set((state) => {
15130
16112
  const newMap = new Map(state.textures);
@@ -15164,6 +16146,27 @@ function createTextureOperations(set) {
15164
16146
  removeMultiple: (keys) => removeTextures(set, keys)
15165
16147
  };
15166
16148
  }
16149
+ function extractTSLValue(value) {
16150
+ if (value === null || value === void 0) return value;
16151
+ if (typeof value !== "object") return value;
16152
+ const node = value;
16153
+ if (!node.isNode) return value;
16154
+ if (node.isConstNode) {
16155
+ return node.value;
16156
+ }
16157
+ if ("value" in node) {
16158
+ let extractedValue = node.value;
16159
+ if (typeof node.traverse === "function") {
16160
+ node.traverse((n) => {
16161
+ if (n.isConstNode) {
16162
+ extractedValue = n.value;
16163
+ }
16164
+ });
16165
+ }
16166
+ return extractedValue;
16167
+ }
16168
+ return value;
16169
+ }
15167
16170
  function vectorize(inObject) {
15168
16171
  if (inObject === null || inObject === void 0) return inObject;
15169
16172
  if (typeof inObject === "string") {
@@ -15176,9 +16179,16 @@ function vectorize(inObject) {
15176
16179
  }
15177
16180
  if (typeof inObject !== "object") return inObject;
15178
16181
  const obj = inObject;
16182
+ if (obj.isNode) {
16183
+ return extractTSLValue(inObject);
16184
+ }
15179
16185
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15180
16186
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15181
16187
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
16188
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
16189
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
16190
+ return new Color(obj.r * scale, obj.g * scale, obj.b * scale);
16191
+ }
15182
16192
  if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
15183
16193
  if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
15184
16194
  return new Vector4(obj.x, obj.y, obj.z, obj.w);
@@ -15238,21 +16248,55 @@ function useUniforms(creatorOrScope, scope) {
15238
16248
  },
15239
16249
  [store]
15240
16250
  );
16251
+ const rebuildUniforms = useCallback(
16252
+ (targetScope) => {
16253
+ store.setState((state) => {
16254
+ let newUniforms = {};
16255
+ if (targetScope && targetScope !== "root") {
16256
+ const { [targetScope]: _, ...rest } = state.uniforms;
16257
+ newUniforms = rest;
16258
+ } else if (targetScope === "root") {
16259
+ for (const [key, value] of Object.entries(state.uniforms)) {
16260
+ if (!isUniformNode$1(value)) newUniforms[key] = value;
16261
+ }
16262
+ }
16263
+ return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
16264
+ });
16265
+ },
16266
+ [store]
16267
+ );
15241
16268
  const inputForMemoization = useMemo(() => {
15242
- return is.fun(creatorOrScope) ? creatorOrScope(store.getState()) : creatorOrScope;
16269
+ let raw = creatorOrScope;
16270
+ if (is.fun(creatorOrScope)) {
16271
+ const wrappedState = createLazyCreatorState(store.getState());
16272
+ raw = creatorOrScope(wrappedState);
16273
+ }
16274
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
16275
+ const normalized = {};
16276
+ for (const [key, value] of Object.entries(raw)) {
16277
+ normalized[key] = vectorize(value);
16278
+ }
16279
+ return normalized;
16280
+ }
16281
+ return raw;
15243
16282
  }, [creatorOrScope, store]);
15244
16283
  const memoizedInput = useCompareMemoize(inputForMemoization);
16284
+ const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
16285
+ const storeUniforms = useThree((s) => s.uniforms);
16286
+ const hmrVersion = useThree((s) => s._hmrVersion);
16287
+ const readerDep = isReader ? storeUniforms : null;
16288
+ const creatorDep = isReader ? null : hmrVersion;
15245
16289
  const uniforms = useMemo(() => {
15246
- const state = store.getState();
15247
- const set = store.setState;
15248
16290
  if (memoizedInput === void 0) {
15249
- return state.uniforms;
16291
+ return storeUniforms;
15250
16292
  }
15251
16293
  if (typeof memoizedInput === "string") {
15252
- const scopeData = state.uniforms[memoizedInput];
16294
+ const scopeData = storeUniforms[memoizedInput];
15253
16295
  if (scopeData && !isUniformNode$1(scopeData)) return scopeData;
15254
16296
  return {};
15255
16297
  }
16298
+ const state = store.getState();
16299
+ const set = store.setState;
15256
16300
  if (typeof memoizedInput !== "object" || memoizedInput === null) {
15257
16301
  throw new Error("Invalid uniform input");
15258
16302
  }
@@ -15296,8 +16340,22 @@ function useUniforms(creatorOrScope, scope) {
15296
16340
  }
15297
16341
  }
15298
16342
  return result;
15299
- }, [store, memoizedInput, scope]);
15300
- return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms };
16343
+ }, [store, memoizedInput, scope, readerDep, creatorDep]);
16344
+ return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
16345
+ }
16346
+ function rebuildAllUniforms(store, scope) {
16347
+ store.setState((state) => {
16348
+ let newUniforms = {};
16349
+ if (scope && scope !== "root") {
16350
+ const { [scope]: _, ...rest } = state.uniforms;
16351
+ newUniforms = rest;
16352
+ } else if (scope === "root") {
16353
+ for (const [key, value] of Object.entries(state.uniforms)) {
16354
+ if (!isUniformNode$1(value)) newUniforms[key] = value;
16355
+ }
16356
+ }
16357
+ return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
16358
+ });
15301
16359
  }
15302
16360
  function removeUniforms(set, names, scope) {
15303
16361
  set((state) => {
@@ -15362,15 +16420,17 @@ function isSameThreeType(a, b) {
15362
16420
  }
15363
16421
 
15364
16422
  const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
16423
+ const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15365
16424
  function useUniform(name, value) {
15366
16425
  const store = useStore();
16426
+ const hmrVersion = useThree((s) => s._hmrVersion);
15367
16427
  return useMemo(() => {
15368
16428
  const state = store.getState();
15369
16429
  const set = store.setState;
15370
16430
  const existing = state.uniforms[name];
15371
16431
  if (existing && isUniformNode(existing)) {
15372
- if (value !== void 0) {
15373
- existing.value = value;
16432
+ if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
16433
+ existing.value = typeof value === "string" ? new Color(value) : value;
15374
16434
  }
15375
16435
  return existing;
15376
16436
  }
@@ -15379,7 +16439,24 @@ function useUniform(name, value) {
15379
16439
  `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15380
16440
  );
15381
16441
  }
15382
- const node = uniform(value);
16442
+ if (isUniformNode(value)) {
16443
+ const node2 = value;
16444
+ if (typeof node2.setName === "function") {
16445
+ node2.setName(name);
16446
+ }
16447
+ set((s) => ({
16448
+ uniforms: { ...s.uniforms, [name]: node2 }
16449
+ }));
16450
+ return node2;
16451
+ }
16452
+ let node;
16453
+ if (isTSLNode$1(value)) {
16454
+ node = uniform(value);
16455
+ } else if (typeof value === "string") {
16456
+ node = uniform(new Color(value));
16457
+ } else {
16458
+ node = uniform(value);
16459
+ }
15383
16460
  if (typeof node.setName === "function") {
15384
16461
  node.setName(name);
15385
16462
  }
@@ -15390,7 +16467,7 @@ function useUniform(name, value) {
15390
16467
  }
15391
16468
  }));
15392
16469
  return node;
15393
- }, [store, name]);
16470
+ }, [store, name, hmrVersion]);
15394
16471
  }
15395
16472
 
15396
16473
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
@@ -15431,19 +16508,46 @@ function useNodes(creatorOrScope, scope) {
15431
16508
  },
15432
16509
  [store]
15433
16510
  );
16511
+ const rebuildNodes = useCallback(
16512
+ (targetScope) => {
16513
+ store.setState((state) => {
16514
+ let newNodes = state.nodes;
16515
+ if (targetScope && targetScope !== "root") {
16516
+ const { [targetScope]: _, ...rest } = state.nodes;
16517
+ newNodes = rest;
16518
+ } else if (targetScope === "root") {
16519
+ newNodes = {};
16520
+ for (const [key, value] of Object.entries(state.nodes)) {
16521
+ if (!isTSLNode(value)) newNodes[key] = value;
16522
+ }
16523
+ } else {
16524
+ newNodes = {};
16525
+ }
16526
+ return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
16527
+ });
16528
+ },
16529
+ [store]
16530
+ );
16531
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16532
+ const storeNodes = useThree((s) => s.nodes);
16533
+ const hmrVersion = useThree((s) => s._hmrVersion);
16534
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16535
+ const readerDep = isReader ? storeNodes : null;
16536
+ const creatorDep = isReader ? null : hmrVersion;
15434
16537
  const nodes = useMemo(() => {
15435
- const state = store.getState();
15436
- const set = store.setState;
15437
16538
  if (creatorOrScope === void 0) {
15438
- return state.nodes;
16539
+ return storeNodes;
15439
16540
  }
15440
16541
  if (typeof creatorOrScope === "string") {
15441
- const scopeData = state.nodes[creatorOrScope];
16542
+ const scopeData = storeNodes[creatorOrScope];
15442
16543
  if (scopeData && !isTSLNode(scopeData)) return scopeData;
15443
16544
  return {};
15444
16545
  }
16546
+ const state = store.getState();
16547
+ const set = store.setState;
15445
16548
  const creator = creatorOrScope;
15446
- const created = creator(state);
16549
+ const wrappedState = createLazyCreatorState(state);
16550
+ const created = creator(wrappedState);
15447
16551
  const result = {};
15448
16552
  let hasNewNodes = false;
15449
16553
  if (scope) {
@@ -15452,7 +16556,7 @@ function useNodes(creatorOrScope, scope) {
15452
16556
  if (currentScope[name]) {
15453
16557
  result[name] = currentScope[name];
15454
16558
  } else {
15455
- if (typeof node.label === "function") node.setName(`${scope}.${name}`);
16559
+ node.setName?.(`${scope}.${name}`);
15456
16560
  result[name] = node;
15457
16561
  hasNewNodes = true;
15458
16562
  }
@@ -15472,7 +16576,7 @@ function useNodes(creatorOrScope, scope) {
15472
16576
  if (existing && isTSLNode(existing)) {
15473
16577
  result[name] = existing;
15474
16578
  } else {
15475
- if (typeof node.label === "function") node.setName(name);
16579
+ node.setName?.(name);
15476
16580
  result[name] = node;
15477
16581
  hasNewNodes = true;
15478
16582
  }
@@ -15481,8 +16585,25 @@ function useNodes(creatorOrScope, scope) {
15481
16585
  set((s) => ({ nodes: { ...s.nodes, ...result } }));
15482
16586
  }
15483
16587
  return result;
15484
- }, [store, typeof creatorOrScope === "string" ? creatorOrScope : scope]);
15485
- return { ...nodes, removeNodes: removeNodes2, clearNodes };
16588
+ }, [store, scopeDep, readerDep, creatorDep]);
16589
+ return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
16590
+ }
16591
+ function rebuildAllNodes(store, scope) {
16592
+ store.setState((state) => {
16593
+ let newNodes = state.nodes;
16594
+ if (scope && scope !== "root") {
16595
+ const { [scope]: _, ...rest } = state.nodes;
16596
+ newNodes = rest;
16597
+ } else if (scope === "root") {
16598
+ newNodes = {};
16599
+ for (const [key, value] of Object.entries(state.nodes)) {
16600
+ if (!isTSLNode(value)) newNodes[key] = value;
16601
+ }
16602
+ } else {
16603
+ newNodes = {};
16604
+ }
16605
+ return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
16606
+ });
15486
16607
  }
15487
16608
  function removeNodes(set, names, scope) {
15488
16609
  set((state) => {
@@ -15516,13 +16637,358 @@ function useLocalNodes(creator) {
15516
16637
  const uniforms = useThree((s) => s.uniforms);
15517
16638
  const nodes = useThree((s) => s.nodes);
15518
16639
  const textures = useThree((s) => s.textures);
16640
+ const hmrVersion = useThree((s) => s._hmrVersion);
15519
16641
  return useMemo(() => {
16642
+ const wrappedState = createLazyCreatorState(store.getState());
16643
+ return creator(wrappedState);
16644
+ }, [store, creator, uniforms, nodes, textures, hmrVersion]);
16645
+ }
16646
+
16647
+ const isBufferLike = (value) => {
16648
+ if (value === null || typeof value !== "object") return false;
16649
+ if (ArrayBuffer.isView(value)) return true;
16650
+ if ("isBufferAttribute" in value) return true;
16651
+ if ("uuid" in value || "nodeType" in value) return true;
16652
+ return false;
16653
+ };
16654
+ const disposeBuffer = (buffer) => {
16655
+ if (buffer === null || typeof buffer !== "object") return;
16656
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
16657
+ buffer.dispose();
16658
+ }
16659
+ };
16660
+ function useBuffers(creatorOrScope, scope) {
16661
+ const store = useStore();
16662
+ const removeBuffers = useCallback(
16663
+ (names, targetScope) => {
16664
+ const nameArray = Array.isArray(names) ? names : [names];
16665
+ store.setState((state) => {
16666
+ if (targetScope) {
16667
+ const currentScope = { ...state.buffers[targetScope] };
16668
+ for (const name of nameArray) delete currentScope[name];
16669
+ return { buffers: { ...state.buffers, [targetScope]: currentScope } };
16670
+ }
16671
+ const buffers2 = { ...state.buffers };
16672
+ for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
16673
+ return { buffers: buffers2 };
16674
+ });
16675
+ },
16676
+ [store]
16677
+ );
16678
+ const clearBuffers = useCallback(
16679
+ (targetScope) => {
16680
+ store.setState((state) => {
16681
+ if (targetScope && targetScope !== "root") {
16682
+ const { [targetScope]: _, ...rest } = state.buffers;
16683
+ return { buffers: rest };
16684
+ }
16685
+ if (targetScope === "root") {
16686
+ const buffers2 = {};
16687
+ for (const [key, value] of Object.entries(state.buffers)) {
16688
+ if (!isBufferLike(value)) buffers2[key] = value;
16689
+ }
16690
+ return { buffers: buffers2 };
16691
+ }
16692
+ return { buffers: {} };
16693
+ });
16694
+ },
16695
+ [store]
16696
+ );
16697
+ const rebuildBuffers = useCallback(
16698
+ (targetScope) => {
16699
+ store.setState((state) => {
16700
+ let newBuffers = state.buffers;
16701
+ if (targetScope && targetScope !== "root") {
16702
+ const { [targetScope]: _, ...rest } = state.buffers;
16703
+ newBuffers = rest;
16704
+ } else if (targetScope === "root") {
16705
+ newBuffers = {};
16706
+ for (const [key, value] of Object.entries(state.buffers)) {
16707
+ if (!isBufferLike(value)) newBuffers[key] = value;
16708
+ }
16709
+ } else {
16710
+ newBuffers = {};
16711
+ }
16712
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16713
+ });
16714
+ },
16715
+ [store]
16716
+ );
16717
+ const disposeBuffers = useCallback(
16718
+ (names, targetScope) => {
16719
+ const nameArray = Array.isArray(names) ? names : [names];
16720
+ const state = store.getState();
16721
+ for (const name of nameArray) {
16722
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
16723
+ if (buffer && isBufferLike(buffer)) {
16724
+ disposeBuffer(buffer);
16725
+ }
16726
+ }
16727
+ removeBuffers(names, targetScope);
16728
+ },
16729
+ [store, removeBuffers]
16730
+ );
16731
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16732
+ const storeBuffers = useThree((s) => s.buffers);
16733
+ const hmrVersion = useThree((s) => s._hmrVersion);
16734
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16735
+ const readerDep = isReader ? storeBuffers : null;
16736
+ const creatorDep = isReader ? null : hmrVersion;
16737
+ const buffers = useMemo(() => {
16738
+ if (creatorOrScope === void 0) {
16739
+ return storeBuffers;
16740
+ }
16741
+ if (typeof creatorOrScope === "string") {
16742
+ const scopeData = storeBuffers[creatorOrScope];
16743
+ if (scopeData && !isBufferLike(scopeData)) return scopeData;
16744
+ return {};
16745
+ }
16746
+ const state = store.getState();
16747
+ const set = store.setState;
16748
+ const creator = creatorOrScope;
16749
+ const wrappedState = createLazyCreatorState(state);
16750
+ const created = creator(wrappedState);
16751
+ const result = {};
16752
+ let hasNewBuffers = false;
16753
+ if (scope) {
16754
+ const currentScope = state.buffers[scope] ?? {};
16755
+ for (const [name, buffer] of Object.entries(created)) {
16756
+ if (currentScope[name]) {
16757
+ result[name] = currentScope[name];
16758
+ } else {
16759
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16760
+ buffer.setName(`${scope}.${name}`);
16761
+ }
16762
+ result[name] = buffer;
16763
+ hasNewBuffers = true;
16764
+ }
16765
+ }
16766
+ if (hasNewBuffers) {
16767
+ set((s) => ({
16768
+ buffers: {
16769
+ ...s.buffers,
16770
+ [scope]: { ...s.buffers[scope], ...result }
16771
+ }
16772
+ }));
16773
+ }
16774
+ return result;
16775
+ }
16776
+ for (const [name, buffer] of Object.entries(created)) {
16777
+ const existing = state.buffers[name];
16778
+ if (existing && isBufferLike(existing)) {
16779
+ result[name] = existing;
16780
+ } else {
16781
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16782
+ buffer.setName(name);
16783
+ }
16784
+ result[name] = buffer;
16785
+ hasNewBuffers = true;
16786
+ }
16787
+ }
16788
+ if (hasNewBuffers) {
16789
+ set((s) => ({ buffers: { ...s.buffers, ...result } }));
16790
+ }
16791
+ return result;
16792
+ }, [store, scopeDep, readerDep, creatorDep]);
16793
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
16794
+ }
16795
+ function rebuildAllBuffers(store, scope) {
16796
+ store.setState((state) => {
16797
+ let newBuffers = state.buffers;
16798
+ if (scope && scope !== "root") {
16799
+ const { [scope]: _, ...rest } = state.buffers;
16800
+ newBuffers = rest;
16801
+ } else if (scope === "root") {
16802
+ newBuffers = {};
16803
+ for (const [key, value] of Object.entries(state.buffers)) {
16804
+ if (!isBufferLike(value)) newBuffers[key] = value;
16805
+ }
16806
+ } else {
16807
+ newBuffers = {};
16808
+ }
16809
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16810
+ });
16811
+ }
16812
+
16813
+ const isStorageLike = (value) => {
16814
+ if (value === null || typeof value !== "object") return false;
16815
+ if ("isTexture" in value) return true;
16816
+ if ("isData3DTexture" in value) return true;
16817
+ if ("uuid" in value || "nodeType" in value) return true;
16818
+ return false;
16819
+ };
16820
+ const disposeStorage = (storage) => {
16821
+ if (storage === null || typeof storage !== "object") return;
16822
+ if ("dispose" in storage && typeof storage.dispose === "function") {
16823
+ storage.dispose();
16824
+ }
16825
+ };
16826
+ function useGPUStorage(creatorOrScope, scope) {
16827
+ const store = useStore();
16828
+ const removeStorage = useCallback(
16829
+ (names, targetScope) => {
16830
+ const nameArray = Array.isArray(names) ? names : [names];
16831
+ store.setState((state) => {
16832
+ if (targetScope) {
16833
+ const currentScope = { ...state.gpuStorage[targetScope] };
16834
+ for (const name of nameArray) delete currentScope[name];
16835
+ return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
16836
+ }
16837
+ const gpuStorage2 = { ...state.gpuStorage };
16838
+ for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
16839
+ return { gpuStorage: gpuStorage2 };
16840
+ });
16841
+ },
16842
+ [store]
16843
+ );
16844
+ const clearStorage = useCallback(
16845
+ (targetScope) => {
16846
+ store.setState((state) => {
16847
+ if (targetScope && targetScope !== "root") {
16848
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16849
+ return { gpuStorage: rest };
16850
+ }
16851
+ if (targetScope === "root") {
16852
+ const gpuStorage2 = {};
16853
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16854
+ if (!isStorageLike(value)) gpuStorage2[key] = value;
16855
+ }
16856
+ return { gpuStorage: gpuStorage2 };
16857
+ }
16858
+ return { gpuStorage: {} };
16859
+ });
16860
+ },
16861
+ [store]
16862
+ );
16863
+ const rebuildStorage = useCallback(
16864
+ (targetScope) => {
16865
+ store.setState((state) => {
16866
+ let newStorage = state.gpuStorage;
16867
+ if (targetScope && targetScope !== "root") {
16868
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16869
+ newStorage = rest;
16870
+ } else if (targetScope === "root") {
16871
+ newStorage = {};
16872
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16873
+ if (!isStorageLike(value)) newStorage[key] = value;
16874
+ }
16875
+ } else {
16876
+ newStorage = {};
16877
+ }
16878
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16879
+ });
16880
+ },
16881
+ [store]
16882
+ );
16883
+ const disposeStorageFn = useCallback(
16884
+ (names, targetScope) => {
16885
+ const nameArray = Array.isArray(names) ? names : [names];
16886
+ const state = store.getState();
16887
+ for (const name of nameArray) {
16888
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
16889
+ if (storage && isStorageLike(storage)) {
16890
+ disposeStorage(storage);
16891
+ }
16892
+ }
16893
+ removeStorage(names, targetScope);
16894
+ },
16895
+ [store, removeStorage]
16896
+ );
16897
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16898
+ const storeStorage = useThree((s) => s.gpuStorage);
16899
+ const hmrVersion = useThree((s) => s._hmrVersion);
16900
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16901
+ const readerDep = isReader ? storeStorage : null;
16902
+ const creatorDep = isReader ? null : hmrVersion;
16903
+ const gpuStorage = useMemo(() => {
16904
+ if (creatorOrScope === void 0) {
16905
+ return storeStorage;
16906
+ }
16907
+ if (typeof creatorOrScope === "string") {
16908
+ const scopeData = storeStorage[creatorOrScope];
16909
+ if (scopeData && !isStorageLike(scopeData)) return scopeData;
16910
+ return {};
16911
+ }
15520
16912
  const state = store.getState();
15521
- return creator(state);
15522
- }, [store, creator, uniforms, nodes, textures]);
16913
+ const set = store.setState;
16914
+ const creator = creatorOrScope;
16915
+ const wrappedState = createLazyCreatorState(state);
16916
+ const created = creator(wrappedState);
16917
+ const result = {};
16918
+ let hasNewStorage = false;
16919
+ if (scope) {
16920
+ const currentScope = state.gpuStorage[scope] ?? {};
16921
+ for (const [name, storage] of Object.entries(created)) {
16922
+ if (currentScope[name]) {
16923
+ result[name] = currentScope[name];
16924
+ } else {
16925
+ if ("setName" in storage && typeof storage.setName === "function") {
16926
+ storage.setName(`${scope}.${name}`);
16927
+ }
16928
+ if ("name" in storage && typeof storage.name === "string") {
16929
+ storage.name = `${scope}.${name}`;
16930
+ }
16931
+ result[name] = storage;
16932
+ hasNewStorage = true;
16933
+ }
16934
+ }
16935
+ if (hasNewStorage) {
16936
+ set((s) => ({
16937
+ gpuStorage: {
16938
+ ...s.gpuStorage,
16939
+ [scope]: { ...s.gpuStorage[scope], ...result }
16940
+ }
16941
+ }));
16942
+ }
16943
+ return result;
16944
+ }
16945
+ for (const [name, storage] of Object.entries(created)) {
16946
+ const existing = state.gpuStorage[name];
16947
+ if (existing && isStorageLike(existing)) {
16948
+ result[name] = existing;
16949
+ } else {
16950
+ if ("setName" in storage && typeof storage.setName === "function") {
16951
+ storage.setName(name);
16952
+ }
16953
+ if ("name" in storage && typeof storage.name === "string") {
16954
+ storage.name = name;
16955
+ }
16956
+ result[name] = storage;
16957
+ hasNewStorage = true;
16958
+ }
16959
+ }
16960
+ if (hasNewStorage) {
16961
+ set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
16962
+ }
16963
+ return result;
16964
+ }, [store, scopeDep, readerDep, creatorDep]);
16965
+ return {
16966
+ ...gpuStorage,
16967
+ removeStorage,
16968
+ clearStorage,
16969
+ rebuildStorage,
16970
+ disposeStorage: disposeStorageFn
16971
+ };
16972
+ }
16973
+ function rebuildAllStorage(store, scope) {
16974
+ store.setState((state) => {
16975
+ let newStorage = state.gpuStorage;
16976
+ if (scope && scope !== "root") {
16977
+ const { [scope]: _, ...rest } = state.gpuStorage;
16978
+ newStorage = rest;
16979
+ } else if (scope === "root") {
16980
+ newStorage = {};
16981
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16982
+ if (!isStorageLike(value)) newStorage[key] = value;
16983
+ }
16984
+ } else {
16985
+ newStorage = {};
16986
+ }
16987
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16988
+ });
15523
16989
  }
15524
16990
 
15525
- function usePostProcessing(mainCB, setupCB) {
16991
+ function useRenderPipeline(mainCB, setupCB) {
15526
16992
  const store = useStore();
15527
16993
  const { scene, camera, renderer, isLegacy } = useThree();
15528
16994
  const callbacksRanRef = useRef(false);
@@ -15532,12 +16998,16 @@ function usePostProcessing(mainCB, setupCB) {
15532
16998
  mainCBRef.current = mainCB;
15533
16999
  setupCBRef.current = setupCB;
15534
17000
  const [rebuildVersion, setRebuildVersion] = useState(0);
17001
+ useEffect(() => {
17002
+ callbacksRanRef.current = false;
17003
+ scenePassCacheRef.current = null;
17004
+ }, []);
15535
17005
  const clearPasses = useCallback(() => {
15536
17006
  store.setState({ passes: {} });
15537
17007
  }, [store]);
15538
17008
  const reset = useCallback(() => {
15539
17009
  store.setState({
15540
- postProcessing: null,
17010
+ renderPipeline: null,
15541
17011
  passes: {}
15542
17012
  });
15543
17013
  callbacksRanRef.current = false;
@@ -15550,13 +17020,13 @@ function usePostProcessing(mainCB, setupCB) {
15550
17020
  }, []);
15551
17021
  useLayoutEffect(() => {
15552
17022
  if (isLegacy) {
15553
- throw new Error("usePostProcessing is only available with WebGPU renderer. Set renderer prop on Canvas.");
17023
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
15554
17024
  }
15555
17025
  if (!renderer || !scene || !camera) return;
15556
17026
  const state = store.getState();
15557
17027
  const set = store.setState;
15558
17028
  try {
15559
- let pp = state.postProcessing;
17029
+ let pp = state.renderPipeline;
15560
17030
  let currentPasses = { ...state.passes };
15561
17031
  let justCreatedPP = false;
15562
17032
  if (!pp) {
@@ -15573,7 +17043,7 @@ function usePostProcessing(mainCB, setupCB) {
15573
17043
  }
15574
17044
  currentPasses.scenePass = scenePass;
15575
17045
  if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
15576
- set({ postProcessing: pp, passes: currentPasses });
17046
+ set({ renderPipeline: pp, passes: currentPasses });
15577
17047
  const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
15578
17048
  if (shouldRunCallbacks) {
15579
17049
  if (setupCBRef.current) {
@@ -15595,22 +17065,22 @@ function usePostProcessing(mainCB, setupCB) {
15595
17065
  callbacksRanRef.current = true;
15596
17066
  }
15597
17067
  } catch (error) {
15598
- console.error("[usePostProcessing] Setup error:", error);
17068
+ console.error("[useRenderPipeline] Setup error:", error);
15599
17069
  }
15600
17070
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
15601
17071
  const passes = useThree((s) => s.passes);
15602
- const postProcessing = useThree((s) => s.postProcessing);
17072
+ const renderPipeline = useThree((s) => s.renderPipeline);
15603
17073
  return {
15604
17074
  passes,
15605
- postProcessing,
17075
+ renderPipeline,
15606
17076
  clearPasses,
15607
17077
  reset,
15608
17078
  rebuild,
15609
- // isReady indicates if PostProcessing is configured and ready for rendering
15610
- isReady: postProcessing !== null
17079
+ // isReady indicates if RenderPipeline is configured and ready for rendering
17080
+ isReady: renderPipeline !== null
15611
17081
  };
15612
17082
  }
15613
17083
 
15614
17084
  extend(THREE);
15615
17085
 
15616
- export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, usePostProcessing, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms };
17086
+ export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, rebuildAllBuffers, rebuildAllNodes, rebuildAllStorage, rebuildAllUniforms, reconciler, registerPrimary, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useBuffers, useEnvironment, useFrame, useGPUStorage, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, useRenderPipeline, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms, waitForPrimary };