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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/legacy.cjs CHANGED
@@ -5,10 +5,17 @@ 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');
11
17
 
18
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
19
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
13
20
 
14
21
  function _interopNamespaceCompat(e) {
@@ -65,6 +72,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
65
72
  WebGPURenderer: WebGPURenderer
66
73
  }, [three__namespace]);
67
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
+
68
458
  var __defProp$2 = Object.defineProperty;
69
459
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
70
460
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -244,7 +634,8 @@ function prepare(target, root, type, props) {
244
634
  object,
245
635
  eventCount: 0,
246
636
  handlers: {},
247
- isHidden: false
637
+ isHidden: false,
638
+ deferredRefs: []
248
639
  };
249
640
  if (object) object.__r3f = instance;
250
641
  }
@@ -293,7 +684,7 @@ function createOcclusionObserverNode(store, uniform) {
293
684
  let occlusionSetupPromise = null;
294
685
  function enableOcclusion(store) {
295
686
  const state = store.getState();
296
- const { internal, renderer, rootScene } = state;
687
+ const { internal, renderer } = state;
297
688
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
298
689
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
299
690
  if (!hasOcclusionSupport) {
@@ -456,6 +847,22 @@ function hasVisibilityHandlers(handlers) {
456
847
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
457
848
  }
458
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
+
459
866
  const RESERVED_PROPS = [
460
867
  "children",
461
868
  "key",
@@ -526,7 +933,7 @@ function getMemoizedPrototype(root) {
526
933
  ctor = new root.constructor();
527
934
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
528
935
  }
529
- } catch (e) {
936
+ } catch {
530
937
  }
531
938
  return ctor;
532
939
  }
@@ -557,7 +964,7 @@ function applyProps(object, props) {
557
964
  const rootState = instance && findInitialRoot(instance).getState();
558
965
  const prevHandlers = instance?.eventCount;
559
966
  for (const prop in props) {
560
- let value = props[prop];
967
+ const value = props[prop];
561
968
  if (RESERVED_PROPS.includes(prop)) continue;
562
969
  if (instance && EVENT_REGEX.test(prop)) {
563
970
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -572,6 +979,25 @@ function applyProps(object, props) {
572
979
  continue;
573
980
  }
574
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
+ }
575
1001
  let { root, key, target } = resolve(object, prop);
576
1002
  if (target === void 0 && (typeof root !== "object" || root === null)) {
577
1003
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -594,7 +1020,10 @@ function applyProps(object, props) {
594
1020
  else target.set(value);
595
1021
  } else {
596
1022
  root[key] = value;
597
- 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
598
1027
  root[key].format === three.RGBAFormat && root[key].type === three.UnsignedByteType) {
599
1028
  root[key].colorSpace = rootState.textureColorSpace;
600
1029
  }
@@ -627,38 +1056,60 @@ function applyProps(object, props) {
627
1056
  return object;
628
1057
  }
629
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
+ }
630
1077
  function makeId(event) {
631
1078
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
632
1079
  }
633
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
634
- 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);
635
1084
  if (captureData) {
636
- captures.delete(obj);
637
- if (captures.size === 0) {
638
- capturedMap.delete(pointerId);
639
- captureData.target.releasePointerCapture(pointerId);
640
- }
1085
+ pointerState.captured.delete(obj);
1086
+ captureData.target.releasePointerCapture(pointerId);
641
1087
  }
642
1088
  }
643
1089
  function removeInteractivity(store, object) {
644
1090
  const { internal } = store.getState();
645
1091
  internal.interaction = internal.interaction.filter((o) => o !== object);
646
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
647
- internal.hovered.forEach((value, key) => {
648
- if (value.eventObject === object || value.object === object) {
649
- 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);
650
1101
  }
651
- });
652
- internal.capturedMap.forEach((captures, pointerId) => {
653
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
654
- });
1102
+ }
655
1103
  unregisterVisibility(store, object);
656
1104
  }
657
1105
  function createEvents(store) {
658
- function calculateDistance(event) {
1106
+ function calculateDistance(event, pointerId) {
659
1107
  const { internal } = store.getState();
660
- const dx = event.offsetX - internal.initialClick[0];
661
- 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;
662
1113
  return Math.round(Math.sqrt(dx * dx + dy * dy));
663
1114
  }
664
1115
  function filterPointerEvents(objects) {
@@ -694,6 +1145,15 @@ function createEvents(store) {
694
1145
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
695
1146
  }
696
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
+ }
697
1157
  const aState = getRootState(a.object);
698
1158
  const bState = getRootState(b.object);
699
1159
  const aPriority = aState?.events?.priority ?? 1;
@@ -709,14 +1169,19 @@ function createEvents(store) {
709
1169
  for (const hit of hits) {
710
1170
  let eventObject = hit.object;
711
1171
  while (eventObject) {
712
- if (eventObject.__r3f?.eventCount)
1172
+ if (eventObject.__r3f?.eventCount) {
713
1173
  intersections.push({ ...hit, eventObject });
1174
+ }
714
1175
  eventObject = eventObject.parent;
715
1176
  }
716
1177
  }
717
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
718
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
719
- 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
+ }
720
1185
  }
721
1186
  }
722
1187
  return intersections;
