@react-three/fiber 10.0.0-canary.f75fbf6 → 10.0.0-canary.fbd44ae

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/extension.cjs +8 -8
  2. package/dist/extension.d.cts +4 -4
  3. package/dist/extension.d.mts +4 -4
  4. package/dist/extension.d.ts +4 -4
  5. package/dist/extension.mjs +1 -1
  6. package/dist/index.cjs +13 -13
  7. package/dist/index.d.cts +6 -6
  8. package/dist/index.d.mts +6 -6
  9. package/dist/index.d.ts +6 -6
  10. package/dist/index.mjs +3 -3
  11. package/dist/legacy.cjs +13 -13
  12. package/dist/legacy.d.cts +10 -7
  13. package/dist/legacy.d.mts +10 -7
  14. package/dist/legacy.d.ts +10 -7
  15. package/dist/legacy.mjs +4 -4
  16. package/dist/shared/{fiber.DH-VJXM_.d.ts → fiber.3jeDXXYd.d.mts} +82 -458
  17. package/dist/shared/{fiber.AN9kOS0N.d.mts → fiber.B_KQAb3I.d.ts} +82 -458
  18. package/dist/shared/{fiber.EDf0d92E.cjs → fiber.Bi_S5KAx.cjs} +2213 -1675
  19. package/dist/shared/{fiber.BgP4izaL.d.cts → fiber.C6hitYDY.d.cts} +1 -1
  20. package/dist/shared/{fiber.BgxhptM_.mjs → fiber.CFK811nR.mjs} +2099 -1561
  21. package/dist/shared/{fiber.Dh8K6RJr.d.cts → fiber.C_MFx7We.d.cts} +472 -20
  22. package/dist/shared/{fiber.Dh8K6RJr.d.mts → fiber.C_MFx7We.d.mts} +472 -20
  23. package/dist/shared/{fiber.Dh8K6RJr.d.ts → fiber.C_MFx7We.d.ts} +472 -20
  24. package/dist/shared/{fiber.BcX0-yko.d.mts → fiber.D4fD6H74.d.mts} +1 -1
  25. package/dist/shared/{fiber.B0oukOFO.mjs → fiber.QxL_6jra.mjs} +80 -54
  26. package/dist/shared/{fiber.BIhZnMzg.d.ts → fiber.YgTUGbja.d.ts} +1 -1
  27. package/dist/shared/{fiber.khFlPHPf.cjs → fiber.eR9ie2vQ.cjs} +80 -52
  28. package/dist/shared/{fiber.cO-9hDzr.d.cts → fiber.fR3DaJb1.d.cts} +82 -458
  29. package/dist/webgpu/index.cjs +13 -13
  30. package/dist/webgpu/index.d.cts +5 -5
  31. package/dist/webgpu/index.d.mts +5 -5
  32. package/dist/webgpu/index.d.ts +5 -5
  33. package/dist/webgpu/index.mjs +4 -4
  34. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
- import { n as notifyDepreciated, a as useThree, u as useStore, h as unstageTextures, i as stageTextures, j as detachRootExtensions, k as attachRootExtensions, d as useIsomorphicLayoutEffect, e as useMutableCallback, c as context, b as useFrame, f as useBridge, l as notifyAlpha, E as ErrorBoundary, B as Block, m as notifyRootExtensionsHmr } from './fiber.B0oukOFO.mjs';
2
+ import { n as notifyDepreciated, i as isDevelopment, h as detachRootExtensions, j as attachRootExtensions, d as useIsomorphicLayoutEffect, c as context, a as useThree, u as useStore, k as unstageTextures, l as stageTextures, e as useMutableCallback, b as useFrame, f as useBridge, m as useGate, o as notifyAlpha, E as ErrorBoundary, B as Block, p as notifyRootExtensionsHmr } from './fiber.QxL_6jra.mjs';
3
3
  import * as React from 'react';
4
4
  import React__default, { useRef, useMemo, useLayoutEffect, useEffect, useImperativeHandle, useCallback, useState } from 'react';
5
5
  import useMeasure from 'react-use-measure';
@@ -107,6 +107,20 @@ function updateFrustum(camera, frustum) {
107
107
  target.setFromProjectionMatrix(frustumMatrix);
108
108
  return target;
109
109
  }
110
+ function computeInitialSize(canvas, size) {
111
+ if (!size && typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
112
+ const { width, height, top, left } = canvas.parentElement.getBoundingClientRect();
113
+ return { width, height, top, left };
114
+ } else if (!size && typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) {
115
+ return {
116
+ width: canvas.width,
117
+ height: canvas.height,
118
+ top: 0,
119
+ left: 0
120
+ };
121
+ }
122
+ return { width: 0, height: 0, top: 0, left: 0, ...size };
123
+ }
110
124
 
111
125
  const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
