@react-three/fiber 10.0.0-alpha.2 → 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.
package/dist/legacy.cjs CHANGED
@@ -5,6 +5,12 @@ const jsxRuntime = require('react/jsx-runtime');
5
5
  const React = require('react');
6
6
  const useMeasure = require('react-use-measure');
7
7
  const itsFine = require('its-fine');
8
+ const fiber = require('@react-three/fiber');
9
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
10
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
11
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
12
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
13
+ const gainmapJs = require('@monogrid/gainmap-js');
8
14
  const Tb = require('scheduler');
9
15
  const traditional = require('zustand/traditional');
10
16
  const suspendReact = require('suspend-react');
@@ -66,6 +72,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
66
72
  WebGPURenderer: WebGPURenderer
67
73
  }, [three__namespace]);
68
74
 
75
+ const primaryRegistry = /* @__PURE__ */ new Map();
76
+ const pendingSubscribers = /* @__PURE__ */ new Map();
77
+ function registerPrimary(id, renderer, store) {
78
+ if (primaryRegistry.has(id)) {
79
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
80
+ }
81
+ const entry = { renderer, store };
82
+ primaryRegistry.set(id, entry);
83
+ const subscribers = pendingSubscribers.get(id);
84
+ if (subscribers) {
85
+ subscribers.forEach((callback) => callback(entry));
86
+ pendingSubscribers.delete(id);
87
+ }
88
+ return () => {
89
+ const currentEntry = primaryRegistry.get(id);
90
+ if (currentEntry?.renderer === renderer) {
91
+ primaryRegistry.delete(id);
92
+ }
93
+ };
94
+ }
95
+ function getPrimary(id) {
96
+ return primaryRegistry.get(id);
97
+ }
98
+ function waitForPrimary(id, timeout = 5e3) {
99
+ const existing = primaryRegistry.get(id);
100
+ if (existing) {
101
+ return Promise.resolve(existing);
102
+ }
103
+ return new Promise((resolve, reject) => {
104
+ const timeoutId = setTimeout(() => {
105
+ const subscribers = pendingSubscribers.get(id);
106
+ if (subscribers) {
107
+ const index = subscribers.indexOf(callback);
108
+ if (index !== -1) subscribers.splice(index, 1);
109
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
110
+ }
111
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
112
+ }, timeout);
113
+ const callback = (entry) => {
114
+ clearTimeout(timeoutId);
115
+ resolve(entry);
116
+ };
117
+ if (!pendingSubscribers.has(id)) {
118
+ pendingSubscribers.set(id, []);
119
+ }
120
+ pendingSubscribers.get(id).push(callback);
121
+ });
122
+ }
123
+ function hasPrimary(id) {
124
+ return primaryRegistry.has(id);
125
+ }
126
+ function unregisterPrimary(id) {
127
+ primaryRegistry.delete(id);
128
+ }
129
+ function getPrimaryIds() {
130
+ return Array.from(primaryRegistry.keys());
131
+ }
132
+
133
+ const presetsObj = {
134
+ apartment: "lebombo_1k.hdr",
135
+ city: "potsdamer_platz_1k.hdr",
136
+ dawn: "kiara_1_dawn_1k.hdr",
137
+ forest: "forest_slope_1k.hdr",
138
+ lobby: "st_fagans_interior_1k.hdr",
139
+ night: "dikhololo_night_1k.hdr",
140
+ park: "rooitou_park_1k.hdr",
141
+ studio: "studio_small_03_1k.hdr",
142
+ sunset: "venice_sunset_1k.hdr",
143
+ warehouse: "empty_warehouse_01_1k.hdr"
144
+ };
145
+
146
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
147
+ const isArray = (arr) => Array.isArray(arr);
148
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
149
+ function useEnvironment({
150
+ files = defaultFiles,
151
+ path = "",
152
+ preset = void 0,
153
+ colorSpace = void 0,
154
+ extensions
155
+ } = {}) {
156
+ if (preset) {
157
+ validatePreset(preset);
158
+ files = presetsObj[preset];
159
+ path = CUBEMAP_ROOT;
160
+ }
161
+ const multiFile = isArray(files);
162
+ const { extension, isCubemap } = getExtension(files);
163
+ const loader = getLoader$1(extension);
164
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
165
+ const renderer = fiber.useThree((state) => state.renderer);
166
+ React.useLayoutEffect(() => {
167
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
168
+ function clearGainmapTexture() {
169
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
170
+ }
171
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
172
+ }, [extension, files, loader, multiFile, renderer.domElement]);
173
+ const loaderResult = fiber.useLoader(
174
+ loader,
175
+ multiFile ? [files] : files,
176
+ (loader2) => {
177
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
178
+ loader2.setRenderer?.(renderer);
179
+ }
180
+ loader2.setPath?.(path);
181
+ if (extensions) extensions(loader2);
182
+ }
183
+ );
184
+ let texture = multiFile ? (
185
+ // @ts-ignore
186
+ loaderResult[0]
187
+ ) : loaderResult;
188
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
189
+ texture = texture.renderTarget?.texture;
190
+ }
191
+ texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
192
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
193
+ return texture;
194
+ }
195
+ const preloadDefaultOptions = {
196
+ files: defaultFiles,
197
+ path: "",
198
+ preset: void 0,
199
+ extensions: void 0
200
+ };
201
+ useEnvironment.preload = (preloadOptions) => {
202
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
203
+ let { files, path = "" } = options;
204
+ const { preset, extensions } = options;
205
+ if (preset) {
206
+ validatePreset(preset);
207
+ files = presetsObj[preset];
208
+ path = CUBEMAP_ROOT;
209
+ }
210
+ const { extension } = getExtension(files);
211
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
212
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
213
+ }
214
+ const loader = getLoader$1(extension);
215
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
216
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
217
+ loader2.setPath?.(path);
218
+ if (extensions) extensions(loader2);
219
+ });
220
+ };
221
+ const clearDefaultOptins = {
222
+ files: defaultFiles,
223
+ preset: void 0
224
+ };
225
+ useEnvironment.clear = (clearOptions) => {
226
+ const options = { ...clearDefaultOptins, ...clearOptions };
227
+ let { files } = options;
228
+ const { preset } = options;
229
+ if (preset) {
230
+ validatePreset(preset);
231
+ files = presetsObj[preset];
232
+ }
233
+ const { extension } = getExtension(files);
234
+ const loader = getLoader$1(extension);
235
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
236
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
237
+ };
238
+ function validatePreset(preset) {
239
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
240
+ }
241
+ function getExtension(files) {
242
+ const isCubemap = isArray(files) && files.length === 6;
243
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
244
+ const firstEntry = isArray(files) ? files[0] : files;
245
+ 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();
246
+ return { extension, isCubemap, isGainmap };
247
+ }
248
+ function getLoader$1(extension) {
249
+ const loader = extension === "cube" ? three.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
250
+ return loader;
251
+ }
252
+
253
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
254
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
255
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
256
+ sceneProps = {
257
+ backgroundBlurriness: 0,
258
+ backgroundIntensity: 1,
259
+ backgroundRotation: [0, 0, 0],
260
+ environmentIntensity: 1,
261
+ environmentRotation: [0, 0, 0],
262
+ ...sceneProps
263
+ };
264
+ const target = resolveScene(scene || defaultScene);
265
+ const oldbg = target.background;
266
+ const oldenv = target.environment;
267
+ const oldSceneProps = {
268
+ // @ts-ignore
269
+ backgroundBlurriness: target.backgroundBlurriness,
270
+ // @ts-ignore
271
+ backgroundIntensity: target.backgroundIntensity,
272
+ // @ts-ignore
273
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
274
+ // @ts-ignore
275
+ environmentIntensity: target.environmentIntensity,
276
+ // @ts-ignore
277
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
278
+ };
279
+ if (background !== "only") target.environment = texture;
280
+ if (background) target.background = texture;
281
+ fiber.applyProps(target, sceneProps);
282
+ return () => {
283
+ if (background !== "only") target.environment = oldenv;
284
+ if (background) target.background = oldbg;
285
+ fiber.applyProps(target, oldSceneProps);
286
+ };
287
+ }
288
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
289
+ const defaultScene = fiber.useThree((state) => state.scene);
290
+ React__namespace.useLayoutEffect(() => {
291
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
292
+ });
293
+ return null;
294
+ }
295
+ function EnvironmentCube({
296
+ background = false,
297
+ scene,
298
+ blur,
299
+ backgroundBlurriness,
300
+ backgroundIntensity,
301
+ backgroundRotation,
302
+ environmentIntensity,
303
+ environmentRotation,
304
+ ...rest
305
+ }) {
306
+ const texture = useEnvironment(rest);
307
+ const defaultScene = fiber.useThree((state) => state.scene);
308
+ React__namespace.useLayoutEffect(() => {
309
+ return setEnvProps(background, scene, defaultScene, texture, {
310
+ backgroundBlurriness: blur ?? backgroundBlurriness,
311
+ backgroundIntensity,
312
+ backgroundRotation,
313
+ environmentIntensity,
314
+ environmentRotation
315
+ });
316
+ });
317
+ React__namespace.useEffect(() => {
318
+ return () => {
319
+ texture.dispose();
320
+ };
321
+ }, [texture]);
322
+ return null;
323
+ }
324
+ function EnvironmentPortal({
325
+ children,
326
+ near = 0.1,
327
+ far = 1e3,
328
+ resolution = 256,
329
+ frames = 1,
330
+ map,
331
+ background = false,
332
+ blur,
333
+ backgroundBlurriness,
334
+ backgroundIntensity,
335
+ backgroundRotation,
336
+ environmentIntensity,
337
+ environmentRotation,
338
+ scene,
339
+ files,
340
+ path,
341
+ preset = void 0,
342
+ extensions
343
+ }) {
344
+ const gl = fiber.useThree((state) => state.gl);
345
+ const defaultScene = fiber.useThree((state) => state.scene);
346
+ const camera = React__namespace.useRef(null);
347
+ const [virtualScene] = React__namespace.useState(() => new three.Scene());
348
+ const fbo = React__namespace.useMemo(() => {
349
+ const fbo2 = new three.WebGLCubeRenderTarget(resolution);
350
+ fbo2.texture.type = three.HalfFloatType;
351
+ return fbo2;
352
+ }, [resolution]);
353
+ React__namespace.useEffect(() => {
354
+ return () => {
355
+ fbo.dispose();
356
+ };
357
+ }, [fbo]);
358
+ React__namespace.useLayoutEffect(() => {
359
+ if (frames === 1) {
360
+ const autoClear = gl.autoClear;
361
+ gl.autoClear = true;
362
+ camera.current.update(gl, virtualScene);
363
+ gl.autoClear = autoClear;
364
+ }
365
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
366
+ backgroundBlurriness: blur ?? backgroundBlurriness,
367
+ backgroundIntensity,
368
+ backgroundRotation,
369
+ environmentIntensity,
370
+ environmentRotation
371
+ });
372
+ }, [
373
+ children,
374
+ virtualScene,
375
+ fbo.texture,
376
+ scene,
377
+ defaultScene,
378
+ background,
379
+ frames,
380
+ gl,
381
+ blur,
382
+ backgroundBlurriness,
383
+ backgroundIntensity,
384
+ backgroundRotation,
385
+ environmentIntensity,
386
+ environmentRotation
387
+ ]);
388
+ let count = 1;
389
+ fiber.useFrame(() => {
390
+ if (frames === Infinity || count < frames) {
391
+ const autoClear = gl.autoClear;
392
+ gl.autoClear = true;
393
+ camera.current.update(gl, virtualScene);
394
+ gl.autoClear = autoClear;
395
+ count++;
396
+ }
397
+ });
398
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
399
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
400
+ children,
401
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
402
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
403
+ ] }),
404
+ virtualScene
405
+ ) });
406
+ }
407
+ function EnvironmentGround(props) {
408
+ const textureDefault = useEnvironment(props);
409
+ const texture = props.map || textureDefault;
410
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
411
+ React__namespace.useEffect(() => {
412
+ return () => {
413
+ textureDefault.dispose();
414
+ };
415
+ }, [textureDefault]);
416
+ const height = props.ground?.height ?? 15;
417
+ const radius = props.ground?.radius ?? 60;
418
+ const scale = props.ground?.scale ?? 1e3;
419
+ const args = React__namespace.useMemo(
420
+ () => [texture, height, radius],
421
+ [texture, height, radius]
422
+ );
423
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
424
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
425
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
426
+ ] });
427
+ }
428
+ function EnvironmentColor({ color, scene }) {
429
+ const defaultScene = fiber.useThree((state) => state.scene);
430
+ React__namespace.useLayoutEffect(() => {
431
+ if (color === void 0) return;
432
+ const target = resolveScene(scene || defaultScene);
433
+ const oldBg = target.background;
434
+ target.background = new three.Color(color);
435
+ return () => {
436
+ target.background = oldBg;
437
+ };
438
+ });
439
+ return null;
440
+ }
441
+ function EnvironmentDualSource(props) {
442
+ const { backgroundFiles, ...envProps } = props;
443
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
444
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
445
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
446
+ ] });
447
+ }
448
+ function Environment(props) {
449
+ if (props.color && !props.files && !props.preset && !props.map) {
450
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
451
+ }
452
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
453
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
454
+ }
455
+ return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
456
+ }
457
+
69
458
  var __defProp$2 = Object.defineProperty;