@@ -729,28 +1194,26 @@ function createEvents(store) {
729
1194
  if (state) {
730
1195
  const { raycaster, pointer, camera, internal } = state;
731
1196
  const unprojectedPoint = new three.Vector3(pointer.x, pointer.y, 0).unproject(camera);
732
- 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
+ };
733
1201
  const setPointerCapture = (id) => {
734
1202
  const captureData = { intersection: hit, target: event.target };
735
- if (internal.capturedMap.has(id)) {
736
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
737
- } else {
738
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
739
- }
1203
+ const pointerState = getPointerState(internal, id);
1204
+ pointerState.captured.set(hit.eventObject, captureData);
740
1205
  event.target.setPointerCapture(id);
741
1206
  };
742
1207
  const releasePointerCapture = (id) => {
743
- const captures = internal.capturedMap.get(id);
744
- if (captures) {
745
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
746
- }
1208
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
747
1209
  };
748
- let extractEventProps = {};
749
- for (let prop in event) {
750
- let property = event[prop];
1210
+ const extractEventProps = {};
1211
+ for (const prop in event) {
1212
+ const property = event[prop];
751
1213
  if (typeof property !== "function") extractEventProps[prop] = property;
752
1214
  }
753
- let raycastEvent = {
1215
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1216
+ const raycastEvent = {
754
1217
  ...hit,
755
1218
  ...extractEventProps,
756
1219
  pointer,
@@ -760,18 +1223,19 @@ function createEvents(store) {
760
1223
  unprojectedPoint,
761
1224
  ray: raycaster.ray,
762
1225
  camera,
1226
+ pointerId: eventPointerId,
763
1227
  // Hijack stopPropagation, which just sets a flag
764
1228
  stopPropagation() {
765
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1229
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
766
1230
  if (
767
1231
  // ...if this pointer hasn't been captured
768
- !capturesForPointer || // ... or if the hit object is capturing the pointer
769
- capturesForPointer.has(hit.eventObject)
1232
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1233
+ pointerState.captured.has(hit.eventObject)
770
1234
  ) {
771
1235
  raycastEvent.stopped = localState.stopped = true;
772
- 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)) {
773
1237
  const higher = intersections.slice(0, intersections.indexOf(hit));
774
- cancelPointer([...higher, hit]);
1238
+ cancelPointer([...higher, hit], eventPointerId);
775
1239
  }
776
1240
  }
777
1241
  },
@@ -787,15 +1251,18 @@ function createEvents(store) {
787
1251
  }
788
1252
  return intersections;
789
1253
  }
790
- function cancelPointer(intersections) {
1254
+ function cancelPointer(intersections, pointerId) {
791
1255
  const { internal } = store.getState();
792
- 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) {
793
1260
  if (!intersections.length || !intersections.find(
794
1261
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
795
1262
  )) {
796
1263
  const eventObject = hoveredObj.eventObject;
797
1264
  const instance = eventObject.__r3f;
798
- internal.hovered.delete(makeId(hoveredObj));
1265
+ pointerState.hovered.delete(hoveredId);
799
1266
  if (instance?.eventCount) {
800
1267
  const handlers = instance.handlers;
801
1268
  const data = { ...hoveredObj, intersections };
@@ -824,41 +1291,118 @@ function createEvents(store) {
824
1291
  instance?.handlers.onDropMissed?.(event);
825
1292
  }
826
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
+ }
827
1340
  function handlePointer(name) {
828
1341
  switch (name) {
829
1342
  case "onPointerLeave":
830
- case "onPointerCancel":
831
1343
  case "onDragLeave":
832
1344
  return () => cancelPointer([]);
1345
+ // Global cancel of these events
1346
+ case "onPointerCancel":
1347
+ return (event) => {
1348
+ const pointerId = getPointerId(event);
1349
+ cleanupPointer(pointerId);
1350
+ };
833
1351
  case "onLostPointerCapture":
834
1352
  return (event) => {
835
1353
  const { internal } = store.getState();
836
- 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) {
837
1357
  requestAnimationFrame(() => {
838
- if (internal.capturedMap.has(event.pointerId)) {
839
- internal.capturedMap.delete(event.pointerId);
840
- cancelPointer([]);
1358
+ const pointerState2 = internal.pointerMap.get(pointerId);
1359
+ if (pointerState2?.captured.size) {
1360
+ pointerState2.captured.clear();
841
1361
  }
1362
+ cancelPointer([], pointerId);
842
1363
  });
843
1364
  }
844
1365
  };
845
1366
  }
846
1367
  return function handleEvent(event) {
847
1368
  const state = store.getState();
848
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1369
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1370
+ const pointerId = getPointerId(event);
849
1371
  internal.lastEvent.current = event;
850
- if (!state.events.enabled) return;
1372
+ if (!events.enabled) return;
851
1373
  const isPointerMove = name === "onPointerMove";
852
1374
  const isDragOver = name === "onDragOver";
853
1375
  const isDrop = name === "onDrop";
854
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
+ }
855
1396
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
856
1397
  const hits = intersect(event, filter);
857
- const delta = isClickEvent ? calculateDistance(event) : 0;
858
- if (name === "onPointerDown") {
859
- internal.initialClick = [event.offsetX, event.offsetY];
860
- internal.initialHits = hits.map((hit) => hit.eventObject);
861
- }
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 ?? [];
862
1406
  if (isClickEvent && !hits.length) {
863
1407
  if (delta <= 2) {
864
1408
  pointerMissed(event, internal.interaction);
@@ -873,7 +1417,9 @@ function createEvents(store) {
873
1417
  dropMissed(event, internal.interaction);
874
1418
  if (onDropMissed) onDropMissed(event);
875
1419
  }
876
- if (isPointerMove || isDragOver) cancelPointer(hits);
1420
+ if (isPointerMove || isDragOver) {
1421
+ cancelPointer(hits, pointerId);
1422
+ }
877
1423
  function onIntersect(data) {
878
1424
  const eventObject = data.eventObject;
879
1425
  const instance = eventObject.__r3f;
@@ -882,9 +1428,10 @@ function createEvents(store) {
882
1428
  if (isPointerMove) {
883
1429
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
884
1430
  const id = makeId(data);
885
- const hoveredItem = internal.hovered.get(id);
1431
+ const pointerState2 = getPointerState(internal, pointerId);
1432
+ const hoveredItem = pointerState2.hovered.get(id);
886
1433
  if (!hoveredItem) {
887
- internal.hovered.set(id, data);
1434
+ pointerState2.hovered.set(id, data);
888
1435
  handlers.onPointerOver?.(data);
889
1436
  handlers.onPointerEnter?.(data);
890
1437
  } else if (hoveredItem.stopped) {
@@ -894,9 +1441,10 @@ function createEvents(store) {
894
1441
  handlers.onPointerMove?.(data);
895
1442
  } else if (isDragOver) {
896
1443
  const id = makeId(data);
897
- const hoveredItem = internal.hovered.get(id);
1444
+ const pointerState2 = getPointerState(internal, pointerId);
1445
+ const hoveredItem = pointerState2.hovered.get(id);
898
1446
  if (!hoveredItem) {
899
- internal.hovered.set(id, data);
1447
+ pointerState2.hovered.set(id, data);
900
1448
  handlers.onDragOverEnter?.(data);
901
1449
  } else if (hoveredItem.stopped) {
902
1450
  data.stopPropagation();
@@ -907,18 +1455,18 @@ function createEvents(store) {
907
1455
  } else {
908
1456
  const handler = handlers[name];
909
1457
  if (handler) {
910
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1458
+ if (!isClickEvent || initialHits.includes(eventObject)) {
911
1459
  pointerMissed(
912
1460
  event,
913
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1461
+ internal.interaction.filter((object) => !initialHits.includes(object))
914
1462
  );
915
1463
  handler(data);
916
1464
  }
917
1465
  } else {
918
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1466
+ if (isClickEvent && initialHits.includes(eventObject)) {
919
1467
  pointerMissed(
920
1468
  event,
921
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1469
+ internal.interaction.filter((object) => !initialHits.includes(object))
922
1470
  );
923
1471
  }
924
1472
  }
@@ -927,7 +1475,15 @@ function createEvents(store) {
927
1475
  handleIntersects(hits, event, delta, onIntersect);
928
1476
  };
929
1477
  }
930
- 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 };
931
1487
  }
932
1488
  const DOM_EVENTS = {
933
1489
  onClick: ["click", false],
@@ -946,11 +1502,16 @@ const DOM_EVENTS = {
946
1502
  onLostPointerCapture: ["lostpointercapture", true]
947
1503
  };
948
1504
  function createPointerEvents(store) {
949
- const { handlePointer } = createEvents(store);
1505
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1506
+ let nextXRPointerId = XR_POINTER_ID_START;
1507
+ const xrPointers = /* @__PURE__ */ new Map();
950
1508
  return {
951
1509
  priority: 1,
952
1510
  enabled: true,
953
- compute(event, state, previous) {
1511
+ frameTimedRaycasts: true,
1512
+ alwaysFireOnScroll: true,
1513
+ updateOnFrame: false,
1514
+ compute(event, state) {
954
1515
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
955
1516
  state.raycaster.setFromCamera(state.pointer, state.camera);
956
1517
  },
@@ -959,11 +1520,33 @@ function createPointerEvents(store) {
959
1520
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
960
1521
  {}
961
1522
  ),
962
- 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: () => {
963
1542
  const { events, internal } = store.getState();
964
- 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
+ }
965
1547
  },
966
1548
  connect: (target) => {
1549
+ if (!target) return;
967
1550
  const { set, events } = store.getState();
968
1551
  events.disconnect?.();
969
1552
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -987,6 +1570,32 @@ function createPointerEvents(store) {
987
1570
  }
988
1571
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
989
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);
990
1599
  }
991
1600
  };
992
1601
  }
@@ -1048,300 +1657,26 @@ function notifyAlpha({ message, link }) {
1048
1657
  }
1049
1658
  }
1050
1659
 
1051
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1052
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1053
- const createStore = (invalidate, advance) => {
1054
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1055
- const position = new three.Vector3();
1056
- const defaultTarget = new three.Vector3();
1057
- const tempTarget = new three.Vector3();
1058
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1059
- const { width, height, top, left } = size;
1060
- const aspect = width / height;
1061
- if (target.isVector3) tempTarget.copy(target);
1062
- else tempTarget.set(...target);
1063
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1064
- if (isOrthographicCamera(camera)) {
1065
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1066
- } else {
1067
- const fov = camera.fov * Math.PI / 180;
1068
- const h = 2 * Math.tan(fov / 2) * distance;
1069
- const w = h * (width / height);
1070
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1071
- }
1072
- }
1073
- let performanceTimeout = void 0;
1074
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1075
- const pointer = new three.Vector2();
1076
- const rootState = {
1077
- set,
1078
- get,
1079
- // Mock objects that have to be configured
1080
- gl: null,
1081
- renderer: null,
1082
- camera: null,
1083
- frustum: new three.Frustum(),
1084
- autoUpdateFrustum: true,
1085
- raycaster: null,
1086
- events: { priority: 1, enabled: true, connected: false },
1087
- scene: null,
1088
- rootScene: null,
1089
- xr: null,
1090
- inspector: null,
1091
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1092
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1093
- legacy: false,
1094
- linear: false,
1095
- flat: false,
1096
- textureColorSpace: "srgb",
1097
- isLegacy: false,
1098
- webGPUSupported: false,
1099
- isNative: false,
1100
- controls: null,
1101
- pointer,
1102
- mouse: pointer,
1103
- frameloop: "always",
1104
- onPointerMissed: void 0,
1105
- onDragOverMissed: void 0,
1106
- onDropMissed: void 0,
1107
- performance: {
1108
- current: 1,
1109
- min: 0.5,
1110
- max: 1,
1111
- debounce: 200,
1112
- regress: () => {
1113
- const state2 = get();
1114
- if (performanceTimeout) clearTimeout(performanceTimeout);
1115
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1116
- performanceTimeout = setTimeout(
1117
- () => setPerformanceCurrent(get().performance.max),
1118
- state2.performance.debounce
1119
- );
1120
- }
1121
- },
1122
- size: { width: 0, height: 0, top: 0, left: 0 },
1123
- viewport: {
1124
- initialDpr: 0,
1125
- dpr: 0,
1126
- width: 0,
1127
- height: 0,
1128
- top: 0,
1129
- left: 0,
1130
- aspect: 0,
1131
- distance: 0,
1132
- factor: 0,
1133
- getCurrentViewport
1134
- },
1135
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1136
- setSize: (width, height, top = 0, left = 0) => {
1137
- const camera = get().camera;
1138
- const size = { width, height, top, left };
1139
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1140
- },
1141
- setDpr: (dpr) => set((state2) => {
1142
- const resolved = calculateDpr(dpr);
1143
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1144
- }),
1145
- setFrameloop: (frameloop = "always") => {
1146
- set(() => ({ frameloop }));
1147
- },
1148
- setError: (error) => set(() => ({ error })),
1149
- error: null,
1150
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1151
- uniforms: {},
1152
- nodes: {},
1153
- textures: /* @__PURE__ */ new Map(),
1154
- postProcessing: null,
1155
- passes: {},
1156
- previousRoot: void 0,
1157
- internal: {
1158
- // Events
1159
- interaction: [],
1160
- hovered: /* @__PURE__ */ new Map(),
1161
- subscribers: [],
1162
- initialClick: [0, 0],
1163
- initialHits: [],
1164
- capturedMap: /* @__PURE__ */ new Map(),
1165
- lastEvent: React__namespace.createRef(),
1166
- // Visibility tracking (onFramed, onOccluded, onVisible)
1167
- visibilityRegistry: /* @__PURE__ */ new Map(),
1168
- // Occlusion system (WebGPU only)
1169
- occlusionEnabled: false,
1170
- occlusionObserver: null,
1171
- occlusionCache: /* @__PURE__ */ new Map(),
1172
- helperGroup: null,
1173
- // Updates
1174
- active: false,
1175
- frames: 0,
1176
- priority: 0,
1177
- subscribe: (ref, priority, store) => {
1178
- const internal = get().internal;
1179
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1180
- internal.subscribers.push({ ref, priority, store });
1181
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1182
- return () => {
1183
- const internal2 = get().internal;
1184
- if (internal2?.subscribers) {
1185
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1186
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1187
- }
1188
- };
1189
- },
1190
- // Renderer Storage (single source of truth)
1191
- actualRenderer: null,
1192
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1193
- scheduler: null
1194
- }
1195
- };
1196
- return rootState;
1197
- });
1198
- const state = rootStore.getState();
1199
- Object.defineProperty(state, "gl", {
1200
- get() {
1201
- const currentState = rootStore.getState();
1202
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1203
- const stack = new Error().stack || "";
1204
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1205
- if (!isInternalAccess) {
1206
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1207
- notifyDepreciated({
1208
- heading: "Accessing state.gl in WebGPU mode",
1209
- 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
1210
- });
1211
- }
1212
- }
1213
- return currentState.internal.actualRenderer;
1214
- },
1215
- set(value) {
1216
- rootStore.getState().internal.actualRenderer = value;
1217
- },
1218
- enumerable: true,
1219
- configurable: true
1220
- });
1221
- Object.defineProperty(state, "renderer", {
1222
- get() {
1223
- return rootStore.getState().internal.actualRenderer;
1224
- },
1225
- set(value) {
1226
- rootStore.getState().internal.actualRenderer = value;
1227
- },
1228
- enumerable: true,
1229
- configurable: true
1230
- });
1231
- let oldScene = state.scene;
1232
- rootStore.subscribe(() => {
1233
- const currentState = rootStore.getState();
1234
- const { scene, rootScene, set } = currentState;
1235
- if (scene !== oldScene) {
1236
- oldScene = scene;
1237
- if (scene?.isScene && scene !== rootScene) {
1238
- set({ rootScene: scene });
1239
- }
1240
- }
1241
- });
1242
- let oldSize = state.size;
1243
- let oldDpr = state.viewport.dpr;
1244
- let oldCamera = state.camera;
1245
- rootStore.subscribe(() => {
1246
- const { camera, size, viewport, set, internal } = rootStore.getState();
1247
- const actualRenderer = internal.actualRenderer;
1248
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1249
- oldSize = size;
1250
- oldDpr = viewport.dpr;
1251
- updateCamera(camera, size);
1252
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1253
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1254
- actualRenderer.setSize(size.width, size.height, updateStyle);
1255
- }
1256
- if (camera !== oldCamera) {
1257
- oldCamera = camera;
1258
- const { rootScene } = rootStore.getState();
1259
- if (camera && rootScene && !camera.parent) {
1260
- rootScene.add(camera);
1261
- }
1262
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1263
- const currentState = rootStore.getState();
1264
- if (currentState.autoUpdateFrustum && camera) {
1265
- updateFrustum(camera, currentState.frustum);
1266
- }
1267
- }
1268
- });
1269
- rootStore.subscribe((state2) => invalidate(state2));
1270
- return rootStore;
1271
- };
1272
-
1273
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1274
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1275
- function getLoader(Proto) {
1276
- if (isConstructor$1(Proto)) {
1277
- let loader = memoizedLoaders.get(Proto);
1278
- if (!loader) {
1279
- loader = new Proto();
1280
- memoizedLoaders.set(Proto, loader);
1281
- }
1282
- return loader;
1283
- }
1284
- return Proto;
1285
- }
1286
- function loadingFn(extensions, onProgress) {
1287
- return function(Proto, input) {
1288
- const loader = getLoader(Proto);
1289
- if (extensions) extensions(loader);
1290
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1291
- return loader.loadAsync(input, onProgress).then((data) => {
1292
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1293
- return data;
1294
- });
1295
- }
1296
- return new Promise(
1297
- (res, reject) => loader.load(
1298
- input,
1299
- (data) => {
1300
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1301
- res(data);
1302
- },
1303
- onProgress,
1304
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1305
- )
1306
- );
1307
- };
1308
- }
1309
- function useLoader(loader, input, extensions, onProgress) {
1310
- const keys = Array.isArray(input) ? input : [input];
1311
- const fn = loadingFn(extensions, onProgress);
1312
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1313
- return Array.isArray(input) ? results : results[0];
1314
- }
1315
- useLoader.preload = function(loader, input, extensions, onProgress) {
1316
- const keys = Array.isArray(input) ? input : [input];
1317
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1318
- };
1319
- useLoader.clear = function(loader, input) {
1320
- const keys = Array.isArray(input) ? input : [input];
1321
- keys.forEach((key) => suspendReact.clear([loader, key]));
1322
- };
1323
- useLoader.loader = getLoader;
1324
-
1325
- var __defProp$1 = Object.defineProperty;
1326
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1327
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1328
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1329
- class PhaseGraph {
1330
- constructor() {
1331
- /** Ordered list of phase nodes */
1332
- __publicField$1(this, "phases", []);
1333
- /** Quick lookup by name */
1334
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1335
- /** Cached ordered names (invalidated on changes) */
1336
- __publicField$1(this, "orderedNamesCache", null);
1337
- this.initializeDefaultPhases();
1338
- }
1339
- //* Initialization --------------------------------
1340
- initializeDefaultPhases() {
1341
- for (const name of DEFAULT_PHASES) {
1342
- const node = { name, isAutoGenerated: false };
1343
- this.phases.push(node);
1344
- 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);
1345
1680
  }
1346
1681
  this.invalidateCache();
1347
1682
  }
@@ -1360,8 +1695,9 @@ class PhaseGraph {
1360
1695
  const node = { name, isAutoGenerated: false };
1361
1696
  let insertIndex = this.phases.length;
1362
1697
  const targetIndex = this.getPhaseIndex(before ?? after);
1363
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1364
- else {
1698
+ if (targetIndex !== -1) {
1699
+ insertIndex = before ? targetIndex : targetIndex + 1;
1700
+ } else {
1365
1701
  const constraintType = before ? "before" : "after";
1366
1702
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1367
1703
  }
@@ -1573,7 +1909,7 @@ function shouldRun(job, now) {
1573
1909
  const minInterval = 1e3 / job.fps;
1574
1910
  const lastRun = job.lastRun ?? 0;
1575
1911
  const elapsed = now - lastRun;
1576
- if (elapsed < minInterval) return false;
1912
+ if (elapsed < minInterval - 1) return false;
1577
1913
  if (job.drop) {
1578
1914
  job.lastRun = now;
1579
1915
  } else {
@@ -2242,116 +2578,444 @@ const _Scheduler = class _Scheduler {
2242
2578
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2243
2579
  root.needsRebuild = false;
2244
2580
  }
2245
- const providedState = root.getState?.() ?? {};
2246
- const frameState = {
2247
- ...providedState,
2248
- time: timestamp,
2249
- delta,
2250
- elapsed: this.loopState.elapsedTime / 1e3,
2251
- // Convert ms to seconds
2252
- frame: this.loopState.frameCount
2253
- };
2254
- for (const job of root.sortedJobs) {
2255
- if (!shouldRun(job, timestamp)) continue;
2256
- try {
2257
- job.callback(frameState, delta);
2258
- } catch (error) {
2259
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2260
- 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
+ }
2949
+ }
2950
+ }
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);
2261
2961
  }
2262
2962
  }
2263
- }
2264
- //* Debug & Inspection Methods ================================
2265
- /**
2266
- * Get the total number of registered jobs across all roots.
2267
- * Includes both per-root jobs and global before/after jobs.
2268
- * @returns {number} Total job count
2269
- */
2270
- getJobCount() {
2271
- let count = 0;
2272
- for (const root of this.roots.values()) {
2273
- count += root.jobs.size;
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);
2274
2976
  }
2275
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2977
+ return loader;
2276
2978
  }
2277
- /**
2278
- * Get all registered job IDs across all roots.
2279
- * Includes both per-root jobs and global before/after jobs.
2280
- * @returns {string[]} Array of all job IDs
2281
- */
2282
- getJobIds() {
2283
- const ids = [];
2284
- for (const root of this.roots.values()) {
2285
- ids.push(...root.jobs.keys());
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
+ });
2286
2990
  }
2287
- ids.push(...this.globalBeforeJobs.keys());
2288
- ids.push(...this.globalAfterJobs.keys());
2289
- return ids;
2290
- }
2291
- /**
2292
- * Get the number of registered roots (Canvas instances).
2293
- * @returns {number} Number of registered roots
2294
- */
2295
- getRootCount() {
2296
- return this.roots.size;
2297
- }
2298
- /**
2299
- * Check if any user (non-system) jobs are registered in a specific phase.
2300
- * Used by the default render job to know if a user has taken over rendering.
2301
- *
2302
- * @param phase The phase to check
2303
- * @param rootId Optional root ID to check (checks all roots if not provided)
2304
- * @returns true if any user jobs exist in the phase
2305
- */
2306
- hasUserJobsInPhase(phase, rootId) {
2307
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2308
- return rootsToCheck.some((root) => {
2309
- if (!root) return false;
2310
- for (const job of root.jobs.values()) {
2311
- if (job.phase === phase && !job.system && job.enabled) return true;
2312
- }
2313
- return false;
2314
- });
2315
- }
2316
- //* Utility Methods ================================
2317
- /**
2318
- * Generate a unique root ID for automatic root registration.
2319
- * @returns {string} A unique root ID in the format 'root_N'
2320
- */
2321
- generateRootId() {
2322
- return `root_${this.nextRootIndex++}`;
2323
- }
2324
- /**
2325
- * Generate a unique job ID.
2326
- * @returns {string} A unique job ID in the format 'job_N'
2327
- * @private
2328
- */
2329
- generateJobId() {
2330
- return `job_${this.nextJobIndex}`;
2331
- }
2332
- /**
2333
- * Normalize before/after constraints to a Set.
2334
- * Handles undefined, single string, or array inputs.
2335
- * @param {string | string[] | undefined} value - The constraint value(s)
2336
- * @returns {Set<string>} Normalized Set of constraint strings
2337
- * @private
2338
- */
2339
- normalizeConstraints(value) {
2340
- if (!value) return /* @__PURE__ */ new Set();
2341
- if (Array.isArray(value)) return new Set(value);
2342
- return /* @__PURE__ */ new Set([value]);
2343
- }
2344
- };
2345
- //* Static State & Methods (Singleton Usage) ================================
2346
- //* Cross-Bundle Singleton Key ==============================
2347
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2348
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2349
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2350
- let Scheduler = _Scheduler;
2351
- const getScheduler = () => Scheduler.get();
2352
- if (hmrData) {
2353
- hmrData.accept?.();
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];
2354
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;
2355
3019
 