112
126
  function findInitialRoot(instance) {
@@ -174,6 +188,7 @@ function prepare(target, root, type, props) {
174
188
  object,
175
189
  eventCount: 0,
176
190
  handlers: {},
191
+ previousVisible: void 0,
177
192
  isHidden: false
178
193
  };
179
194
  if (object) object.__r3f = instance;
@@ -422,6 +437,28 @@ const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
422
437
  const INDEX_REGEX = /-\d+$/;
423
438
  const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
424
439
  const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
440
+ const programProps = /* @__PURE__ */ new Set([
441
+ "transparent",
442
+ "vertexColors",
443
+ "flatShading",
444
+ "fog",
445
+ // Culling is live, but DOUBLE_SIDED / FLIP_SIDED normal flipping is compiled in
446
+ "side",
447
+ "alphaHash",
448
+ "premultipliedAlpha",
449
+ // Both feed the OPAQUE define when the material is not transparent
450
+ "blending",
451
+ "alphaToCoverage",
452
+ "dithering",
453
+ "sizeAttenuation",
454
+ "combine",
455
+ "normalMapType",
456
+ "depthPacking"
457
+ ]);
458
+ function changesProgram(root, key, previous, next) {
459
+ if (!root?.isMaterial || previous === next) return false;
460
+ return programProps.has(key) || isTexture(previous) !== isTexture(next);
461
+ }
425
462
  function resolve(root, key) {
426
463
  if (!key.includes("-")) return { root, key, target: root[key] };
427
464
  if (key in root) return { root, key, target: root[key] };
@@ -452,6 +489,7 @@ function attach(parent, child) {
452
489
  const { root, key } = resolve(parent.object, child.props.attach);
453
490
  child.previousAttach = root[key];
454
491
  root[key] = child.object;
492
+ if (changesProgram(root, key, child.previousAttach, child.object)) root.needsUpdate = true;
455
493
  } else if (is.fun(child.props.attach)) {
456
494
  child.previousAttach = child.props.attach(parent.object, child.object);
457
495
  }
@@ -462,6 +500,7 @@ function detach(parent, child) {
462
500
  const previous = child.previousAttach;
463
501
  if (previous === void 0) delete root[key];
464
502
  else root[key] = previous;
503
+ if (changesProgram(root, key, child.object, previous)) root.needsUpdate = true;
465
504
  } else {
466
505
  child.previousAttach?.(parent.object, child.object);
467
506
  }
@@ -541,6 +580,10 @@ function applyProps(object, props) {
541
580
  }
542
581
  continue;
543
582
  }
583
+ if (prop === "visible" && instance?.isHidden && instance.previousVisible !== void 0) {
584
+ instance.previousVisible = value;
585
+ continue;
586
+ }
544
587
  let { root, key, target } = resolve(object, prop);
545
588
  if (target === void 0 && (typeof root !== "object" || root === null)) {
546
589
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -576,9 +619,12 @@ function applyProps(object, props) {
576
619
  }
577
620
  } else {
578
621
  if (key === "material") assertNodeMaterialSupported(rootState, value);
622
+ const previous = root[key];
579
623
  root[key] = value;
580
624
  if (key.endsWith("Node") && root.isMaterial) {
581
625
  root.needsUpdate = true;
626
+ } else if (changesProgram(root, key, previous, value)) {
627
+ root.needsUpdate = true;
582
628
  }
583
629
  const three = rootState && getThree();
584
630
  if (three && 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
@@ -619,468 +665,425 @@ function applyProps(object, props) {
619
665
  return object;
620
666
  }
621
667
 
622
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
623
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
624
- function getLoader(Proto) {
625
- if (isConstructor$1(Proto)) {
626
- let loader = memoizedLoaders.get(Proto);
627
- if (!loader) {
628
- loader = new Proto();
629
- memoizedLoaders.set(Proto, loader);
630
- }
631
- return loader;
632
- }
633
- return Proto;
634
- }
635
- function loadingFn(extensions, onProgress) {
636
- return function(Proto, input) {
637
- const loader = getLoader(Proto);
638
- if (extensions) extensions(loader);
639
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
640
- return loader.loadAsync(input, onProgress).then((data) => {
641
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
642
- return data;
643
- });
644
- }
645
- return new Promise(
646
- (res, reject) => loader.load(
647
- input,
648
- (data) => {
649
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
650
- res(data);
651
- },
652
- onProgress,
653
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
654
- )
655
- );
656
- };
657
- }
658
- function useLoader(loader, input, extensions, onProgress, cacheKey) {
659
- const inputs = Array.isArray(input) ? input : [input];
660
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
661
- const fn = loadingFn(extensions, onProgress);
662
- const results = cacheKeys.map(
663
- (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
664
- );
665
- const stableRef = useRef(null);
666
- const previous = stableRef.current;
667
- const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
668
- stableRef.current = stable;
669
- return Array.isArray(input) ? stable : stable[0];
668
+ const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
669
+ function isInternalRendererAccess(stack, callerDepth = 2) {
670
+ if (!stack) return false;
671
+ const caller = stack.split("\n")[callerDepth] ?? "";
672
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
670
673
  }
671
- useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
672
- const inputs = Array.isArray(input) ? input : [input];
673
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
674
- cacheKeys.forEach(
675
- (key, index) => preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
676
- );
677
- };
678
- useLoader.clear = function(loader, input, cacheKey) {
679
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
680
- cacheKeys.forEach((key) => clear([loader, key]));
681
- };
682
- useLoader.loader = getLoader;
683
674
 
684
- const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
685
- function getUrls(input) {
686
- if (typeof input === "string") return [input];
687
- if (Array.isArray(input)) return input;
688
- return Object.values(input);
689
- }
690
- function inputSignature(input) {
691
- if (typeof input === "string") return input;
692
- if (Array.isArray(input)) return input.join("\0");
693
- return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
694
- }
695
- function allUrlsCached(urls, textureCache) {
696
- return urls.every((url) => textureCache.has(url));
675
+ function updateSize(state, size, imperative) {
676
+ state.set((current) => ({
677
+ size,
678
+ viewport: { ...current.viewport, ...current.viewport.getCurrentViewport(current.camera, void 0, size) },
679
+ _sizeImperative: imperative
680
+ }));
681
+ state.invalidate();
697
682
  }
698
- function buildFromCache(input, textureCache) {
699
- if (typeof input === "string") {
700
- return textureCache.get(input);
701
- } else if (Array.isArray(input)) {
702
- return input.map((url) => textureCache.get(url));
703
- } else {
704
- const result = {};
705
- const objInput = input;
706
- for (const key in objInput) {
707
- result[key] = textureCache.get(objInput[key]);
708
- }
709
- return result;
710
- }
683
+ function configureSize(store, size) {
684
+ const state = store.getState();
685
+ if (state._sizeImperative || is.equ(size, state.size, { objects: "shallow", strict: false })) return;
686
+ updateSize(state, size, false);
711
687
  }
712
- function useTexture(input, optionsOrOnLoad) {
713
- const renderer = useThree((state) => state.internal.actualRenderer);
714
- const store = useStore();
715
- const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
716
- const { onLoad, cache = true } = options;
717
- const onLoadRef = useRef(onLoad);
718
- onLoadRef.current = onLoad;
719
- const onLoadCalledForRef = useRef(null);
720
- const inputKey = useMemo(() => inputSignature(input), [input]);
721
- const stableInput = useMemo(() => input, [inputKey]);
722
- const urls = useMemo(() => getUrls(stableInput), [stableInput]);
723
- const cachedResult = useMemo(() => {
724
- if (!cache) return null;
725
- const textures = store.getState().textures;
726
- if (!allUrlsCached(urls, textures)) return null;
727
- return buildFromCache(stableInput, textures);
728
- }, [cache, urls, stableInput, store]);
729
- const loadedTextures = useLoader(
730
- getThree().TextureLoader,
731
- IsObject(stableInput) ? Object.values(stableInput) : stableInput
732
- );
733
- const mappedTextures = useMemo(() => {
734
- if (cachedResult) return cachedResult;
735
- if (IsObject(stableInput)) {
736
- const keyed = {};
737
- const textureArray = loadedTextures;
738
- let i = 0;
739
- for (const key in stableInput) keyed[key] = textureArray[i++];
740
- return keyed;
741
- } else {
742
- return loadedTextures;
743
- }
744
- }, [stableInput, loadedTextures, cachedResult]);
745
- useLayoutEffect(() => {
746
- if (cachedResult) return;
747
- if (onLoadCalledForRef.current === inputKey) return;
748
- onLoadCalledForRef.current = inputKey;
749
- onLoadRef.current?.(mappedTextures);
750
- }, [cachedResult, mappedTextures, inputKey]);
751
- useEffect(() => {
752
- if (cachedResult) return;
753
- if ("initTexture" in renderer) {
754
- let textureArray = [];
755
- if (Array.isArray(loadedTextures)) {
756
- textureArray = loadedTextures;
757
- } else if (loadedTextures.isTexture) {
758
- textureArray = [loadedTextures];
759
- } else if (IsObject(loadedTextures)) {
760
- textureArray = Object.values(loadedTextures);
688
+ const createStore = (invalidate, advance) => {
689
+ const rootStore = createWithEqualityFn((set, get) => {
690
+ let position;
691
+ let defaultTarget;
692
+ let tempTarget;
693
+ function getCurrentViewport(camera = get().camera, target = void 0, size = get().size) {
694
+ const { Vector3 } = getThree();
695
+ position ?? (position = new Vector3());
696
+ defaultTarget ?? (defaultTarget = new Vector3());
697
+ tempTarget ?? (tempTarget = new Vector3());
698
+ target ?? (target = defaultTarget);
699
+ const { width, height, top, left } = size;
700
+ const aspect = width / height;
701
+ if (target.isVector3) tempTarget.copy(target);
702
+ else tempTarget.set(...target);
703
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
704
+ if (isOrthographicCamera(camera)) {
705
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
706
+ } else {
707
+ const fov = camera.fov * Math.PI / 180;
708
+ const h = 2 * Math.tan(fov / 2) * distance;
709
+ const w = h * (width / height);
710
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
761
711
  }
762
- textureArray.forEach((texture) => {
763
- if (texture?.isTexture) {
764
- renderer.initTexture(texture);
765
- }
766
- });
767
712
  }
768
- }, [renderer, loadedTextures, cachedResult]);
769
- useEffect(() => {
770
- if (!cache) return;
771
- const urlTextureMap = urlTextureEntries(stableInput, mappedTextures);
772
- store.setState((state) => {
773
- const refs = new Map(state._textureRefs);
774
- let textures = state.textures;
775
- let added = false;
776
- for (const [url, texture] of urlTextureMap) {
777
- if (!textures.has(url)) {
778
- if (!added) {
779
- textures = new Map(textures);
780
- added = true;
781
- }
782
- textures.set(url, texture);
713
+ let performanceTimeout = void 0;
714
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
715
+ const rootState = {
716
+ set,
717
+ get,
718
+ // Mock objects that have to be configured
719
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
720
+ primaryStore: null,
721
+ gl: null,
722
+ renderer: null,
723
+ camera: null,
724
+ // frustum, pointer and mouse are three objects; configure() creates them with the renderer
725
+ frustum: null,
726
+ autoUpdateFrustum: true,
727
+ raycaster: null,
728
+ events: {
729
+ priority: 1,
730
+ enabled: true,
731
+ connected: false,
732
+ frameTimedRaycasts: true,
733
+ alwaysFireOnScroll: true,
734
+ updateOnFrame: false
735
+ },
736
+ scene: null,
737
+ rootScene: null,
738
+ xr: null,
739
+ inspector: null,
740
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
741
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
742
+ textureColorSpace: "srgb",
743
+ // THREE.SRGBColorSpace
744
+ isLegacy: false,
745
+ webGPUSupported: false,
746
+ isNative: false,
747
+ controls: null,
748
+ pointer: null,
749
+ mouse: null,
750
+ frameloop: "always",
751
+ onPointerMissed: void 0,
752
+ onDragOverMissed: void 0,
753
+ onDropMissed: void 0,
754
+ performance: {
755
+ current: 1,
756
+ min: 0.5,
757
+ max: 1,
758
+ debounce: 200,
759
+ regress: () => {
760
+ const state2 = get();
761
+ if (performanceTimeout) clearTimeout(performanceTimeout);
762
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
763
+ performanceTimeout = setTimeout(
764
+ () => setPerformanceCurrent(get().performance.max),
765
+ state2.performance.debounce
766
+ );
783
767
  }
784
- refs.set(url, (refs.get(url) ?? 0) + 1);
785
- }
786
- return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
787
- });
788
- unstageTextures(
789
- store,
790
- urlTextureMap.map(([url]) => url)
791
- );
792
- return () => store.setState((state) => {
793
- const refs = new Map(state._textureRefs);
794
- for (const [url] of urlTextureMap) {
795
- const next = (refs.get(url) ?? 0) - 1;
796
- if (next <= 0) refs.delete(url);
797
- else refs.set(url, next);
798
- }
799
- return { _textureRefs: refs };
800
- });
801
- }, [cache, stableInput, mappedTextures, store]);
802
- if (cache && !cachedResult) stageTextures(store, urlTextureEntries(stableInput, mappedTextures));
803
- return mappedTextures;
804
- }
805
- function urlTextureEntries(input, textures) {
806
- if (typeof input === "string") return [[input, textures]];
807
- if (Array.isArray(input)) return input.map((url, i) => [url, textures[i]]);
808
- const record = textures;
809
- return Object.keys(input).map((key) => [input[key], record[key]]);
810
- }
811
- useTexture.preload = (url) => {
812
- whenThree().then((three) => useLoader.preload(three.TextureLoader, url));
813
- };
814
- useTexture.clear = (input) => {
815
- if (hasThree()) useLoader.clear(getThree().TextureLoader, input);
816
- };
817
- const Texture = ({
818
- children,
819
- input,
820
- onLoad,
821
- cache
822
- }) => {
823
- const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
824
- const ret = useTexture(input, options);
825
- return /* @__PURE__ */ jsx(Fragment, { children: children?.(ret) });
826
- };
827
-
828
- function useTextures(selector) {
829
- const store = useStore();
830
- const registry = useMemo(() => {
831
- const getState = store.getState;
832
- const setState = store.setState;
833
- const getOne = (key) => getState().textures.get(key);
834
- return {
835
- get all() {
836
- return getState().textures;
837
768
  },
838
- get(input) {
839
- if (typeof input === "string") return getOne(input);
840
- if (Array.isArray(input)) return input.map(getOne);
841
- const out = {};
842
- for (const name in input) out[name] = getOne(input[name]);
843
- return out;
769
+ size: { width: 0, height: 0, top: 0, left: 0 },
770
+ viewport: {
771
+ initialDpr: 0,
772
+ dpr: 0,
773
+ width: 0,
774
+ height: 0,
775
+ top: 0,
776
+ left: 0,
777
+ aspect: 0,
778
+ distance: 0,
779
+ factor: 0,
780
+ getCurrentViewport
844
781
  },
845
- has: (key) => getState().textures.has(key),
846
- add(keyOrRecord, texture) {
847
- setState((state) => {
848
- const textures = new Map(state.textures);
849
- if (typeof keyOrRecord === "string") {
850
- textures.set(keyOrRecord, texture);
851
- } else {
852
- for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
782
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
783
+ setSize: (width, height, top, left) => {
784
+ const state2 = get();
785
+ if (width === void 0) {
786
+ if (state2._sizeProps) {
787
+ const { width: propW, height: propH } = state2._sizeProps;
788
+ if (propW !== void 0 || propH !== void 0) {
789
+ const currentSize = state2.size;
790
+ const newSize = {
791
+ width: propW ?? currentSize.width,
792
+ height: propH ?? currentSize.height,
793
+ top: currentSize.top,
794
+ left: currentSize.left
795
+ };
796
+ updateSize(state2, newSize, false);
797
+ return;
798
+ }
853
799
  }
854
- return { textures };
855
- });
856
- },
857
- dispose(key, options) {
858
- const state = getState();
859
- const refs = state._textureRefs.get(key) ?? 0;
860
- if (refs > 0 && !options?.force) {
861
- console.warn(
862
- `[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
863
- );
864
- return false;
800
+ set({ _sizeImperative: false });
801
+ return;
865
802
  }
866
- state.textures.get(key)?.dispose();
867
- setState((s) => {
868
- const textures = new Map(s.textures);
869
- textures.delete(key);
870
- const nextRefs = new Map(s._textureRefs);
871
- nextRefs.delete(key);
872
- return { textures, _textureRefs: nextRefs };
873
- });
874
- return true;
803
+ const w = width;
804
+ const h = height ?? width;
805
+ const t = top ?? state2.size.top;
806
+ const l = left ?? state2.size.left;
807
+ const size = { width: w, height: h, top: t, left: l };
808
+ updateSize(state2, size, true);
875
809
  },
876
- disposeAll() {
877
- for (const texture of getState().textures.values()) texture.dispose();
878
- setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
810
+ setDpr: (dpr) => set((state2) => {
811
+ const resolved = calculateDpr(dpr);
812
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
813
+ }),
814
+ setFrameloop: (frameloop = "always") => {
815
+ const rootId = get().internal.rootId;
816
+ if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
817
+ set(() => ({ frameloop }));
818
+ },
819
+ setError: (error) => set(() => ({ error })),
820
+ error: null,
821
+ //* Texture registry (useTextures) ==============================
822
+ // TSL fields (uniforms, nodes, ..., renderPipeline) are added by @react-three/tsl's root extension.
823
+ textures: /* @__PURE__ */ new Map(),
824
+ _textureRefs: /* @__PURE__ */ new Map(),
825
+ _sizeImperative: false,
826
+ _sizeProps: null,
827
+ previousRoot: void 0,
828
+ internal: {
829
+ // Events
830
+ interaction: [],
831
+ subscribers: [],
832
+ // Per-pointer state (new unified structure)
833
+ pointerMap: /* @__PURE__ */ new Map(),
834
+ pointerDirty: /* @__PURE__ */ new Map(),
835
+ lastEvent: React.createRef(),
836
+ // Deprecated but kept for backwards compatibility
837
+ hovered: /* @__PURE__ */ new Map(),
838
+ initialClick: [0, 0],
839
+ initialHits: [],
840
+ capturedMap: /* @__PURE__ */ new Map(),
841
+ // Visibility tracking (onFramed, onOccluded, onVisible)
842
+ visibilityRegistry: /* @__PURE__ */ new Map(),
843
+ // Occlusion system (WebGPU only)
844
+ occlusionEnabled: false,
845
+ occlusionObserver: null,
846
+ occlusionCache: /* @__PURE__ */ new Map(),
847
+ helperGroup: null,
848
+ // Updates
849
+ active: false,
850
+ frames: 0,
851
+ priority: 0,
852
+ subscribe: (ref, priority, store) => {
853
+ const internal = get().internal;
854
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
855
+ internal.subscribers.push({ ref, priority, store });
856
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
857
+ return () => {
858
+ const internal2 = get().internal;
859
+ if (internal2?.subscribers) {
860
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
861
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
862
+ }
863
+ };
864
+ },
865
+ // Renderer Storage (single source of truth)
866
+ actualRenderer: null,
867
+ // The renderer support configure() loads for this root
868
+ support: null,
869
+ // Scheduler for useFrameNext (initialized in loop.ts)
870
+ scheduler: null,
871
+ // Replaces the default render call when set (see setRenderOverride)
872
+ renderOverride: null
879
873
  }
880
874
  };
881
- }, [store]);
882
- const subscribe = selector ? () => selector(registry) : (state) => state.textures;
883
- const selected = useThree(subscribe);
884
- return selector ? selected : registry;
885
- }
886
-
887
- function useRenderTarget(widthOrOptions, heightOrOptions, options) {
888
- const support = useThree((s) => s.internal.support);
889
- const size = useThree((s) => s.size);
890
- let width;
891
- let height;
892
- let opts;
893
- if (typeof widthOrOptions === "object") {
894
- opts = widthOrOptions;
895
- } else if (typeof widthOrOptions === "number") {
896
- width = widthOrOptions;
897
- if (typeof heightOrOptions === "object") {
898
- height = widthOrOptions;
899
- opts = heightOrOptions;
900
- } else if (typeof heightOrOptions === "number") {
901
- height = heightOrOptions;
902
- opts = options;
903
- } else {
904
- height = widthOrOptions;
875
+ return rootState;
876
+ });
877
+ const state = rootStore.getState();
878
+ Object.defineProperty(state, "gl", {
879
+ get() {
880
+ const currentState = rootStore.getState();
881
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
882
+ const stack = new Error().stack || "";
883
+ if (!isInternalRendererAccess(stack)) {
884
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
885
+ notifyDepreciated({
886
+ heading: "Accessing state.gl in WebGPU mode",
887
+ 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
888
+ });
889
+ }
890
+ }
891
+ return currentState.internal.actualRenderer;
892
+ },
893
+ set(value) {
894
+ rootStore.getState().internal.actualRenderer = value;
895
+ },
896
+ enumerable: true,
897
+ configurable: true
898
+ });
899
+ Object.defineProperty(state, "renderer", {
900
+ get() {
901
+ return rootStore.getState().internal.actualRenderer;
902
+ },
903
+ set(value) {
904
+ rootStore.getState().internal.actualRenderer = value;
905
+ },
906
+ enumerable: true,
907
+ configurable: true
908
+ });
909
+ let oldScene = state.scene;
910
+ rootStore.subscribe(() => {
911
+ const currentState = rootStore.getState();
912
+ const { scene, rootScene, set } = currentState;
913
+ if (scene !== oldScene) {
914
+ oldScene = scene;
915
+ if (scene?.isScene && scene !== rootScene) {
916
+ set({ rootScene: scene });
917
+ }
905
918
  }
906
- }
907
- return useMemo(() => {
908
- const w = width ?? size.width;
909
- const h = height ?? size.height;
910
- const Target = support.RenderTarget;
911
- return new Target(w, h, opts);
912
- }, [width, height, size.width, size.height, opts, support]);
913
- }
914
-
915
- const presetsObj = {
916
- apartment: "lebombo_1k.hdr",
917
- city: "potsdamer_platz_1k.hdr",
918
- dawn: "kiara_1_dawn_1k.hdr",
919
- forest: "forest_slope_1k.hdr",
920
- lobby: "st_fagans_interior_1k.hdr",
921
- night: "dikhololo_night_1k.hdr",
922
- park: "rooitou_park_1k.hdr",
923
- studio: "studio_small_03_1k.hdr",
924
- sunset: "venice_sunset_1k.hdr",
925
- warehouse: "empty_warehouse_01_1k.hdr"
919
+ });
920
+ let oldSize = state.size;
921
+ let oldDpr = state.viewport.dpr;
922
+ let oldCamera = state.camera;
923
+ rootStore.subscribe(() => {
924
+ const { camera, size, viewport, set, internal } = rootStore.getState();
925
+ const actualRenderer = internal.actualRenderer;
926
+ const canvasTarget = internal.canvasTarget;
927
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
928
+ oldSize = size;
929
+ oldDpr = viewport.dpr;
930
+ updateCamera(camera, size);
931
+ const resizeTarget = canvasTarget ?? actualRenderer;
932
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
933
+ resizeTarget.setSize(size.width, size.height, false);
934
+ const activeTarget = actualRenderer.getCanvasTarget?.();
935
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
936
+ }
937
+ if (camera !== oldCamera) {
938
+ oldCamera = camera;
939
+ const { rootScene } = rootStore.getState();
940
+ if (camera && rootScene && !camera.parent) {
941
+ rootScene.add(camera);
942
+ }
943
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
944
+ const currentState = rootStore.getState();
945
+ if (currentState.autoUpdateFrustum && camera) {
946
+ updateFrustum(camera, currentState.frustum);
947
+ }
948
+ }
949
+ });
950
+ rootStore.subscribe((state2) => {
951
+ if (state2.internal.active) invalidate(state2);
952
+ });
953
+ return rootStore;
926
954
  };
927
955
 
928
- const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
929
- const isArray = (arr) => Array.isArray(arr);
930
- const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
931
- const loadedLoaders = /* @__PURE__ */ new Map();
932
- const pendingLoaders = /* @__PURE__ */ new Map();
933
- function loadLoader(format) {
934
- let pending = pendingLoaders.get(format);
935
- if (!pending) {
936
- pending = importLoader(format).then(
937
- (loader) => {
938
- loadedLoaders.set(format, loader);
939
- return loader;
956
+ const shallowLoose$1 = { objects: "shallow", strict: false };
957
+ function configureScheduler(store, canvasId, schedulerConfig, previous) {
958
+ const state = store.getState();
959
+ const scheduler = getScheduler();
960
+ const rootId = state.internal.rootId;
961
+ const ordersItself = schedulerConfig?.before !== void 0 || schedulerConfig?.after !== void 0;
962
+ const rootBefore = schedulerConfig?.before;
963
+ const rootAfter = ordersItself ? schedulerConfig?.after : state.internal.sharedAfter;
964
+ if (!rootId) {
965
+ const newRootId = state.internal.pendingRootId ?? canvasId ?? scheduler.generateRootId();
966
+ const unregisterRoot = scheduler.registerRoot(newRootId, {
967
+ getState: () => store.getState(),
968
+ onError: (err) => store.getState().setError(err),
969
+ frameloop: store.getState().frameloop,
970
+ before: rootBefore,
971
+ after: rootAfter,
972
+ order: schedulerConfig?.order
973
+ });
974
+ const unregisterCanvasTarget = scheduler.register(
975
+ () => {
976
+ const state2 = store.getState();
977
+ const canvasTarget = state2.internal.canvasTarget;
978
+ if (canvasTarget) {
979
+ const renderer = state2.internal.actualRenderer;
980
+ if (renderer.getCanvasTarget() !== canvasTarget) renderer.setCanvasTarget(canvasTarget);
981
+ if (state2.internal.canvasTargetSizeDirty) {
982
+ state2.internal.canvasTargetSizeDirty = false;
983
+ renderer.backend?.updateSize?.();
984
+ }
985
+ }
940
986
  },
941
- (error) => {
942
- pendingLoaders.delete(format);
943
- throw error;
987
+ {
988
+ id: `${newRootId}_canvasTarget`,
989
+ rootId: newRootId,
990
+ phase: "start",
991
+ system: true
944
992
  }
945
993
  );
946
- pendingLoaders.set(format, pending);
947
- }
948
- return pending;
949
- }
950
- async function importLoader(format) {
951
- let loader;
952
- switch (format) {
953
- case "cube":
954
- loader = getThree().CubeTextureLoader;
955
- break;
956
- case "hdr-cube":
957
- loader = (await import('three/examples/jsm/loaders/HDRCubeTextureLoader.js')).HDRCubeTextureLoader;
958
- break;
959
- case "hdr":
960
- loader = (await import('three/examples/jsm/loaders/HDRLoader.js')).HDRLoader;
961
- break;
962
- case "exr":
963
- loader = (await import('three/examples/jsm/loaders/EXRLoader.js')).EXRLoader;
964
- break;
965
- case "jpg":
966
- loader = (await import('three/examples/jsm/loaders/UltraHDRLoader.js')).UltraHDRLoader;
967
- break;
968
- }
969
- return loader;
970
- }
971
- const LOADER_KEY = Symbol("r3f-environment-loader");
972
- function useEnvironmentLoader(format) {
973
- return suspend(() => loadLoader(format), [LOADER_KEY, format]);
974
- }
975
- function useEnvironment({
976
- files = defaultFiles,
977
- path = "",
978
- preset = void 0,
979
- colorSpace = void 0,
980
- extensions
981
- } = {}) {
982
- if (preset) {
983
- validatePreset(preset);
984
- files = presetsObj[preset];
985
- path = CUBEMAP_ROOT;
986
- }
987
- const multiFile = isArray(files);
988
- const { format, isCubemap } = getFormat(files);
989
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
990
- const loader = useEnvironmentLoader(format);
991
- const loaderResult = useLoader(
992
- loader,
993
- multiFile ? [files] : files,
994
- (loader2) => {
995
- loader2.setPath?.(path);
996
- if (extensions) extensions(loader2);
997
- }
998
- );
999
- const texture = multiFile ? (
1000
- // @ts-ignore
1001
- loaderResult[0]
1002
- ) : loaderResult;
1003
- const three = getThree();
1004
- texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
1005
- texture.colorSpace = colorSpace ?? (isCubemap && format !== "hdr-cube" ? "srgb" : "srgb-linear");
1006
- return texture;
1007
- }
1008
- const preloadDefaultOptions = {
1009
- files: defaultFiles,
1010
- path: "",
1011
- preset: void 0,
1012
- extensions: void 0
1013
- };
1014
- useEnvironment.preload = (preloadOptions) => {
1015
- const options = { ...preloadDefaultOptions, ...preloadOptions };
1016
- let { files, path = "" } = options;
1017
- const { preset, extensions } = options;
1018
- if (preset) {
1019
- validatePreset(preset);
1020
- files = presetsObj[preset];
1021
- path = CUBEMAP_ROOT;
1022
- }
1023
- const { format } = getFormat(files);
1024
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1025
- const input = isArray(files) ? [files] : files;
1026
- loadLoader(format).then((loader) => {
1027
- useLoader.preload(loader, input, (loader2) => {
1028
- loader2.setPath?.(path);
1029
- if (extensions) extensions(loader2);
1030
- });
1031
- });
1032
- };
1033
- const clearDefaultOptins = {
1034
- files: defaultFiles,
1035
- preset: void 0
1036
- };
1037
- useEnvironment.clear = (clearOptions) => {
1038
- const options = { ...clearDefaultOptins, ...clearOptions };
1039
- let { files } = options;
1040
- const { preset } = options;
1041
- if (preset) {
1042
- validatePreset(preset);
1043
- files = presetsObj[preset];
1044
- }
1045
- const { format } = getFormat(files);
1046
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1047
- const input = isArray(files) ? [files] : files;
1048
- const clear = (loader2) => useLoader.clear(loader2, input);
1049
- const loader = loadedLoaders.get(format);
1050
- if (loader) {
1051
- clear(loader);
994
+ const unregisterEventsFlush = scheduler.register(
995
+ () => {
996
+ const state2 = store.getState();
997
+ state2.events.flush?.();
998
+ },
999
+ {
1000
+ id: `${newRootId}_events`,
1001
+ rootId: newRootId,
1002
+ phase: "input",
1003
+ system: true
1004
+ }
1005
+ );
1006
+ const unregisterFrustum = scheduler.register(
1007
+ () => {
1008
+ const state2 = store.getState();
1009
+ if (state2.autoUpdateFrustum && state2.camera) {
1010
+ updateFrustum(state2.camera, state2.frustum);
1011
+ }
1012
+ },
1013
+ {
1014
+ id: `${newRootId}_frustum`,
1015
+ rootId: newRootId,
1016
+ before: "render",
1017
+ system: true
1018
+ }
1019
+ );
1020
+ const unregisterVisibility = scheduler.register(
1021
+ () => {
1022
+ const state2 = store.getState();
1023
+ checkVisibility(state2);
1024
+ },
1025
+ {
1026
+ id: `${newRootId}_visibility`,
1027
+ rootId: newRootId,
1028
+ before: "render",
1029
+ system: true,
1030
+ after: `${newRootId}_frustum`
1031
+ }
1032
+ );
1033
+ const unregisterRender = scheduler.register(
1034
+ () => {
1035
+ const state2 = store.getState();
1036
+ const renderer = state2.internal.actualRenderer;
1037
+ const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
1038
+ if (userHandlesRender || state2.internal.priority) return;
1039
+ try {
1040
+ const renderOverride = state2.internal.renderOverride;
1041
+ if (renderOverride) renderOverride();
1042
+ else if (renderer?.render) renderer.render(state2.scene, state2.camera);
1043
+ } catch (error) {
1044
+ state2.setError(error instanceof Error ? error : new Error(String(error)));
1045
+ }
1046
+ },
1047
+ {
1048
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
1049
+ id: canvasId || `${newRootId}_render`,
1050
+ rootId: newRootId,
1051
+ phase: "render",
1052
+ system: true,
1053
+ // Internal flag: this is a system job, not user-controlled
1054
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
1055
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
1056
+ }
1057
+ );
1058
+ state.set((state2) => ({
1059
+ internal: {
1060
+ ...state2.internal,
1061
+ rootId: newRootId,
1062
+ pendingRootId: void 0,
1063
+ unregisterRoot: () => {
1064
+ unregisterRoot();
1065
+ unregisterCanvasTarget();
1066
+ unregisterEventsFlush();
1067
+ unregisterFrustum();
1068
+ unregisterVisibility();
1069
+ unregisterRender();
1070
+ },
1071
+ scheduler
1072
+ }
1073
+ }));
1052
1074
  } else {
1053
- const pending = pendingLoaders.get(format);
1054
- if (pending) void pending.then(clear, () => {
1055
- });
1075
+ const constraintsChanged = !is.equ(schedulerConfig?.before, previous?.before, shallowLoose$1) || !is.equ(schedulerConfig?.after, previous?.after, shallowLoose$1);
1076
+ if (constraintsChanged) {
1077
+ scheduler.setRootConstraints(rootId, {
1078
+ before: rootBefore,
1079
+ after: rootAfter
1080
+ });
1081
+ }
1082
+ if (schedulerConfig?.order !== previous?.order) {
1083
+ scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
1084
+ }
1056
1085
  }
1057
- };
1058
- function validatePreset(preset) {
1059
- if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
1060
- }
1061
- function getFormat(files) {
1062
- const isCubemap = isArray(files) && files.length === 6;
1063
- const firstEntry = isArray(files) ? files[0] : files;
1064
- const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1065
- let format;
1066
- if (isCubemap) format = firstExtension === "hdr" ? "hdr-cube" : "cube";
1067
- else if (firstEntry.startsWith("data:application/exr")) format = "exr";
1068
- else if (firstEntry.startsWith("data:application/hdr")) format = "hdr";
1069
- else if (firstEntry.startsWith("data:image/jpeg")) format = "jpg";
1070
- else if (firstExtension === "hdr" || firstExtension === "exr") format = firstExtension;
1071
- else if (firstExtension === "jpg" || firstExtension === "jpeg") format = "jpg";
1072
- return { format, isCubemap };
1073
- }
1074
-
1075
- function useInstanceHandle(ref) {
1076
- const instance = useRef(null);
1077
- useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1078
- return instance;
1079
- }
1080
- function useGraph(object) {
1081
- return useMemo(() => buildGraph(object), [object]);
1082
1086
  }
1083
-
1084
1087
  let effectId = 0;
1085
1088
  function addEffect(callback) {
1086
1089
  notifyDepreciated({
@@ -1121,22 +1124,815 @@ function advance(timestamp, runGlobalEffects, state) {
1121
1124
  else scheduler.step(timestamp);
1122
1125
  }
1123
1126
 
1124
- const version = "10.0.0-alpha.5";
1125
- const packageData = {
1126
- version: version};
1127
+ const isPromiseLike = (value) => typeof value?.then === "function";
1128
+ const fulfilled = (value) => Object.assign(Promise.resolve(value), { status: "fulfilled", value });
1129
+ const rejected = (reason) => {
1130
+ const promise = Promise.reject(reason);
1131
+ promise.catch(() => {
1132
+ });
1133
+ return Object.assign(promise, { status: "rejected", reason });
1134
+ };
1135
+ const deferred = () => {
1136
+ let resolve;
1137
+ let reject;
1138
+ const promise = new Promise((yes, no) => {
1139
+ resolve = (value) => {
1140
+ Object.assign(promise, { status: "fulfilled", value });
1141
+ yes(value);
1142
+ };
1143
+ reject = (reason) => {
1144
+ Object.assign(promise, { status: "rejected", reason });
1145
+ no(reason);
1146
+ };
1147
+ });
1148
+ promise.status = "pending";
1149
+ promise.catch(() => {
1150
+ });
1151
+ return { promise, resolve, reject };
1152
+ };
1153
+ function resolveThen(value, apply) {
1154
+ return isPromiseLike(value) ? Promise.resolve(value).then(apply) : apply(value);
1155
+ }
1127
1156
 
1128
- function $1(Ut) {
1129
- return Ut && Ut.__esModule && Object.prototype.hasOwnProperty.call(Ut, "default") ? Ut.default : Ut;
1157
+ const isRenderer = (def) => !!def?.render;
1158
+ function ensureStoreThreeObjects(store) {
1159
+ const state = store.getState();
1160
+ if (state.pointer) return;
1161
+ const { Vector2, Frustum } = getThree();
1162
+ const pointer = new Vector2();
1163
+ state.set({ pointer, mouse: pointer, frustum: new Frustum() });
1130
1164
  }
1131
- var S0 = { exports: {} }, Ob = { exports: {} }, P1;
1132
- function Q1() {
1133
- return P1 || (P1 = 1, (function(Ut) {
1134
- Ut.exports = function(m) {
1135
- function et(t, r, a, l) {
1136
- return new Fe(t, r, a, l);
1137
- }
1138
- function zf() {
1139
- }
1165
+ const R3F_RENDERER_SETTINGS = ["textureColorSpace", "shadows"];
1166
+ const NON_APPLIED_RENDERER_PROPS = /* @__PURE__ */ new Set([
1167
+ ...R3F_RENDERER_SETTINGS,
1168
+ "canvas",
1169
+ "antialias",
1170
+ "alpha",
1171
+ "samples",
1172
+ "powerPreference",
1173
+ "depth",
1174
+ "stencil",
1175
+ "logarithmicDepthBuffer",
1176
+ "reversedDepthBuffer",
1177
+ // WebGPURenderer only
1178
+ "forceWebGL",
1179
+ "outputBufferType",
1180
+ "multiview",
1181
+ "getFallback",
1182
+ "trackTimestamp"
1183
+ ]);
1184
+ function settingsBag(config) {
1185
+ return is.obj(config) && !isRenderer(config) ? config : void 0;
1186
+ }
1187
+ function isInstanceOrFactory(config) {
1188
+ return is.fun(config) || isRenderer(config);
1189
+ }
1190
+ function assertNoRemovedConfig(props) {
1191
+ if (props.primaryCanvas !== void 0) {
1192
+ throw new Error(
1193
+ 'R3F: `primaryCanvas` was removed: mark the owner <Canvas primary> and other canvases share it automatically (or use share="id")'
1194
+ );
1195
+ }
1196
+ if (props.shadows !== void 0) {
1197
+ throw new Error(
1198
+ "R3F: the `shadows` Canvas prop moved into the renderer settings: use `renderer={{ shadows }}` (or `gl={{ shadows }}` for WebGL and @react-three/fiber/legacy)"
1199
+ );
1200
+ }
1201
+ for (const name of ["renderer", "gl"]) {
1202
+ const bag = settingsBag(props[name]);
1203
+ if (!bag) continue;
1204
+ if ("primaryCanvas" in bag) {
1205
+ throw new Error(
1206
+ `R3F: \`${name}={{ primaryCanvas }}\` was removed: mark the owner <Canvas primary> and other canvases share it automatically (or use share="id")`
1207
+ );
1208
+ }
1209
+ if ("scheduler" in bag) {
1210
+ throw new Error(`R3F: \`${name}={{ scheduler }}\` moved to <Canvas scheduler>`);
1211
+ }
1212
+ }
1213
+ }
1214
+ function resolveRenderer(config, defaultProps, RendererClass) {
1215
+ if (typeof config === "function") {
1216
+ return resolveThen(config(defaultProps), (renderer) => ({
1217
+ renderer,
1218
+ owned: true
1219
+ }));
1220
+ }
1221
+ if (isRenderer(config)) return { renderer: config, owned: false };
1222
+ const params = { ...defaultProps };
1223
+ for (const key in settingsBag(config)) if (!R3F_RENDERER_SETTINGS.includes(key)) params[key] = config[key];
1224
+ return { renderer: new RendererClass(params), owned: true };
1225
+ }
1226
+ function createOwnedWebGPURenderer(rendererConfig, defaultGPUProps, canvas, propsSize, dpr, support) {
1227
+ return resolveThen(resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer), (resolved) => {
1228
+ const renderer = resolved.renderer;
1229
+ if (!renderer.hasInitialized?.()) {
1230
+ const size = computeInitialSize(canvas, propsSize);
1231
+ if (size.width > 0 && size.height > 0) {
1232
+ renderer.setPixelRatio(calculateDpr(dpr));
1233
+ renderer.setSize(size.width, size.height, false);
1234
+ }
1235
+ return resolveThen(renderer.init(), () => ({ renderer, owned: resolved.owned }));
1236
+ }
1237
+ return { renderer, owned: resolved.owned };
1238
+ });
1239
+ }
1240
+ function initializeRenderer(canvas, provider, store, props) {
1241
+ const {
1242
+ id: canvasId,
1243
+ primary = false,
1244
+ share,
1245
+ gl: glConfig,
1246
+ renderer: rendererConfig,
1247
+ size: propsSize,
1248
+ dpr = [1, 2],
1249
+ _primaryToken
1250
+ } = props;
1251
+ assertNoRemovedConfig(props);
1252
+ const rendererBag = settingsBag(rendererConfig);
1253
+ const state = store.getState();
1254
+ const { webgl, webgpu } = provider;
1255
+ const defaultGLProps = {
1256
+ canvas,
1257
+ powerPreference: "high-performance",
1258
+ antialias: true,
1259
+ alpha: true
1260
+ };
1261
+ const defaultGPUProps = {
1262
+ canvas,
1263
+ antialias: true
1264
+ };
1265
+ if (glConfig && !webgl) {
1266
+ throw new Error(
1267
+ "WebGLRenderer (gl prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/legacy instead."
1268
+ );
1269
+ }
1270
+ if (rendererConfig && !webgpu) {
1271
+ throw new Error(
1272
+ "WebGPURenderer (renderer prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
1273
+ );
1274
+ }
1275
+ if (glConfig && rendererConfig) {
1276
+ throw new Error("Cannot use both gl and renderer props at the same time");
1277
+ }
1278
+ if (primary && !webgpu) {
1279
+ throw new Error(
1280
+ "R3F: <Canvas primary> shares a WebGPU renderer and is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
1281
+ );
1282
+ }
1283
+ if (primary && glConfig) {
1284
+ throw new Error(
1285
+ "R3F: <Canvas primary> shares a WebGPU renderer and cannot be used with WebGL. Remove the `gl` prop or use the `renderer` prop."
1286
+ );
1287
+ }
1288
+ if (primary && is.str(share)) {
1289
+ throw new Error(`R3F: a <Canvas primary> owns its renderer; remove share="${share}" from it.`);
1290
+ }
1291
+ if (is.str(share)) {
1292
+ if (!webgpu || glConfig) {
1293
+ throw new Error(
1294
+ `R3F: share="${share}" borrows a WebGPU renderer and cannot be used with WebGL. Remove the \`gl\` prop, or use @react-three/fiber or @react-three/fiber/webgpu.`
1295
+ );
1296
+ }
1297
+ if (isInstanceOrFactory(rendererConfig)) {
1298
+ throw new Error(
1299
+ `R3F: share="${share}" borrows the primary's renderer, so it cannot be combined with a renderer instance or factory. Remove one of the two.`
1300
+ );
1301
+ }
1302
+ }
1303
+ const primaryKey = canvasId || DEFAULT_PRIMARY;
1304
+ const primaryToken = _primaryToken ?? store;
1305
+ if (primary && !state.internal.actualRenderer) {
1306
+ announcePrimary(primaryKey, primaryToken);
1307
+ state.internal.unregisterPrimary = () => withdrawPrimary(primaryKey, primaryToken);
1308
+ if (primaryConflict(primaryKey, primaryToken)) {
1309
+ throw new Error(
1310
+ primaryKey === DEFAULT_PRIMARY ? 'R3F: two <Canvas primary> are mounted without ids. Give each primary a distinct `id`, and point the other canvases at one with share="id".' : `R3F: two <Canvas primary> use the id "${primaryKey}". Give each primary a distinct \`id\`.`
1311
+ );
1312
+ }
1313
+ }
1314
+ let shareKey;
1315
+ if (!state.internal.actualRenderer && webgpu && !primary && !glConfig && !state.isLegacy) {
1316
+ if (is.str(share)) {
1317
+ shareKey = share;
1318
+ } else if (share !== false && !isInstanceOrFactory(rendererConfig)) {
1319
+ const live = livePrimaryKeys();
1320
+ if (live.length > 1) {
1321
+ throw new Error(
1322
+ `R3F: ${live.length} primaries are mounted (${live.map(describePrimary).join(", ")}), so this canvas cannot pick one to share. Pass share="id" to name one, or share={false} for its own renderer.`
1323
+ );
1324
+ }
1325
+ shareKey = live[0];
1326
+ }
1327
+ }
1328
+ const explicitShare = is.str(share);
1329
+ const wantsGL = !!webgl && (state.isLegacy || !!glConfig || !webgpu || !rendererConfig && !primary && shareKey === void 0);
1330
+ if (webgl && webgpu && !state.isLegacy && wantsGL) {
1331
+ notifyDepreciated({
1332
+ heading: "WebGlRenderer Usage",
1333
+ body: "WebGlRenderer usage is deprecated in favor of WebGPU. Import from /legacy directly or upgrade to WebGPU.",
1334
+ link: "https://docs.pmnd.rs/react-three-fiber/api/renderer"
1335
+ });
1336
+ }
1337
+ let renderer = state.internal.actualRenderer;
1338
+ if (!state.internal.actualRenderer) {
1339
+ return resolveThen(wantsGL ? webgl() : webgpu(), (support) => {
1340
+ registerThree(support.three);
1341
+ state.internal.support = support;
1342
+ ensureStoreThreeObjects(store);
1343
+ if (support.kind === "webgl") {
1344
+ return resolveThen(resolveRenderer(glConfig, defaultGLProps, support.Renderer), (resolved) => {
1345
+ renderer = resolved.renderer;
1346
+ state.internal.actualRenderer = renderer;
1347
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1348
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
1349
+ });
1350
+ }
1351
+ const primaryReady = shareKey === void 0 ? null : explicitShare ? waitForPrimary(shareKey) : waitForAnnouncedPrimary(shareKey);
1352
+ return resolveThen(primaryReady, (primaryEntry) => {
1353
+ if (primaryEntry && primaryEntry.store.getState().webGPUSupported) {
1354
+ renderer = primaryEntry.renderer;
1355
+ state.internal.actualRenderer = renderer;
1356
+ state.internal.releaseRenderer = borrowRenderer(renderer);
1357
+ state.internal.sharedAfter = primaryEntry.rootId;
1358
+ const canvasTarget = new support.CanvasTarget(canvas);
1359
+ primaryEntry.store.setState((prev) => ({
1360
+ internal: { ...prev.internal, isMultiCanvas: true }
1361
+ }));
1362
+ state.set((prev) => ({
1363
+ webGPUSupported: primaryEntry.store.getState().webGPUSupported,
1364
+ renderer,
1365
+ primaryStore: primaryEntry.store,
1366
+ internal: {
1367
+ ...prev.internal,
1368
+ canvasTarget,
1369
+ isMultiCanvas: true,
1370
+ isSecondary: true,
1371
+ targetId: shareKey === DEFAULT_PRIMARY ? void 0 : shareKey
1372
+ }
1373
+ }));
1374
+ return;
1375
+ }
1376
+ if (primaryEntry) {
1377
+ return resolveThen(
1378
+ createOwnedWebGPURenderer(
1379
+ { ...rendererBag, forceWebGL: true },
1380
+ defaultGPUProps,
1381
+ canvas,
1382
+ propsSize,
1383
+ dpr,
1384
+ support
1385
+ ),
1386
+ (resolved) => {
1387
+ renderer = resolved.renderer;
1388
+ const backend = renderer.backend;
1389
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
1390
+ state.internal.actualRenderer = renderer;
1391
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1392
+ state.set((prev) => ({
1393
+ webGPUSupported: isWebGPUBackend,
1394
+ renderer,
1395
+ primaryStore: store,
1396
+ internal: {
1397
+ ...prev.internal,
1398
+ canvasTarget: renderer.getCanvasTarget?.(),
1399
+ sharedRendererFallback: true
1400
+ }
1401
+ }));
1402
+ }
1403
+ );
1404
+ }
1405
+ return resolveThen(
1406
+ createOwnedWebGPURenderer(rendererConfig, defaultGPUProps, canvas, propsSize, dpr, support),
1407
+ (resolved) => {
1408
+ var _a;
1409
+ renderer = resolved.renderer;
1410
+ const backend = renderer.backend;
1411
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
1412
+ state.internal.actualRenderer = renderer;
1413
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1414
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
1415
+ if (primary) {
1416
+ const canvasTarget = renderer.getCanvasTarget?.();
1417
+ (_a = state.internal).pendingRootId ?? (_a.pendingRootId = canvasId || getScheduler().generateRootId());
1418
+ registerPrimary(primaryKey, renderer, store, {
1419
+ token: primaryToken,
1420
+ rootId: state.internal.pendingRootId
1421
+ });
1422
+ state.set((prev) => ({ internal: { ...prev.internal, canvasTarget } }));
1423
+ } else if (share !== false && !isInstanceOrFactory(rendererConfig)) {
1424
+ state.internal.untrackStandalone = trackStandalone(store);
1425
+ }
1426
+ }
1427
+ );
1428
+ });
1429
+ });
1430
+ }
1431
+ }
1432
+ const records = /* @__PURE__ */ new WeakMap();
1433
+ function take(renderer, record) {
1434
+ record.leases++;
1435
+ let released = false;
1436
+ return () => {
1437
+ if (released) return;
1438
+ released = true;
1439
+ if (--record.leases > 0) return;
1440
+ records.delete(renderer);
1441
+ if (record.owned) return disposeRenderer(renderer);
1442
+ };
1443
+ }
1444
+ function leaseRenderer(renderer, owned) {
1445
+ let record = records.get(renderer);
1446
+ if (!record) records.set(renderer, record = { owned, leases: 0 });
1447
+ return take(renderer, record);
1448
+ }
1449
+ function borrowRenderer(renderer) {
1450
+ const record = records.get(renderer);
1451
+ if (!record) throw new Error("R3F: cannot share a renderer whose canvas has already unmounted");
1452
+ return take(renderer, record);
1453
+ }
1454
+ function disposeRenderer(renderer) {
1455
+ if (renderer.hasInitialized?.() === false) return;
1456
+ const warn = (error) => console.warn("[R3F] Error disposing renderer", error);
1457
+ const loseContext = () => {
1458
+ try {
1459
+ ;
1460
+ renderer.forceContextLoss?.();
1461
+ } catch (error) {
1462
+ warn(error);
1463
+ }
1464
+ };
1465
+ let disposed;
1466
+ try {
1467
+ disposed = renderer.dispose ? renderer.dispose() : renderer.renderLists?.dispose?.();
1468
+ } catch (error) {
1469
+ warn(error);
1470
+ }
1471
+ if (typeof disposed?.then === "function") {
1472
+ return Promise.resolve(disposed).then(loseContext, (error) => {
1473
+ warn(error);
1474
+ loseContext();
1475
+ });
1476
+ }
1477
+ loseContext();
1478
+ }
1479
+ const DEFAULT_PRIMARY = "\0r3f:primary";
1480
+ const primaryRegistry = /* @__PURE__ */ new Map();
1481
+ const announcements = /* @__PURE__ */ new Map();
1482
+ const withdrawing = /* @__PURE__ */ new Set();
1483
+ const pendingSubscribers = /* @__PURE__ */ new Map();
1484
+ const standaloneRoots = /* @__PURE__ */ new Set();
1485
+ const warnedLatePrimary = /* @__PURE__ */ new WeakSet();
1486
+ function describePrimary(key) {
1487
+ return key === DEFAULT_PRIMARY ? "<Canvas primary>" : `<Canvas id="${key}" primary>`;
1488
+ }
1489
+ function notify(key, entry) {
1490
+ const subscribers = pendingSubscribers.get(key);
1491
+ if (!subscribers) return;
1492
+ pendingSubscribers.delete(key);
1493
+ subscribers.forEach((callback) => callback(entry));
1494
+ }
1495
+ function isLive(key) {
1496
+ const owners = announcements.get(key);
1497
+ if (owners) {
1498
+ for (const owner of owners) if (!withdrawing.has(owner)) return true;
1499
+ }
1500
+ const entry = primaryRegistry.get(key);
1501
+ return !!entry && !(entry.token && withdrawing.has(entry.token));
1502
+ }
1503
+ function announcePrimary(key, owner) {
1504
+ withdrawing.delete(owner);
1505
+ let owners = announcements.get(key);
1506
+ if (!owners) announcements.set(key, owners = /* @__PURE__ */ new Set());
1507
+ if (owners.has(owner)) return;
1508
+ owners.add(owner);
1509
+ if (standaloneRoots.size > 0 && !warnedLatePrimary.has(owner) && isDevelopment()) {
1510
+ warnedLatePrimary.add(owner);
1511
+ console.warn(
1512
+ `R3F: ${describePrimary(key)} mounted after ${standaloneRoots.size} other WebGPU canvas(es) had already created their own renderer; those keep it and do not share the primary. Mount the primary with or before the canvases that share it, or give them share="id" so they wait for it.`
1513
+ );
1514
+ }
1515
+ }
1516
+ function markWithdrawing(owner) {
1517
+ withdrawing.add(owner);
1518
+ }
1519
+ function withdrawPrimary(key, owner) {
1520
+ withdrawing.delete(owner);
1521
+ const owners = announcements.get(key);
1522
+ if (owners) {
1523
+ owners.delete(owner);
1524
+ if (owners.size === 0) announcements.delete(key);
1525
+ }
1526
+ const entry = primaryRegistry.get(key);
1527
+ if (entry?.token === owner) primaryRegistry.delete(key);
1528
+ if (!isLive(key)) notify(key, null);
1529
+ }
1530
+ function primaryConflict(key, owner) {
1531
+ const owners = announcements.get(key);
1532
+ if (owners) {
1533
+ for (const other of owners) {
1534
+ if (other === owner) break;
1535
+ if (!withdrawing.has(other)) return true;
1536
+ }
1537
+ }
1538
+ const entry = primaryRegistry.get(key);
1539
+ return !!entry && entry.token !== owner && !(entry.token && withdrawing.has(entry.token));
1540
+ }
1541
+ function livePrimaryKeys() {
1542
+ const keys = /* @__PURE__ */ new Set([...announcements.keys(), ...primaryRegistry.keys()]);
1543
+ return [...keys].filter(isLive);
1544
+ }
1545
+ function trackStandalone(owner) {
1546
+ standaloneRoots.add(owner);
1547
+ return () => {
1548
+ standaloneRoots.delete(owner);
1549
+ };
1550
+ }
1551
+ function registerPrimary(id, renderer, store, options = {}) {
1552
+ if (options.token && !announcements.get(id)?.has(options.token)) return () => {
1553
+ };
1554
+ if (primaryRegistry.has(id)) {
1555
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
1556
+ }
1557
+ const entry = { renderer, store, rootId: options.rootId, token: options.token };
1558
+ primaryRegistry.set(id, entry);
1559
+ notify(id, entry);
1560
+ return () => {
1561
+ const currentEntry = primaryRegistry.get(id);
1562
+ if (currentEntry?.renderer === renderer) {
1563
+ primaryRegistry.delete(id);
1564
+ }
1565
+ };
1566
+ }
1567
+ function getPrimary(id) {
1568
+ return primaryRegistry.get(id);
1569
+ }
1570
+ function subscribe(id, callback) {
1571
+ if (!pendingSubscribers.has(id)) pendingSubscribers.set(id, []);
1572
+ pendingSubscribers.get(id).push(callback);
1573
+ return () => {
1574
+ const subscribers = pendingSubscribers.get(id);
1575
+ if (!subscribers) return;
1576
+ const index = subscribers.indexOf(callback);
1577
+ if (index !== -1) subscribers.splice(index, 1);
1578
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
1579
+ };
1580
+ }
1581
+ function waitForPrimary(id, timeout = 5e3) {
1582
+ const existing = primaryRegistry.get(id);
1583
+ if (existing) {
1584
+ return Promise.resolve(existing);
1585
+ }
1586
+ return new Promise((resolve, reject) => {
1587
+ let unsubscribe = () => {
1588
+ };
1589
+ const timeoutId = setTimeout(() => {
1590
+ unsubscribe();
1591
+ reject(
1592
+ new Error(
1593
+ `Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}" primary> is mounted for share="${id}".`
1594
+ )
1595
+ );
1596
+ }, timeout);
1597
+ const callback = (entry) => {
1598
+ if (!entry) {
1599
+ unsubscribe = subscribe(id, callback);
1600
+ return;
1601
+ }
1602
+ clearTimeout(timeoutId);
1603
+ resolve(entry);
1604
+ };
1605
+ unsubscribe = subscribe(id, callback);
1606
+ });
1607
+ }
1608
+ function waitForAnnouncedPrimary(key, timeout = 5e3) {
1609
+ const existing = primaryRegistry.get(key);
1610
+ if (existing && isLive(key)) return Promise.resolve(existing);
1611
+ if (!isLive(key)) return Promise.resolve(null);
1612
+ return new Promise((resolve, reject) => {
1613
+ const timeoutId = setTimeout(() => {
1614
+ unsubscribe();
1615
+ reject(
1616
+ new Error(
1617
+ `Timeout waiting for ${describePrimary(key)} to create its renderer. A canvas mounted next to it shares its renderer automatically; pass share={false} to give it its own.`
1618
+ )
1619
+ );
1620
+ }, timeout);
1621
+ const unsubscribe = subscribe(key, (entry) => {
1622
+ clearTimeout(timeoutId);
1623
+ resolve(entry);
1624
+ });
1625
+ });
1626
+ }
1627
+ function hasPrimary(id) {
1628
+ return primaryRegistry.has(id);
1629
+ }
1630
+ function unregisterPrimary(id) {
1631
+ primaryRegistry.delete(id);
1632
+ }
1633
+ function getPrimaryIds() {
1634
+ return Array.from(primaryRegistry.keys());
1635
+ }
1636
+
1637
+ const shallowLoose = { objects: "shallow", strict: false };
1638
+ function watchDpr(onChange) {
1639
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function" || !window.devicePixelRatio) {
1640
+ return () => {
1641
+ };
1642
+ }
1643
+ let watching = true;
1644
+ let query;
1645
+ const listen = () => {
1646
+ try {
1647
+ query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
1648
+ if (typeof query?.addEventListener === "function") query.addEventListener("change", handleChange);
1649
+ else query?.addListener?.(handleChange);
1650
+ } catch {
1651
+ query = void 0;
1652
+ }
1653
+ };
1654
+ const unlisten = () => {
1655
+ if (typeof query?.removeEventListener === "function") query.removeEventListener("change", handleChange);
1656
+ else query?.removeListener?.(handleChange);
1657
+ query = void 0;
1658
+ };
1659
+ const handleChange = () => {
1660
+ if (!watching) return;
1661
+ unlisten();
1662
+ listen();
1663
+ onChange();
1664
+ };
1665
+ listen();
1666
+ return () => {
1667
+ watching = false;
1668
+ unlisten();
1669
+ };
1670
+ }
1671
+ function createRootConfiguration(store, canvas) {
1672
+ let previous;
1673
+ let lastCamera;
1674
+ const lastConfiguredProps = {};
1675
+ let resolvedDpr = 0;
1676
+ let dprChangedInXR = false;
1677
+ let unwatchDpr;
1678
+ const resolveDpr = () => {
1679
+ const state = store.getState();
1680
+ const xr = state.internal.actualRenderer?.xr;
1681
+ if (xr?.isPresenting || xr?.getSession?.()) {
1682
+ dprChangedInXR = true;
1683
+ return;
1684
+ }
1685
+ dprChangedInXR = false;
1686
+ const dpr = lastConfiguredProps.dpr;
1687
+ if (dpr === void 0 || state.viewport.dpr !== resolvedDpr || calculateDpr(dpr) === resolvedDpr) return;
1688
+ state.setDpr(dpr);
1689
+ resolvedDpr = store.getState().viewport.dpr;
1690
+ };
1691
+ let warnedSharedSettings = false;
1692
+ let configured = false;
1693
+ return (props) => {
1694
+ const {
1695
+ gl: glConfig,
1696
+ renderer: rendererConfig,
1697
+ size: propsSize,
1698
+ scene: sceneOptions,
1699
+ events,
1700
+ orthographic = false,
1701
+ frameloop = "always",
1702
+ dpr = [1, 2],
1703
+ performance,
1704
+ raycaster: raycastOptions,
1705
+ camera: cameraOptions,
1706
+ onPointerMissed,
1707
+ onDragOverMissed,
1708
+ onDropMissed,
1709
+ autoUpdateFrustum = true,
1710
+ occlusion = false,
1711
+ _sizeProps,
1712
+ forceEven
1713
+ } = props;
1714
+ assertNoRemovedConfig(props);
1715
+ const rendererBag = settingsBag(rendererConfig);
1716
+ const settings = settingsBag(glConfig) ?? rendererBag;
1717
+ const requestedTextureColorSpace = settings?.textureColorSpace || void 0;
1718
+ const shadows = isInstanceOrFactory(glConfig ?? rendererConfig) ? void 0 : settings?.shadows ?? false;
1719
+ const next = { ...props, shadows, frameloop, autoUpdateFrustum, occlusion };
1720
+ const last = previous;
1721
+ const changed = (key) => !last || !is.equ(next[key], last[key], { objects: "shallow" });
1722
+ previous = void 0;
1723
+ const state = store.getState();
1724
+ const renderer = state.internal.actualRenderer;
1725
+ const three = getThree();
1726
+ const borrowsRenderer = state.internal.isSecondary === true;
1727
+ if (borrowsRenderer && !warnedSharedSettings && rendererBag && Object.keys(rendererBag).some((key) => key !== "textureColorSpace") && isDevelopment()) {
1728
+ warnedSharedSettings = true;
1729
+ console.warn(
1730
+ "R3F: renderer settings on a sharing canvas are ignored; set them on the <Canvas primary>. This canvas borrows the primary's renderer, so renderer-wide settings come from the primary. Pass share={false} to give this canvas its own renderer."
1731
+ );
1732
+ }
1733
+ let raycaster = state.raycaster;
1734
+ if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
1735
+ if (changed("raycaster")) {
1736
+ const { params, ...options } = raycastOptions || {};
1737
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
1738
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
1739
+ applyProps(raycaster, { params: { ...raycaster.params, ...params } });
1740
+ }
1741
+ }
1742
+ let tempCamera = state.camera;
1743
+ if (!state.camera || state.camera === lastCamera && changed("camera")) {
1744
+ lastCamera = cameraOptions;
1745
+ const isCamera = cameraOptions?.isCamera;
1746
+ const camera = isCamera ? cameraOptions : orthographic ? new three.OrthographicCamera(0, 0, 0, 0, 0.1, 1e3) : new three.PerspectiveCamera(50, 0, 0.1, 1e3);
1747
+ if (!isCamera) {
1748
+ camera.position.z = 5;
1749
+ if (cameraOptions) {
1750
+ applyProps(camera, cameraOptions);
1751
+ if (!camera.manual) {
1752
+ const projectionProps = ["aspect", "left", "right", "bottom", "top"];
1753
+ if (projectionProps.some((prop) => prop in cameraOptions)) {
1754
+ camera.manual = true;
1755
+ camera.updateProjectionMatrix();
1756
+ }
1757
+ }
1758
+ }
1759
+ if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
1760
+ }
1761
+ state.set({ camera });
1762
+ tempCamera = camera;
1763
+ raycaster.camera = camera;
1764
+ }
1765
+ if (!state.scene) {
1766
+ let scene;
1767
+ if (sceneOptions?.isScene) {
1768
+ scene = sceneOptions;
1769
+ prepare(scene, store, "", {});
1770
+ } else {
1771
+ scene = new three.Scene();
1772
+ prepare(scene, store, "", {});
1773
+ if (sceneOptions) applyProps(scene, sceneOptions);
1774
+ }
1775
+ state.set((prev) => ({
1776
+ scene,
1777
+ rootScene: scene,
1778
+ internal: { ...prev.internal, container: scene }
1779
+ }));
1780
+ const camera = tempCamera;
1781
+ if (camera && !camera.parent) scene.add(camera);
1782
+ }
1783
+ if (events && !state.events.handlers) {
1784
+ state.set({ events: events(store) });
1785
+ let wasEnabled = true;
1786
+ store.subscribe((state2) => {
1787
+ const { enabled } = state2.events;
1788
+ if (enabled && !wasEnabled) {
1789
+ state2.events.update?.();
1790
+ }
1791
+ wasEnabled = enabled;
1792
+ });
1793
+ }
1794
+ if (_sizeProps !== void 0 && changed("_sizeProps")) {
1795
+ state.set({ _sizeProps });
1796
+ }
1797
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
1798
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
1799
+ }
1800
+ configureSize(store, computeInitialSize(canvas, propsSize));
1801
+ if (dpr !== void 0 && (!last || !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose))) {
1802
+ state.setDpr(dpr);
1803
+ lastConfiguredProps.dpr = dpr;
1804
+ resolvedDpr = store.getState().viewport.dpr;
1805
+ }
1806
+ const internal = store.getState().internal;
1807
+ if (Array.isArray(dpr) && (!unwatchDpr || internal.unwatchDpr !== unwatchDpr)) {
1808
+ internal.unwatchDpr?.();
1809
+ internal.unwatchDpr = unwatchDpr = watchDpr(resolveDpr);
1810
+ }
1811
+ resolveDpr();
1812
+ if (frameloop !== void 0 && changed("frameloop")) {
1813
+ state.setFrameloop(frameloop);
1814
+ }
1815
+ if (changed("onPointerMissed")) state.set({ onPointerMissed });
1816
+ if (changed("onDragOverMissed")) state.set({ onDragOverMissed });
1817
+ if (changed("onDropMissed")) state.set({ onDropMissed });
1818
+ if (changed("autoUpdateFrustum") && state.autoUpdateFrustum !== autoUpdateFrustum) {
1819
+ state.set({ autoUpdateFrustum });
1820
+ }
1821
+ if (occlusion && !state.internal.occlusionEnabled) {
1822
+ enableOcclusion(store);
1823
+ }
1824
+ if (performance && changed("performance")) {
1825
+ state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
1826
+ }
1827
+ if (!state.xr) {
1828
+ const handleXRFrame = (timestamp, _frame) => {
1829
+ const state2 = store.getState();
1830
+ if (state2.frameloop === "never") return;
1831
+ advance(timestamp, true, state2);
1832
+ };
1833
+ const actualRenderer = state.internal.actualRenderer;
1834
+ const handleSessionChange = () => {
1835
+ const state2 = store.getState();
1836
+ const renderer2 = state2.internal.actualRenderer;
1837
+ actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
1838
+ renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
1839
+ if (!renderer2.xr.isPresenting) {
1840
+ if (dprChangedInXR) resolveDpr();
1841
+ invalidate(state2);
1842
+ }
1843
+ };
1844
+ const xr = {
1845
+ connect() {
1846
+ const { gl, renderer: renderer2 } = store.getState();
1847
+ const xrManager = (renderer2 || gl).xr;
1848
+ xrManager.addEventListener("sessionstart", handleSessionChange);
1849
+ xrManager.addEventListener("sessionend", handleSessionChange);
1850
+ },
1851
+ disconnect() {
1852
+ const { gl, renderer: renderer2 } = store.getState();
1853
+ const xrManager = (renderer2 || gl).xr;
1854
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
1855
+ xrManager.removeEventListener("sessionend", handleSessionChange);
1856
+ }
1857
+ };
1858
+ if (!borrowsRenderer && typeof renderer.xr?.addEventListener === "function") xr.connect();
1859
+ state.set({ xr });
1860
+ }
1861
+ if (!borrowsRenderer && shadows !== void 0 && renderer.shadowMap && changed("shadows")) {
1862
+ const oldEnabled = renderer.shadowMap.enabled;
1863
+ const oldType = renderer.shadowMap.type;
1864
+ renderer.shadowMap.enabled = !!shadows;
1865
+ if (is.boo(shadows)) {
1866
+ renderer.shadowMap.type = three.PCFShadowMap;
1867
+ } else if (is.str(shadows)) {
1868
+ if (shadows === "soft") {
1869
+ notifyDepreciated({
1870
+ heading: 'shadows="soft" is deprecated',
1871
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
1872
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
1873
+ });
1874
+ }
1875
+ const types = {
1876
+ basic: three.BasicShadowMap,
1877
+ percentage: three.PCFShadowMap,
1878
+ soft: three.PCFShadowMap,
1879
+ variance: three.VSMShadowMap
1880
+ };
1881
+ renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
1882
+ } else if (is.obj(shadows)) {
1883
+ Object.assign(renderer.shadowMap, shadows);
1884
+ }
1885
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
1886
+ renderer.shadowMap.needsUpdate = true;
1887
+ }
1888
+ }
1889
+ if (!configured && !borrowsRenderer) {
1890
+ renderer.outputColorSpace = three.SRGBColorSpace;
1891
+ renderer.toneMapping = three.ACESFilmicToneMapping;
1892
+ }
1893
+ const textureColorSpace = requestedTextureColorSpace ?? three.SRGBColorSpace;
1894
+ if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
1895
+ if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
1896
+ lastConfiguredProps.textureColorSpace = textureColorSpace;
1897
+ }
1898
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && changed("gl")) {
1899
+ const glProps = {};
1900
+ for (const key in glConfig) {
1901
+ if (!NON_APPLIED_RENDERER_PROPS.has(key)) glProps[key] = glConfig[key];
1902
+ }
1903
+ applyProps(renderer, glProps);
1904
+ }
1905
+ if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer && !borrowsRenderer) {
1906
+ const currentRenderer = state.renderer;
1907
+ if (changed("renderer")) {
1908
+ const rendererProps = {};
1909
+ for (const key in rendererConfig) {
1910
+ if (!NON_APPLIED_RENDERER_PROPS.has(key)) rendererProps[key] = rendererConfig[key];
1911
+ }
1912
+ applyProps(currentRenderer, rendererProps);
1913
+ }
1914
+ }
1915
+ configured = true;
1916
+ previous = next;
1917
+ };
1918
+ }
1919
+
1920
+ const version = "10.0.0-alpha.5";
1921
+ const packageData = {
1922
+ version: version};
1923
+
1924
+ function $1(Ut) {
1925
+ return Ut && Ut.__esModule && Object.prototype.hasOwnProperty.call(Ut, "default") ? Ut.default : Ut;
1926
+ }
1927
+ var S0 = { exports: {} }, Ob = { exports: {} }, P1;
1928
+ function Q1() {
1929
+ return P1 || (P1 = 1, (function(Ut) {
1930
+ Ut.exports = function(m) {
1931
+ function et(t, r, a, l) {
1932
+ return new Fe(t, r, a, l);
1933
+ }
1934
+ function zf() {
1935
+ }
1140
1936
  function F(t) {
1141
1937
  var r = "https://react.dev/errors/" + t;
1142
1938
  if (1 < arguments.length) {
@@ -13319,15 +14115,16 @@ function swapInteractivity(store, object, newObject) {
13319
14115
  pointerState.hovered.set(makeId(next), next);
13320
14116
  }
13321
14117
  });
13322
- const captureData = pointerState.captured.get(object);
13323
- if (captureData) {
13324
- pointerState.captured.delete(object);
13325
- pointerState.captured.set(newObject, {
14118
+ for (const [key, captureData] of Array.from(pointerState.captured)) {
14119
+ const { intersection } = captureData;
14120
+ if (key !== object && intersection.object !== object && intersection.eventObject !== object) continue;
14121
+ if (key === object) pointerState.captured.delete(key);
14122
+ pointerState.captured.set(key === object ? newObject : key, {
13326
14123
  ...captureData,
13327
14124
  intersection: {
13328
- ...captureData.intersection,
13329
- object: captureData.intersection.object === object ? newObject : captureData.intersection.object,
13330
- eventObject: captureData.intersection.eventObject === object ? newObject : captureData.intersection.eventObject
14125
+ ...intersection,
14126
+ object: intersection.object === object ? newObject : intersection.object,
14127
+ eventObject: intersection.eventObject === object ? newObject : intersection.eventObject
13331
14128
  }
13332
14129
  });
13333
14130
  }
@@ -13856,9 +14653,9 @@ const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
13856
14653
  const R3F_EXTEND_ID = Symbol.for("@react-three/fiber.extendId");
13857
14654
  const ids = globalThis;
13858
14655
  const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
13859
- const isConstructor = (object) => typeof object === "function";
14656
+ const isConstructor$1 = (object) => typeof object === "function";
13860
14657
  function extend(objects) {
13861
- if (isConstructor(objects)) {
14658
+ if (isConstructor$1(objects)) {
13862
14659
  const id = ids[R3F_EXTEND_ID] ?? 0;
13863
14660
  ids[R3F_EXTEND_ID] = id + 1;
13864
14661
  const Component = `${id}`;
@@ -13867,15 +14664,14 @@ function extend(objects) {
13867
14664
  } else {
13868
14665
  for (const name in objects) {
13869
14666
  const object = objects[name];
13870
- if (isConstructor(object)) catalogue[name] = object;
14667
+ if (isConstructor$1(object)) catalogue[name] = object;
13871
14668
  }
13872
14669
  }
13873
14670
  }
13874
-
13875
14671
  function resolveConstructor(name, root) {
13876
14672
  if (Object.prototype.hasOwnProperty.call(catalogue, name)) return catalogue[name];
13877
14673
  const object = root.getState().internal.support?.three[name];
13878
- return isConstructor(object) ? object : void 0;
14674
+ return isConstructor$1(object) ? object : void 0;
13879
14675
  }
13880
14676
 
13881
14677
  function createReconciler(config) {
@@ -13912,6 +14708,7 @@ function hideInstance(instance) {
13912
14708
  if (instance.props.attach && instance.parent?.object) {
13913
14709
  detach(instance.parent, instance);
13914
14710
  } else if (isObject3D(instance.object)) {
14711
+ instance.previousVisible = instance.object.visible;
13915
14712
  instance.object.visible = false;
13916
14713
  }
13917
14714
  instance.isHidden = true;
@@ -13922,9 +14719,10 @@ function unhideInstance(instance) {
13922
14719
  if (instance.isHidden) {
13923
14720
  if (instance.props.attach && instance.parent?.object) {
13924
14721
  attach(instance.parent, instance);
13925
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
13926
- instance.object.visible = true;
14722
+ } else if (isObject3D(instance.object)) {
14723
+ instance.object.visible = instance.previousVisible ?? true;
13927
14724
  }
14725
+ instance.previousVisible = void 0;
13928
14726
  instance.isHidden = false;
13929
14727
  invalidateInstance(instance);
13930
14728
  }
@@ -14337,466 +15135,129 @@ const reconciler = /* @__PURE__ */ createReconciler({
14337
15135
  },
14338
15136
  resetFormInstance() {
14339
15137
  },
14340
- // @ts-ignore DefinitelyTyped is not up to date
14341
- rendererPackageName: "@react-three/fiber",
14342
- rendererVersion: packageData.version,
14343
- // 19.2 Reconciler
14344
- // https://github.com/facebook/react/pull/31975
14345
- // https://github.com/facebook/react/pull/31999
14346
- applyViewTransitionName(_instance, _name, _className) {
14347
- },
14348
- restoreViewTransitionName(_instance, _props) {
14349
- },
14350
- cancelViewTransitionName(_instance, _name, _props) {
14351
- },
14352
- cancelRootViewTransitionName(_rootContainer) {
14353
- },
14354
- restoreRootViewTransitionName(_rootContainer) {
14355
- },
14356
- InstanceMeasurement: null,
14357
- measureInstance: (_instance) => null,
14358
- wasInstanceInViewport: (_measurement) => true,
14359
- hasInstanceChanged: (_oldMeasurement, _newMeasurement) => false,
14360
- hasInstanceAffectedParent: (_oldMeasurement, _newMeasurement) => false,
14361
- // https://github.com/facebook/react/pull/32002
14362
- // https://github.com/facebook/react/pull/34486
14363
- suspendOnActiveViewTransition(_state, _container) {
14364
- },
14365
- // https://github.com/facebook/react/pull/32451
14366
- // https://github.com/facebook/react/pull/32760
14367
- // React hands the whole commit to the host when a transition touches a <ViewTransition> subtree.
14368
- // three.js has nothing to animate between commits, so this mirrors the react-dom fallback for
14369
- // browsers without document.startViewTransition and flushes the commit synchronously.
14370
- startViewTransition(_suspendedState, _rootContainer, _transitionTypes, mutationCallback, layoutCallback, _afterMutationCallback, spawnedWorkCallback, _passiveCallback, _errorCallback, _blockedCallback, finishedAnimation) {
14371
- mutationCallback();
14372
- layoutCallback();
14373
- finishedAnimation?.();
14374
- spawnedWorkCallback();
14375
- return null;
14376
- },
14377
- startGestureTransition(_suspendedState, _rootContainer, _timeline, _rangeStart, _rangeEnd, _transitionTypes, mutationCallback, animateCallback, _errorCallback, finishedAnimation) {
14378
- mutationCallback();
14379
- animateCallback();
14380
- finishedAnimation?.();
14381
- return null;
14382
- },
14383
- stopViewTransition(_transition) {
14384
- },
14385
- // https://github.com/facebook/react/pull/35564
14386
- // Only reached with a non-null transition from startViewTransition, which never returns one here
14387
- addViewTransitionFinishedListener(_transition, callback) {
14388
- callback();
14389
- },
14390
- // https://github.com/facebook/react/pull/32038
14391
- createViewTransitionInstance: (_name) => null,
14392
- // https://github.com/facebook/react/pull/32379
14393
- // https://github.com/facebook/react/pull/32786
14394
- getCurrentGestureOffset(_provider) {
14395
- throw new Error("startGestureTransition is not yet supported in react-three-fiber.");
14396
- },
14397
- // https://github.com/facebook/react/pull/32500
14398
- cloneMutableInstance(instance, _keepChildren) {
14399
- return instance;
14400
- },
14401
- cloneMutableTextInstance(textInstance) {
14402
- return textInstance;
14403
- },
14404
- // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
14405
- // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
14406
- // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
14407
- // React never calls them. They stay as no-ops rather than throws: the host config is shared by
14408
- // every root, and a throw here would turn a React-internal probe into a hard crash for users.
14409
- // Guarded by a test asserting a full render + unmount never reaches them.
14410
- cloneRootViewTransitionContainer(_rootContainer) {
14411
- },
14412
- removeRootViewTransitionClone(_rootContainer, _clone) {
14413
- },
14414
- // https://github.com/facebook/react/pull/32465
14415
- createFragmentInstance: (_fiber) => null,
14416
- updateFragmentInstanceFiber(_fiber, _instance) {
14417
- },
14418
- commitNewChildToFragmentInstance(_child, _fragmentInstance) {
14419
- },
14420
- deleteChildFromFragmentInstance(_child, _fragmentInstance) {
14421
- },
14422
- // https://github.com/facebook/react/pull/32653
14423
- measureClonedInstance: (_instance) => null,
14424
- // https://github.com/facebook/react/pull/32819
14425
- maySuspendCommitOnUpdate: (_type, _oldProps, _newProps) => false,
14426
- maySuspendCommitInSyncRender: (_type, _props) => false,
14427
- // https://github.com/facebook/react/pull/34486
14428
- startSuspendingCommit: () => null,
14429
- // https://github.com/facebook/react/pull/34522
14430
- getSuspendedCommitReason: (_state, _rootContainer) => null
14431
- });
14432
-
14433
- const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
14434
- function isInternalRendererAccess(stack, callerDepth = 2) {
14435
- if (!stack) return false;
14436
- const caller = stack.split("\n")[callerDepth] ?? "";
14437
- return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
14438
- }
14439
-
14440
- const createStore = (invalidate, advance) => {
14441
- const rootStore = createWithEqualityFn((set, get) => {
14442
- let position;
14443
- let defaultTarget;
14444
- let tempTarget;
14445
- function getCurrentViewport(camera = get().camera, target = void 0, size = get().size) {
14446
- const { Vector3 } = getThree();
14447
- position ?? (position = new Vector3());
14448
- defaultTarget ?? (defaultTarget = new Vector3());
14449
- tempTarget ?? (tempTarget = new Vector3());
14450
- target ?? (target = defaultTarget);
14451
- const { width, height, top, left } = size;
14452
- const aspect = width / height;
14453
- if (target.isVector3) tempTarget.copy(target);
14454
- else tempTarget.set(...target);
14455
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
14456
- if (isOrthographicCamera(camera)) {
14457
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
14458
- } else {
14459
- const fov = camera.fov * Math.PI / 180;
14460
- const h = 2 * Math.tan(fov / 2) * distance;
14461
- const w = h * (width / height);
14462
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
14463
- }
14464
- }
14465
- let performanceTimeout = void 0;
14466
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
14467
- const rootState = {
14468
- set,
14469
- get,
14470
- // Mock objects that have to be configured
14471
- // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
14472
- primaryStore: null,
14473
- gl: null,
14474
- renderer: null,
14475
- camera: null,
14476
- // frustum, pointer and mouse are three objects; configure() creates them with the renderer
14477
- frustum: null,
14478
- autoUpdateFrustum: true,
14479
- raycaster: null,
14480
- events: {
14481
- priority: 1,
14482
- enabled: true,
14483
- connected: false,
14484
- frameTimedRaycasts: true,
14485
- alwaysFireOnScroll: true,
14486
- updateOnFrame: false
14487
- },
14488
- scene: null,
14489
- rootScene: null,
14490
- xr: null,
14491
- inspector: null,
14492
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
14493
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
14494
- textureColorSpace: "srgb",
14495
- // THREE.SRGBColorSpace
14496
- isLegacy: false,
14497
- webGPUSupported: false,
14498
- isNative: false,
14499
- controls: null,
14500
- pointer: null,
14501
- mouse: null,
14502
- frameloop: "always",
14503
- onPointerMissed: void 0,
14504
- onDragOverMissed: void 0,
14505
- onDropMissed: void 0,
14506
- performance: {
14507
- current: 1,
14508
- min: 0.5,
14509
- max: 1,
14510
- debounce: 200,
14511
- regress: () => {
14512
- const state2 = get();
14513
- if (performanceTimeout) clearTimeout(performanceTimeout);
14514
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
14515
- performanceTimeout = setTimeout(
14516
- () => setPerformanceCurrent(get().performance.max),
14517
- state2.performance.debounce
14518
- );
14519
- }
14520
- },
14521
- size: { width: 0, height: 0, top: 0, left: 0 },
14522
- viewport: {
14523
- initialDpr: 0,
14524
- dpr: 0,
14525
- width: 0,
14526
- height: 0,
14527
- top: 0,
14528
- left: 0,
14529
- aspect: 0,
14530
- distance: 0,
14531
- factor: 0,
14532
- getCurrentViewport
14533
- },
14534
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
14535
- setSize: (width, height, top, left) => {
14536
- const state2 = get();
14537
- if (width === void 0) {
14538
- set({ _sizeImperative: false });
14539
- if (state2._sizeProps) {
14540
- const { width: propW, height: propH } = state2._sizeProps;
14541
- if (propW !== void 0 || propH !== void 0) {
14542
- const currentSize = state2.size;
14543
- const newSize = {
14544
- width: propW ?? currentSize.width,
14545
- height: propH ?? currentSize.height,
14546
- top: currentSize.top,
14547
- left: currentSize.left
14548
- };
14549
- set((s) => ({
14550
- size: newSize,
14551
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
14552
- }));
14553
- get().invalidate();
14554
- }
14555
- }
14556
- return;
14557
- }
14558
- const w = width;
14559
- const h = height ?? width;
14560
- const t = top ?? state2.size.top;
14561
- const l = left ?? state2.size.left;
14562
- const size = { width: w, height: h, top: t, left: l };
14563
- set((s) => ({
14564
- size,
14565
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
14566
- _sizeImperative: true
14567
- }));
14568
- get().invalidate();
14569
- },
14570
- setDpr: (dpr) => set((state2) => {
14571
- const resolved = calculateDpr(dpr);
14572
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
14573
- }),
14574
- setFrameloop: (frameloop = "always") => {
14575
- const rootId = get().internal.rootId;
14576
- if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
14577
- set(() => ({ frameloop }));
14578
- },
14579
- setError: (error) => set(() => ({ error })),
14580
- error: null,
14581
- //* Texture registry (useTextures) ==============================
14582
- // TSL fields (uniforms, nodes, ..., renderPipeline) are added by @react-three/tsl's root extension.
14583
- textures: /* @__PURE__ */ new Map(),
14584
- _textureRefs: /* @__PURE__ */ new Map(),
14585
- _sizeImperative: false,
14586
- _sizeProps: null,
14587
- previousRoot: void 0,
14588
- internal: {
14589
- // Events
14590
- interaction: [],
14591
- subscribers: [],
14592
- // Per-pointer state (new unified structure)
14593
- pointerMap: /* @__PURE__ */ new Map(),
14594
- pointerDirty: /* @__PURE__ */ new Map(),
14595
- lastEvent: React.createRef(),
14596
- // Deprecated but kept for backwards compatibility
14597
- hovered: /* @__PURE__ */ new Map(),
14598
- initialClick: [0, 0],
14599
- initialHits: [],
14600
- capturedMap: /* @__PURE__ */ new Map(),
14601
- // Visibility tracking (onFramed, onOccluded, onVisible)
14602
- visibilityRegistry: /* @__PURE__ */ new Map(),
14603
- // Occlusion system (WebGPU only)
14604
- occlusionEnabled: false,
14605
- occlusionObserver: null,
14606
- occlusionCache: /* @__PURE__ */ new Map(),
14607
- helperGroup: null,
14608
- // Updates
14609
- active: false,
14610
- frames: 0,
14611
- priority: 0,
14612
- subscribe: (ref, priority, store) => {
14613
- const internal = get().internal;
14614
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
14615
- internal.subscribers.push({ ref, priority, store });
14616
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
14617
- return () => {
14618
- const internal2 = get().internal;
14619
- if (internal2?.subscribers) {
14620
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
14621
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
14622
- }
14623
- };
14624
- },
14625
- // Renderer Storage (single source of truth)
14626
- actualRenderer: null,
14627
- // The renderer support configure() loads for this root
14628
- support: null,
14629
- // Scheduler for useFrameNext (initialized in renderer.tsx)
14630
- scheduler: null,
14631
- // Replaces the default render call when set (see setRenderOverride)
14632
- renderOverride: null
14633
- }
14634
- };
14635
- return rootState;
14636
- });
14637
- const state = rootStore.getState();
14638
- Object.defineProperty(state, "gl", {
14639
- get() {
14640
- const currentState = rootStore.getState();
14641
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
14642
- const stack = new Error().stack || "";
14643
- if (!isInternalRendererAccess(stack)) {
14644
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
14645
- notifyDepreciated({
14646
- heading: "Accessing state.gl in WebGPU mode",
14647
- 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
14648
- });
14649
- }
14650
- }
14651
- return currentState.internal.actualRenderer;
14652
- },
14653
- set(value) {
14654
- rootStore.getState().internal.actualRenderer = value;
14655
- },
14656
- enumerable: true,
14657
- configurable: true
14658
- });
14659
- Object.defineProperty(state, "renderer", {
14660
- get() {
14661
- return rootStore.getState().internal.actualRenderer;
14662
- },
14663
- set(value) {
14664
- rootStore.getState().internal.actualRenderer = value;
14665
- },
14666
- enumerable: true,
14667
- configurable: true
14668
- });
14669
- let oldScene = state.scene;
14670
- rootStore.subscribe(() => {
14671
- const currentState = rootStore.getState();
14672
- const { scene, rootScene, set } = currentState;
14673
- if (scene !== oldScene) {
14674
- oldScene = scene;
14675
- if (scene?.isScene && scene !== rootScene) {
14676
- set({ rootScene: scene });
14677
- }
14678
- }
14679
- });
14680
- let oldSize = state.size;
14681
- let oldDpr = state.viewport.dpr;
14682
- let oldCamera = state.camera;
14683
- rootStore.subscribe(() => {
14684
- const { camera, size, viewport, set, internal } = rootStore.getState();
14685
- const actualRenderer = internal.actualRenderer;
14686
- const canvasTarget = internal.canvasTarget;
14687
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
14688
- oldSize = size;
14689
- oldDpr = viewport.dpr;
14690
- updateCamera(camera, size);
14691
- const resizeTarget = canvasTarget ?? actualRenderer;
14692
- if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
14693
- resizeTarget.setSize(size.width, size.height, false);
14694
- const activeTarget = actualRenderer.getCanvasTarget?.();
14695
- if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
14696
- }
14697
- if (camera !== oldCamera) {
14698
- oldCamera = camera;
14699
- const { rootScene } = rootStore.getState();
14700
- if (camera && rootScene && !camera.parent) {
14701
- rootScene.add(camera);
14702
- }
14703
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
14704
- const currentState = rootStore.getState();
14705
- if (currentState.autoUpdateFrustum && camera) {
14706
- updateFrustum(camera, currentState.frustum);
14707
- }
14708
- }
14709
- });
14710
- rootStore.subscribe((state2) => invalidate(state2));
14711
- return rootStore;
14712
- };
14713
-
14714
- const primaryRegistry = /* @__PURE__ */ new Map();
14715
- const pendingSubscribers = /* @__PURE__ */ new Map();
14716
- function registerPrimary(id, renderer, store) {
14717
- if (primaryRegistry.has(id)) {
14718
- console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
14719
- }
14720
- const entry = { renderer, store };
14721
- primaryRegistry.set(id, entry);
14722
- const subscribers = pendingSubscribers.get(id);
14723
- if (subscribers) {
14724
- subscribers.forEach((callback) => callback(entry));
14725
- pendingSubscribers.delete(id);
14726
- }
14727
- return () => {
14728
- const currentEntry = primaryRegistry.get(id);
14729
- if (currentEntry?.renderer === renderer) {
14730
- primaryRegistry.delete(id);
14731
- }
14732
- };
14733
- }
14734
- function getPrimary(id) {
14735
- return primaryRegistry.get(id);
14736
- }
14737
- function waitForPrimary(id, timeout = 5e3) {
14738
- const existing = primaryRegistry.get(id);
14739
- if (existing) {
14740
- return Promise.resolve(existing);
14741
- }
14742
- return new Promise((resolve, reject) => {
14743
- const timeoutId = setTimeout(() => {
14744
- const subscribers = pendingSubscribers.get(id);
14745
- if (subscribers) {
14746
- const index = subscribers.indexOf(callback);
14747
- if (index !== -1) subscribers.splice(index, 1);
14748
- if (subscribers.length === 0) pendingSubscribers.delete(id);
14749
- }
14750
- reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
14751
- }, timeout);
14752
- const callback = (entry) => {
14753
- clearTimeout(timeoutId);
14754
- resolve(entry);
14755
- };
14756
- if (!pendingSubscribers.has(id)) {
14757
- pendingSubscribers.set(id, []);
14758
- }
14759
- pendingSubscribers.get(id).push(callback);
14760
- });
14761
- }
14762
- function hasPrimary(id) {
14763
- return primaryRegistry.has(id);
14764
- }
14765
- function unregisterPrimary(id) {
14766
- primaryRegistry.delete(id);
14767
- }
14768
- function getPrimaryIds() {
14769
- return Array.from(primaryRegistry.keys());
15138
+ // @ts-ignore DefinitelyTyped is not up to date
15139
+ rendererPackageName: "@react-three/fiber",
15140
+ rendererVersion: packageData.version,
15141
+ // 19.2 Reconciler
15142
+ // https://github.com/facebook/react/pull/31975
15143
+ // https://github.com/facebook/react/pull/31999
15144
+ applyViewTransitionName(_instance, _name, _className) {
15145
+ },
15146
+ restoreViewTransitionName(_instance, _props) {
15147
+ },
15148
+ cancelViewTransitionName(_instance, _name, _props) {
15149
+ },
15150
+ cancelRootViewTransitionName(_rootContainer) {
15151
+ },
15152
+ restoreRootViewTransitionName(_rootContainer) {
15153
+ },
15154
+ InstanceMeasurement: null,
15155
+ measureInstance: (_instance) => null,
15156
+ wasInstanceInViewport: (_measurement) => true,
15157
+ hasInstanceChanged: (_oldMeasurement, _newMeasurement) => false,
15158
+ hasInstanceAffectedParent: (_oldMeasurement, _newMeasurement) => false,
15159
+ // https://github.com/facebook/react/pull/32002
15160
+ // https://github.com/facebook/react/pull/34486
15161
+ suspendOnActiveViewTransition(_state, _container) {
15162
+ },
15163
+ // https://github.com/facebook/react/pull/32451
15164
+ // https://github.com/facebook/react/pull/32760
15165
+ // React hands the whole commit to the host when a transition touches a <ViewTransition> subtree.
15166
+ // three.js has nothing to animate between commits, so this mirrors the react-dom fallback for
15167
+ // browsers without document.startViewTransition and flushes the commit synchronously.
15168
+ startViewTransition(_suspendedState, _rootContainer, _transitionTypes, mutationCallback, layoutCallback, _afterMutationCallback, spawnedWorkCallback, _passiveCallback, _errorCallback, _blockedCallback, finishedAnimation) {
15169
+ mutationCallback();
15170
+ layoutCallback();
15171
+ finishedAnimation?.();
15172
+ spawnedWorkCallback();
15173
+ return null;
15174
+ },
15175
+ startGestureTransition(_suspendedState, _rootContainer, _timeline, _rangeStart, _rangeEnd, _transitionTypes, mutationCallback, animateCallback, _errorCallback, finishedAnimation) {
15176
+ mutationCallback();
15177
+ animateCallback();
15178
+ finishedAnimation?.();
15179
+ return null;
15180
+ },
15181
+ stopViewTransition(_transition) {
15182
+ },
15183
+ // https://github.com/facebook/react/pull/35564
15184
+ // Only reached with a non-null transition from startViewTransition, which never returns one here
15185
+ addViewTransitionFinishedListener(_transition, callback) {
15186
+ callback();
15187
+ },
15188
+ // https://github.com/facebook/react/pull/32038
15189
+ createViewTransitionInstance: (_name) => null,
15190
+ // https://github.com/facebook/react/pull/32379
15191
+ // https://github.com/facebook/react/pull/32786
15192
+ getCurrentGestureOffset(_provider) {
15193
+ throw new Error("startGestureTransition is not yet supported in react-three-fiber.");
15194
+ },
15195
+ // https://github.com/facebook/react/pull/32500
15196
+ cloneMutableInstance(instance, _keepChildren) {
15197
+ return instance;
15198
+ },
15199
+ cloneMutableTextInstance(textInstance) {
15200
+ return textInstance;
15201
+ },
15202
+ // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
15203
+ // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
15204
+ // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
15205
+ // React never calls them. They stay as no-ops rather than throws: the host config is shared by
15206
+ // every root, and a throw here would turn a React-internal probe into a hard crash for users.
15207
+ // Guarded by a test asserting a full render + unmount never reaches them.
15208
+ cloneRootViewTransitionContainer(_rootContainer) {
15209
+ },
15210
+ removeRootViewTransitionClone(_rootContainer, _clone) {
15211
+ },
15212
+ // https://github.com/facebook/react/pull/32465
15213
+ createFragmentInstance: (_fiber) => null,
15214
+ updateFragmentInstanceFiber(_fiber, _instance) {
15215
+ },
15216
+ commitNewChildToFragmentInstance(_child, _fragmentInstance) {
15217
+ },
15218
+ deleteChildFromFragmentInstance(_child, _fragmentInstance) {
15219
+ },
15220
+ // https://github.com/facebook/react/pull/32653
15221
+ measureClonedInstance: (_instance) => null,
15222
+ // https://github.com/facebook/react/pull/32819
15223
+ maySuspendCommitOnUpdate: (_type, _oldProps, _newProps) => false,
15224
+ maySuspendCommitInSyncRender: (_type, _props) => false,
15225
+ // https://github.com/facebook/react/pull/34486
15226
+ startSuspendingCommit: () => null,
15227
+ // https://github.com/facebook/react/pull/34522
15228
+ getSuspendedCommitReason: (_state, _rootContainer) => null
15229
+ });
15230
+ function flushSync(fn) {
15231
+ return reconciler.flushSyncFromReconciler(fn);
14770
15232
  }
14771
15233
 
14772
- const isRenderer = (def) => !!def?.render;
15234
+ const noop = () => {
15235
+ };
14773
15236
  const _roots = /* @__PURE__ */ new Map();
14774
- function ensureStoreThreeObjects(store) {
14775
- const state = store.getState();
14776
- if (state.pointer) return;
14777
- const { Vector2, Frustum } = getThree();
14778
- const pointer = new Vector2();
14779
- state.set({ pointer, mouse: pointer, frustum: new Frustum() });
14780
- }
14781
- const shallowLoose = { objects: "shallow", strict: false };
14782
- async function resolveRenderer(config, defaultProps, RendererClass) {
14783
- if (typeof config === "function") return await config(defaultProps);
14784
- if (isRenderer(config)) return config;
14785
- return new RendererClass({ ...defaultProps, ...config });
14786
- }
14787
- function computeInitialSize(canvas, size) {
14788
- if (!size && typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
14789
- const { width, height, top, left } = canvas.parentElement.getBoundingClientRect();
14790
- return { width, height, top, left };
14791
- } else if (!size && typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) {
14792
- return {
14793
- width: canvas.width,
14794
- height: canvas.height,
14795
- top: 0,
14796
- left: 0
14797
- };
15237
+ function transitionRoot(root, event) {
15238
+ const state = root.state;
15239
+ switch (event.type) {
15240
+ case "use":
15241
+ if (state.status === "disposed") return false;
15242
+ if (state.status === "closing") {
15243
+ root.store.getState().internal.active = true;
15244
+ state.settle();
15245
+ }
15246
+ root.state = { status: "open" };
15247
+ return true;
15248
+ case "unmount": {
15249
+ if (state.status === "disposed") return false;
15250
+ if (state.status === "closing") state.settle();
15251
+ const { promise, resolve } = deferred();
15252
+ root.state = { status: "closing", token: event.token, settled: promise, settle: resolve };
15253
+ return true;
15254
+ }
15255
+ case "dispose":
15256
+ if (state.status !== "closing" || state.token !== event.token) return false;
15257
+ root.state = { status: "disposed" };
15258
+ state.settle();
15259
+ return true;
14798
15260
  }
14799
- return { width: 0, height: 0, top: 0, left: 0, ...size };
14800
15261
  }
14801
15262
  function createRoot(canvas, provider) {
14802
15263
  const prevRoot = _roots.get(canvas);
@@ -14834,540 +15295,615 @@ function createRoot(canvas, provider) {
14834
15295
  null
14835
15296
  // transitionCallbacks
14836
15297
  );
14837
- if (!prevRoot) _roots.set(canvas, { fiber, store });
14838
- let onCreated;
14839
- let lastCamera;
14840
- const lastConfiguredProps = {};
14841
- let configured = false;
14842
- let pending = null;
14843
- let rendererSetup = null;
14844
- return {
14845
- async configure(props = {}) {
14846
- let resolve;
14847
- pending = new Promise((_resolve) => resolve = _resolve);
14848
- const {
14849
- id: canvasId,
14850
- primaryCanvas,
14851
- scheduler: schedulerConfig,
14852
- gl: glConfig,
14853
- renderer: rendererConfig,
14854
- size: propsSize,
14855
- scene: sceneOptions,
14856
- events,
14857
- onCreated: onCreatedCallback,
14858
- shadows = false,
14859
- orthographic = false,
14860
- frameloop = "always",
14861
- dpr = [1, 2],
14862
- performance,
14863
- raycaster: raycastOptions,
14864
- camera: cameraOptions,
14865
- onPointerMissed,
14866
- onDragOverMissed,
14867
- onDropMissed,
14868
- autoUpdateFrustum = true,
14869
- occlusion = false,
14870
- _sizeProps,
14871
- forceEven
14872
- } = props;
14873
- const requestedTextureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || void 0;
14874
- let state = store.getState();
14875
- const { webgl, webgpu } = provider;
14876
- const defaultGLProps = {
14877
- canvas,
14878
- powerPreference: "high-performance",
14879
- antialias: true,
14880
- alpha: true
14881
- };
14882
- const defaultGPUProps = {
14883
- canvas,
14884
- antialias: true
14885
- };
14886
- if (glConfig && !webgl) {
14887
- throw new Error(
14888
- "WebGLRenderer (gl prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/legacy instead."
14889
- );
14890
- }
14891
- if (rendererConfig && !webgpu) {
14892
- throw new Error(
14893
- "WebGPURenderer (renderer prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
14894
- );
14895
- }
14896
- const wantsGL = !!webgl && (state.isLegacy || !!glConfig || !webgpu || !rendererConfig);
14897
- if (glConfig && rendererConfig) {
14898
- throw new Error("Cannot use both gl and renderer props at the same time");
14899
- }
14900
- if (webgl && webgpu && !state.isLegacy && wantsGL) {
14901
- notifyDepreciated({
14902
- heading: "WebGlRenderer Usage",
14903
- body: "WebGlRenderer usage is deprecated in favor of WebGPU. Import from /legacy directly or upgrade to WebGPU.",
14904
- link: "https://docs.pmnd.rs/react-three-fiber/api/renderer"
14905
- });
14906
- }
14907
- let renderer = state.internal.actualRenderer;
14908
- if (primaryCanvas && !webgpu) {
14909
- throw new Error(
14910
- "The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
14911
- );
14912
- }
14913
- if (primaryCanvas && wantsGL) {
14914
- throw new Error(
14915
- "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
14916
- );
15298
+ const root = prevRoot || {
15299
+ fiber,
15300
+ store,
15301
+ state: { status: "open" },
15302
+ ready: fulfilled(void 0),
15303
+ configuration: {}
15304
+ };
15305
+ if (!prevRoot) _roots.set(canvas, root);
15306
+ if (!prevRoot) root.configuration.apply = createRootConfiguration(store, canvas);
15307
+ let mounted = false;
15308
+ return {
15309
+ get ready() {
15310
+ return root.ready;
15311
+ },
15312
+ configure(props = {}) {
15313
+ if (!transitionRoot(root, { type: "use" })) {
15314
+ return rejected(new Error("R3F: Cannot configure a root after it has unmounted (disposal has started)."));
15315
+ }
15316
+ const previous = root.ready;
15317
+ const { promise, resolve, reject } = deferred();
15318
+ root.ready = promise;
15319
+ const run = () => {
15320
+ try {
15321
+ const initialized = initializeRenderer(canvas, provider, store, props);
15322
+ const apply = () => {
15323
+ if (props.id) {
15324
+ for (const other of _roots.values()) {
15325
+ if (other !== root && other.state.status === "closing" && other.store.getState().internal.rootId === props.id) {
15326
+ other.state.settled.then(apply).catch(reject);
15327
+ return;
15328
+ }
15329
+ }
15330
+ }
15331
+ const previous2 = root.configuration.previous;
15332
+ root.configuration.apply(props);
15333
+ configureScheduler(store, props.id, props.scheduler, previous2?.scheduler);
15334
+ attachRootExtensions(store);
15335
+ root.configuration.previous = props;
15336
+ resolve(this);
15337
+ };
15338
+ if (isPromiseLike(initialized)) Promise.resolve(initialized).then(apply).catch(reject);
15339
+ else apply();
15340
+ } catch (error) {
15341
+ reject(error);
15342
+ }
15343
+ };
15344
+ if (previous.status === "pending") previous.then(run, reject);
15345
+ else run();
15346
+ return promise;
15347
+ },
15348
+ render(children) {
15349
+ if (!transitionRoot(root, { type: "use" })) return store;
15350
+ if (!store.getState().internal.actualRenderer && root.ready.status === "fulfilled") this.configure();
15351
+ if (root.ready.status === "rejected") throw root.ready.reason;
15352
+ const commit = () => {
15353
+ if (root.state.status !== "open" || root.ready.status === "rejected") return;
15354
+ if (root.ready.status === "pending") {
15355
+ root.ready.then(commit, logRecoverableError);
15356
+ return;
15357
+ }
15358
+ const onCreated = root.configuration.previous?.onCreated;
15359
+ const element = /* @__PURE__ */ jsx(Provider, { store, children, onCreated, rootElement: canvas });
15360
+ if (mounted) {
15361
+ reconciler.updateContainer(element, fiber, null, noop);
15362
+ return;
15363
+ }
15364
+ mounted = true;
15365
+ reconciler.updateContainerSync(element, fiber, null, noop);
15366
+ reconciler.flushSyncWork();
15367
+ };
15368
+ commit();
15369
+ return store;
15370
+ },
15371
+ unmount() {
15372
+ if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
15373
+ }
15374
+ };
15375
+ }
15376
+ function Provider({
15377
+ store,
15378
+ children,
15379
+ onCreated,
15380
+ rootElement
15381
+ }) {
15382
+ useIsomorphicLayoutEffect(() => {
15383
+ const state = store.getState();
15384
+ state.set((state2) => ({ internal: { ...state2.internal, active: true } }));
15385
+ if (onCreated) onCreated(state);
15386
+ if (!store.getState().events.connected) state.events.connect?.(rootElement);
15387
+ }, []);
15388
+ return /* @__PURE__ */ jsx(context.Provider, { value: store, children });
15389
+ }
15390
+ function unmountComponentAtNode(canvas, callback) {
15391
+ const claim = Symbol("unmount");
15392
+ const root = _roots.get(canvas);
15393
+ if (!root || !transitionRoot(root, { type: "unmount", token: claim })) return;
15394
+ root.store.getState().internal.active = false;
15395
+ reconciler.updateContainer(null, root.fiber, null, () => {
15396
+ if (root.state.status !== "closing" || root.state.token !== claim) return;
15397
+ reconciler.updateContainer(null, root.fiber, null, () => {
15398
+ if (root.state.status !== "closing" || root.state.token !== claim) return;
15399
+ if (root.ready.status === "pending") root.ready.then(teardown, teardown);
15400
+ else teardown();
15401
+ });
15402
+ });
15403
+ function teardown() {
15404
+ if (root.ready.status === "pending") {
15405
+ root.ready.then(teardown, teardown);
15406
+ return;
15407
+ }
15408
+ if (!transitionRoot(root, { type: "dispose", token: claim })) return;
15409
+ _roots.delete(canvas);
15410
+ const state = root.store.getState();
15411
+ const internal = state.internal;
15412
+ internal.active = false;
15413
+ const attempt = (step) => {
15414
+ try {
15415
+ return step();
15416
+ } catch (error) {
15417
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
15418
+ }
15419
+ };
15420
+ attempt(() => internal.unwatchDpr?.());
15421
+ internal.unwatchDpr = void 0;
15422
+ attempt(() => internal.unregisterRoot?.());
15423
+ internal.unregisterRoot = void 0;
15424
+ attempt(() => detachRootExtensions(root.store));
15425
+ attempt(() => internal.unregisterPrimary?.());
15426
+ attempt(() => internal.untrackStandalone?.());
15427
+ attempt(() => state.events.disconnect?.());
15428
+ if (!internal.isSecondary && internal.actualRenderer?.xr && state.xr) attempt(() => state.xr.disconnect());
15429
+ attempt(() => cleanupHelperGroup(root.store));
15430
+ if (state.scene) attempt(() => dispose(state.scene));
15431
+ const canvasTarget = internal.canvasTarget;
15432
+ if (internal.isSecondary && canvasTarget?.dispose) attempt(() => canvasTarget.dispose());
15433
+ const released = attempt(() => internal.releaseRenderer?.());
15434
+ internal.releaseRenderer = void 0;
15435
+ if (callback) {
15436
+ if (released) {
15437
+ released.then(() => callback(canvas)).catch((error) => console.warn("[R3F] Error in unmount callback", error));
15438
+ } else {
15439
+ callback(canvas);
15440
+ }
15441
+ }
15442
+ }
15443
+ }
15444
+
15445
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
15446
+ const isConstructor = (value) => typeof value === "function" && value?.prototype?.constructor === value;
15447
+ function getLoader(Proto) {
15448
+ if (isConstructor(Proto)) {
15449
+ let loader = memoizedLoaders.get(Proto);
15450
+ if (!loader) {
15451
+ loader = new Proto();
15452
+ memoizedLoaders.set(Proto, loader);
15453
+ }
15454
+ return loader;
15455
+ }
15456
+ return Proto;
15457
+ }
15458
+ function loadingFn(extensions, onProgress) {
15459
+ return function(Proto, input) {
15460
+ const loader = getLoader(Proto);
15461
+ if (extensions) extensions(loader);
15462
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
15463
+ return loader.loadAsync(input, onProgress).then((data) => {
15464
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
15465
+ return data;
15466
+ });
15467
+ }
15468
+ return new Promise(
15469
+ (res, reject) => loader.load(
15470
+ input,
15471
+ (data) => {
15472
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
15473
+ res(data);
15474
+ },
15475
+ onProgress,
15476
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
15477
+ )
15478
+ );
15479
+ };
15480
+ }
15481
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
15482
+ const inputs = Array.isArray(input) ? input : [input];
15483
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15484
+ const fn = loadingFn(extensions, onProgress);
15485
+ const results = cacheKeys.map(
15486
+ (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
15487
+ );
15488
+ const stableRef = useRef(null);
15489
+ const previous = stableRef.current;
15490
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
15491
+ stableRef.current = stable;
15492
+ return Array.isArray(input) ? stable : stable[0];
15493
+ }
15494
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
15495
+ const inputs = Array.isArray(input) ? input : [input];
15496
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15497
+ cacheKeys.forEach(
15498
+ (key, index) => preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
15499
+ );
15500
+ };
15501
+ useLoader.clear = function(loader, input, cacheKey) {
15502
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15503
+ cacheKeys.forEach((key) => clear([loader, key]));
15504
+ };
15505
+ useLoader.loader = getLoader;
15506
+
15507
+ const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
15508
+ function getUrls(input) {
15509
+ if (typeof input === "string") return [input];
15510
+ if (Array.isArray(input)) return input;
15511
+ return Object.values(input);
15512
+ }
15513
+ function inputSignature(input) {
15514
+ if (typeof input === "string") return input;
15515
+ if (Array.isArray(input)) return input.join("\0");
15516
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
15517
+ }
15518
+ function allUrlsCached(urls, textureCache) {
15519
+ return urls.every((url) => textureCache.has(url));
15520
+ }
15521
+ function buildFromCache(input, textureCache) {
15522
+ if (typeof input === "string") {
15523
+ return textureCache.get(input);
15524
+ } else if (Array.isArray(input)) {
15525
+ return input.map((url) => textureCache.get(url));
15526
+ } else {
15527
+ const result = {};
15528
+ const objInput = input;
15529
+ for (const key in objInput) {
15530
+ result[key] = textureCache.get(objInput[key]);
15531
+ }
15532
+ return result;
15533
+ }
15534
+ }
15535
+ function useTexture(input, optionsOrOnLoad) {
15536
+ const renderer = useThree((state) => state.internal.actualRenderer);
15537
+ const store = useStore();
15538
+ const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
15539
+ const { onLoad, cache = true } = options;
15540
+ const onLoadRef = useRef(onLoad);
15541
+ onLoadRef.current = onLoad;
15542
+ const onLoadCalledForRef = useRef(null);
15543
+ const inputKey = useMemo(() => inputSignature(input), [input]);
15544
+ const stableInput = useMemo(() => input, [inputKey]);
15545
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
15546
+ const cachedResult = useMemo(() => {
15547
+ if (!cache) return null;
15548
+ const textures = store.getState().textures;
15549
+ if (!allUrlsCached(urls, textures)) return null;
15550
+ return buildFromCache(stableInput, textures);
15551
+ }, [cache, urls, stableInput, store]);
15552
+ const loadedTextures = useLoader(
15553
+ getThree().TextureLoader,
15554
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
15555
+ );
15556
+ const mappedTextures = useMemo(() => {
15557
+ if (cachedResult) return cachedResult;
15558
+ if (IsObject(stableInput)) {
15559
+ const keyed = {};
15560
+ const textureArray = loadedTextures;
15561
+ let i = 0;
15562
+ for (const key in stableInput) keyed[key] = textureArray[i++];
15563
+ return keyed;
15564
+ } else {
15565
+ return loadedTextures;
15566
+ }
15567
+ }, [stableInput, loadedTextures, cachedResult]);
15568
+ useLayoutEffect(() => {
15569
+ if (cachedResult) return;
15570
+ if (onLoadCalledForRef.current === inputKey) return;
15571
+ onLoadCalledForRef.current = inputKey;
15572
+ onLoadRef.current?.(mappedTextures);
15573
+ }, [cachedResult, mappedTextures, inputKey]);
15574
+ useEffect(() => {
15575
+ if (cachedResult) return;
15576
+ if ("initTexture" in renderer) {
15577
+ let textureArray = [];
15578
+ if (Array.isArray(loadedTextures)) {
15579
+ textureArray = loadedTextures;
15580
+ } else if (loadedTextures.isTexture) {
15581
+ textureArray = [loadedTextures];
15582
+ } else if (IsObject(loadedTextures)) {
15583
+ textureArray = Object.values(loadedTextures);
14917
15584
  }
14918
- if (!state.internal.actualRenderer) {
14919
- if (!rendererSetup) {
14920
- rendererSetup = (async () => {
14921
- const support = wantsGL ? await webgl() : await webgpu();
14922
- registerThree(support.three);
14923
- state.internal.support = support;
14924
- ensureStoreThreeObjects(store);
14925
- if (support.kind === "webgl") {
14926
- renderer = await resolveRenderer(glConfig, defaultGLProps, support.Renderer);
14927
- state.internal.actualRenderer = renderer;
14928
- state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14929
- } else if (primaryCanvas) {
14930
- const primary = await waitForPrimary(primaryCanvas);
14931
- renderer = primary.renderer;
14932
- state.internal.actualRenderer = renderer;
14933
- const canvasTarget = new support.CanvasTarget(canvas);
14934
- primary.store.setState((prev) => ({
14935
- internal: { ...prev.internal, isMultiCanvas: true }
14936
- }));
14937
- state.set((prev) => ({
14938
- webGPUSupported: primary.store.getState().webGPUSupported,
14939
- renderer,
14940
- primaryStore: primary.store,
14941
- internal: {
14942
- ...prev.internal,
14943
- canvasTarget,
14944
- isMultiCanvas: true,
14945
- isSecondary: true,
14946
- targetId: primaryCanvas
14947
- }
14948
- }));
14949
- } else {
14950
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer);
14951
- if (!renderer.hasInitialized?.()) {
14952
- const size2 = computeInitialSize(canvas, propsSize);
14953
- if (size2.width > 0 && size2.height > 0) {
14954
- renderer.setPixelRatio(calculateDpr(dpr));
14955
- renderer.setSize(size2.width, size2.height, false);
14956
- }
14957
- await renderer.init();
14958
- }
14959
- const backend = renderer.backend;
14960
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14961
- state.internal.actualRenderer = renderer;
14962
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14963
- if (canvasId && !state.internal.isSecondary) {
14964
- const canvasTarget = renderer.getCanvasTarget?.();
14965
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14966
- state.set((prev) => ({
14967
- internal: {
14968
- ...prev.internal,
14969
- canvasTarget,
14970
- unregisterPrimary
14971
- }
14972
- }));
14973
- }
14974
- }
14975
- })().catch((err) => {
14976
- rendererSetup = null;
14977
- throw err;
14978
- });
15585
+ textureArray.forEach((texture) => {
15586
+ if (texture?.isTexture) {
15587
+ renderer.initTexture(texture);
14979
15588
  }
14980
- await rendererSetup;
14981
- state = store.getState();
14982
- renderer = state.internal.actualRenderer;
14983
- }
14984
- const three = getThree();
14985
- let raycaster = state.raycaster;
14986
- if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
14987
- const { params, ...options } = raycastOptions || {};
14988
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14989
- if (!is.equ(params, raycaster.params, shallowLoose)) {
14990
- applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14991
- }
14992
- let tempCamera = state.camera;
14993
- if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14994
- lastCamera = cameraOptions;
14995
- const isCamera = cameraOptions?.isCamera;
14996
- const camera = isCamera ? cameraOptions : orthographic ? new three.OrthographicCamera(0, 0, 0, 0, 0.1, 1e3) : new three.PerspectiveCamera(50, 0, 0.1, 1e3);
14997
- if (!isCamera) {
14998
- camera.position.z = 5;
14999
- if (cameraOptions) {
15000
- applyProps(camera, cameraOptions);
15001
- if (!camera.manual) {
15002
- const projectionProps = ["aspect", "left", "right", "bottom", "top"];
15003
- if (projectionProps.some((prop) => prop in cameraOptions)) {
15004
- camera.manual = true;
15005
- camera.updateProjectionMatrix();
15006
- }
15007
- }
15589
+ });
15590
+ }
15591
+ }, [renderer, loadedTextures, cachedResult]);
15592
+ useEffect(() => {
15593
+ if (!cache) return;
15594
+ const urlTextureMap = urlTextureEntries(stableInput, mappedTextures);
15595
+ store.setState((state) => {
15596
+ const refs = new Map(state._textureRefs);
15597
+ let textures = state.textures;
15598
+ let added = false;
15599
+ for (const [url, texture] of urlTextureMap) {
15600
+ if (!textures.has(url)) {
15601
+ if (!added) {
15602
+ textures = new Map(textures);
15603
+ added = true;
15008
15604
  }
15009
- if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
15605
+ textures.set(url, texture);
15010
15606
  }
15011
- state.set({ camera });
15012
- tempCamera = camera;
15013
- raycaster.camera = camera;
15014
- }
15015
- if (!state.scene) {
15016
- let scene;
15017
- if (sceneOptions?.isScene) {
15018
- scene = sceneOptions;
15019
- prepare(scene, store, "", {});
15020
- } else {
15021
- scene = new three.Scene();
15022
- prepare(scene, store, "", {});
15023
- if (sceneOptions) applyProps(scene, sceneOptions);
15024
- }
15025
- state.set((prev) => ({
15026
- scene,
15027
- rootScene: scene,
15028
- internal: { ...prev.internal, container: scene }
15029
- }));
15030
- const camera = tempCamera;
15031
- if (camera && !camera.parent) scene.add(camera);
15032
- }
15033
- if (events && !state.events.handlers) {
15034
- state.set({ events: events(store) });
15035
- let wasEnabled = true;
15036
- store.subscribe((state2) => {
15037
- const { enabled } = state2.events;
15038
- if (enabled && !wasEnabled) {
15039
- state2.events.update?.();
15040
- }
15041
- wasEnabled = enabled;
15042
- });
15043
- }
15044
- if (_sizeProps !== void 0) {
15045
- state.set({ _sizeProps });
15607
+ refs.set(url, (refs.get(url) ?? 0) + 1);
15046
15608
  }
15047
- if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15048
- state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15609
+ return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
15610
+ });
15611
+ unstageTextures(
15612
+ store,
15613
+ urlTextureMap.map(([url]) => url)
15614
+ );
15615
+ return () => store.setState((state) => {
15616
+ const refs = new Map(state._textureRefs);
15617
+ for (const [url] of urlTextureMap) {
15618
+ const next = (refs.get(url) ?? 0) - 1;
15619
+ if (next <= 0) refs.delete(url);
15620
+ else refs.set(url, next);
15049
15621
  }
15050
- const size = computeInitialSize(canvas, propsSize);
15051
- if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15052
- const wasImperative = state._sizeImperative;
15053
- state.setSize(size.width, size.height, size.top, size.left);
15054
- if (!wasImperative) state.set({ _sizeImperative: false });
15055
- }
15056
- if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
15057
- state.setDpr(dpr);
15058
- lastConfiguredProps.dpr = dpr;
15059
- }
15060
- if (frameloop !== void 0 && frameloop !== lastConfiguredProps.frameloop) {
15061
- state.setFrameloop(frameloop);
15062
- lastConfiguredProps.frameloop = frameloop;
15063
- }
15064
- if (!state.onPointerMissed) state.set({ onPointerMissed });
15065
- if (!state.onDragOverMissed) state.set({ onDragOverMissed });
15066
- if (!state.onDropMissed) state.set({ onDropMissed });
15067
- if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
15068
- if (occlusion && !state.internal.occlusionEnabled) {
15069
- enableOcclusion(store);
15070
- }
15071
- if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
15072
- state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
15073
- lastConfiguredProps.performance = performance;
15074
- }
15075
- if (!state.xr) {
15076
- const handleXRFrame = (timestamp, _frame) => {
15077
- const state2 = store.getState();
15078
- if (state2.frameloop === "never") return;
15079
- advance(timestamp, true, state2);
15080
- };
15081
- const actualRenderer = state.internal.actualRenderer;
15082
- const handleSessionChange = () => {
15083
- const state2 = store.getState();
15084
- const renderer2 = state2.internal.actualRenderer;
15085
- actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
15086
- renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
15087
- if (!renderer2.xr.isPresenting) invalidate(state2);
15088
- };
15089
- const xr = {
15090
- connect() {
15091
- const { gl, renderer: renderer2 } = store.getState();
15092
- const xrManager = (renderer2 || gl).xr;
15093
- xrManager.addEventListener("sessionstart", handleSessionChange);
15094
- xrManager.addEventListener("sessionend", handleSessionChange);
15095
- },
15096
- disconnect() {
15097
- const { gl, renderer: renderer2 } = store.getState();
15098
- const xrManager = (renderer2 || gl).xr;
15099
- xrManager.removeEventListener("sessionstart", handleSessionChange);
15100
- xrManager.removeEventListener("sessionend", handleSessionChange);
15101
- }
15102
- };
15103
- if (typeof renderer.xr?.addEventListener === "function") xr.connect();
15104
- state.set({ xr });
15105
- }
15106
- if (renderer.shadowMap && !is.equ(shadows, lastConfiguredProps.shadows, shallowLoose)) {
15107
- lastConfiguredProps.shadows = shadows;
15108
- const oldEnabled = renderer.shadowMap.enabled;
15109
- const oldType = renderer.shadowMap.type;
15110
- renderer.shadowMap.enabled = !!shadows;
15111
- if (is.boo(shadows)) {
15112
- renderer.shadowMap.type = three.PCFShadowMap;
15113
- } else if (is.str(shadows)) {
15114
- if (shadows === "soft") {
15115
- notifyDepreciated({
15116
- heading: 'shadows="soft" is deprecated',
15117
- body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15118
- link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15119
- });
15622
+ return { _textureRefs: refs };
15623
+ });
15624
+ }, [cache, stableInput, mappedTextures, store]);
15625
+ if (cache && !cachedResult) stageTextures(store, urlTextureEntries(stableInput, mappedTextures));
15626
+ return mappedTextures;
15627
+ }
15628
+ function urlTextureEntries(input, textures) {
15629
+ if (typeof input === "string") return [[input, textures]];
15630
+ if (Array.isArray(input)) return input.map((url, i) => [url, textures[i]]);
15631
+ const record = textures;
15632
+ return Object.keys(input).map((key) => [input[key], record[key]]);
15633
+ }
15634
+ useTexture.preload = (url) => {
15635
+ whenThree().then((three) => useLoader.preload(three.TextureLoader, url));
15636
+ };
15637
+ useTexture.clear = (input) => {
15638
+ if (hasThree()) useLoader.clear(getThree().TextureLoader, input);
15639
+ };
15640
+ const Texture = ({
15641
+ children,
15642
+ input,
15643
+ onLoad,
15644
+ cache
15645
+ }) => {
15646
+ const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
15647
+ const ret = useTexture(input, options);
15648
+ return /* @__PURE__ */ jsx(Fragment, { children: children?.(ret) });
15649
+ };
15650
+
15651
+ function useTextures(selector) {
15652
+ const store = useStore();
15653
+ const registry = useMemo(() => {
15654
+ const getState = store.getState;
15655
+ const setState = store.setState;
15656
+ const getOne = (key) => getState().textures.get(key);
15657
+ return {
15658
+ get all() {
15659
+ return getState().textures;
15660
+ },
15661
+ get(input) {
15662
+ if (typeof input === "string") return getOne(input);
15663
+ if (Array.isArray(input)) return input.map(getOne);
15664
+ const out = {};
15665
+ for (const name in input) out[name] = getOne(input[name]);
15666
+ return out;
15667
+ },
15668
+ has: (key) => getState().textures.has(key),
15669
+ add(keyOrRecord, texture) {
15670
+ setState((state) => {
15671
+ const textures = new Map(state.textures);
15672
+ if (typeof keyOrRecord === "string") {
15673
+ textures.set(keyOrRecord, texture);
15674
+ } else {
15675
+ for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
15120
15676
  }
15121
- const types = {
15122
- basic: three.BasicShadowMap,
15123
- percentage: three.PCFShadowMap,
15124
- soft: three.PCFShadowMap,
15125
- variance: three.VSMShadowMap
15126
- };
15127
- renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
15128
- } else if (is.obj(shadows)) {
15129
- Object.assign(renderer.shadowMap, shadows);
15130
- }
15131
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
15132
- renderer.shadowMap.needsUpdate = true;
15133
- }
15134
- }
15135
- if (!configured) {
15136
- renderer.outputColorSpace = three.SRGBColorSpace;
15137
- renderer.toneMapping = three.ACESFilmicToneMapping;
15138
- }
15139
- const textureColorSpace = requestedTextureColorSpace ?? three.SRGBColorSpace;
15140
- if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
15141
- if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15142
- lastConfiguredProps.textureColorSpace = textureColorSpace;
15143
- }
15144
- const r3fProps = ["textureColorSpace"];
15145
- const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15146
- const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15147
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15148
- const glProps = {};
15149
- for (const key in glConfig) {
15150
- if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15151
- }
15152
- applyProps(renderer, glProps);
15153
- }
15154
- if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15155
- const currentRenderer = state.renderer;
15156
- if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15157
- const rendererProps = {};
15158
- for (const key in rendererConfig) {
15159
- if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15160
- }
15161
- applyProps(currentRenderer, rendererProps);
15162
- }
15163
- }
15164
- const scheduler = getScheduler();
15165
- const rootId = state.internal.rootId;
15166
- if (!rootId) {
15167
- const newRootId = canvasId || scheduler.generateRootId();
15168
- const unregisterRoot = scheduler.registerRoot(newRootId, {
15169
- getState: () => store.getState(),
15170
- onError: (err) => store.getState().setError(err),
15171
- frameloop: store.getState().frameloop,
15172
- before: schedulerConfig?.before,
15173
- after: schedulerConfig?.after,
15174
- order: schedulerConfig?.order
15677
+ return { textures };
15175
15678
  });
15176
- const unregisterCanvasTarget = scheduler.register(
15177
- () => {
15178
- const state2 = store.getState();
15179
- const canvasTarget = state2.internal.canvasTarget;
15180
- if (canvasTarget) {
15181
- const renderer2 = state2.internal.actualRenderer;
15182
- if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
15183
- if (state2.internal.canvasTargetSizeDirty) {
15184
- state2.internal.canvasTargetSizeDirty = false;
15185
- renderer2.backend?.updateSize?.();
15186
- }
15187
- }
15188
- },
15189
- {
15190
- id: `${newRootId}_canvasTarget`,
15191
- rootId: newRootId,
15192
- phase: "start",
15193
- system: true
15194
- }
15195
- );
15196
- const unregisterEventsFlush = scheduler.register(
15197
- () => {
15198
- const state2 = store.getState();
15199
- state2.events.flush?.();
15200
- },
15201
- {
15202
- id: `${newRootId}_events`,
15203
- rootId: newRootId,
15204
- phase: "input",
15205
- system: true
15206
- }
15207
- );
15208
- const unregisterFrustum = scheduler.register(
15209
- () => {
15210
- const state2 = store.getState();
15211
- if (state2.autoUpdateFrustum && state2.camera) {
15212
- updateFrustum(state2.camera, state2.frustum);
15213
- }
15214
- },
15215
- {
15216
- id: `${newRootId}_frustum`,
15217
- rootId: newRootId,
15218
- before: "render",
15219
- system: true
15220
- }
15221
- );
15222
- const unregisterVisibility = scheduler.register(
15223
- () => {
15224
- const state2 = store.getState();
15225
- checkVisibility(state2);
15226
- },
15227
- {
15228
- id: `${newRootId}_visibility`,
15229
- rootId: newRootId,
15230
- before: "render",
15231
- system: true,
15232
- after: `${newRootId}_frustum`
15233
- }
15234
- );
15235
- const unregisterRender = scheduler.register(
15236
- () => {
15237
- const state2 = store.getState();
15238
- const renderer2 = state2.internal.actualRenderer;
15239
- const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
15240
- if (userHandlesRender || state2.internal.priority) return;
15241
- try {
15242
- const renderOverride = state2.internal.renderOverride;
15243
- if (renderOverride) renderOverride();
15244
- else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15245
- } catch (error) {
15246
- state2.setError(error instanceof Error ? error : new Error(String(error)));
15247
- }
15248
- },
15249
- {
15250
- // Use canvas ID directly as job ID if available, otherwise use generated rootId
15251
- id: canvasId || `${newRootId}_render`,
15252
- rootId: newRootId,
15253
- phase: "render",
15254
- system: true,
15255
- // Internal flag: this is a system job, not user-controlled
15256
- // FPS throttles only the default render job; Canvas ordering belongs to the root.
15257
- ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
15258
- }
15259
- );
15260
- state.set((state2) => ({
15261
- internal: {
15262
- ...state2.internal,
15263
- rootId: newRootId,
15264
- unregisterRoot: () => {
15265
- unregisterRoot();
15266
- unregisterCanvasTarget();
15267
- unregisterEventsFlush();
15268
- unregisterFrustum();
15269
- unregisterVisibility();
15270
- unregisterRender();
15271
- },
15272
- scheduler
15273
- }
15274
- }));
15275
- } else {
15276
- const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
15277
- if (constraintsChanged) {
15278
- scheduler.setRootConstraints(rootId, {
15279
- before: schedulerConfig?.before,
15280
- after: schedulerConfig?.after
15281
- });
15282
- }
15283
- if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
15284
- scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
15679
+ },
15680
+ dispose(key, options) {
15681
+ const state = getState();
15682
+ const refs = state._textureRefs.get(key) ?? 0;
15683
+ if (refs > 0 && !options?.force) {
15684
+ console.warn(
15685
+ `[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
15686
+ );
15687
+ return false;
15285
15688
  }
15286
- }
15287
- lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
15288
- lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
15289
- lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
15290
- attachRootExtensions(store);
15291
- onCreated = onCreatedCallback;
15292
- configured = true;
15293
- resolve();
15294
- return this;
15295
- },
15296
- render(children) {
15297
- if (!configured && !pending) this.configure();
15298
- pending.then(() => {
15299
- reconciler.updateContainer(
15300
- /* @__PURE__ */ jsx(Provider, { store, children, onCreated, rootElement: canvas }),
15301
- fiber,
15302
- null,
15303
- () => void 0
15304
- );
15305
- });
15306
- return store;
15307
- },
15308
- unmount() {
15309
- unmountComponentAtNode(canvas);
15310
- }
15311
- };
15689
+ state.textures.get(key)?.dispose();
15690
+ setState((s) => {
15691
+ const textures = new Map(s.textures);
15692
+ textures.delete(key);
15693
+ const nextRefs = new Map(s._textureRefs);
15694
+ nextRefs.delete(key);
15695
+ return { textures, _textureRefs: nextRefs };
15696
+ });
15697
+ return true;
15698
+ },
15699
+ disposeAll() {
15700
+ for (const texture of getState().textures.values()) texture.dispose();
15701
+ setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
15702
+ }
15703
+ };
15704
+ }, [store]);
15705
+ const subscribe = selector ? () => selector(registry) : (state) => state.textures;
15706
+ const selected = useThree(subscribe);
15707
+ return selector ? selected : registry;
15312
15708
  }
15313
- function Provider({
15314
- store,
15315
- children,
15316
- onCreated,
15317
- rootElement
15318
- }) {
15319
- useIsomorphicLayoutEffect(() => {
15320
- const state = store.getState();
15321
- state.set((state2) => ({ internal: { ...state2.internal, active: true } }));
15322
- if (onCreated) onCreated(state);
15323
- if (!store.getState().events.connected) state.events.connect?.(rootElement);
15324
- }, []);
15325
- return /* @__PURE__ */ jsx(context.Provider, { value: store, children });
15709
+
15710
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
15711
+ const support = useThree((s) => s.internal.support);
15712
+ const size = useThree((s) => s.size);
15713
+ let width;
15714
+ let height;
15715
+ let opts;
15716
+ if (typeof widthOrOptions === "object") {
15717
+ opts = widthOrOptions;
15718
+ } else if (typeof widthOrOptions === "number") {
15719
+ width = widthOrOptions;
15720
+ if (typeof heightOrOptions === "object") {
15721
+ height = widthOrOptions;
15722
+ opts = heightOrOptions;
15723
+ } else if (typeof heightOrOptions === "number") {
15724
+ height = heightOrOptions;
15725
+ opts = options;
15726
+ } else {
15727
+ height = widthOrOptions;
15728
+ }
15729
+ }
15730
+ return useMemo(() => {
15731
+ const w = width ?? size.width;
15732
+ const h = height ?? size.height;
15733
+ const Target = support.RenderTarget;
15734
+ return new Target(w, h, opts);
15735
+ }, [width, height, size.width, size.height, opts, support]);
15326
15736
  }
15327
- function unmountComponentAtNode(canvas, callback) {
15328
- const root = _roots.get(canvas);
15329
- const fiber = root?.fiber;
15330
- if (fiber) {
15331
- const state = root?.store.getState();
15332
- if (state) {
15333
- state.internal.active = false;
15334
- const unregisterRoot = state.internal.unregisterRoot;
15335
- if (unregisterRoot) {
15336
- unregisterRoot();
15337
- state.internal.unregisterRoot = void 0;
15737
+
15738
+ const presetsObj = {
15739
+ apartment: "lebombo_1k.hdr",
15740
+ city: "potsdamer_platz_1k.hdr",
15741
+ dawn: "kiara_1_dawn_1k.hdr",
15742
+ forest: "forest_slope_1k.hdr",
15743
+ lobby: "st_fagans_interior_1k.hdr",
15744
+ night: "dikhololo_night_1k.hdr",
15745
+ park: "rooitou_park_1k.hdr",
15746
+ studio: "studio_small_03_1k.hdr",
15747
+ sunset: "venice_sunset_1k.hdr",
15748
+ warehouse: "empty_warehouse_01_1k.hdr"
15749
+ };
15750
+
15751
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
15752
+ const isArray = (arr) => Array.isArray(arr);
15753
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
15754
+ const loadedLoaders = /* @__PURE__ */ new Map();
15755
+ const pendingLoaders = /* @__PURE__ */ new Map();
15756
+ function loadLoader(format) {
15757
+ let pending = pendingLoaders.get(format);
15758
+ if (!pending) {
15759
+ pending = importLoader(format).then(
15760
+ (loader) => {
15761
+ loadedLoaders.set(format, loader);
15762
+ return loader;
15763
+ },
15764
+ (error) => {
15765
+ pendingLoaders.delete(format);
15766
+ throw error;
15338
15767
  }
15768
+ );
15769
+ pendingLoaders.set(format, pending);
15770
+ }
15771
+ return pending;
15772
+ }
15773
+ async function importLoader(format) {
15774
+ let loader;
15775
+ switch (format) {
15776
+ case "cube":
15777
+ loader = getThree().CubeTextureLoader;
15778
+ break;
15779
+ case "hdr-cube":
15780
+ loader = (await import('three/examples/jsm/loaders/HDRCubeTextureLoader.js')).HDRCubeTextureLoader;
15781
+ break;
15782
+ case "hdr":
15783
+ loader = (await import('three/examples/jsm/loaders/HDRLoader.js')).HDRLoader;
15784
+ break;
15785
+ case "exr":
15786
+ loader = (await import('three/examples/jsm/loaders/EXRLoader.js')).EXRLoader;
15787
+ break;
15788
+ case "jpg":
15789
+ loader = (await import('three/examples/jsm/loaders/UltraHDRLoader.js')).UltraHDRLoader;
15790
+ break;
15791
+ }
15792
+ return loader;
15793
+ }
15794
+ const LOADER_KEY = Symbol("r3f-environment-loader");
15795
+ function useEnvironmentLoader(format) {
15796
+ return suspend(() => loadLoader(format), [LOADER_KEY, format]);
15797
+ }
15798
+ function useEnvironment({
15799
+ files = defaultFiles,
15800
+ path = "",
15801
+ preset = void 0,
15802
+ colorSpace = void 0,
15803
+ extensions
15804
+ } = {}) {
15805
+ if (preset) {
15806
+ validatePreset(preset);
15807
+ files = presetsObj[preset];
15808
+ path = CUBEMAP_ROOT;
15809
+ }
15810
+ const multiFile = isArray(files);
15811
+ const { format, isCubemap } = getFormat(files);
15812
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15813
+ const loader = useEnvironmentLoader(format);
15814
+ const loaderResult = useLoader(
15815
+ loader,
15816
+ multiFile ? [files] : files,
15817
+ (loader2) => {
15818
+ loader2.setPath?.(path);
15819
+ if (extensions) extensions(loader2);
15339
15820
  }
15340
- reconciler.updateContainer(null, fiber, null, () => {
15341
- if (root?.store) detachRootExtensions(root.store);
15342
- if (state) {
15343
- setTimeout(() => {
15344
- try {
15345
- const renderer = state.internal.actualRenderer;
15346
- const unregisterPrimary = state.internal.unregisterPrimary;
15347
- if (unregisterPrimary) unregisterPrimary();
15348
- const canvasTarget = state.internal.canvasTarget;
15349
- if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
15350
- state.events.disconnect?.();
15351
- cleanupHelperGroup(root.store);
15352
- if (state.isLegacy && renderer) {
15353
- ;
15354
- renderer.renderLists?.dispose?.();
15355
- renderer.forceContextLoss?.();
15356
- }
15357
- if (!state.internal.isSecondary) {
15358
- if (renderer?.xr) state.xr.disconnect();
15359
- }
15360
- dispose(state.scene);
15361
- _roots.delete(canvas);
15362
- if (callback) callback(canvas);
15363
- } catch (error) {
15364
- console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
15365
- }
15366
- }, 500);
15367
- }
15821
+ );
15822
+ const texture = multiFile ? (
15823
+ // @ts-ignore
15824
+ loaderResult[0]
15825
+ ) : loaderResult;
15826
+ const three = getThree();
15827
+ texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
15828
+ texture.colorSpace = colorSpace ?? (isCubemap && format !== "hdr-cube" ? "srgb" : "srgb-linear");
15829
+ return texture;
15830
+ }
15831
+ const preloadDefaultOptions = {
15832
+ files: defaultFiles,
15833
+ path: "",
15834
+ preset: void 0,
15835
+ extensions: void 0
15836
+ };
15837
+ useEnvironment.preload = (preloadOptions) => {
15838
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
15839
+ let { files, path = "" } = options;
15840
+ const { preset, extensions } = options;
15841
+ if (preset) {
15842
+ validatePreset(preset);
15843
+ files = presetsObj[preset];
15844
+ path = CUBEMAP_ROOT;
15845
+ }
15846
+ const { format } = getFormat(files);
15847
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15848
+ const input = isArray(files) ? [files] : files;
15849
+ loadLoader(format).then((loader) => {
15850
+ useLoader.preload(loader, input, (loader2) => {
15851
+ loader2.setPath?.(path);
15852
+ if (extensions) extensions(loader2);
15853
+ });
15854
+ });
15855
+ };
15856
+ const clearDefaultOptins = {
15857
+ files: defaultFiles,
15858
+ preset: void 0
15859
+ };
15860
+ useEnvironment.clear = (clearOptions) => {
15861
+ const options = { ...clearDefaultOptins, ...clearOptions };
15862
+ let { files } = options;
15863
+ const { preset } = options;
15864
+ if (preset) {
15865
+ validatePreset(preset);
15866
+ files = presetsObj[preset];
15867
+ }
15868
+ const { format } = getFormat(files);
15869
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15870
+ const input = isArray(files) ? [files] : files;
15871
+ const clear = (loader2) => useLoader.clear(loader2, input);
15872
+ const loader = loadedLoaders.get(format);
15873
+ if (loader) {
15874
+ clear(loader);
15875
+ } else {
15876
+ const pending = pendingLoaders.get(format);
15877
+ if (pending) void pending.then(clear, () => {
15368
15878
  });
15369
15879
  }
15880
+ };
15881
+ function validatePreset(preset) {
15882
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
15883
+ }
15884
+ function getFormat(files) {
15885
+ const isCubemap = isArray(files) && files.length === 6;
15886
+ const firstEntry = isArray(files) ? files[0] : files;
15887
+ const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
15888
+ let format;
15889
+ if (isCubemap) format = firstExtension === "hdr" ? "hdr-cube" : "cube";
15890
+ else if (firstEntry.startsWith("data:application/exr")) format = "exr";
15891
+ else if (firstEntry.startsWith("data:application/hdr")) format = "hdr";
15892
+ else if (firstEntry.startsWith("data:image/jpeg")) format = "jpg";
15893
+ else if (firstExtension === "hdr" || firstExtension === "exr") format = firstExtension;
15894
+ else if (firstExtension === "jpg" || firstExtension === "jpeg") format = "jpg";
15895
+ return { format, isCubemap };
15896
+ }
15897
+
15898
+ function useInstanceHandle(ref) {
15899
+ const instance = useRef(null);
15900
+ useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
15901
+ return instance;
15902
+ }
15903
+ function useGraph(object) {
15904
+ return useMemo(() => buildGraph(object), [object]);
15370
15905
  }
15906
+
15371
15907
  function createPortal(children, container, state) {
15372
15908
  return /* @__PURE__ */ jsx(Portal, { children, container, state });
15373
15909
  }
@@ -15404,16 +15940,13 @@ function PortalInner({ state = {}, children, container }) {
15404
15940
  const [portalScene] = useState(() => {
15405
15941
  if ("isScene" in container && container.isScene) return container;
15406
15942
  if (!injectScene) return container;
15407
- const scene = new (getThree()).Scene();
15408
- container.add(scene);
15409
- return scene;
15943
+ return new (getThree()).Scene();
15410
15944
  });
15411
15945
  useIsomorphicLayoutEffect(() => {
15946
+ if (portalScene === container || !injectScene) return;
15947
+ container.add(portalScene);
15412
15948
  return () => {
15413
- if (portalScene !== container && injectScene) {
15414
- container.remove(portalScene);
15415
- dispose(portalScene);
15416
- }
15949
+ container.remove(portalScene);
15417
15950
  };
15418
15951
  }, [portalScene, container, injectScene]);
15419
15952
  const lastRootState = useRef(null);
@@ -15482,9 +16015,6 @@ function PortalInner({ state = {}, children, container }) {
15482
16015
  ) })
15483
16016
  );
15484
16017
  }
15485
- function flushSync(fn) {
15486
- return reconciler.flushSyncFromReconciler(fn);
15487
- }
15488
16018
 
15489
16019
  const isRef = (obj) => obj.current && obj.current.isScene;
15490
16020
  const resolveScene = (scene) => isRef(scene) ? scene.current : scene;
@@ -15720,19 +16250,6 @@ function parseBackground(background) {
15720
16250
  return null;
15721
16251
  }
15722
16252
 
15723
- function parseRendererConfig(rendererProp) {
15724
- const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15725
- if (isRendererConfig) {
15726
- const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15727
- return {
15728
- primaryCanvas: pc,
15729
- scheduler: sc,
15730
- renderer: Object.keys(rest).length > 0 ? rest : rendererProp
15731
- };
15732
- }
15733
- return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
15734
- }
15735
-
15736
16253
  function CanvasImpl({
15737
16254
  ref,
15738
16255
  children,
@@ -15741,11 +16258,13 @@ function CanvasImpl({
15741
16258
  style,
15742
16259
  id,
15743
16260
  gl,
15744
- renderer: rendererProp,
16261
+ renderer,
16262
+ primary,
16263
+ share,
16264
+ scheduler,
15745
16265
  events = createPointerEvents,
15746
16266
  eventSource,
15747
16267
  eventPrefix,
15748
- shadows,
15749
16268
  orthographic,
15750
16269
  frameloop,
15751
16270
  dpr,
@@ -15767,7 +16286,11 @@ function CanvasImpl({
15767
16286
  provider,
15768
16287
  ...props
15769
16288
  }) {
15770
- const { primaryCanvas, scheduler, renderer } = parseRendererConfig(rendererProp);
16289
+ const {
16290
+ shadows: removedShadows,
16291
+ primaryCanvas: removedPrimaryCanvas,
16292
+ ...divProps
16293
+ } = props;
15771
16294
  const Bridge = useBridge();
15772
16295
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
15773
16296
  const [hasInitialSize, setHasInitialSize] = React.useState(false);
@@ -15813,10 +16336,22 @@ function CanvasImpl({
15813
16336
  if (block) throw block;
15814
16337
  if (error) throw error;
15815
16338
  const root = React.useRef(null);
15816
- const effectActiveRef = React.useRef(true);
15817
- const unsubscribeErrorRef = React.useRef(null);
16339
+ const [gate, waitFor] = useGate();
16340
+ const rootState = React.useRef(null);
16341
+ const eventTarget = () => eventSource ? isRef$1(eventSource) ? eventSource.current : eventSource : divRef.current;
16342
+ const pointerMissed = React.useCallback(
16343
+ (...args) => handlePointerMissed.current?.(...args),
16344
+ [handlePointerMissed]
16345
+ );
16346
+ const dragOverMissed = React.useCallback(
16347
+ (...args) => handleDragOverMissed.current?.(...args),
16348
+ [handleDragOverMissed]
16349
+ );
16350
+ const dropMissed = React.useCallback(
16351
+ (...args) => handleDropMissed.current?.(...args),
16352
+ [handleDropMissed]
16353
+ );
15818
16354
  useIsomorphicLayoutEffect(() => {
15819
- effectActiveRef.current = true;
15820
16355
  if (fallbackVisible) return;
15821
16356
  const canvas = canvasRef.current;
15822
16357
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
@@ -15826,97 +16361,97 @@ function CanvasImpl({
15826
16361
  message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
15827
16362
  link: "https://github.com/pmndrs/react-three-fiber/discussions"
15828
16363
  });
15829
- const rootEntry = _roots.get(canvas);
15830
- if (rootEntry?.store) {
15831
- if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
15832
- unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
15833
- if (state.error && effectActiveRef.current) {
15834
- setError(state.error);
15835
- }
15836
- });
15837
- }
15838
16364
  }
15839
- async function run() {
15840
- if (!effectActiveRef.current || !root.current) return;
15841
- const configured = await root.current.configure({
15842
- id,
15843
- primaryCanvas,
15844
- scheduler,
15845
- gl,
15846
- renderer,
15847
- scene,
15848
- events,
15849
- shadows,
15850
- orthographic,
15851
- frameloop,
15852
- dpr,
15853
- performance,
15854
- raycaster,
15855
- camera,
15856
- autoUpdateFrustum,
15857
- occlusion,
15858
- size: effectiveSize,
15859
- // Store size props for reset functionality
15860
- _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15861
- forceEven,
15862
- // Pass mutable reference to onPointerMissed so it's free to update
15863
- onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15864
- onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
15865
- onDropMissed: (...args) => handleDropMissed.current?.(...args),
15866
- onCreated: (state) => {
15867
- state.events.connect?.(
15868
- eventSource ? isRef$1(eventSource) ? eventSource.current : eventSource : divRef.current
15869
- );
15870
- if (eventPrefix) {
15871
- state.setEvents({
15872
- compute: (event, state2) => {
15873
- const x = event[eventPrefix + "X"];
15874
- const y = event[eventPrefix + "Y"];
15875
- state2.pointer.set(x / state2.size.width * 2 - 1, -(y / state2.size.height) * 2 + 1);
15876
- state2.raycaster.setFromCamera(state2.pointer, state2.camera);
15877
- }
15878
- });
15879
- }
15880
- onCreated?.(state);
15881
- }
15882
- }).then(
15883
- () => true,
15884
- // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15885
- // inside <canvas>, which browsers don't display, so surface it as visible DOM
15886
- // instead; with no fallback, rethrow to an external error boundary. (#3757)
15887
- (setupError) => {
15888
- if (effectActiveRef.current) {
15889
- if (fallback != null) setFallbackVisible(true);
15890
- else setError(setupError);
15891
- }
15892
- return false;
16365
+ const current = root.current;
16366
+ current.configure({
16367
+ id,
16368
+ primary,
16369
+ share,
16370
+ scheduler,
16371
+ gl,
16372
+ renderer,
16373
+ scene,
16374
+ events,
16375
+ _primaryToken: primaryToken,
16376
+ ...{ shadows: removedShadows, primaryCanvas: removedPrimaryCanvas },
16377
+ orthographic,
16378
+ frameloop,
16379
+ dpr,
16380
+ performance,
16381
+ raycaster,
16382
+ camera,
16383
+ autoUpdateFrustum,
16384
+ occlusion,
16385
+ size: effectiveSize,
16386
+ // Store size props for reset functionality
16387
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
16388
+ forceEven,
16389
+ // Pass mutable reference to onPointerMissed so it's free to update
16390
+ onPointerMissed: pointerMissed,
16391
+ onDragOverMissed: dragOverMissed,
16392
+ onDropMissed: dropMissed,
16393
+ onCreated: (state) => {
16394
+ rootState.current = state;
16395
+ state.events.connect?.(eventTarget() ?? divRef.current);
16396
+ if (eventPrefix) {
16397
+ state.setEvents({
16398
+ compute: (event, state2) => {
16399
+ const x = event[eventPrefix + "X"];
16400
+ const y = event[eventPrefix + "Y"];
16401
+ state2.pointer.set(x / state2.size.width * 2 - 1, -(y / state2.size.height) * 2 + 1);
16402
+ state2.raycaster.setFromCamera(state2.pointer, state2.camera);
16403
+ }
16404
+ });
15893
16405
  }
15894
- );
15895
- if (!configured || !effectActiveRef.current || !root.current) return;
15896
- root.current.render(
16406
+ onCreated?.(state);
16407
+ }
16408
+ }).catch((setupError) => {
16409
+ if (root.current !== current) return;
16410
+ if (fallback != null) setFallbackVisible(true);
16411
+ else setError(setupError);
16412
+ });
16413
+ if (current.ready.status === "fulfilled") {
16414
+ current.render(
15897
16415
  /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
15898
16416
  backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
15899
16417
  children ?? null
15900
16418
  ] }) }) })
15901
16419
  );
16420
+ } else if (current.ready.status === "pending") {
16421
+ waitFor(current.ready);
15902
16422
  }
15903
- run();
15904
16423
  }
15905
- return () => {
15906
- effectActiveRef.current = false;
15907
- if (unsubscribeErrorRef.current) {
15908
- unsubscribeErrorRef.current();
15909
- unsubscribeErrorRef.current = null;
15910
- }
16424
+ });
16425
+ useIsomorphicLayoutEffect(() => {
16426
+ const store = canvasRef.current && _roots.get(canvasRef.current)?.store;
16427
+ if (!store) return;
16428
+ const report = () => {
16429
+ const error2 = store.getState().error;
16430
+ if (error2) setError(error2);
15911
16431
  };
16432
+ report();
16433
+ return store.subscribe(report);
16434
+ });
16435
+ React.useEffect(() => {
16436
+ const state = rootState.current?.get();
16437
+ const target = eventTarget();
16438
+ if (state && target && state.events.connected !== target) state.events.connect?.(target);
15912
16439
  });
15913
16440
  const insertionMounted = React.useRef(false);
16441
+ const [primaryToken] = React.useState(() => ({}));
16442
+ const primaryKey = React.useRef(null);
15914
16443
  React.useInsertionEffect(() => {
15915
16444
  insertionMounted.current = true;
16445
+ if (primary && provider.webgpu) {
16446
+ primaryKey.current ?? (primaryKey.current = id || DEFAULT_PRIMARY);
16447
+ announcePrimary(primaryKey.current, primaryToken);
16448
+ }
15916
16449
  return () => {
15917
16450
  insertionMounted.current = false;
16451
+ if (primaryKey.current) markWithdrawing(primaryToken);
15918
16452
  queueMicrotask(() => {
15919
16453
  if (insertionMounted.current) return;
16454
+ if (primaryKey.current) withdrawPrimary(primaryKey.current, primaryToken);
15920
16455
  const current = root.current;
15921
16456
  root.current = null;
15922
16457
  current?.unmount();
@@ -15957,7 +16492,7 @@ function CanvasImpl({
15957
16492
  }
15958
16493
  }, [hmr]);
15959
16494
  const pointerEvents = eventSource ? "none" : "auto";
15960
- return /* @__PURE__ */ jsx(
16495
+ return /* @__PURE__ */ jsxs(
15961
16496
  "div",
15962
16497
  {
15963
16498
  ref: divRef,
@@ -15969,21 +16504,24 @@ function CanvasImpl({
15969
16504
  pointerEvents,
15970
16505
  ...style
15971
16506
  },
15972
- ...props,
15973
- children: fallbackVisible ? (
15974
- // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15975
- // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15976
- fallback
15977
- ) : /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
15978
- "canvas",
15979
- {
15980
- ref: canvasRef,
15981
- id,
15982
- className: "r3f-canvas",
15983
- style: { display: "block", width: "100%", height: "100%" },
15984
- children: fallback
15985
- }
15986
- ) })
16507
+ ...divProps,
16508
+ children: [
16509
+ fallbackVisible ? (
16510
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
16511
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
16512
+ fallback
16513
+ ) : /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
16514
+ "canvas",
16515
+ {
16516
+ ref: canvasRef,
16517
+ id,
16518
+ className: "r3f-canvas",
16519
+ style: { display: "block", width: "100%", height: "100%" },
16520
+ children: fallback
16521
+ }
16522
+ ) }),
16523
+ gate
16524
+ ]
15987
16525
  }
15988
16526
  );
15989
16527
  }
@@ -15991,4 +16529,4 @@ function Canvas(props) {
15991
16529
  return /* @__PURE__ */ jsx(FiberProvider, { children: /* @__PURE__ */ jsx(CanvasImpl, { ...props }) });
15992
16530
  }
15993
16531
 
15994
- export { attach as $, addAfterEffect as A, addTail as B, Canvas as C, invalidate as D, EnvironmentMap as E, advance as F, extend as G, reconciler as H, IsObject as I, isRenderer as J, unmountComponentAtNode as K, createPortal as L, flushSync as M, createStore as N, findInitialRoot as O, Portal as P, getRootState as Q, REACT_INTERNAL_PROPS as R, buildGraph as S, Texture as T, dispose as U, getInstanceProps as V, prepare as W, invalidateInstance as X, RESERVED_PROPS as Y, resolve as Z, _roots as _, createPointerEvents as a, detach as a0, diffProps as a1, applyProps as a2, calculateDpr as a3, getUuidPrefix as a4, updateCamera as a5, updateFrustum as a6, is as a7, isOrthographicCamera as a8, isRef$1 as a9, isColorRepresentation as aa, isObject3D as ab, isTexture as ac, isLayers as ad, isUniform as ae, isVectorLike as af, isCopyable as ag, hasConstructor as ah, FROM_REF as ai, fromRef as aj, isFromRef as ak, ONCE as al, once as am, isOnce as an, getPrimary as b, createRoot as c, waitForPrimary as d, hasPrimary as e, getPrimaryIds as f, getThree as g, hasThree as h, EnvironmentCube as i, EnvironmentPortal as j, Environment as k, removeInteractivity as l, createEvents as m, useInstanceHandle as n, useGraph as o, presetsObj as p, useLoader as q, registerPrimary as r, swapInteractivity as s, useTexture as t, unregisterPrimary as u, useTextures as v, whenThree as w, useRenderTarget as x, useEnvironment as y, addEffect as z };
16532
+ export { prepare as $, useGraph as A, useLoader as B, Canvas as C, useTexture as D, EnvironmentMap as E, useTextures as F, useRenderTarget as G, useEnvironment as H, IsObject as I, extend as J, reconciler as K, flushSync as L, unmountComponentAtNode as M, createPortal as N, calculateDpr as O, Portal as P, getUuidPrefix as Q, updateCamera as R, updateFrustum as S, Texture as T, REACT_INTERNAL_PROPS as U, findInitialRoot as V, getRootState as W, buildGraph as X, dispose as Y, getInstanceProps as Z, _roots as _, createPointerEvents as a, invalidateInstance as a0, RESERVED_PROPS as a1, resolve as a2, attach as a3, detach as a4, diffProps as a5, applyProps as a6, is as a7, isOrthographicCamera as a8, isRef$1 as a9, isColorRepresentation as aa, isObject3D as ab, isTexture as ac, isLayers as ad, isUniform as ae, isVectorLike as af, isCopyable as ag, hasConstructor as ah, FROM_REF as ai, fromRef as aj, isFromRef as ak, ONCE as al, once as am, isOnce as an, addEffect as b, createRoot as c, addAfterEffect as d, addTail as e, advance as f, isRenderer as g, getPrimary as h, invalidate as i, hasPrimary as j, getPrimaryIds as k, createStore as l, getThree as m, hasThree as n, whenThree as o, EnvironmentCube as p, EnvironmentPortal as q, registerPrimary as r, Environment as s, presetsObj as t, unregisterPrimary as u, swapInteractivity as v, waitForPrimary as w, removeInteractivity as x, createEvents as y, useInstanceHandle as z };