70
459
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
71
460
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -245,7 +634,8 @@ function prepare(target, root, type, props) {
245
634
  object,
246
635
  eventCount: 0,
247
636
  handlers: {},
248
- isHidden: false
637
+ isHidden: false,
638
+ deferredRefs: []
249
639
  };
250
640
  if (object) object.__r3f = instance;
251
641
  }
@@ -294,7 +684,7 @@ function createOcclusionObserverNode(store, uniform) {
294
684
  let occlusionSetupPromise = null;
295
685
  function enableOcclusion(store) {
296
686
  const state = store.getState();
297
- const { internal, renderer, rootScene } = state;
687
+ const { internal, renderer } = state;
298
688
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
299
689
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
300
690
  if (!hasOcclusionSupport) {
@@ -457,6 +847,22 @@ function hasVisibilityHandlers(handlers) {
457
847
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
458
848
  }
459
849
 
850
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
851
+ function fromRef(ref) {
852
+ return { [FROM_REF]: ref };
853
+ }
854
+ function isFromRef(value) {
855
+ return value !== null && typeof value === "object" && FROM_REF in value;
856
+ }
857
+
858
+ const ONCE = Symbol.for("@react-three/fiber.once");
859
+ function once(...args) {
860
+ return { [ONCE]: args.length ? args : true };
861
+ }
862
+ function isOnce(value) {
863
+ return value !== null && typeof value === "object" && ONCE in value;
864
+ }
865
+
460
866
  const RESERVED_PROPS = [
461
867
  "children",
462
868
  "key",
@@ -527,7 +933,7 @@ function getMemoizedPrototype(root) {
527
933
  ctor = new root.constructor();
528
934
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
529
935
  }
530
- } catch (e) {
936
+ } catch {
531
937
  }
532
938
  return ctor;
533
939
  }
@@ -573,6 +979,25 @@ function applyProps(object, props) {
573
979
  continue;
574
980
  }
575
981
  if (value === void 0) continue;
982
+ if (isFromRef(value)) {
983
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
984
+ continue;
985
+ }
986
+ if (isOnce(value)) {
987
+ if (instance?.appliedOnce?.has(prop)) continue;
988
+ if (instance) {
989
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
990
+ instance.appliedOnce.add(prop);
991
+ }
992
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
993
+ const args = value[ONCE];
994
+ if (typeof targetRoot[targetKey] === "function") {
995
+ targetRoot[targetKey](...args === true ? [] : args);
996
+ } else if (args !== true && args.length > 0) {
997
+ targetRoot[targetKey] = args[0];
998
+ }
999
+ continue;
1000
+ }
576
1001
  let { root, key, target } = resolve(object, prop);
577
1002
  if (target === void 0 && (typeof root !== "object" || root === null)) {
578
1003
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -595,7 +1020,10 @@ function applyProps(object, props) {
595
1020
  else target.set(value);
596
1021
  } else {
597
1022
  root[key] = value;
598
- 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
1023
+ if (key.endsWith("Node") && root.isMaterial) {
1024
+ root.needsUpdate = true;
1025
+ }
1026
+ if (rootState && rootState.renderer?.outputColorSpace === three.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
599
1027
  root[key].format === three.RGBAFormat && root[key].type === three.UnsignedByteType) {
600
1028
  root[key].colorSpace = rootState.textureColorSpace;
601
1029
  }
@@ -628,38 +1056,60 @@ function applyProps(object, props) {
628
1056
  return object;
629
1057
  }
630
1058
 
1059
+ const DEFAULT_POINTER_ID = 0;
1060
+ const XR_POINTER_ID_START = 1e3;
1061
+ function getPointerState(internal, pointerId) {
1062
+ let state = internal.pointerMap.get(pointerId);
1063
+ if (!state) {
1064
+ state = {
1065
+ hovered: /* @__PURE__ */ new Map(),
1066
+ captured: /* @__PURE__ */ new Map(),
1067
+ initialClick: [0, 0],
1068
+ initialHits: []
1069
+ };
1070
+ internal.pointerMap.set(pointerId, state);
1071
+ }
1072
+ return state;
1073
+ }
1074
+ function getPointerId(event) {
1075
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1076
+ }
631
1077
  function makeId(event) {
632
1078
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
633
1079
  }
634
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
635
- const captureData = captures.get(obj);
1080
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1081
+ const pointerState = internal.pointerMap.get(pointerId);
1082
+ if (!pointerState) return;
1083
+ const captureData = pointerState.captured.get(obj);
636
1084
  if (captureData) {
637
- captures.delete(obj);
638
- if (captures.size === 0) {
639
- capturedMap.delete(pointerId);
640
- captureData.target.releasePointerCapture(pointerId);
641
- }
1085
+ pointerState.captured.delete(obj);
1086
+ captureData.target.releasePointerCapture(pointerId);
642
1087
  }
643
1088
  }
644
1089
  function removeInteractivity(store, object) {
645
1090
  const { internal } = store.getState();
646
1091
  internal.interaction = internal.interaction.filter((o) => o !== object);
647
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
648
- internal.hovered.forEach((value, key) => {
649
- if (value.eventObject === object || value.object === object) {
650
- internal.hovered.delete(key);
1092
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1093
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1094
+ pointerState.hovered.forEach((value, key) => {
1095
+ if (value.eventObject === object || value.object === object) {
1096
+ pointerState.hovered.delete(key);
1097
+ }
1098
+ });
1099
+ if (pointerState.captured.has(object)) {
1100
+ releaseInternalPointerCapture(internal, object, pointerId);
651
1101
  }
652
- });
653
- internal.capturedMap.forEach((captures, pointerId) => {
654
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
655
- });
1102
+ }
656
1103
  unregisterVisibility(store, object);
657
1104
  }
658
1105
  function createEvents(store) {
659
- function calculateDistance(event) {
1106
+ function calculateDistance(event, pointerId) {
660
1107
  const { internal } = store.getState();
661
- const dx = event.offsetX - internal.initialClick[0];
662
- const dy = event.offsetY - internal.initialClick[1];
1108
+ const pointerState = internal.pointerMap.get(pointerId);
1109
+ if (!pointerState) return 0;
1110
+ const [initialX, initialY] = pointerState.initialClick;
1111
+ const dx = event.offsetX - initialX;
1112
+ const dy = event.offsetY - initialY;
663
1113
  return Math.round(Math.sqrt(dx * dx + dy * dy));
664
1114
  }
665
1115
  function filterPointerEvents(objects) {
@@ -695,6 +1145,15 @@ function createEvents(store) {
695
1145
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
696
1146
  }
697
1147
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1148
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1149
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1150
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1151
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1152
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1153
+ if (aInteractivePriority !== bInteractivePriority) {
1154
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1155
+ }
1156
+ }
698
1157
  const aState = getRootState(a.object);
699
1158
  const bState = getRootState(b.object);
700
1159
  const aPriority = aState?.events?.priority ?? 1;
@@ -716,9 +1175,13 @@ function createEvents(store) {
716
1175
  eventObject = eventObject.parent;
717
1176
  }
718
1177
  }
719
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
720
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
721
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1178
+ if ("pointerId" in event) {
1179
+ const pointerId = event.pointerId;
1180
+ const pointerState = state.internal.pointerMap.get(pointerId);
1181
+ if (pointerState?.captured.size) {
1182
+ for (const captureData of pointerState.captured.values()) {
1183
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1184
+ }
722
1185
  }
723
1186
  }
724
1187
  return intersections;
@@ -731,27 +1194,25 @@ function createEvents(store) {
731
1194
  if (state) {
732
1195
  const { raycaster, pointer, camera, internal } = state;
733
1196
  const unprojectedPoint = new three.Vector3(pointer.x, pointer.y, 0).unproject(camera);
734
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1197
+ const hasPointerCapture = (id) => {
1198
+ const pointerState = internal.pointerMap.get(id);
1199
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1200
+ };
735
1201
  const setPointerCapture = (id) => {
736
1202
  const captureData = { intersection: hit, target: event.target };
737
- if (internal.capturedMap.has(id)) {
738
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
739
- } else {
740
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
741
- }
1203
+ const pointerState = getPointerState(internal, id);
1204
+ pointerState.captured.set(hit.eventObject, captureData);
742
1205
  event.target.setPointerCapture(id);
743
1206
  };
744
1207
  const releasePointerCapture = (id) => {
745
- const captures = internal.capturedMap.get(id);
746
- if (captures) {
747
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
748
- }
1208
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
749
1209
  };
750
1210
  const extractEventProps = {};
751
1211
  for (const prop in event) {
752
1212
  const property = event[prop];
753
1213
  if (typeof property !== "function") extractEventProps[prop] = property;
754
1214
  }
1215
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
755
1216
  const raycastEvent = {
756
1217
  ...hit,
757
1218
  ...extractEventProps,
@@ -762,18 +1223,19 @@ function createEvents(store) {
762
1223
  unprojectedPoint,
763
1224
  ray: raycaster.ray,
764
1225
  camera,
1226
+ pointerId: eventPointerId,
765
1227
  // Hijack stopPropagation, which just sets a flag
766
1228
  stopPropagation() {
767
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1229
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
768
1230
  if (
769
1231
  // ...if this pointer hasn't been captured
770
- !capturesForPointer || // ... or if the hit object is capturing the pointer
771
- capturesForPointer.has(hit.eventObject)
1232
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1233
+ pointerState.captured.has(hit.eventObject)
772
1234
  ) {
773
1235
  raycastEvent.stopped = localState.stopped = true;
774
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1236
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
775
1237
  const higher = intersections.slice(0, intersections.indexOf(hit));
776
- cancelPointer([...higher, hit]);
1238
+ cancelPointer([...higher, hit], eventPointerId);
777
1239
  }
778
1240
  }
779
1241
  },
@@ -789,15 +1251,18 @@ function createEvents(store) {
789
1251
  }
790
1252
  return intersections;
791
1253
  }
792
- function cancelPointer(intersections) {
1254
+ function cancelPointer(intersections, pointerId) {
793
1255
  const { internal } = store.getState();
794
- for (const hoveredObj of internal.hovered.values()) {
1256
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1257
+ const pointerState = internal.pointerMap.get(pid);
1258
+ if (!pointerState) return;
1259
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
795
1260
  if (!intersections.length || !intersections.find(
796
1261
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
797
1262
  )) {
798
1263
  const eventObject = hoveredObj.eventObject;
799
1264
  const instance = eventObject.__r3f;
800
- internal.hovered.delete(makeId(hoveredObj));
1265
+ pointerState.hovered.delete(hoveredId);
801
1266
  if (instance?.eventCount) {
802
1267
  const handlers = instance.handlers;
803
1268
  const data = { ...hoveredObj, intersections };
@@ -826,41 +1291,118 @@ function createEvents(store) {
826
1291
  instance?.handlers.onDropMissed?.(event);
827
1292
  }
828
1293
  }
1294
+ function cleanupPointer(pointerId) {
1295
+ const { internal } = store.getState();
1296
+ const pointerState = internal.pointerMap.get(pointerId);
1297
+ if (pointerState) {
1298
+ for (const [, hoveredObj] of pointerState.hovered) {
1299
+ const eventObject = hoveredObj.eventObject;
1300
+ const instance = eventObject.__r3f;
1301
+ if (instance?.eventCount) {
1302
+ const handlers = instance.handlers;
1303
+ const data = { ...hoveredObj, intersections: [] };
1304
+ handlers.onPointerOut?.(data);
1305
+ handlers.onPointerLeave?.(data);
1306
+ }
1307
+ }
1308
+ internal.pointerMap.delete(pointerId);
1309
+ }
1310
+ internal.pointerDirty.delete(pointerId);
1311
+ }
1312
+ function processDeferredPointer(event, pointerId) {
1313
+ const state = store.getState();
1314
+ const { internal } = state;
1315
+ if (!state.events.enabled) return;
1316
+ const filter = filterPointerEvents;
1317
+ const hits = intersect(event, filter);
1318
+ cancelPointer(hits, pointerId);
1319
+ function onIntersect(data) {
1320
+ const eventObject = data.eventObject;
1321
+ const instance = eventObject.__r3f;
1322
+ if (!instance?.eventCount) return;
1323
+ const handlers = instance.handlers;
1324
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1325
+ const id = makeId(data);
1326
+ const pointerState = getPointerState(internal, pointerId);
1327
+ const hoveredItem = pointerState.hovered.get(id);
1328
+ if (!hoveredItem) {
1329
+ pointerState.hovered.set(id, data);
1330
+ handlers.onPointerOver?.(data);
1331
+ handlers.onPointerEnter?.(data);
1332
+ } else if (hoveredItem.stopped) {
1333
+ data.stopPropagation();
1334
+ }
1335
+ }
1336
+ handlers.onPointerMove?.(data);
1337
+ }
1338
+ handleIntersects(hits, event, 0, onIntersect);
1339
+ }
829
1340
  function handlePointer(name) {
830
1341
  switch (name) {
831
1342
  case "onPointerLeave":
832
- case "onPointerCancel":
833
1343
  case "onDragLeave":
834
1344
  return () => cancelPointer([]);
1345
+ // Global cancel of these events
1346
+ case "onPointerCancel":
1347
+ return (event) => {
1348
+ const pointerId = getPointerId(event);
1349
+ cleanupPointer(pointerId);
1350
+ };
835
1351
  case "onLostPointerCapture":
836
1352
  return (event) => {
837
1353
  const { internal } = store.getState();
838
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1354
+ const pointerId = getPointerId(event);
1355
+ const pointerState = internal.pointerMap.get(pointerId);
1356
+ if (pointerState?.captured.size) {
839
1357
  requestAnimationFrame(() => {
840
- if (internal.capturedMap.has(event.pointerId)) {
841
- internal.capturedMap.delete(event.pointerId);
842
- cancelPointer([]);
1358
+ const pointerState2 = internal.pointerMap.get(pointerId);
1359
+ if (pointerState2?.captured.size) {
1360
+ pointerState2.captured.clear();
843
1361
  }
1362
+ cancelPointer([], pointerId);
844
1363
  });
845
1364
  }
846
1365
  };
847
1366
  }
848
1367
  return function handleEvent(event) {
849
1368
  const state = store.getState();
850
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1369
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1370
+ const pointerId = getPointerId(event);
851
1371
  internal.lastEvent.current = event;
852
- if (!state.events.enabled) return;
1372
+ if (!events.enabled) return;
853
1373
  const isPointerMove = name === "onPointerMove";
854
1374
  const isDragOver = name === "onDragOver";
855
1375
  const isDrop = name === "onDrop";
856
1376
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1377
+ const isPointerDown = name === "onPointerDown";
1378
+ const isPointerUp = name === "onPointerUp";
1379
+ const isWheel = name === "onWheel";
1380
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1381
+ if (isPointerMove && canDeferRaycasts) {
1382
+ events.compute?.(event, state);
1383
+ internal.pointerDirty.set(pointerId, event);
1384
+ return;
1385
+ }
1386
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1387
+ events.compute?.(event, state);
1388
+ internal.pointerDirty.set(pointerId, event);
1389
+ return;
1390
+ }
1391
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1392
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1393
+ internal.pointerDirty.delete(pointerId);
1394
+ processDeferredPointer(deferredEvent, pointerId);
1395
+ }
857
1396
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
858
1397
  const hits = intersect(event, filter);
859
- const delta = isClickEvent ? calculateDistance(event) : 0;
860
- if (name === "onPointerDown") {
861
- internal.initialClick = [event.offsetX, event.offsetY];
862
- internal.initialHits = hits.map((hit) => hit.eventObject);
863
- }
1398
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1399
+ if (isPointerDown) {
1400
+ const pointerState2 = getPointerState(internal, pointerId);
1401
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1402
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1403
+ }
1404
+ const pointerState = internal.pointerMap.get(pointerId);
1405
+ const initialHits = pointerState?.initialHits ?? [];
864
1406
  if (isClickEvent && !hits.length) {
865
1407
  if (delta <= 2) {
866
1408
  pointerMissed(event, internal.interaction);
@@ -875,7 +1417,9 @@ function createEvents(store) {
875
1417
  dropMissed(event, internal.interaction);
876
1418
  if (onDropMissed) onDropMissed(event);
877
1419
  }
878
- if (isPointerMove || isDragOver) cancelPointer(hits);
1420
+ if (isPointerMove || isDragOver) {
1421
+ cancelPointer(hits, pointerId);
1422
+ }
879
1423
  function onIntersect(data) {
880
1424
  const eventObject = data.eventObject;
881
1425
  const instance = eventObject.__r3f;
@@ -884,9 +1428,10 @@ function createEvents(store) {
884
1428
  if (isPointerMove) {
885
1429
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
886
1430
  const id = makeId(data);
887
- const hoveredItem = internal.hovered.get(id);
1431
+ const pointerState2 = getPointerState(internal, pointerId);
1432
+ const hoveredItem = pointerState2.hovered.get(id);
888
1433
  if (!hoveredItem) {
889
- internal.hovered.set(id, data);
1434
+ pointerState2.hovered.set(id, data);
890
1435
  handlers.onPointerOver?.(data);
891
1436
  handlers.onPointerEnter?.(data);
892
1437
  } else if (hoveredItem.stopped) {
@@ -896,9 +1441,10 @@ function createEvents(store) {
896
1441
  handlers.onPointerMove?.(data);
897
1442
  } else if (isDragOver) {
898
1443
  const id = makeId(data);
899
- const hoveredItem = internal.hovered.get(id);
1444
+ const pointerState2 = getPointerState(internal, pointerId);
1445
+ const hoveredItem = pointerState2.hovered.get(id);
900
1446
  if (!hoveredItem) {
901
- internal.hovered.set(id, data);
1447
+ pointerState2.hovered.set(id, data);
902
1448
  handlers.onDragOverEnter?.(data);
903
1449
  } else if (hoveredItem.stopped) {
904
1450
  data.stopPropagation();
@@ -909,18 +1455,18 @@ function createEvents(store) {
909
1455
  } else {
910
1456
  const handler = handlers[name];
911
1457
  if (handler) {
912
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1458
+ if (!isClickEvent || initialHits.includes(eventObject)) {
913
1459
  pointerMissed(
914
1460
  event,
915
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1461
+ internal.interaction.filter((object) => !initialHits.includes(object))
916
1462
  );
917
1463
  handler(data);
918
1464
  }
919
1465
  } else {
920
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1466
+ if (isClickEvent && initialHits.includes(eventObject)) {
921
1467
  pointerMissed(
922
1468
  event,
923
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1469
+ internal.interaction.filter((object) => !initialHits.includes(object))
924
1470
  );
925
1471
  }
926
1472
  }
@@ -929,7 +1475,15 @@ function createEvents(store) {
929
1475
  handleIntersects(hits, event, delta, onIntersect);
930
1476
  };
931
1477
  }
932
- return { handlePointer };
1478
+ function flushDeferredPointers() {
1479
+ const { internal, events } = store.getState();
1480
+ if (!events.frameTimedRaycasts) return;
1481
+ for (const [pointerId, event] of internal.pointerDirty) {
1482
+ processDeferredPointer(event, pointerId);
1483
+ }
1484
+ internal.pointerDirty.clear();
1485
+ }
1486
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
933
1487
  }
934
1488
  const DOM_EVENTS = {
935
1489
  onClick: ["click", false],
@@ -948,11 +1502,16 @@ const DOM_EVENTS = {
948
1502
  onLostPointerCapture: ["lostpointercapture", true]
949
1503
  };
950
1504
  function createPointerEvents(store) {
951
- const { handlePointer } = createEvents(store);
1505
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1506
+ let nextXRPointerId = XR_POINTER_ID_START;
1507
+ const xrPointers = /* @__PURE__ */ new Map();
952
1508
  return {
953
1509
  priority: 1,
954
1510
  enabled: true,
955
- compute(event, state, previous) {
1511
+ frameTimedRaycasts: true,
1512
+ alwaysFireOnScroll: true,
1513
+ updateOnFrame: false,
1514
+ compute(event, state) {
956
1515
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
957
1516
  state.raycaster.setFromCamera(state.pointer, state.camera);
958
1517
  },
@@ -961,11 +1520,33 @@ function createPointerEvents(store) {
961
1520
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
962
1521
  {}
963
1522
  ),
964
- update: () => {
1523
+ update: (pointerId) => {
1524
+ const { events, internal } = store.getState();
1525
+ if (!events.handlers) return;
1526
+ if (pointerId !== void 0) {
1527
+ const event = internal.pointerDirty.get(pointerId);
1528
+ if (event) {
1529
+ internal.pointerDirty.delete(pointerId);
1530
+ processDeferredPointer(event, pointerId);
1531
+ } else if (internal.lastEvent?.current) {
1532
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1533
+ }
1534
+ } else {
1535
+ flushDeferredPointers();
1536
+ if (internal.lastEvent?.current) {
1537
+ events.handlers.onPointerMove(internal.lastEvent.current);
1538
+ }
1539
+ }
1540
+ },
1541
+ flush: () => {
965
1542
  const { events, internal } = store.getState();
966
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1543
+ flushDeferredPointers();
1544
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1545
+ events.handlers.onPointerMove(internal.lastEvent.current);
1546
+ }
967
1547
  },
968
1548
  connect: (target) => {
1549
+ if (!target) return;
969
1550
  const { set, events } = store.getState();
970
1551
  events.disconnect?.();
971
1552
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -989,6 +1570,32 @@ function createPointerEvents(store) {
989
1570
  }
990
1571
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
991
1572
  }
1573
+ },
1574
+ registerPointer: (config) => {
1575
+ const pointerId = nextXRPointerId++;
1576
+ xrPointers.set(pointerId, config);
1577
+ const { internal } = store.getState();
1578
+ getPointerState(internal, pointerId);
1579
+ return pointerId;
1580
+ },
1581
+ unregisterPointer: (pointerId) => {
1582
+ xrPointers.delete(pointerId);
1583
+ const { internal } = store.getState();
1584
+ const pointerState = internal.pointerMap.get(pointerId);
1585
+ if (pointerState) {
1586
+ for (const [, hoveredObj] of pointerState.hovered) {
1587
+ const eventObject = hoveredObj.eventObject;
1588
+ const instance = eventObject.__r3f;
1589
+ if (instance?.eventCount) {
1590
+ const handlers = instance.handlers;
1591
+ const data = { ...hoveredObj, intersections: [] };
1592
+ handlers.onPointerOut?.(data);
1593
+ handlers.onPointerLeave?.(data);
1594
+ }
1595
+ }
1596
+ internal.pointerMap.delete(pointerId);
1597
+ }
1598
+ internal.pointerDirty.delete(pointerId);
992
1599
  }
993
1600
  };
994
1601
  }
@@ -1050,331 +1657,26 @@ function notifyAlpha({ message, link }) {
1050
1657
  }
1051
1658
  }
1052
1659
 
1053
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1054
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1055
- const createStore = (invalidate, advance) => {
1056
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1057
- const position = new three.Vector3();
1058
- const defaultTarget = new three.Vector3();
1059
- const tempTarget = new three.Vector3();
1060
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1061
- const { width, height, top, left } = size;
1062
- const aspect = width / height;
1063
- if (target.isVector3) tempTarget.copy(target);
1064
- else tempTarget.set(...target);
1065
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1066
- if (isOrthographicCamera(camera)) {
1067
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1068
- } else {
1069
- const fov = camera.fov * Math.PI / 180;
1070
- const h = 2 * Math.tan(fov / 2) * distance;
1071
- const w = h * (width / height);
1072
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1073
- }
1074
- }
1075
- let performanceTimeout = void 0;
1076
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1077
- const pointer = new three.Vector2();
1078
- const rootState = {
1079
- set,
1080
- get,
1081
- // Mock objects that have to be configured
1082
- gl: null,
1083
- renderer: null,
1084
- camera: null,
1085
- frustum: new three.Frustum(),
1086
- autoUpdateFrustum: true,
1087
- raycaster: null,
1088
- events: { priority: 1, enabled: true, connected: false },
1089
- scene: null,
1090
- rootScene: null,
1091
- xr: null,
1092
- inspector: null,
1093
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1094
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1095
- legacy: false,
1096
- linear: false,
1097
- flat: false,
1098
- textureColorSpace: "srgb",
1099
- isLegacy: false,
1100
- webGPUSupported: false,
1101
- isNative: false,
1102
- controls: null,
1103
- pointer,
1104
- mouse: pointer,
1105
- frameloop: "always",
1106
- onPointerMissed: void 0,
1107
- onDragOverMissed: void 0,
1108
- onDropMissed: void 0,
1109
- performance: {
1110
- current: 1,
1111
- min: 0.5,
1112
- max: 1,
1113
- debounce: 200,
1114
- regress: () => {
1115
- const state2 = get();
1116
- if (performanceTimeout) clearTimeout(performanceTimeout);
1117
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1118
- performanceTimeout = setTimeout(
1119
- () => setPerformanceCurrent(get().performance.max),
1120
- state2.performance.debounce
1121
- );
1122
- }
1123
- },
1124
- size: { width: 0, height: 0, top: 0, left: 0 },
1125
- viewport: {
1126
- initialDpr: 0,
1127
- dpr: 0,
1128
- width: 0,
1129
- height: 0,
1130
- top: 0,
1131
- left: 0,
1132
- aspect: 0,
1133
- distance: 0,
1134
- factor: 0,
1135
- getCurrentViewport
1136
- },
1137
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1138
- setSize: (width, height, top, left) => {
1139
- const state2 = get();
1140
- if (width === void 0) {
1141
- set({ _sizeImperative: false });
1142
- if (state2._sizeProps) {
1143
- const { width: propW, height: propH } = state2._sizeProps;
1144
- if (propW !== void 0 || propH !== void 0) {
1145
- const currentSize = state2.size;
1146
- const newSize = {
1147
- width: propW ?? currentSize.width,
1148
- height: propH ?? currentSize.height,
1149
- top: currentSize.top,
1150
- left: currentSize.left
1151
- };
1152
- set((s) => ({
1153
- size: newSize,
1154
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1155
- }));
1156
- }
1157
- }
1158
- return;
1159
- }
1160
- const w = width;
1161
- const h = height ?? width;
1162
- const t = top ?? state2.size.top;
1163
- const l = left ?? state2.size.left;
1164
- const size = { width: w, height: h, top: t, left: l };
1165
- set((s) => ({
1166
- size,
1167
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1168
- _sizeImperative: true
1169
- }));
1170
- },
1171
- setDpr: (dpr) => set((state2) => {
1172
- const resolved = calculateDpr(dpr);
1173
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1174
- }),
1175
- setFrameloop: (frameloop = "always") => {
1176
- set(() => ({ frameloop }));
1177
- },
1178
- setError: (error) => set(() => ({ error })),
1179
- error: null,
1180
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1181
- uniforms: {},
1182
- nodes: {},
1183
- textures: /* @__PURE__ */ new Map(),
1184
- postProcessing: null,
1185
- passes: {},
1186
- _hmrVersion: 0,
1187
- _sizeImperative: false,
1188
- _sizeProps: null,
1189
- previousRoot: void 0,
1190
- internal: {
1191
- // Events
1192
- interaction: [],
1193
- hovered: /* @__PURE__ */ new Map(),
1194
- subscribers: [],
1195
- initialClick: [0, 0],
1196
- initialHits: [],
1197
- capturedMap: /* @__PURE__ */ new Map(),
1198
- lastEvent: React__namespace.createRef(),
1199
- // Visibility tracking (onFramed, onOccluded, onVisible)
1200
- visibilityRegistry: /* @__PURE__ */ new Map(),
1201
- // Occlusion system (WebGPU only)
1202
- occlusionEnabled: false,
1203
- occlusionObserver: null,
1204
- occlusionCache: /* @__PURE__ */ new Map(),
1205
- helperGroup: null,
1206
- // Updates
1207
- active: false,
1208
- frames: 0,
1209
- priority: 0,
1210
- subscribe: (ref, priority, store) => {
1211
- const internal = get().internal;
1212
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1213
- internal.subscribers.push({ ref, priority, store });
1214
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1215
- return () => {
1216
- const internal2 = get().internal;
1217
- if (internal2?.subscribers) {
1218
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1219
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1220
- }
1221
- };
1222
- },
1223
- // Renderer Storage (single source of truth)
1224
- actualRenderer: null,
1225
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1226
- scheduler: null
1227
- }
1228
- };
1229
- return rootState;
1230
- });
1231
- const state = rootStore.getState();
1232
- Object.defineProperty(state, "gl", {
1233
- get() {
1234
- const currentState = rootStore.getState();
1235
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1236
- const stack = new Error().stack || "";
1237
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1238
- if (!isInternalAccess) {
1239
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1240
- notifyDepreciated({
1241
- heading: "Accessing state.gl in WebGPU mode",
1242
- 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
1243
- });
1244
- }
1245
- }
1246
- return currentState.internal.actualRenderer;
1247
- },
1248
- set(value) {
1249
- rootStore.getState().internal.actualRenderer = value;
1250
- },
1251
- enumerable: true,
1252
- configurable: true
1253
- });
1254
- Object.defineProperty(state, "renderer", {
1255
- get() {
1256
- return rootStore.getState().internal.actualRenderer;
1257
- },
1258
- set(value) {
1259
- rootStore.getState().internal.actualRenderer = value;
1260
- },
1261
- enumerable: true,
1262
- configurable: true
1263
- });
1264
- let oldScene = state.scene;
1265
- rootStore.subscribe(() => {
1266
- const currentState = rootStore.getState();
1267
- const { scene, rootScene, set } = currentState;
1268
- if (scene !== oldScene) {
1269
- oldScene = scene;
1270
- if (scene?.isScene && scene !== rootScene) {
1271
- set({ rootScene: scene });
1272
- }
1273
- }
1274
- });
1275
- let oldSize = state.size;
1276
- let oldDpr = state.viewport.dpr;
1277
- let oldCamera = state.camera;
1278
- rootStore.subscribe(() => {
1279
- const { camera, size, viewport, set, internal } = rootStore.getState();
1280
- const actualRenderer = internal.actualRenderer;
1281
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1282
- oldSize = size;
1283
- oldDpr = viewport.dpr;
1284
- updateCamera(camera, size);
1285
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1286
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1287
- actualRenderer.setSize(size.width, size.height, updateStyle);
1288
- }
1289
- if (camera !== oldCamera) {
1290
- oldCamera = camera;
1291
- const { rootScene } = rootStore.getState();
1292
- if (camera && rootScene && !camera.parent) {
1293
- rootScene.add(camera);
1294
- }
1295
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1296
- const currentState = rootStore.getState();
1297
- if (currentState.autoUpdateFrustum && camera) {
1298
- updateFrustum(camera, currentState.frustum);
1299
- }
1300
- }
1301
- });
1302
- rootStore.subscribe((state2) => invalidate(state2));
1303
- return rootStore;
1304
- };
1305
-
1306
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1307
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1308
- function getLoader(Proto) {
1309
- if (isConstructor$1(Proto)) {
1310
- let loader = memoizedLoaders.get(Proto);
1311
- if (!loader) {
1312
- loader = new Proto();
1313
- memoizedLoaders.set(Proto, loader);
1314
- }
1315
- return loader;
1316
- }
1317
- return Proto;
1318
- }
1319
- function loadingFn(extensions, onProgress) {
1320
- return function(Proto, input) {
1321
- const loader = getLoader(Proto);
1322
- if (extensions) extensions(loader);
1323
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1324
- return loader.loadAsync(input, onProgress).then((data) => {
1325
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1326
- return data;
1327
- });
1328
- }
1329
- return new Promise(
1330
- (res, reject) => loader.load(
1331
- input,
1332
- (data) => {
1333
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1334
- res(data);
1335
- },
1336
- onProgress,
1337
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1338
- )
1339
- );
1340
- };
1341
- }
1342
- function useLoader(loader, input, extensions, onProgress) {
1343
- const keys = Array.isArray(input) ? input : [input];
1344
- const fn = loadingFn(extensions, onProgress);
1345
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1346
- return Array.isArray(input) ? results : results[0];
1347
- }
1348
- useLoader.preload = function(loader, input, extensions, onProgress) {
1349
- const keys = Array.isArray(input) ? input : [input];
1350
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1351
- };
1352
- useLoader.clear = function(loader, input) {
1353
- const keys = Array.isArray(input) ? input : [input];
1354
- keys.forEach((key) => suspendReact.clear([loader, key]));
1355
- };
1356
- useLoader.loader = getLoader;
1357
-
1358
- var __defProp$1 = Object.defineProperty;
1359
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1360
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1361
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1362
- class PhaseGraph {
1363
- constructor() {
1364
- /** Ordered list of phase nodes */
1365
- __publicField$1(this, "phases", []);
1366
- /** Quick lookup by name */
1367
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1368
- /** Cached ordered names (invalidated on changes) */
1369
- __publicField$1(this, "orderedNamesCache", null);
1370
- this.initializeDefaultPhases();
1371
- }
1372
- //* Initialization --------------------------------
1373
- initializeDefaultPhases() {
1374
- for (const name of DEFAULT_PHASES) {
1375
- const node = { name, isAutoGenerated: false };
1376
- this.phases.push(node);
1377
- this.phaseMap.set(name, node);
1660
+ var __defProp$1 = Object.defineProperty;
1661
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1662
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1663
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1664
+ class PhaseGraph {
1665
+ constructor() {
1666
+ /** Ordered list of phase nodes */
1667
+ __publicField$1(this, "phases", []);
1668
+ /** Quick lookup by name */
1669
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1670
+ /** Cached ordered names (invalidated on changes) */
1671
+ __publicField$1(this, "orderedNamesCache", null);
1672
+ this.initializeDefaultPhases();
1673
+ }
1674
+ //* Initialization --------------------------------
1675
+ initializeDefaultPhases() {
1676
+ for (const name of DEFAULT_PHASES) {
1677
+ const node = { name, isAutoGenerated: false };
1678
+ this.phases.push(node);
1679
+ this.phaseMap.set(name, node);
1378
1680
  }
1379
1681
  this.invalidateCache();
1380
1682
  }
@@ -1607,7 +1909,7 @@ function shouldRun(job, now) {
1607
1909
  const minInterval = 1e3 / job.fps;
1608
1910
  const lastRun = job.lastRun ?? 0;
1609
1911
  const elapsed = now - lastRun;
1610
- if (elapsed < minInterval) return false;
1912
+ if (elapsed < minInterval - 1) return false;
1611
1913
  if (job.drop) {
1612
1914
  job.lastRun = now;
1613
1915
  } else {
@@ -2276,116 +2578,444 @@ const _Scheduler = class _Scheduler {
2276
2578
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2277
2579
  root.needsRebuild = false;
2278
2580
  }
2279
- const providedState = root.getState?.() ?? {};
2280
- const frameState = {
2281
- ...providedState,
2282
- time: timestamp,
2283
- delta,
2284
- elapsed: this.loopState.elapsedTime / 1e3,
2285
- // Convert ms to seconds
2286
- frame: this.loopState.frameCount
2287
- };
2288
- for (const job of root.sortedJobs) {
2289
- if (!shouldRun(job, timestamp)) continue;
2290
- try {
2291
- job.callback(frameState, delta);
2292
- } catch (error) {
2293
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2294
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
2581
+ const providedState = root.getState?.() ?? {};
2582
+ const frameState = {
2583
+ ...providedState,
2584
+ time: timestamp,
2585
+ delta,
2586
+ elapsed: this.loopState.elapsedTime / 1e3,
2587
+ // Convert ms to seconds
2588
+ frame: this.loopState.frameCount
2589
+ };
2590
+ for (const job of root.sortedJobs) {
2591
+ if (!shouldRun(job, timestamp)) continue;
2592
+ try {
2593
+ job.callback(frameState, delta);
2594
+ } catch (error) {
2595
+ console.error(`[Scheduler] Error in job "${job.id}":`, error);
2596
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
2597
+ }
2598
+ }
2599
+ }
2600
+ //* Debug & Inspection Methods ================================
2601
+ /**
2602
+ * Get the total number of registered jobs across all roots.
2603
+ * Includes both per-root jobs and global before/after jobs.
2604
+ * @returns {number} Total job count
2605
+ */
2606
+ getJobCount() {
2607
+ let count = 0;
2608
+ for (const root of this.roots.values()) {
2609
+ count += root.jobs.size;
2610
+ }
2611
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2612
+ }
2613
+ /**
2614
+ * Get all registered job IDs across all roots.
2615
+ * Includes both per-root jobs and global before/after jobs.
2616
+ * @returns {string[]} Array of all job IDs
2617
+ */
2618
+ getJobIds() {
2619
+ const ids = [];
2620
+ for (const root of this.roots.values()) {
2621
+ ids.push(...root.jobs.keys());
2622
+ }
2623
+ ids.push(...this.globalBeforeJobs.keys());
2624
+ ids.push(...this.globalAfterJobs.keys());
2625
+ return ids;
2626
+ }
2627
+ /**
2628
+ * Get the number of registered roots (Canvas instances).
2629
+ * @returns {number} Number of registered roots
2630
+ */
2631
+ getRootCount() {
2632
+ return this.roots.size;
2633
+ }
2634
+ /**
2635
+ * Check if any user (non-system) jobs are registered in a specific phase.
2636
+ * Used by the default render job to know if a user has taken over rendering.
2637
+ *
2638
+ * @param phase The phase to check
2639
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2640
+ * @returns true if any user jobs exist in the phase
2641
+ */
2642
+ hasUserJobsInPhase(phase, rootId) {
2643
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2644
+ return rootsToCheck.some((root) => {
2645
+ if (!root) return false;
2646
+ for (const job of root.jobs.values()) {
2647
+ if (job.phase === phase && !job.system && job.enabled) return true;
2648
+ }
2649
+ return false;
2650
+ });
2651
+ }
2652
+ //* Utility Methods ================================
2653
+ /**
2654
+ * Generate a unique root ID for automatic root registration.
2655
+ * @returns {string} A unique root ID in the format 'root_N'
2656
+ */
2657
+ generateRootId() {
2658
+ return `root_${this.nextRootIndex++}`;
2659
+ }
2660
+ /**
2661
+ * Generate a unique job ID.
2662
+ * @returns {string} A unique job ID in the format 'job_N'
2663
+ * @private
2664
+ */
2665
+ generateJobId() {
2666
+ return `job_${this.nextJobIndex}`;
2667
+ }
2668
+ /**
2669
+ * Normalize before/after constraints to a Set.
2670
+ * Handles undefined, single string, or array inputs.
2671
+ * @param {string | string[] | undefined} value - The constraint value(s)
2672
+ * @returns {Set<string>} Normalized Set of constraint strings
2673
+ * @private
2674
+ */
2675
+ normalizeConstraints(value) {
2676
+ if (!value) return /* @__PURE__ */ new Set();
2677
+ if (Array.isArray(value)) return new Set(value);
2678
+ return /* @__PURE__ */ new Set([value]);
2679
+ }
2680
+ };
2681
+ //* Static State & Methods (Singleton Usage) ================================
2682
+ //* Cross-Bundle Singleton Key ==============================
2683
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2684
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2685
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2686
+ let Scheduler = _Scheduler;
2687
+ const getScheduler = () => Scheduler.get();
2688
+ if (hmrData) {
2689
+ hmrData.accept?.();
2690
+ }
2691
+
2692
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2693
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2694
+ const createStore = (invalidate, advance) => {
2695
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2696
+ const position = new three.Vector3();
2697
+ const defaultTarget = new three.Vector3();
2698
+ const tempTarget = new three.Vector3();
2699
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2700
+ const { width, height, top, left } = size;
2701
+ const aspect = width / height;
2702
+ if (target.isVector3) tempTarget.copy(target);
2703
+ else tempTarget.set(...target);
2704
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2705
+ if (isOrthographicCamera(camera)) {
2706
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2707
+ } else {
2708
+ const fov = camera.fov * Math.PI / 180;
2709
+ const h = 2 * Math.tan(fov / 2) * distance;
2710
+ const w = h * (width / height);
2711
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2712
+ }
2713
+ }
2714
+ let performanceTimeout = void 0;
2715
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2716
+ const pointer = new three.Vector2();
2717
+ const rootState = {
2718
+ set,
2719
+ get,
2720
+ // Mock objects that have to be configured
2721
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2722
+ primaryStore: null,
2723
+ gl: null,
2724
+ renderer: null,
2725
+ camera: null,
2726
+ frustum: new three.Frustum(),
2727
+ autoUpdateFrustum: true,
2728
+ raycaster: null,
2729
+ events: {
2730
+ priority: 1,
2731
+ enabled: true,
2732
+ connected: false,
2733
+ frameTimedRaycasts: true,
2734
+ alwaysFireOnScroll: true,
2735
+ updateOnFrame: false
2736
+ },
2737
+ scene: null,
2738
+ rootScene: null,
2739
+ xr: null,
2740
+ inspector: null,
2741
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2742
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2743
+ textureColorSpace: three.SRGBColorSpace,
2744
+ isLegacy: false,
2745
+ webGPUSupported: false,
2746
+ isNative: false,
2747
+ controls: null,
2748
+ pointer,
2749
+ mouse: pointer,
2750
+ frameloop: "always",
2751
+ onPointerMissed: void 0,
2752
+ onDragOverMissed: void 0,
2753
+ onDropMissed: void 0,
2754
+ performance: {
2755
+ current: 1,
2756
+ min: 0.5,
2757
+ max: 1,
2758
+ debounce: 200,
2759
+ regress: () => {
2760
+ const state2 = get();
2761
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2762
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2763
+ performanceTimeout = setTimeout(
2764
+ () => setPerformanceCurrent(get().performance.max),
2765
+ state2.performance.debounce
2766
+ );
2767
+ }
2768
+ },
2769
+ size: { width: 0, height: 0, top: 0, left: 0 },
2770
+ viewport: {
2771
+ initialDpr: 0,
2772
+ dpr: 0,
2773
+ width: 0,
2774
+ height: 0,
2775
+ top: 0,
2776
+ left: 0,
2777
+ aspect: 0,
2778
+ distance: 0,
2779
+ factor: 0,
2780
+ getCurrentViewport
2781
+ },
2782
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2783
+ setSize: (width, height, top, left) => {
2784
+ const state2 = get();
2785
+ if (width === void 0) {
2786
+ set({ _sizeImperative: false });
2787
+ if (state2._sizeProps) {
2788
+ const { width: propW, height: propH } = state2._sizeProps;
2789
+ if (propW !== void 0 || propH !== void 0) {
2790
+ const currentSize = state2.size;
2791
+ const newSize = {
2792
+ width: propW ?? currentSize.width,
2793
+ height: propH ?? currentSize.height,
2794
+ top: currentSize.top,
2795
+ left: currentSize.left
2796
+ };
2797
+ set((s) => ({
2798
+ size: newSize,
2799
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2800
+ }));
2801
+ getScheduler().invalidate();
2802
+ }
2803
+ }
2804
+ return;
2805
+ }
2806
+ const w = width;
2807
+ const h = height ?? width;
2808
+ const t = top ?? state2.size.top;
2809
+ const l = left ?? state2.size.left;
2810
+ const size = { width: w, height: h, top: t, left: l };
2811
+ set((s) => ({
2812
+ size,
2813
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2814
+ _sizeImperative: true
2815
+ }));
2816
+ getScheduler().invalidate();
2817
+ },
2818
+ setDpr: (dpr) => set((state2) => {
2819
+ const resolved = calculateDpr(dpr);
2820
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2821
+ }),
2822
+ setFrameloop: (frameloop = "always") => {
2823
+ set(() => ({ frameloop }));
2824
+ },
2825
+ setError: (error) => set(() => ({ error })),
2826
+ error: null,
2827
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2828
+ uniforms: {},
2829
+ nodes: {},
2830
+ buffers: {},
2831
+ gpuStorage: {},
2832
+ textures: /* @__PURE__ */ new Map(),
2833
+ renderPipeline: null,
2834
+ passes: {},
2835
+ _hmrVersion: 0,
2836
+ _sizeImperative: false,
2837
+ _sizeProps: null,
2838
+ previousRoot: void 0,
2839
+ internal: {
2840
+ // Events
2841
+ interaction: [],
2842
+ subscribers: [],
2843
+ // Per-pointer state (new unified structure)
2844
+ pointerMap: /* @__PURE__ */ new Map(),
2845
+ pointerDirty: /* @__PURE__ */ new Map(),
2846
+ lastEvent: React__namespace.createRef(),
2847
+ // Deprecated but kept for backwards compatibility
2848
+ hovered: /* @__PURE__ */ new Map(),
2849
+ initialClick: [0, 0],
2850
+ initialHits: [],
2851
+ capturedMap: /* @__PURE__ */ new Map(),
2852
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2853
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2854
+ // Occlusion system (WebGPU only)
2855
+ occlusionEnabled: false,
2856
+ occlusionObserver: null,
2857
+ occlusionCache: /* @__PURE__ */ new Map(),
2858
+ helperGroup: null,
2859
+ // Updates
2860
+ active: false,
2861
+ frames: 0,
2862
+ priority: 0,
2863
+ subscribe: (ref, priority, store) => {
2864
+ const internal = get().internal;
2865
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2866
+ internal.subscribers.push({ ref, priority, store });
2867
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2868
+ return () => {
2869
+ const internal2 = get().internal;
2870
+ if (internal2?.subscribers) {
2871
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2872
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2873
+ }
2874
+ };
2875
+ },
2876
+ // Renderer Storage (single source of truth)
2877
+ actualRenderer: null,
2878
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2879
+ scheduler: null
2880
+ }
2881
+ };
2882
+ return rootState;
2883
+ });
2884
+ const state = rootStore.getState();
2885
+ Object.defineProperty(state, "gl", {
2886
+ get() {
2887
+ const currentState = rootStore.getState();
2888
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2889
+ const stack = new Error().stack || "";
2890
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2891
+ if (!isInternalAccess) {
2892
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2893
+ notifyDepreciated({
2894
+ heading: "Accessing state.gl in WebGPU mode",
2895
+ 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
2896
+ });
2897
+ }
2898
+ }
2899
+ return currentState.internal.actualRenderer;
2900
+ },
2901
+ set(value) {
2902
+ rootStore.getState().internal.actualRenderer = value;
2903
+ },
2904
+ enumerable: true,
2905
+ configurable: true
2906
+ });
2907
+ Object.defineProperty(state, "renderer", {
2908
+ get() {
2909
+ return rootStore.getState().internal.actualRenderer;
2910
+ },
2911
+ set(value) {
2912
+ rootStore.getState().internal.actualRenderer = value;
2913
+ },
2914
+ enumerable: true,
2915
+ configurable: true
2916
+ });
2917
+ let oldScene = state.scene;
2918
+ rootStore.subscribe(() => {
2919
+ const currentState = rootStore.getState();
2920
+ const { scene, rootScene, set } = currentState;
2921
+ if (scene !== oldScene) {
2922
+ oldScene = scene;
2923
+ if (scene?.isScene && scene !== rootScene) {
2924
+ set({ rootScene: scene });
2925
+ }
2926
+ }
2927
+ });
2928
+ let oldSize = state.size;
2929
+ let oldDpr = state.viewport.dpr;
2930
+ let oldCamera = state.camera;
2931
+ rootStore.subscribe(() => {
2932
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2933
+ const actualRenderer = internal.actualRenderer;
2934
+ const canvasTarget = internal.canvasTarget;
2935
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2936
+ oldSize = size;
2937
+ oldDpr = viewport.dpr;
2938
+ updateCamera(camera, size);
2939
+ if (internal.isSecondary && canvasTarget) {
2940
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2941
+ canvasTarget.setSize(size.width, size.height, false);
2942
+ } else {
2943
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2944
+ actualRenderer.setSize(size.width, size.height, false);
2945
+ if (canvasTarget) {
2946
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2947
+ canvasTarget.setSize(size.width, size.height, false);
2948
+ }
2295
2949
  }
2296
2950
  }
2297
- }
2298
- //* Debug & Inspection Methods ================================
2299
- /**
2300
- * Get the total number of registered jobs across all roots.
2301
- * Includes both per-root jobs and global before/after jobs.
2302
- * @returns {number} Total job count
2303
- */
2304
- getJobCount() {
2305
- let count = 0;
2306
- for (const root of this.roots.values()) {
2307
- count += root.jobs.size;
2951
+ if (camera !== oldCamera) {
2952
+ oldCamera = camera;
2953
+ const { rootScene } = rootStore.getState();
2954
+ if (camera && rootScene && !camera.parent) {
2955
+ rootScene.add(camera);
2956
+ }
2957
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2958
+ const currentState = rootStore.getState();
2959
+ if (currentState.autoUpdateFrustum && camera) {
2960
+ updateFrustum(camera, currentState.frustum);
2961
+ }
2308
2962
  }
2309
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2310
- }
2311
- /**
2312
- * Get all registered job IDs across all roots.
2313
- * Includes both per-root jobs and global before/after jobs.
2314
- * @returns {string[]} Array of all job IDs
2315
- */
2316
- getJobIds() {
2317
- const ids = [];
2318
- for (const root of this.roots.values()) {
2319
- ids.push(...root.jobs.keys());
2963
+ });
2964
+ rootStore.subscribe((state2) => invalidate(state2));
2965
+ return rootStore;
2966
+ };
2967
+
2968
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2969
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2970
+ function getLoader(Proto) {
2971
+ if (isConstructor$1(Proto)) {
2972
+ let loader = memoizedLoaders.get(Proto);
2973
+ if (!loader) {
2974
+ loader = new Proto();
2975
+ memoizedLoaders.set(Proto, loader);
2320
2976
  }
2321
- ids.push(...this.globalBeforeJobs.keys());
2322
- ids.push(...this.globalAfterJobs.keys());
2323
- return ids;
2324
- }
2325
- /**
2326
- * Get the number of registered roots (Canvas instances).
2327
- * @returns {number} Number of registered roots
2328
- */
2329
- getRootCount() {
2330
- return this.roots.size;
2331
- }
2332
- /**
2333
- * Check if any user (non-system) jobs are registered in a specific phase.
2334
- * Used by the default render job to know if a user has taken over rendering.
2335
- *
2336
- * @param phase The phase to check
2337
- * @param rootId Optional root ID to check (checks all roots if not provided)
2338
- * @returns true if any user jobs exist in the phase
2339
- */
2340
- hasUserJobsInPhase(phase, rootId) {
2341
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2342
- return rootsToCheck.some((root) => {
2343
- if (!root) return false;
2344
- for (const job of root.jobs.values()) {
2345
- if (job.phase === phase && !job.system && job.enabled) return true;
2346
- }
2347
- return false;
2348
- });
2349
- }
2350
- //* Utility Methods ================================
2351
- /**
2352
- * Generate a unique root ID for automatic root registration.
2353
- * @returns {string} A unique root ID in the format 'root_N'
2354
- */
2355
- generateRootId() {
2356
- return `root_${this.nextRootIndex++}`;
2357
- }
2358
- /**
2359
- * Generate a unique job ID.
2360
- * @returns {string} A unique job ID in the format 'job_N'
2361
- * @private
2362
- */
2363
- generateJobId() {
2364
- return `job_${this.nextJobIndex}`;
2365
- }
2366
- /**
2367
- * Normalize before/after constraints to a Set.
2368
- * Handles undefined, single string, or array inputs.
2369
- * @param {string | string[] | undefined} value - The constraint value(s)
2370
- * @returns {Set<string>} Normalized Set of constraint strings
2371
- * @private
2372
- */
2373
- normalizeConstraints(value) {
2374
- if (!value) return /* @__PURE__ */ new Set();
2375
- if (Array.isArray(value)) return new Set(value);
2376
- return /* @__PURE__ */ new Set([value]);
2977
+ return loader;
2377
2978
  }
2378
- };
2379
- //* Static State & Methods (Singleton Usage) ================================
2380
- //* Cross-Bundle Singleton Key ==============================
2381
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2382
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2383
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2384
- let Scheduler = _Scheduler;
2385
- const getScheduler = () => Scheduler.get();
2386
- if (hmrData) {
2387
- hmrData.accept?.();
2979
+ return Proto;
2980
+ }
2981
+ function loadingFn(extensions, onProgress) {
2982
+ return function(Proto, input) {
2983
+ const loader = getLoader(Proto);
2984
+ if (extensions) extensions(loader);
2985
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2986
+ return loader.loadAsync(input, onProgress).then((data) => {
2987
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2988
+ return data;
2989
+ });
2990
+ }
2991
+ return new Promise(
2992
+ (res, reject) => loader.load(
2993
+ input,
2994
+ (data) => {
2995
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2996
+ res(data);
2997
+ },
2998
+ onProgress,
2999
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
3000
+ )
3001
+ );
3002
+ };
3003
+ }
3004
+ function useLoader(loader, input, extensions, onProgress) {
3005
+ const keys = Array.isArray(input) ? input : [input];
3006
+ const fn = loadingFn(extensions, onProgress);
3007
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
3008
+ return Array.isArray(input) ? results : results[0];
2388
3009
  }
3010
+ useLoader.preload = function(loader, input, extensions, onProgress) {
3011
+ const keys = Array.isArray(input) ? input : [input];
3012
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
3013
+ };
3014
+ useLoader.clear = function(loader, input) {
3015
+ const keys = Array.isArray(input) ? input : [input];
3016
+ keys.forEach((key) => suspendReact.clear([loader, key]));
3017
+ };
3018
+ useLoader.loader = getLoader;
2389
3019
 
2390
3020
  function useFrame(callback, priorityOrOptions) {
2391
3021
  const store = React__namespace.useContext(context);
@@ -2566,6 +3196,9 @@ function useTexture(input, optionsOrOnLoad) {
2566
3196
  const textureCache = useThree((state) => state.textures);
2567
3197
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2568
3198
  const { onLoad, cache = false } = options;
3199
+ const onLoadRef = React.useRef(onLoad);
3200
+ onLoadRef.current = onLoad;
3201
+ const onLoadCalledForRef = React.useRef(null);
2569
3202
  const urls = React.useMemo(() => getUrls(input), [input]);
2570
3203
  const cachedResult = React.useMemo(() => {
2571
3204
  if (!cache) return null;
@@ -2576,9 +3209,13 @@ function useTexture(input, optionsOrOnLoad) {
2576
3209
  three.TextureLoader,
2577
3210
  IsObject(input) ? Object.values(input) : input
2578
3211
  );
3212
+ const inputKey = urls.join("\0");
2579
3213
  React.useLayoutEffect(() => {
2580
- if (!cachedResult) onLoad?.(loadedTextures);
2581
- }, [onLoad, cachedResult, loadedTextures]);
3214
+ if (cachedResult) return;
3215
+ if (onLoadCalledForRef.current === inputKey) return;
3216
+ onLoadCalledForRef.current = inputKey;
3217
+ onLoadRef.current?.(loadedTextures);
3218
+ }, [cachedResult, loadedTextures, inputKey]);
2582
3219
  React.useEffect(() => {
2583
3220
  if (cachedResult) return;
2584
3221
  if ("initTexture" in renderer) {
@@ -2745,14 +3382,31 @@ function useTextures() {
2745
3382
  }, [store]);
2746
3383
  }
2747
3384
 
2748
- function useRenderTarget(width, height, options) {
3385
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2749
3386
  const isLegacy = useThree((s) => s.isLegacy);
2750
3387
  const size = useThree((s) => s.size);
3388
+ let width;
3389
+ let height;
3390
+ let opts;
3391
+ if (typeof widthOrOptions === "object") {
3392
+ opts = widthOrOptions;
3393
+ } else if (typeof widthOrOptions === "number") {
3394
+ width = widthOrOptions;
3395
+ if (typeof heightOrOptions === "object") {
3396
+ height = widthOrOptions;
3397
+ opts = heightOrOptions;
3398
+ } else if (typeof heightOrOptions === "number") {
3399
+ height = heightOrOptions;
3400
+ opts = options;
3401
+ } else {
3402
+ height = widthOrOptions;
3403
+ }
3404
+ }
2751
3405
  return React.useMemo(() => {
2752
3406
  const w = width ?? size.width;
2753
3407
  const h = height ?? size.height;
2754
- return new three.WebGLRenderTarget(w, h, options);
2755
- }, [width, height, size.width, size.height, options, isLegacy]);
3408
+ return new three.WebGLRenderTarget(w, h, opts);
3409
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2756
3410
  }
2757
3411
 
2758
3412
  function useStore() {
@@ -2802,7 +3456,7 @@ function addTail(callback) {
2802
3456
  function invalidate(state, frames = 1, stackFrames = false) {
2803
3457
  getScheduler().invalidate(frames, stackFrames);
2804
3458
  }
2805
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3459
+ function advance(timestamp) {
2806
3460
  getScheduler().step(timestamp);
2807
3461
  }
2808
3462
 
@@ -14256,6 +14910,7 @@ function swapInstances() {
14256
14910
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14257
14911
  instance.object.__r3f = instance;
14258
14912
  setFiberRef(fiber, instance.object);
14913
+ delete instance.appliedOnce;
14259
14914
  applyProps(instance.object, instance.props);
14260
14915
  if (instance.props.attach) {
14261
14916
  attach(parent, instance);
@@ -14329,8 +14984,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14329
14984
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14330
14985
  if (isTailSibling) swapInstances();
14331
14986
  },
14332
- finalizeInitialChildren: () => false,
14333
- commitMount() {
14987
+ finalizeInitialChildren: (instance) => {
14988
+ for (const prop in instance.props) {
14989
+ if (isFromRef(instance.props[prop])) return true;
14990
+ }
14991
+ return false;
14992
+ },
14993
+ commitMount(instance) {
14994
+ const resolved = {};
14995
+ for (const prop in instance.props) {
14996
+ const value = instance.props[prop];
14997
+ if (isFromRef(value)) {
14998
+ const ref = value[FROM_REF];
14999
+ if (ref.current != null) resolved[prop] = ref.current;
15000
+ }
15001
+ }
15002
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14334
15003
  },
14335
15004
  getPublicInstance: (instance) => instance?.object,
14336
15005
  prepareForCommit: () => null,
@@ -14551,6 +15220,9 @@ function createRoot(canvas) {
14551
15220
  let resolve;
14552
15221
  pending = new Promise((_resolve) => resolve = _resolve);
14553
15222
  const {
15223
+ id: canvasId,
15224
+ primaryCanvas,
15225
+ scheduler: schedulerConfig,
14554
15226
  gl: glConfig,
14555
15227
  renderer: rendererConfig,
14556
15228
  size: propsSize,
@@ -14558,10 +15230,6 @@ function createRoot(canvas) {
14558
15230
  events,
14559
15231
  onCreated: onCreatedCallback,
14560
15232
  shadows = false,
14561
- linear = false,
14562
- flat = false,
14563
- textureColorSpace = three.SRGBColorSpace,
14564
- legacy = false,
14565
15233
  orthographic = false,
14566
15234
  frameloop = "always",
14567
15235
  dpr = [1, 2],
@@ -14573,8 +15241,10 @@ function createRoot(canvas) {
14573
15241
  onDropMissed,
14574
15242
  autoUpdateFrustum = true,
14575
15243
  occlusion = false,
14576
- _sizeProps
15244
+ _sizeProps,
15245
+ forceEven
14577
15246
  } = props;
15247
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || three.SRGBColorSpace;
14578
15248
  const state = store.getState();
14579
15249
  const defaultGLProps = {
14580
15250
  canvas,
@@ -14587,15 +15257,25 @@ function createRoot(canvas) {
14587
15257
  "WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
14588
15258
  );
14589
15259
  }
14590
- (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
15260
+ const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
14591
15261
  if (glConfig && rendererConfig) {
14592
15262
  throw new Error("Cannot use both gl and renderer props at the same time");
14593
15263
  }
14594
15264
  let renderer = state.internal.actualRenderer;
15265
+ if (primaryCanvas && !R3F_BUILD_WEBGPU) {
15266
+ throw new Error(
15267
+ "The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
15268
+ );
15269
+ }
15270
+ if (primaryCanvas && wantsGL) {
15271
+ throw new Error(
15272
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15273
+ );
15274
+ }
14595
15275
  if (!state.internal.actualRenderer) {
14596
15276
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14597
15277
  state.internal.actualRenderer = renderer;
14598
- state.set({ isLegacy: true, gl: renderer, renderer });
15278
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14599
15279
  }
14600
15280
  let raycaster = state.raycaster;
14601
15281
  if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
@@ -14604,6 +15284,7 @@ function createRoot(canvas) {
14604
15284
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14605
15285
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14606
15286
  }
15287
+ let tempCamera = state.camera;
14607
15288
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14608
15289
  lastCamera = cameraOptions;
14609
15290
  const isCamera = cameraOptions?.isCamera;
@@ -14623,6 +15304,7 @@ function createRoot(canvas) {
14623
15304
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14624
15305
  }
14625
15306
  state.set({ camera });
15307
+ tempCamera = camera;
14626
15308
  raycaster.camera = camera;
14627
15309
  }
14628
15310
  if (!state.scene) {
@@ -14640,7 +15322,7 @@ function createRoot(canvas) {
14640
15322
  rootScene: scene,
14641
15323
  internal: { ...prev.internal, container: scene }
14642
15324
  }));
14643
- const camera = state.camera;
15325
+ const camera = tempCamera;
14644
15326
  if (camera && !camera.parent) scene.add(camera);
14645
15327
  }
14646
15328
  if (events && !state.events.handlers) {
@@ -14657,6 +15339,9 @@ function createRoot(canvas) {
14657
15339
  if (_sizeProps !== void 0) {
14658
15340
  state.set({ _sizeProps });
14659
15341
  }
15342
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15343
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15344
+ }
14660
15345
  const size = computeInitialSize(canvas, propsSize);
14661
15346
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14662
15347
  const wasImperative = state._sizeImperative;
@@ -14683,10 +15368,10 @@ function createRoot(canvas) {
14683
15368
  lastConfiguredProps.performance = performance;
14684
15369
  }
14685
15370
  if (!state.xr) {
14686
- const handleXRFrame = (timestamp, frame) => {
15371
+ const handleXRFrame = (timestamp, _frame) => {
14687
15372
  const state2 = store.getState();
14688
15373
  if (state2.frameloop === "never") return;
14689
- advance(timestamp, true);
15374
+ advance(timestamp);
14690
15375
  };
14691
15376
  const actualRenderer = state.internal.actualRenderer;
14692
15377
  const handleSessionChange = () => {
@@ -14698,16 +15383,16 @@ function createRoot(canvas) {
14698
15383
  };
14699
15384
  const xr = {
14700
15385
  connect() {
14701
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14702
- const actualRenderer2 = renderer2 || gl;
14703
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14704
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15386
+ const { gl, renderer: renderer2 } = store.getState();
15387
+ const xrManager = (renderer2 || gl).xr;
15388
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15389
+ xrManager.addEventListener("sessionend", handleSessionChange);
14705
15390
  },
14706
15391
  disconnect() {
14707
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14708
- const actualRenderer2 = renderer2 || gl;
14709
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14710
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15392
+ const { gl, renderer: renderer2 } = store.getState();
15393
+ const xrManager = (renderer2 || gl).xr;
15394
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15395
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14711
15396
  }
14712
15397
  };
14713
15398
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14719,15 +15404,22 @@ function createRoot(canvas) {
14719
15404
  const oldType = renderer.shadowMap.type;
14720
15405
  renderer.shadowMap.enabled = !!shadows;
14721
15406
  if (is.boo(shadows)) {
14722
- renderer.shadowMap.type = three.PCFSoftShadowMap;
15407
+ renderer.shadowMap.type = three.PCFShadowMap;
14723
15408
  } else if (is.str(shadows)) {
15409
+ if (shadows === "soft") {
15410
+ notifyDepreciated({
15411
+ heading: 'shadows="soft" is deprecated',
15412
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15413
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15414
+ });
15415
+ }
14724
15416
  const types = {
14725
15417
  basic: three.BasicShadowMap,
14726
15418
  percentage: three.PCFShadowMap,
14727
- soft: three.PCFSoftShadowMap,
15419
+ soft: three.PCFShadowMap,
14728
15420
  variance: three.VSMShadowMap
14729
15421
  };
14730
- renderer.shadowMap.type = types[shadows] ?? three.PCFSoftShadowMap;
15422
+ renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
14731
15423
  } else if (is.obj(shadows)) {
14732
15424
  Object.assign(renderer.shadowMap, shadows);
14733
15425
  }
@@ -14735,47 +15427,69 @@ function createRoot(canvas) {
14735
15427
  renderer.shadowMap.needsUpdate = true;
14736
15428
  }
14737
15429
  }
14738
- {
14739
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14740
- const linearChanged = linear !== lastConfiguredProps.linear;
14741
- const flatChanged = flat !== lastConfiguredProps.flat;
14742
- if (legacyChanged) {
14743
- three.ColorManagement.enabled = !legacy;
14744
- lastConfiguredProps.legacy = legacy;
14745
- }
14746
- if (!configured || linearChanged) {
14747
- renderer.outputColorSpace = linear ? three.LinearSRGBColorSpace : three.SRGBColorSpace;
14748
- lastConfiguredProps.linear = linear;
14749
- }
14750
- if (!configured || flatChanged) {
14751
- renderer.toneMapping = flat ? three.NoToneMapping : three.ACESFilmicToneMapping;
14752
- lastConfiguredProps.flat = flat;
14753
- }
14754
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14755
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14756
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15430
+ if (!configured) {
15431
+ renderer.outputColorSpace = three.SRGBColorSpace;
15432
+ renderer.toneMapping = three.ACESFilmicToneMapping;
14757
15433
  }
14758
15434
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14759
15435
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14760
15436
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14761
15437
  }
15438
+ const r3fProps = ["textureColorSpace"];
15439
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15440
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
14762
15441
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14763
- applyProps(renderer, glConfig);
15442
+ const glProps = {};
15443
+ for (const key in glConfig) {
15444
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15445
+ }
15446
+ applyProps(renderer, glProps);
14764
15447
  }
14765
15448
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14766
15449
  const currentRenderer = state.renderer;
14767
15450
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14768
- applyProps(currentRenderer, rendererConfig);
15451
+ const rendererProps = {};
15452
+ for (const key in rendererConfig) {
15453
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15454
+ }
15455
+ applyProps(currentRenderer, rendererProps);
14769
15456
  }
14770
15457
  }
14771
15458
  const scheduler = getScheduler();
14772
15459
  const rootId = state.internal.rootId;
14773
15460
  if (!rootId) {
14774
- const newRootId = scheduler.generateRootId();
15461
+ const newRootId = canvasId || scheduler.generateRootId();
14775
15462
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14776
15463
  getState: () => store.getState(),
14777
15464
  onError: (err) => store.getState().setError(err)
14778
15465
  });
15466
+ const unregisterCanvasTarget = scheduler.register(
15467
+ () => {
15468
+ const state2 = store.getState();
15469
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15470
+ const renderer2 = state2.internal.actualRenderer;
15471
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15472
+ }
15473
+ },
15474
+ {
15475
+ id: `${newRootId}_canvasTarget`,
15476
+ rootId: newRootId,
15477
+ phase: "start",
15478
+ system: true
15479
+ }
15480
+ );
15481
+ const unregisterEventsFlush = scheduler.register(
15482
+ () => {
15483
+ const state2 = store.getState();
15484
+ state2.events.flush?.();
15485
+ },
15486
+ {
15487
+ id: `${newRootId}_events`,
15488
+ rootId: newRootId,
15489
+ phase: "input",
15490
+ system: true
15491
+ }
15492
+ );
14779
15493
  const unregisterFrustum = scheduler.register(
14780
15494
  () => {
14781
15495
  const state2 = store.getState();
@@ -14810,18 +15524,22 @@ function createRoot(canvas) {
14810
15524
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14811
15525
  if (userHandlesRender || state2.internal.priority) return;
14812
15526
  try {
14813
- if (state2.postProcessing?.render) state2.postProcessing.render();
15527
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14814
15528
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14815
15529
  } catch (error) {
14816
15530
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14817
15531
  }
14818
15532
  },
14819
15533
  {
14820
- id: `${newRootId}_render`,
15534
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15535
+ id: canvasId || `${newRootId}_render`,
14821
15536
  rootId: newRootId,
14822
15537
  phase: "render",
14823
- system: true
15538
+ system: true,
14824
15539
  // Internal flag: this is a system job, not user-controlled
15540
+ // Apply scheduler config for render ordering and rate limiting
15541
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15542
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14825
15543
  }
14826
15544
  );
14827
15545
  state.set((state2) => ({
@@ -14830,6 +15548,8 @@ function createRoot(canvas) {
14830
15548
  rootId: newRootId,
14831
15549
  unregisterRoot: () => {
14832
15550
  unregisterRoot();
15551
+ unregisterCanvasTarget();
15552
+ unregisterEventsFlush();
14833
15553
  unregisterFrustum();
14834
15554
  unregisterVisibility();
14835
15555
  unregisterRender();
@@ -14888,15 +15608,24 @@ function unmountComponentAtNode(canvas, callback) {
14888
15608
  const renderer = state.internal.actualRenderer;
14889
15609
  const unregisterRoot = state.internal.unregisterRoot;
14890
15610
  if (unregisterRoot) unregisterRoot();
15611
+ const unregisterPrimary = state.internal.unregisterPrimary;
15612
+ if (unregisterPrimary) unregisterPrimary();
15613
+ const canvasTarget = state.internal.canvasTarget;
15614
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14891
15615
  state.events.disconnect?.();
14892
15616
  cleanupHelperGroup(root.store);
14893
- renderer?.renderLists?.dispose?.();
14894
- renderer?.forceContextLoss?.();
14895
- if (renderer?.xr) state.xr.disconnect();
15617
+ if (state.isLegacy && renderer) {
15618
+ ;
15619
+ renderer.renderLists?.dispose?.();
15620
+ renderer.forceContextLoss?.();
15621
+ }
15622
+ if (!state.internal.isSecondary) {
15623
+ if (renderer?.xr) state.xr.disconnect();
15624
+ }
14896
15625
  dispose(state.scene);
14897
15626
  _roots.delete(canvas);
14898
15627
  if (callback) callback(canvas);
14899
- } catch (e) {
15628
+ } catch {
14900
15629
  }
14901
15630
  }, 500);
14902
15631
  }
@@ -14904,36 +15633,34 @@ function unmountComponentAtNode(canvas, callback) {
14904
15633
  }
14905
15634
  }
14906
15635
  function createPortal(children, container, state) {
14907
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15636
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14908
15637
  }
14909
- function PortalWrapper({ children, container, state }) {
15638
+ function Portal({ children, container, state }) {
14910
15639
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14911
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15640
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14912
15641
  if (isRef(container)) return container.current ?? null;
14913
15642
  return container;
14914
15643
  });
15644
+ const setResolvedContainer = React.useCallback(
15645
+ (newContainer) => {
15646
+ if (!newContainer || newContainer === resolvedContainer) return;
15647
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15648
+ },
15649
+ [resolvedContainer, _setResolvedContainer, isRef]
15650
+ );
14915
15651
  React.useMemo(() => {
14916
- if (isRef(container)) {
14917
- const current = container.current;
14918
- if (!current) {
14919
- queueMicrotask(() => {
14920
- const updated = container.current;
14921
- if (updated && updated !== resolvedContainer) {
14922
- setResolvedContainer(updated);
14923
- }
14924
- });
14925
- } else if (current !== resolvedContainer) {
14926
- setResolvedContainer(current);
14927
- }
14928
- } else if (container !== resolvedContainer) {
14929
- setResolvedContainer(container);
15652
+ if (isRef(container) && !container.current) {
15653
+ return queueMicrotask(() => {
15654
+ setResolvedContainer(container.current);
15655
+ });
14930
15656
  }
14931
- }, [container, resolvedContainer, isRef]);
15657
+ setResolvedContainer(container);
15658
+ }, [container, isRef, setResolvedContainer]);
14932
15659
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14933
15660
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14934
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15661
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14935
15662
  }
14936
- function Portal({ state = {}, children, container }) {
15663
+ function PortalInner({ state = {}, children, container }) {
14937
15664
  const { events, size, injectScene = true, ...rest } = state;
14938
15665
  const previousRoot = useStore();
14939
15666
  const [raycaster] = React.useState(() => new three.Raycaster());
@@ -14954,11 +15681,12 @@ function Portal({ state = {}, children, container }) {
14954
15681
  };
14955
15682
  }, [portalScene, container, injectScene]);
14956
15683
  const inject = useMutableCallback((rootState, injectState) => {
15684
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14957
15685
  let viewport = void 0;
14958
- if (injectState.camera && size) {
15686
+ if (injectState.camera && (size || injectState.size)) {
14959
15687
  const camera = injectState.camera;
14960
- viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), size);
14961
- if (camera !== rootState.camera) updateCamera(camera, size);
15688
+ viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), resolvedSize);
15689
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14962
15690
  }
14963
15691
  return {
14964
15692
  // The intersect consists of the previous root state
@@ -14975,7 +15703,7 @@ function Portal({ state = {}, children, container }) {
14975
15703
  previousRoot,
14976
15704
  // Events, size and viewport can be overridden by the inject layer
14977
15705
  events: { ...rootState.events, ...injectState.events, ...events },
14978
- size: { ...rootState.size, ...size },
15706
+ size: resolvedSize,
14979
15707
  viewport: { ...rootState.viewport, ...viewport },
14980
15708
  // Layers are allowed to override events
14981
15709
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14987,9 +15715,13 @@ function Portal({ state = {}, children, container }) {
14987
15715
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14988
15716
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14989
15717
  onMutate(previousRoot.getState());
14990
- previousRoot.subscribe(onMutate);
14991
15718
  return store;
14992
15719
  }, [previousRoot, container]);
15720
+ useIsomorphicLayoutEffect(() => {
15721
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15722
+ const unsubscribe = previousRoot.subscribe(onMutate);
15723
+ return unsubscribe;
15724
+ }, [previousRoot, usePortalStore]);
14993
15725
  return (
14994
15726
  // @ts-ignore, reconciler types are not maintained
14995
15727
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -15009,15 +15741,13 @@ function CanvasImpl({
15009
15741
  fallback,
15010
15742
  resize,
15011
15743
  style,
15744
+ id,
15012
15745
  gl,
15013
- renderer,
15746
+ renderer: rendererProp,
15014
15747
  events = createPointerEvents,
15015
15748
  eventSource,
15016
15749
  eventPrefix,
15017
15750
  shadows,
15018
- linear,
15019
- flat,
15020
- legacy,
15021
15751
  orthographic,
15022
15752
  frameloop,
15023
15753
  dpr,
@@ -15032,10 +15762,53 @@ function CanvasImpl({
15032
15762
  hmr,
15033
15763
  width,
15034
15764
  height,
15765
+ background,
15766
+ forceEven,
15035
15767
  ...props
15036
15768
  }) {
15769
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15770
+ let primaryCanvas;
15771
+ let scheduler;
15772
+ let renderer;
15773
+ if (isRendererConfig) {
15774
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15775
+ primaryCanvas = pc;
15776
+ scheduler = sc;
15777
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15778
+ } else {
15779
+ renderer = rendererProp;
15780
+ }
15037
15781
  React__namespace.useMemo(() => extend(THREE), []);
15038
15782
  const Bridge = useBridge();
15783
+ const backgroundProps = React__namespace.useMemo(() => {
15784
+ if (!background) return null;
15785
+ if (typeof background === "object" && !background.isColor) {
15786
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15787
+ return {
15788
+ ...rest,
15789
+ preset,
15790
+ files: envMap || files,
15791
+ backgroundFiles: backgroundMap,
15792
+ background: true
15793
+ };
15794
+ }
15795
+ if (typeof background === "number") {
15796
+ return { color: background, background: true };
15797
+ }
15798
+ if (typeof background === "string") {
15799
+ if (background in presetsObj) {
15800
+ return { preset: background, background: true };
15801
+ }
15802
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15803
+ return { files: background, background: true };
15804
+ }
15805
+ return { color: background, background: true };
15806
+ }
15807
+ if (background.isColor) {
15808
+ return { color: background, background: true };
15809
+ }
15810
+ return null;
15811
+ }, [background]);
15039
15812
  const hasInitialSizeRef = React__namespace.useRef(false);
15040
15813
  const measureConfig = React__namespace.useMemo(() => {
15041
15814
  if (!hasInitialSizeRef.current) {
@@ -15052,15 +15825,20 @@ function CanvasImpl({
15052
15825
  };
15053
15826
  }, [resize, hasInitialSizeRef.current]);
15054
15827
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15055
- const effectiveSize = React__namespace.useMemo(
15056
- () => ({
15057
- width: width ?? containerRect.width,
15058
- height: height ?? containerRect.height,
15828
+ const effectiveSize = React__namespace.useMemo(() => {
15829
+ let w = width ?? containerRect.width;
15830
+ let h = height ?? containerRect.height;
15831
+ if (forceEven) {
15832
+ w = Math.ceil(w / 2) * 2;
15833
+ h = Math.ceil(h / 2) * 2;
15834
+ }
15835
+ return {
15836
+ width: w,
15837
+ height: h,
15059
15838
  top: containerRect.top,
15060
15839
  left: containerRect.left
15061
- }),
15062
- [width, height, containerRect]
15063
- );
15840
+ };
15841
+ }, [width, height, containerRect, forceEven]);
15064
15842
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15065
15843
  hasInitialSizeRef.current = true;
15066
15844
  }
@@ -15100,14 +15878,14 @@ function CanvasImpl({
15100
15878
  async function run() {
15101
15879
  if (!effectActiveRef.current || !root.current) return;
15102
15880
  await root.current.configure({
15881
+ id,
15882
+ primaryCanvas,
15883
+ scheduler,
15103
15884
  gl,
15104
15885
  renderer,
15105
15886
  scene,
15106
15887
  events,
15107
15888
  shadows,
15108
- linear,
15109
- flat,
15110
- legacy,
15111
15889
  orthographic,
15112
15890
  frameloop,
15113
15891
  dpr,
@@ -15117,6 +15895,7 @@ function CanvasImpl({
15117
15895
  size: effectiveSize,
15118
15896
  // Store size props for reset functionality
15119
15897
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15898
+ forceEven,
15120
15899
  // Pass mutable reference to onPointerMissed so it's free to update
15121
15900
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15122
15901
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15140,7 +15919,10 @@ function CanvasImpl({
15140
15919
  });
15141
15920
  if (!effectActiveRef.current || !root.current) return;
15142
15921
  root.current.render(
15143
- /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15922
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
15923
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15924
+ children ?? null
15925
+ ] }) }) })
15144
15926
  );
15145
15927
  }
15146
15928
  run();
@@ -15167,20 +15949,22 @@ function CanvasImpl({
15167
15949
  const canvas = canvasRef.current;
15168
15950
  if (!canvas) return;
15169
15951
  const handleHMR = () => {
15170
- const rootEntry = _roots.get(canvas);
15171
- if (rootEntry?.store) {
15172
- rootEntry.store.setState((state) => ({
15173
- nodes: {},
15174
- uniforms: {},
15175
- _hmrVersion: state._hmrVersion + 1
15176
- }));
15177
- }
15952
+ queueMicrotask(() => {
15953
+ const rootEntry = _roots.get(canvas);
15954
+ if (rootEntry?.store) {
15955
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15956
+ rootEntry.store.setState((state) => ({
15957
+ nodes: {},
15958
+ uniforms: {},
15959
+ _hmrVersion: state._hmrVersion + 1
15960
+ }));
15961
+ }
15962
+ });
15178
15963
  };
15179
15964
  if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('legacy.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15180
15965
  const hot = undefined;
15181
15966
  hot.on("vite:afterUpdate", handleHMR);
15182
- return () => hot.dispose?.(() => {
15183
- });
15967
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15184
15968
  }
15185
15969
  if (typeof module !== "undefined" && module.hot) {
15186
15970
  const hot = module.hot;
@@ -15203,7 +15987,16 @@ function CanvasImpl({
15203
15987
  ...style
15204
15988
  },
15205
15989
  ...props,
15206
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15990
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
15991
+ "canvas",
15992
+ {
15993
+ ref: canvasRef,
15994
+ id,
15995
+ className: "r3f-canvas",
15996
+ style: { display: "block", width: "100%", height: "100%" },
15997
+ children: fallback
15998
+ }
15999
+ ) })
15207
16000
  }
15208
16001
  );
15209
16002
  }
@@ -15215,8 +16008,15 @@ extend(THREE);
15215
16008
 
15216
16009
  exports.Block = Block;
15217
16010
  exports.Canvas = Canvas;
16011
+ exports.Environment = Environment;
16012
+ exports.EnvironmentCube = EnvironmentCube;
16013
+ exports.EnvironmentMap = EnvironmentMap;
16014
+ exports.EnvironmentPortal = EnvironmentPortal;
15218
16015
  exports.ErrorBoundary = ErrorBoundary;
16016
+ exports.FROM_REF = FROM_REF;
15219
16017
  exports.IsObject = IsObject;
16018
+ exports.ONCE = ONCE;
16019
+ exports.Portal = Portal;
15220
16020
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15221
16021
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15222
16022
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15246,30 +16046,41 @@ exports.events = createPointerEvents;
15246
16046
  exports.extend = extend;
15247
16047
  exports.findInitialRoot = findInitialRoot;
15248
16048
  exports.flushSync = flushSync;
16049
+ exports.fromRef = fromRef;
15249
16050
  exports.getInstanceProps = getInstanceProps;
16051
+ exports.getPrimary = getPrimary;
16052
+ exports.getPrimaryIds = getPrimaryIds;
15250
16053
  exports.getRootState = getRootState;
15251
16054
  exports.getScheduler = getScheduler;
15252
16055
  exports.getUuidPrefix = getUuidPrefix;
15253
16056
  exports.hasConstructor = hasConstructor;
16057
+ exports.hasPrimary = hasPrimary;
15254
16058
  exports.invalidate = invalidate;
15255
16059
  exports.invalidateInstance = invalidateInstance;
15256
16060
  exports.is = is;
15257
16061
  exports.isColorRepresentation = isColorRepresentation;
15258
16062
  exports.isCopyable = isCopyable;
16063
+ exports.isFromRef = isFromRef;
15259
16064
  exports.isObject3D = isObject3D;
16065
+ exports.isOnce = isOnce;
15260
16066
  exports.isOrthographicCamera = isOrthographicCamera;
15261
16067
  exports.isRef = isRef;
15262
16068
  exports.isRenderer = isRenderer;
15263
16069
  exports.isTexture = isTexture;
15264
16070
  exports.isVectorLike = isVectorLike;
16071
+ exports.once = once;
15265
16072
  exports.prepare = prepare;
16073
+ exports.presetsObj = presetsObj;
15266
16074
  exports.reconciler = reconciler;
16075
+ exports.registerPrimary = registerPrimary;
15267
16076
  exports.removeInteractivity = removeInteractivity;
15268
16077
  exports.resolve = resolve;
15269
16078
  exports.unmountComponentAtNode = unmountComponentAtNode;
16079
+ exports.unregisterPrimary = unregisterPrimary;
15270
16080
  exports.updateCamera = updateCamera;
15271
16081
  exports.updateFrustum = updateFrustum;
15272
16082
  exports.useBridge = useBridge;
16083
+ exports.useEnvironment = useEnvironment;
15273
16084
  exports.useFrame = useFrame;
15274
16085
  exports.useGraph = useGraph;
15275
16086
  exports.useInstanceHandle = useInstanceHandle;
@@ -15281,3 +16092,4 @@ exports.useStore = useStore;
15281
16092
  exports.useTexture = useTexture;
15282
16093
  exports.useTextures = useTextures;
15283
16094
  exports.useThree = useThree;
16095
+ exports.waitForPrimary = waitForPrimary;