2356
3020
  function useFrame(callback, priorityOrOptions) {
2357
3021
  const store = React__namespace.useContext(context);
@@ -2532,6 +3196,9 @@ function useTexture(input, optionsOrOnLoad) {
2532
3196
  const textureCache = useThree((state) => state.textures);
2533
3197
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2534
3198
  const { onLoad, cache = false } = options;
3199
+ const onLoadRef = React.useRef(onLoad);
3200
+ onLoadRef.current = onLoad;
3201
+ const onLoadCalledForRef = React.useRef(null);
2535
3202
  const urls = React.useMemo(() => getUrls(input), [input]);
2536
3203
  const cachedResult = React.useMemo(() => {
2537
3204
  if (!cache) return null;
@@ -2542,9 +3209,13 @@ function useTexture(input, optionsOrOnLoad) {
2542
3209
  three.TextureLoader,
2543
3210
  IsObject(input) ? Object.values(input) : input
2544
3211
  );
3212
+ const inputKey = urls.join("\0");
2545
3213
  React.useLayoutEffect(() => {
2546
- if (!cachedResult) onLoad?.(loadedTextures);
2547
- }, [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]);
2548
3219
  React.useEffect(() => {
2549
3220
  if (cachedResult) return;
2550
3221
  if ("initTexture" in renderer) {
@@ -2711,14 +3382,31 @@ function useTextures() {
2711
3382
  }, [store]);
2712
3383
  }
2713
3384
 
2714
- function useRenderTarget(width, height, options) {
3385
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2715
3386
  const isLegacy = useThree((s) => s.isLegacy);
2716
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
+ }
2717
3405
  return React.useMemo(() => {
2718
3406
  const w = width ?? size.width;
2719
3407
  const h = height ?? size.height;
2720
- return new three.WebGLRenderTarget(w, h, options);
2721
- }, [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]);
2722
3410
  }
2723
3411
 
2724
3412
  function useStore() {
@@ -2768,28 +3456,18 @@ function addTail(callback) {
2768
3456
  function invalidate(state, frames = 1, stackFrames = false) {
2769
3457
  getScheduler().invalidate(frames, stackFrames);
2770
3458
  }
2771
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3459
+ function advance(timestamp) {
2772
3460
  getScheduler().step(timestamp);
2773
3461
  }
2774
3462
 
2775
- const version = "10.0.0-alpha.1";
3463
+ const version = "10.0.0-alpha.2";
2776
3464
  const packageData = {
2777
3465
  version: version};
2778
3466
 
2779
3467
  function Xb(Tt) {
2780
3468
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2781
3469
  }
2782
- var Rm = { exports: {} }, Og = { exports: {} };
2783
- /**
2784
- * @license React
2785
- * react-reconciler.production.js
2786
- *
2787
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2788
- *
2789
- * This source code is licensed under the MIT license found in the
2790
- * LICENSE file in the root directory of this source tree.
2791
- */
2792
- var _b;
3470
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2793
3471
  function Kb() {
2794
3472
  return _b || (_b = 1, (function(Tt) {
2795
3473
  Tt.exports = function(m) {
@@ -3861,7 +4539,6 @@ Error generating stack: ` + l.message + `
3861
4539
  if (J === cl || J === jc) throw J;
3862
4540
  var Ge = Yn(29, J, null, P.mode);
3863
4541
  return Ge.lanes = H, Ge.return = P, Ge;
3864
- } finally {
3865
4542
  }
3866
4543
  };
3867
4544
  }
@@ -4515,7 +5192,6 @@ Error generating stack: ` + l.message + `
4515
5192
  var h = r.lastRenderedState, y = d(h, a);
4516
5193
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4517
5194
  } catch {
4518
- } finally {
4519
5195
  }
4520
5196
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4521
5197
  }
@@ -6936,10 +7612,7 @@ Error generating stack: ` + l.message + `
6936
7612
  function vr(t, r) {
6937
7613
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6938
7614
  }
6939
- var ie = {}, Fm = React__default, tt = Tb__default, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy");
6940
- var gc = Symbol.for("react.activity");
6941
- var $r = Symbol.for("react.memo_cache_sentinel");
6942
- var Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
7615
+ var ie = {}, Fm = React__default, tt = Tb__default, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy"), gc = Symbol.for("react.activity"), $r = Symbol.for("react.memo_cache_sentinel"), Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
6943
7616
  m.cloneMutableInstance;
6944
7617
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6945
7618
  m.cloneMutableTextInstance;
@@ -7308,17 +7981,7 @@ No matching component was found for:
7308
7981
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7309
7982
  })(Og)), Og.exports;
7310
7983
  }
7311
- var Mg = { exports: {} };
7312
- /**
7313
- * @license React
7314
- * react-reconciler.development.js
7315
- *
7316
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7317
- *
7318
- * This source code is licensed under the MIT license found in the
7319
- * LICENSE file in the root directory of this source tree.
7320
- */
7321
- var Rb;
7984
+ var Mg = { exports: {} }, Rb;
7322
7985
  function e0() {
7323
7986
  return Rb || (Rb = 1, (function(Tt) {
7324
7987
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13085,10 +13748,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13085
13748
  function Ic() {
13086
13749
  return di;
13087
13750
  }
13088
- var le = {}, qm = React__default, St = Tb__default, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy");
13089
- var Ds = Symbol.for("react.activity");
13090
- var Bh = Symbol.for("react.memo_cache_sentinel");
13091
- var ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13751
+ var le = {}, qm = React__default, St = Tb__default, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy"), Ds = Symbol.for("react.activity"), Bh = Symbol.for("react.memo_cache_sentinel"), ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13092
13752
  m.cloneMutableInstance;
13093
13753
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13094
13754
  m.cloneMutableTextInstance;
@@ -14056,15 +14716,6 @@ function n0() {
14056
14716
  var t0 = n0();
14057
14717
  const r0 = Xb(t0);
14058
14718
 
14059
- /**
14060
- * @license React
14061
- * react-reconciler-constants.production.js
14062
- *
14063
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14064
- *
14065
- * This source code is licensed under the MIT license found in the
14066
- * LICENSE file in the root directory of this source tree.
14067
- */
14068
14719
  const t = 1, o = 8, r = 32, e = 2;
14069
14720
 
14070
14721
  function createReconciler(config) {
@@ -14091,10 +14742,11 @@ function extend(objects) {
14091
14742
  function validateInstance(type, props) {
14092
14743
  const name = toPascalCase(type);
14093
14744
  const target = catalogue[name];
14094
- if (type !== "primitive" && !target)
14745
+ if (type !== "primitive" && !target) {
14095
14746
  throw new Error(
14096
14747
  `R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`
14097
14748
  );
14749
+ }
14098
14750
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14099
14751
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14100
14752
  }
@@ -14258,6 +14910,7 @@ function swapInstances() {
14258
14910
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14259
14911
  instance.object.__r3f = instance;
14260
14912
  setFiberRef(fiber, instance.object);
14913
+ delete instance.appliedOnce;
14261
14914
  applyProps(instance.object, instance.props);
14262
14915
  if (instance.props.attach) {
14263
14916
  attach(parent, instance);
@@ -14331,8 +14984,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14331
14984
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14332
14985
  if (isTailSibling) swapInstances();
14333
14986
  },
14334
- finalizeInitialChildren: () => false,
14335
- 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);
14336
15003
  },
14337
15004
  getPublicInstance: (instance) => instance?.object,
14338
15005
  prepareForCommit: () => null,
@@ -14545,14 +15212,17 @@ function createRoot(canvas) {
14545
15212
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14546
15213
  let onCreated;
14547
15214
  let lastCamera;
14548
- let lastConfiguredProps = {};
15215
+ const lastConfiguredProps = {};
14549
15216
  let configured = false;
14550
15217
  let pending = null;
14551
15218
  return {
14552
15219
  async configure(props = {}) {
14553
15220
  let resolve;
14554
15221
  pending = new Promise((_resolve) => resolve = _resolve);
14555
- let {
15222
+ const {
15223
+ id: canvasId,
15224
+ primaryCanvas,
15225
+ scheduler: schedulerConfig,
14556
15226
  gl: glConfig,
14557
15227
  renderer: rendererConfig,
14558
15228
  size: propsSize,
@@ -14560,10 +15230,6 @@ function createRoot(canvas) {
14560
15230
  events,
14561
15231
  onCreated: onCreatedCallback,
14562
15232
  shadows = false,
14563
- linear = false,
14564
- flat = false,
14565
- textureColorSpace = three.SRGBColorSpace,
14566
- legacy = false,
14567
15233
  orthographic = false,
14568
15234
  frameloop = "always",
14569
15235
  dpr = [1, 2],
@@ -14574,9 +15240,12 @@ function createRoot(canvas) {
14574
15240
  onDragOverMissed,
14575
15241
  onDropMissed,
14576
15242
  autoUpdateFrustum = true,
14577
- occlusion = false
15243
+ occlusion = false,
15244
+ _sizeProps,
15245
+ forceEven
14578
15246
  } = props;
14579
- let state = store.getState();
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;
15248
+ const state = store.getState();
14580
15249
  const defaultGLProps = {
14581
15250
  canvas,
14582
15251
  powerPreference: "high-performance",
@@ -14588,22 +15257,34 @@ function createRoot(canvas) {
14588
15257
  "WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
14589
15258
  );
14590
15259
  }
14591
- (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
15260
+ const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
14592
15261
  if (glConfig && rendererConfig) {
14593
15262
  throw new Error("Cannot use both gl and renderer props at the same time");
14594
15263
  }
14595
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
+ }
14596
15275
  if (!state.internal.actualRenderer) {
14597
15276
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14598
15277
  state.internal.actualRenderer = renderer;
14599
- state.set({ isLegacy: true, gl: renderer, renderer });
15278
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14600
15279
  }
14601
15280
  let raycaster = state.raycaster;
14602
15281
  if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
14603
15282
  const { params, ...options } = raycastOptions || {};
14604
15283
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14605
- if (!is.equ(params, raycaster.params, shallowLoose))
15284
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14606
15285
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
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) {
@@ -14654,9 +15336,17 @@ function createRoot(canvas) {
14654
15336
  wasEnabled = enabled;
14655
15337
  });
14656
15338
  }
15339
+ if (_sizeProps !== void 0) {
15340
+ state.set({ _sizeProps });
15341
+ }
15342
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15343
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15344
+ }
14657
15345
  const size = computeInitialSize(canvas, propsSize);
14658
- if (!is.equ(size, state.size, shallowLoose)) {
15346
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15347
+ const wasImperative = state._sizeImperative;
14659
15348
  state.setSize(size.width, size.height, size.top, size.left);
15349
+ if (!wasImperative) state.set({ _sizeImperative: false });
14660
15350
  }
14661
15351
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14662
15352
  state.setDpr(dpr);
@@ -14678,10 +15368,10 @@ function createRoot(canvas) {
14678
15368
  lastConfiguredProps.performance = performance;
14679
15369
  }
14680
15370
  if (!state.xr) {
14681
- const handleXRFrame = (timestamp, frame) => {
15371
+ const handleXRFrame = (timestamp, _frame) => {
14682
15372
  const state2 = store.getState();
14683
15373
  if (state2.frameloop === "never") return;
14684
- advance(timestamp, true);
15374
+ advance(timestamp);
14685
15375
  };
14686
15376
  const actualRenderer = state.internal.actualRenderer;
14687
15377
  const handleSessionChange = () => {
@@ -14693,16 +15383,16 @@ function createRoot(canvas) {
14693
15383
  };
14694
15384
  const xr = {
14695
15385
  connect() {
14696
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14697
- const actualRenderer2 = renderer2 || gl;
14698
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14699
- 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);
14700
15390
  },
14701
15391
  disconnect() {
14702
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14703
- const actualRenderer2 = renderer2 || gl;
14704
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14705
- 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);
14706
15396
  }
14707
15397
  };
14708
15398
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14714,61 +15404,92 @@ function createRoot(canvas) {
14714
15404
  const oldType = renderer.shadowMap.type;
14715
15405
  renderer.shadowMap.enabled = !!shadows;
14716
15406
  if (is.boo(shadows)) {
14717
- renderer.shadowMap.type = three.PCFSoftShadowMap;
15407
+ renderer.shadowMap.type = three.PCFShadowMap;
14718
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
+ }
14719
15416
  const types = {
14720
15417
  basic: three.BasicShadowMap,
14721
15418
  percentage: three.PCFShadowMap,
14722
- soft: three.PCFSoftShadowMap,
15419
+ soft: three.PCFShadowMap,
14723
15420
  variance: three.VSMShadowMap
14724
15421
  };
14725
- renderer.shadowMap.type = types[shadows] ?? three.PCFSoftShadowMap;
15422
+ renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
14726
15423
  } else if (is.obj(shadows)) {
14727
15424
  Object.assign(renderer.shadowMap, shadows);
14728
15425
  }
14729
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15426
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14730
15427
  renderer.shadowMap.needsUpdate = true;
14731
- }
14732
- {
14733
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14734
- const linearChanged = linear !== lastConfiguredProps.linear;
14735
- const flatChanged = flat !== lastConfiguredProps.flat;
14736
- if (legacyChanged) {
14737
- three.ColorManagement.enabled = !legacy;
14738
- lastConfiguredProps.legacy = legacy;
14739
15428
  }
14740
- if (!configured || linearChanged) {
14741
- renderer.outputColorSpace = linear ? three.LinearSRGBColorSpace : three.SRGBColorSpace;
14742
- lastConfiguredProps.linear = linear;
14743
- }
14744
- if (!configured || flatChanged) {
14745
- renderer.toneMapping = flat ? three.NoToneMapping : three.ACESFilmicToneMapping;
14746
- lastConfiguredProps.flat = flat;
14747
- }
14748
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14749
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14750
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15429
+ }
15430
+ if (!configured) {
15431
+ renderer.outputColorSpace = three.SRGBColorSpace;
15432
+ renderer.toneMapping = three.ACESFilmicToneMapping;
14751
15433
  }
14752
15434
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14753
15435
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14754
15436
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14755
15437
  }
14756
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
14757
- applyProps(renderer, glConfig);
15438
+ const r3fProps = ["textureColorSpace"];
15439
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15440
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15441
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15442
+ const glProps = {};
15443
+ for (const key in glConfig) {
15444
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15445
+ }
15446
+ applyProps(renderer, glProps);
15447
+ }
14758
15448
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14759
15449
  const currentRenderer = state.renderer;
14760
15450
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14761
- 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);
14762
15456
  }
14763
15457
  }
14764
15458
  const scheduler = getScheduler();
14765
15459
  const rootId = state.internal.rootId;
14766
15460
  if (!rootId) {
14767
- const newRootId = scheduler.generateRootId();
15461
+ const newRootId = canvasId || scheduler.generateRootId();
14768
15462
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14769
15463
  getState: () => store.getState(),
14770
15464
  onError: (err) => store.getState().setError(err)
14771
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
+ );
14772
15493
  const unregisterFrustum = scheduler.register(
14773
15494
  () => {
14774
15495
  const state2 = store.getState();
@@ -14803,18 +15524,22 @@ function createRoot(canvas) {
14803
15524
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14804
15525
  if (userHandlesRender || state2.internal.priority) return;
14805
15526
  try {
14806
- if (state2.postProcessing?.render) state2.postProcessing.render();
15527
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14807
15528
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14808
15529
  } catch (error) {
14809
15530
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14810
15531
  }
14811
15532
  },
14812
15533
  {
14813
- id: `${newRootId}_render`,
15534
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15535
+ id: canvasId || `${newRootId}_render`,
14814
15536
  rootId: newRootId,
14815
15537
  phase: "render",
14816
- system: true
15538
+ system: true,
14817
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 }
14818
15543
  }
14819
15544
  );
14820
15545
  state.set((state2) => ({
@@ -14823,6 +15548,8 @@ function createRoot(canvas) {
14823
15548
  rootId: newRootId,
14824
15549
  unregisterRoot: () => {
14825
15550
  unregisterRoot();
15551
+ unregisterCanvasTarget();
15552
+ unregisterEventsFlush();
14826
15553
  unregisterFrustum();
14827
15554
  unregisterVisibility();
14828
15555
  unregisterRender();
@@ -14881,15 +15608,24 @@ function unmountComponentAtNode(canvas, callback) {
14881
15608
  const renderer = state.internal.actualRenderer;
14882
15609
  const unregisterRoot = state.internal.unregisterRoot;
14883
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();
14884
15615
  state.events.disconnect?.();
14885
15616
  cleanupHelperGroup(root.store);
14886
- renderer?.renderLists?.dispose?.();
14887
- renderer?.forceContextLoss?.();
14888
- 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
+ }
14889
15625
  dispose(state.scene);
14890
15626
  _roots.delete(canvas);
14891
15627
  if (callback) callback(canvas);
14892
- } catch (e) {
15628
+ } catch {
14893
15629
  }
14894
15630
  }, 500);
14895
15631
  }
@@ -14897,36 +15633,34 @@ function unmountComponentAtNode(canvas, callback) {
14897
15633
  }
14898
15634
  }
14899
15635
  function createPortal(children, container, state) {
14900
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15636
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14901
15637
  }
14902
- function PortalWrapper({ children, container, state }) {
15638
+ function Portal({ children, container, state }) {
14903
15639
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14904
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15640
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14905
15641
  if (isRef(container)) return container.current ?? null;
14906
15642
  return container;
14907
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
+ );
14908
15651
  React.useMemo(() => {
14909
- if (isRef(container)) {
14910
- const current = container.current;
14911
- if (!current) {
14912
- queueMicrotask(() => {
14913
- const updated = container.current;
14914
- if (updated && updated !== resolvedContainer) {
14915
- setResolvedContainer(updated);
14916
- }
14917
- });
14918
- } else if (current !== resolvedContainer) {
14919
- setResolvedContainer(current);
14920
- }
14921
- } else if (container !== resolvedContainer) {
14922
- setResolvedContainer(container);
15652
+ if (isRef(container) && !container.current) {
15653
+ return queueMicrotask(() => {
15654
+ setResolvedContainer(container.current);
15655
+ });
14923
15656
  }
14924
- }, [container, resolvedContainer, isRef]);
15657
+ setResolvedContainer(container);
15658
+ }, [container, isRef, setResolvedContainer]);
14925
15659
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14926
15660
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14927
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15661
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14928
15662
  }
14929
- function Portal({ state = {}, children, container }) {
15663
+ function PortalInner({ state = {}, children, container }) {
14930
15664
  const { events, size, injectScene = true, ...rest } = state;
14931
15665
  const previousRoot = useStore();
14932
15666
  const [raycaster] = React.useState(() => new three.Raycaster());
@@ -14947,11 +15681,12 @@ function Portal({ state = {}, children, container }) {
14947
15681
  };
14948
15682
  }, [portalScene, container, injectScene]);
14949
15683
  const inject = useMutableCallback((rootState, injectState) => {
15684
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14950
15685
  let viewport = void 0;
14951
- if (injectState.camera && size) {
15686
+ if (injectState.camera && (size || injectState.size)) {
14952
15687
  const camera = injectState.camera;
14953
- viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), size);
14954
- 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);
14955
15690
  }
14956
15691
  return {
14957
15692
  // The intersect consists of the previous root state
@@ -14968,7 +15703,7 @@ function Portal({ state = {}, children, container }) {
14968
15703
  previousRoot,
14969
15704
  // Events, size and viewport can be overridden by the inject layer
14970
15705
  events: { ...rootState.events, ...injectState.events, ...events },
14971
- size: { ...rootState.size, ...size },
15706
+ size: resolvedSize,
14972
15707
  viewport: { ...rootState.viewport, ...viewport },
14973
15708
  // Layers are allowed to override events
14974
15709
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14980,9 +15715,13 @@ function Portal({ state = {}, children, container }) {
14980
15715
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14981
15716
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14982
15717
  onMutate(previousRoot.getState());
14983
- previousRoot.subscribe(onMutate);
14984
15718
  return store;
14985
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]);
14986
15725
  return (
14987
15726
  // @ts-ignore, reconciler types are not maintained
14988
15727
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -15002,15 +15741,13 @@ function CanvasImpl({
15002
15741
  fallback,
15003
15742
  resize,
15004
15743
  style,
15744
+ id,
15005
15745
  gl,
15006
- renderer,
15746
+ renderer: rendererProp,
15007
15747
  events = createPointerEvents,
15008
15748
  eventSource,
15009
15749
  eventPrefix,
15010
15750
  shadows,
15011
- linear,
15012
- flat,
15013
- legacy,
15014
15751
  orthographic,
15015
15752
  frameloop,
15016
15753
  dpr,
@@ -15022,10 +15759,56 @@ function CanvasImpl({
15022
15759
  onDragOverMissed,
15023
15760
  onDropMissed,
15024
15761
  onCreated,
15762
+ hmr,
15763
+ width,
15764
+ height,
15765
+ background,
15766
+ forceEven,
15025
15767
  ...props
15026
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
+ }
15027
15781
  React__namespace.useMemo(() => extend(THREE), []);
15028
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]);
15029
15812
  const hasInitialSizeRef = React__namespace.useRef(false);
15030
15813
  const measureConfig = React__namespace.useMemo(() => {
15031
15814
  if (!hasInitialSizeRef.current) {
@@ -15042,7 +15825,21 @@ function CanvasImpl({
15042
15825
  };
15043
15826
  }, [resize, hasInitialSizeRef.current]);
15044
15827
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15045
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
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,
15838
+ top: containerRect.top,
15839
+ left: containerRect.left
15840
+ };
15841
+ }, [width, height, containerRect, forceEven]);
15842
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15046
15843
  hasInitialSizeRef.current = true;
15047
15844
  }
15048
15845
  const canvasRef = React__namespace.useRef(null);
@@ -15061,7 +15858,7 @@ function CanvasImpl({
15061
15858
  useIsomorphicLayoutEffect(() => {
15062
15859
  effectActiveRef.current = true;
15063
15860
  const canvas = canvasRef.current;
15064
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15861
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15065
15862
  if (!root.current) {
15066
15863
  root.current = createRoot(canvas);
15067
15864
  notifyAlpha({
@@ -15081,21 +15878,24 @@ function CanvasImpl({
15081
15878
  async function run() {
15082
15879
  if (!effectActiveRef.current || !root.current) return;
15083
15880
  await root.current.configure({
15881
+ id,
15882
+ primaryCanvas,
15883
+ scheduler,
15084
15884
  gl,
15085
15885
  renderer,
15086
15886
  scene,
15087
15887
  events,
15088
15888
  shadows,
15089
- linear,
15090
- flat,
15091
- legacy,
15092
15889
  orthographic,
15093
15890
  frameloop,
15094
15891
  dpr,
15095
15892
  performance,
15096
15893
  raycaster,
15097
15894
  camera,
15098
- size: containerRect,
15895
+ size: effectiveSize,
15896
+ // Store size props for reset functionality
15897
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15898
+ forceEven,
15099
15899
  // Pass mutable reference to onPointerMissed so it's free to update
15100
15900
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15101
15901
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15119,7 +15919,10 @@ function CanvasImpl({
15119
15919
  });
15120
15920
  if (!effectActiveRef.current || !root.current) return;
15121
15921
  root.current.render(
15122
- /* @__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
+ ] }) }) })
15123
15926
  );
15124
15927
  }
15125
15928
  run();
@@ -15141,6 +15944,35 @@ function CanvasImpl({
15141
15944
  };
15142
15945
  }
15143
15946
  }, []);
15947
+ React__namespace.useEffect(() => {
15948
+ if (hmr === false) return;
15949
+ const canvas = canvasRef.current;
15950
+ if (!canvas) return;
15951
+ const handleHMR = () => {
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
+ });
15963
+ };
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) {
15965
+ const hot = undefined;
15966
+ hot.on("vite:afterUpdate", handleHMR);
15967
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15968
+ }
15969
+ if (typeof module !== "undefined" && module.hot) {
15970
+ const hot = module.hot;
15971
+ hot.addStatusHandler((status) => {
15972
+ if (status === "idle") handleHMR();
15973
+ });
15974
+ }
15975
+ }, [hmr]);
15144
15976
  const pointerEvents = eventSource ? "none" : "auto";
15145
15977
  return /* @__PURE__ */ jsxRuntime.jsx(
15146
15978
  "div",
@@ -15155,7 +15987,16 @@ function CanvasImpl({
15155
15987
  ...style
15156
15988
  },
15157
15989
  ...props,
15158
- 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
+ ) })
15159
16000
  }
15160
16001
  );
15161
16002
  }
@@ -15167,8 +16008,15 @@ extend(THREE);
15167
16008
 
15168
16009
  exports.Block = Block;
15169
16010
  exports.Canvas = Canvas;
16011
+ exports.Environment = Environment;
16012
+ exports.EnvironmentCube = EnvironmentCube;
16013
+ exports.EnvironmentMap = EnvironmentMap;
16014
+ exports.EnvironmentPortal = EnvironmentPortal;
15170
16015
  exports.ErrorBoundary = ErrorBoundary;
16016
+ exports.FROM_REF = FROM_REF;
15171
16017
  exports.IsObject = IsObject;
16018
+ exports.ONCE = ONCE;
16019
+ exports.Portal = Portal;
15172
16020
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15173
16021
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15174
16022
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15198,30 +16046,41 @@ exports.events = createPointerEvents;
15198
16046
  exports.extend = extend;
15199
16047
  exports.findInitialRoot = findInitialRoot;
15200
16048
  exports.flushSync = flushSync;
16049
+ exports.fromRef = fromRef;
15201
16050
  exports.getInstanceProps = getInstanceProps;
16051
+ exports.getPrimary = getPrimary;
16052
+ exports.getPrimaryIds = getPrimaryIds;
15202
16053
  exports.getRootState = getRootState;
15203
16054
  exports.getScheduler = getScheduler;
15204
16055
  exports.getUuidPrefix = getUuidPrefix;
15205
16056
  exports.hasConstructor = hasConstructor;
16057
+ exports.hasPrimary = hasPrimary;
15206
16058
  exports.invalidate = invalidate;
15207
16059
  exports.invalidateInstance = invalidateInstance;
15208
16060
  exports.is = is;
15209
16061
  exports.isColorRepresentation = isColorRepresentation;
15210
16062
  exports.isCopyable = isCopyable;
16063
+ exports.isFromRef = isFromRef;
15211
16064
  exports.isObject3D = isObject3D;
16065
+ exports.isOnce = isOnce;
15212
16066
  exports.isOrthographicCamera = isOrthographicCamera;
15213
16067
  exports.isRef = isRef;
15214
16068
  exports.isRenderer = isRenderer;
15215
16069
  exports.isTexture = isTexture;
15216
16070
  exports.isVectorLike = isVectorLike;
16071
+ exports.once = once;
15217
16072
  exports.prepare = prepare;
16073
+ exports.presetsObj = presetsObj;
15218
16074
  exports.reconciler = reconciler;
16075
+ exports.registerPrimary = registerPrimary;
15219
16076
  exports.removeInteractivity = removeInteractivity;
15220
16077
  exports.resolve = resolve;
15221
16078
  exports.unmountComponentAtNode = unmountComponentAtNode;
16079
+ exports.unregisterPrimary = unregisterPrimary;
15222
16080
  exports.updateCamera = updateCamera;
15223
16081
  exports.updateFrustum = updateFrustum;
15224
16082
  exports.useBridge = useBridge;
16083
+ exports.useEnvironment = useEnvironment;
15225
16084
  exports.useFrame = useFrame;
15226
16085
  exports.useGraph = useGraph;
15227
16086
  exports.useInstanceHandle = useInstanceHandle;
@@ -15233,3 +16092,4 @@ exports.useStore = useStore;
15233
16092
  exports.useTexture = useTexture;
15234
16093
  exports.useTextures = useTextures;
15235
16094
  exports.useThree = useThree;
16095
+ exports.waitForPrimary = waitForPrimary;