@react-three/fiber 10.0.0-canary.d458ee7 → 10.0.0-canary.d46ab63

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 (41) hide show
  1. package/dist/extension.cjs +8 -8
  2. package/dist/extension.d.cts +50 -6
  3. package/dist/extension.d.mts +50 -6
  4. package/dist/extension.d.ts +50 -6
  5. package/dist/extension.mjs +1 -1
  6. package/dist/index.cjs +17 -19
  7. package/dist/index.d.cts +15 -47
  8. package/dist/index.d.mts +15 -47
  9. package/dist/index.d.ts +15 -47
  10. package/dist/index.mjs +7 -8
  11. package/dist/legacy.cjs +37 -15
  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 +13 -5
  16. package/dist/shared/{fiber.DNehrxK5.d.ts → fiber.34TOMPo3.d.cts} +157 -463
  17. package/dist/shared/fiber.BFuGd7gN.d.cts +90 -0
  18. package/dist/shared/{fiber.BgxhptM_.mjs → fiber.BSYBnw8x.mjs} +2066 -1497
  19. package/dist/shared/{fiber.B0oukOFO.mjs → fiber.Biz9olEe.mjs} +181 -64
  20. package/dist/shared/fiber.Bll9fqKA.d.mts +90 -0
  21. package/dist/shared/{fiber.TWXdbcWP.d.mts → fiber.CjMAAsJy.d.ts} +157 -463
  22. package/dist/shared/{fiber.I5Q4dVvl.d.cts → fiber.DQTSZ24m.d.cts} +488 -26
  23. package/dist/shared/{fiber.I5Q4dVvl.d.mts → fiber.DQTSZ24m.d.mts} +488 -26
  24. package/dist/shared/{fiber.I5Q4dVvl.d.ts → fiber.DQTSZ24m.d.ts} +488 -26
  25. package/dist/shared/fiber.DUgKvaq-.mjs +9 -0
  26. package/dist/shared/{fiber.khFlPHPf.cjs → fiber.DfiCufha.cjs} +186 -62
  27. package/dist/shared/fiber.DuA4F8L5.d.ts +90 -0
  28. package/dist/shared/{fiber.11sXr8Eb.d.cts → fiber.Dy7Uz273.d.mts} +157 -463
  29. package/dist/shared/fiber.X4yI0DId.cjs +14 -0
  30. package/dist/shared/{fiber.EDf0d92E.cjs → fiber.nDnBf1np.cjs} +2092 -1523
  31. package/dist/webgpu/index.cjs +16 -18
  32. package/dist/webgpu/index.d.cts +17 -78
  33. package/dist/webgpu/index.d.mts +17 -78
  34. package/dist/webgpu/index.d.ts +17 -78
  35. package/dist/webgpu/index.mjs +7 -9
  36. package/package.json +3 -3
  37. package/dist/chunks/webgl.cjs +0 -27
  38. package/dist/chunks/webgl.mjs +0 -11
  39. package/dist/shared/fiber.BU0vBHc9.d.mts +0 -48
  40. package/dist/shared/fiber.Cx-UAmMb.d.ts +0 -48
  41. package/dist/shared/fiber.DAl4H27x.d.cts +0 -48
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const jsxRuntime = require('react/jsx-runtime');
4
- const extensions = require('./fiber.khFlPHPf.cjs');
4
+ const textureStaging = require('./fiber.DfiCufha.cjs');
5
5
  const React = require('react');
6
6
  const useMeasure = require('react-use-measure');
7
7
  const itsFine = require('its-fine');
@@ -25,8 +25,8 @@ function _interopNamespaceCompat(e) {
25
25
  return n;
26
26
  }
27
27
 
28
- const React__default = /*#__PURE__*/_interopDefaultCompat(React);
29
28
  const React__namespace = /*#__PURE__*/_interopNamespaceCompat(React);
29
+ const React__default = /*#__PURE__*/_interopDefaultCompat(React);
30
30
  const useMeasure__default = /*#__PURE__*/_interopDefaultCompat(useMeasure);
31
31
  const w1__default = /*#__PURE__*/_interopDefaultCompat(w1);
32
32
 
@@ -128,6 +128,20 @@ function updateFrustum(camera, frustum) {
128
128
  target.setFromProjectionMatrix(frustumMatrix);
129
129
  return target;
130
130
  }
131
+ function computeInitialSize(canvas, size) {
132
+ if (!size && typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
133
+ const { width, height, top, left } = canvas.parentElement.getBoundingClientRect();
134
+ return { width, height, top, left };
135
+ } else if (!size && typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) {
136
+ return {
137
+ width: canvas.width,
138
+ height: canvas.height,
139
+ top: 0,
140
+ left: 0
141
+ };
142
+ }
143
+ return { width: 0, height: 0, top: 0, left: 0, ...size };
144
+ }
131
145
 
132
146
  const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
133
147
  function findInitialRoot(instance) {
@@ -195,6 +209,7 @@ function prepare(target, root, type, props) {
195
209
  object,
196
210
  eventCount: 0,
197
211
  handlers: {},
212
+ previousVisible: void 0,
198
213
  isHidden: false
199
214
  };
200
215
  if (object) object.__r3f = instance;
@@ -422,7 +437,19 @@ function assertNodeMaterialSupported(state, value) {
422
437
  const material = (Array.isArray(value) ? value : [value]).find((m) => m?.isNodeMaterial);
423
438
  if (!material) return;
424
439
  throw new Error(
425
- `R3F: ${material.type ?? "Node material"} requires WebGPURenderer (it falls back to WebGL2 automatically). Pass \`renderer\` to <Canvas> from @react-three/fiber, or import from @react-three/fiber/webgpu.`
440
+ `R3F: ${material.type ?? "Node material"} requires WebGPURenderer (it falls back to WebGL2 automatically). Import Canvas from @react-three/fiber or @react-three/fiber/webgpu instead of @react-three/fiber/legacy.`
441
+ );
442
+ }
443
+ const warnedGLSL = /* @__PURE__ */ new Set();
444
+ function warnGLSLMaterialUnsupported(state, value) {
445
+ if (!state || state.isLegacy || state.internal?.support?.kind !== "webgpu" || !textureStaging.isDevelopment()) return;
446
+ const material = (Array.isArray(value) ? value : [value]).find((m) => m?.isShaderMaterial && !m.isNodeMaterial);
447
+ if (!material) return;
448
+ const type = material.type ?? "ShaderMaterial";
449
+ if (warnedGLSL.has(type)) return;
450
+ warnedGLSL.add(type);
451
+ console.warn(
452
+ `R3F: ${type} is a GLSL material, which WebGPURenderer cannot compile; three draws it with a blank default material. @react-three/fiber renders with WebGPU: import Canvas from @react-three/fiber/legacy to keep WebGLRenderer, or port the shader to a node material (TSL).`
426
453
  );
427
454
  }
428
455
 
@@ -443,6 +470,28 @@ const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
443
470
  const INDEX_REGEX = /-\d+$/;
444
471
  const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
445
472
  const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
473
+ const programProps = /* @__PURE__ */ new Set([
474
+ "transparent",
475
+ "vertexColors",
476
+ "flatShading",
477
+ "fog",
478
+ // Culling is live, but DOUBLE_SIDED / FLIP_SIDED normal flipping is compiled in
479
+ "side",
480
+ "alphaHash",
481
+ "premultipliedAlpha",
482
+ // Both feed the OPAQUE define when the material is not transparent
483
+ "blending",
484
+ "alphaToCoverage",
485
+ "dithering",
486
+ "sizeAttenuation",
487
+ "combine",
488
+ "normalMapType",
489
+ "depthPacking"
490
+ ]);
491
+ function changesProgram(root, key, previous, next) {
492
+ if (!root?.isMaterial || previous === next) return false;
493
+ return programProps.has(key) || isTexture(previous) !== isTexture(next);
494
+ }
446
495
  function resolve(root, key) {
447
496
  if (!key.includes("-")) return { root, key, target: root[key] };
448
497
  if (key in root) return { root, key, target: root[key] };
@@ -473,6 +522,7 @@ function attach(parent, child) {
473
522
  const { root, key } = resolve(parent.object, child.props.attach);
474
523
  child.previousAttach = root[key];
475
524
  root[key] = child.object;
525
+ if (changesProgram(root, key, child.previousAttach, child.object)) root.needsUpdate = true;
476
526
  } else if (is.fun(child.props.attach)) {
477
527
  child.previousAttach = child.props.attach(parent.object, child.object);
478
528
  }
@@ -483,6 +533,7 @@ function detach(parent, child) {
483
533
  const previous = child.previousAttach;
484
534
  if (previous === void 0) delete root[key];
485
535
  else root[key] = previous;
536
+ if (changesProgram(root, key, child.object, previous)) root.needsUpdate = true;
486
537
  } else {
487
538
  child.previousAttach?.(parent.object, child.object);
488
539
  }
@@ -527,7 +578,7 @@ function applyProps(object, props) {
527
578
  const value = props[prop];
528
579
  if (RESERVED_PROPS.includes(prop)) continue;
529
580
  if (prop === "onUpdate") {
530
- extensions.notifyDepreciated({
581
+ textureStaging.notifyDepreciated({
531
582
  heading: "onUpdate is no longer an R3F prop",
532
583
  body: "v10 removed the onUpdate hook: R3F no longer calls it after prop updates. The value is assigned to the object by name. On a Texture that sets three's Texture.onUpdate, which the renderer calls after a GPU upload, not after prop changes. On a node it replaces the Node.onUpdate() method. On other objects it is never called.\n\nUse an effect keyed on the props that change, read the object in useFrame, or use a ref callback.",
533
584
  link: "https://docs.pmnd.rs/react-three-fiber/migration/v10#onupdate-removed"
@@ -562,6 +613,10 @@ function applyProps(object, props) {
562
613
  }
563
614
  continue;
564
615
  }
616
+ if (prop === "visible" && instance?.isHidden && instance.previousVisible !== void 0) {
617
+ instance.previousVisible = value;
618
+ continue;
619
+ }
565
620
  let { root, key, target } = resolve(object, prop);
566
621
  if (target === void 0 && (typeof root !== "object" || root === null)) {
567
622
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -589,17 +644,24 @@ function applyProps(object, props) {
589
644
  for (const name in nextUniforms) {
590
645
  const uniform = nextUniforms[name];
591
646
  const targetUniform = uniforms[name];
592
- if (targetUniform) Object.assign(targetUniform, uniform);
593
- else {
647
+ if (targetUniform) {
648
+ Object.assign(targetUniform, uniform);
649
+ } else {
594
650
  const nextUniform = isUniform(uniform) ? uniform.clone() : new (getThree()).Uniform(uniform.value);
595
651
  uniforms[name] = nextUniform;
596
652
  }
597
653
  }
598
654
  } else {
599
- if (key === "material") assertNodeMaterialSupported(rootState, value);
655
+ if (key === "material") {
656
+ assertNodeMaterialSupported(rootState, value);
657
+ warnGLSLMaterialUnsupported(rootState, value);
658
+ }
659
+ const previous = root[key];
600
660
  root[key] = value;
601
661
  if (key.endsWith("Node") && root.isMaterial) {
602
662
  root.needsUpdate = true;
663
+ } else if (changesProgram(root, key, previous, value)) {
664
+ root.needsUpdate = true;
603
665
  }
604
666
  const three = rootState && getThree();
605
667
  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
@@ -640,471 +702,392 @@ function applyProps(object, props) {
640
702
  return object;
641
703
  }
642
704
 
643
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
644
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
645
- function getLoader(Proto) {
646
- if (isConstructor$1(Proto)) {
647
- let loader = memoizedLoaders.get(Proto);
648
- if (!loader) {
649
- loader = new Proto();
650
- memoizedLoaders.set(Proto, loader);
651
- }
652
- return loader;
653
- }
654
- return Proto;
655
- }
656
- function loadingFn(extensions, onProgress) {
657
- return function(Proto, input) {
658
- const loader = getLoader(Proto);
659
- if (extensions) extensions(loader);
660
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
661
- return loader.loadAsync(input, onProgress).then((data) => {
662
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
663
- return data;
664
- });
665
- }
666
- return new Promise(
667
- (res, reject) => loader.load(
668
- input,
669
- (data) => {
670
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
671
- res(data);
672
- },
673
- onProgress,
674
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
675
- )
676
- );
677
- };
678
- }
679
- function useLoader(loader, input, extensions, onProgress, cacheKey) {
680
- const inputs = Array.isArray(input) ? input : [input];
681
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
682
- const fn = loadingFn(extensions, onProgress);
683
- const results = cacheKeys.map(
684
- (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
685
- );
686
- const stableRef = React.useRef(null);
687
- const previous = stableRef.current;
688
- const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
689
- stableRef.current = stable;
690
- return Array.isArray(input) ? stable : stable[0];
691
- }
692
- useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
693
- const inputs = Array.isArray(input) ? input : [input];
694
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
695
- cacheKeys.forEach(
696
- (key, index) => suspendReact.preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
697
- );
698
- };
699
- useLoader.clear = function(loader, input, cacheKey) {
700
- const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
701
- cacheKeys.forEach((key) => suspendReact.clear([loader, key]));
702
- };
703
- useLoader.loader = getLoader;
704
-
705
- const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
706
- function getUrls(input) {
707
- if (typeof input === "string") return [input];
708
- if (Array.isArray(input)) return input;
709
- return Object.values(input);
710
- }
711
- function inputSignature(input) {
712
- if (typeof input === "string") return input;
713
- if (Array.isArray(input)) return input.join("\0");
714
- return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
715
- }
716
- function allUrlsCached(urls, textureCache) {
717
- return urls.every((url) => textureCache.has(url));
705
+ function updateSize(state, size, imperative) {
706
+ state.set((current) => ({
707
+ size,
708
+ viewport: { ...current.viewport, ...current.viewport.getCurrentViewport(current.camera, void 0, size) },
709
+ _sizeImperative: imperative
710
+ }));
711
+ state.invalidate();
718
712
  }
719
- function buildFromCache(input, textureCache) {
720
- if (typeof input === "string") {
721
- return textureCache.get(input);
722
- } else if (Array.isArray(input)) {
723
- return input.map((url) => textureCache.get(url));
724
- } else {
725
- const result = {};
726
- const objInput = input;
727
- for (const key in objInput) {
728
- result[key] = textureCache.get(objInput[key]);
729
- }
730
- return result;
731
- }
713
+ function configureSize(store, size) {
714
+ const state = store.getState();
715
+ if (state._sizeImperative || is.equ(size, state.size, { objects: "shallow", strict: false })) return;
716
+ updateSize(state, size, false);
732
717
  }
733
- function useTexture(input, optionsOrOnLoad) {
734
- const renderer = extensions.useThree((state) => state.internal.actualRenderer);
735
- const store = extensions.useStore();
736
- const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
737
- const { onLoad, cache = true } = options;
738
- const onLoadRef = React.useRef(onLoad);
739
- onLoadRef.current = onLoad;
740
- const onLoadCalledForRef = React.useRef(null);
741
- const inputKey = React.useMemo(() => inputSignature(input), [input]);
742
- const stableInput = React.useMemo(() => input, [inputKey]);
743
- const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
744
- const cachedResult = React.useMemo(() => {
745
- if (!cache) return null;
746
- const textures = store.getState().textures;
747
- if (!allUrlsCached(urls, textures)) return null;
748
- return buildFromCache(stableInput, textures);
749
- }, [cache, urls, stableInput, store]);
750
- const loadedTextures = useLoader(
751
- getThree().TextureLoader,
752
- IsObject(stableInput) ? Object.values(stableInput) : stableInput
753
- );
754
- const mappedTextures = React.useMemo(() => {
755
- if (cachedResult) return cachedResult;
756
- if (IsObject(stableInput)) {
757
- const keyed = {};
758
- const textureArray = loadedTextures;
759
- let i = 0;
760
- for (const key in stableInput) keyed[key] = textureArray[i++];
761
- return keyed;
762
- } else {
763
- return loadedTextures;
764
- }
765
- }, [stableInput, loadedTextures, cachedResult]);
766
- React.useLayoutEffect(() => {
767
- if (cachedResult) return;
768
- if (onLoadCalledForRef.current === inputKey) return;
769
- onLoadCalledForRef.current = inputKey;
770
- onLoadRef.current?.(mappedTextures);
771
- }, [cachedResult, mappedTextures, inputKey]);
772
- React.useEffect(() => {
773
- if (cachedResult) return;
774
- if ("initTexture" in renderer) {
775
- let textureArray = [];
776
- if (Array.isArray(loadedTextures)) {
777
- textureArray = loadedTextures;
778
- } else if (loadedTextures.isTexture) {
779
- textureArray = [loadedTextures];
780
- } else if (IsObject(loadedTextures)) {
781
- textureArray = Object.values(loadedTextures);
718
+ const createStore = (invalidate, advance) => {
719
+ const rootStore = traditional.createWithEqualityFn((rawSet, get, api) => {
720
+ const set = api.setState = textureStaging.preserveAccessors(rawSet, get);
721
+ let position;
722
+ let defaultTarget;
723
+ let tempTarget;
724
+ function getCurrentViewport(camera = get().camera, target = void 0, size = get().size) {
725
+ const { Vector3 } = getThree();
726
+ position ?? (position = new Vector3());
727
+ defaultTarget ?? (defaultTarget = new Vector3());
728
+ tempTarget ?? (tempTarget = new Vector3());
729
+ target ?? (target = defaultTarget);
730
+ const { width, height, top, left } = size;
731
+ const aspect = width / height;
732
+ if (target.isVector3) tempTarget.copy(target);
733
+ else tempTarget.set(...target);
734
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
735
+ if (isOrthographicCamera(camera)) {
736
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
737
+ } else {
738
+ const fov = camera.fov * Math.PI / 180;
739
+ const h = 2 * Math.tan(fov / 2) * distance;
740
+ const w = h * (width / height);
741
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
782
742
  }
783
- textureArray.forEach((texture) => {
784
- if (texture?.isTexture) {
785
- renderer.initTexture(texture);
786
- }
787
- });
788
743
  }
789
- }, [renderer, loadedTextures, cachedResult]);
790
- React.useEffect(() => {
791
- if (!cache) return;
792
- const urlTextureMap = urlTextureEntries(stableInput, mappedTextures);
793
- store.setState((state) => {
794
- const refs = new Map(state._textureRefs);
795
- let textures = state.textures;
796
- let added = false;
797
- for (const [url, texture] of urlTextureMap) {
798
- if (!textures.has(url)) {
799
- if (!added) {
800
- textures = new Map(textures);
801
- added = true;
802
- }
803
- textures.set(url, texture);
804
- }
805
- refs.set(url, (refs.get(url) ?? 0) + 1);
806
- }
807
- return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
808
- });
809
- extensions.unstageTextures(
810
- store,
811
- urlTextureMap.map(([url]) => url)
812
- );
813
- return () => store.setState((state) => {
814
- const refs = new Map(state._textureRefs);
815
- for (const [url] of urlTextureMap) {
816
- const next = (refs.get(url) ?? 0) - 1;
817
- if (next <= 0) refs.delete(url);
818
- else refs.set(url, next);
819
- }
820
- return { _textureRefs: refs };
821
- });
822
- }, [cache, stableInput, mappedTextures, store]);
823
- if (cache && !cachedResult) extensions.stageTextures(store, urlTextureEntries(stableInput, mappedTextures));
824
- return mappedTextures;
825
- }
826
- function urlTextureEntries(input, textures) {
827
- if (typeof input === "string") return [[input, textures]];
828
- if (Array.isArray(input)) return input.map((url, i) => [url, textures[i]]);
829
- const record = textures;
830
- return Object.keys(input).map((key) => [input[key], record[key]]);
831
- }
832
- useTexture.preload = (url) => {
833
- whenThree().then((three) => useLoader.preload(three.TextureLoader, url));
834
- };
835
- useTexture.clear = (input) => {
836
- if (hasThree()) useLoader.clear(getThree().TextureLoader, input);
837
- };
838
- const Texture = ({
839
- children,
840
- input,
841
- onLoad,
842
- cache
843
- }) => {
844
- const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
845
- const ret = useTexture(input, options);
846
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children?.(ret) });
847
- };
848
-
849
- function useTextures(selector) {
850
- const store = extensions.useStore();
851
- const registry = React.useMemo(() => {
852
- const getState = store.getState;
853
- const setState = store.setState;
854
- const getOne = (key) => getState().textures.get(key);
855
- return {
856
- get all() {
857
- return getState().textures;
858
- },
859
- get(input) {
860
- if (typeof input === "string") return getOne(input);
861
- if (Array.isArray(input)) return input.map(getOne);
862
- const out = {};
863
- for (const name in input) out[name] = getOne(input[name]);
864
- return out;
865
- },
866
- has: (key) => getState().textures.has(key),
867
- add(keyOrRecord, texture) {
868
- setState((state) => {
869
- const textures = new Map(state.textures);
870
- if (typeof keyOrRecord === "string") {
871
- textures.set(keyOrRecord, texture);
872
- } else {
873
- for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
874
- }
875
- return { textures };
876
- });
744
+ let performanceTimeout = void 0;
745
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
746
+ const rootState = {
747
+ set,
748
+ get,
749
+ // Mock objects that have to be configured
750
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
751
+ primaryStore: null,
752
+ gl: null,
753
+ renderer: null,
754
+ camera: null,
755
+ // frustum, pointer and mouse are three objects; configure() creates them with the renderer
756
+ frustum: null,
757
+ autoUpdateFrustum: true,
758
+ raycaster: null,
759
+ events: {
760
+ priority: 1,
761
+ enabled: true,
762
+ connected: false,
763
+ frameTimedRaycasts: true,
764
+ alwaysFireOnScroll: true,
765
+ updateOnFrame: false
877
766
  },
878
- dispose(key, options) {
879
- const state = getState();
880
- const refs = state._textureRefs.get(key) ?? 0;
881
- if (refs > 0 && !options?.force) {
882
- console.warn(
883
- `[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
767
+ scene: null,
768
+ rootScene: null,
769
+ xr: null,
770
+ inspector: null,
771
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
772
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
773
+ textureColorSpace: "srgb",
774
+ // THREE.SRGBColorSpace
775
+ isLegacy: false,
776
+ webGPUSupported: false,
777
+ isNative: false,
778
+ controls: null,
779
+ pointer: null,
780
+ mouse: null,
781
+ frameloop: "always",
782
+ onPointerMissed: void 0,
783
+ onDragOverMissed: void 0,
784
+ onDropMissed: void 0,
785
+ performance: {
786
+ current: 1,
787
+ min: 0.5,
788
+ max: 1,
789
+ debounce: 200,
790
+ regress: () => {
791
+ const state2 = get();
792
+ if (performanceTimeout) clearTimeout(performanceTimeout);
793
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
794
+ performanceTimeout = setTimeout(
795
+ () => setPerformanceCurrent(get().performance.max),
796
+ state2.performance.debounce
884
797
  );
885
- return false;
886
798
  }
887
- state.textures.get(key)?.dispose();
888
- setState((s) => {
889
- const textures = new Map(s.textures);
890
- textures.delete(key);
891
- const nextRefs = new Map(s._textureRefs);
892
- nextRefs.delete(key);
893
- return { textures, _textureRefs: nextRefs };
894
- });
895
- return true;
896
799
  },
897
- disposeAll() {
898
- for (const texture of getState().textures.values()) texture.dispose();
899
- setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
800
+ size: { width: 0, height: 0, top: 0, left: 0 },
801
+ viewport: {
802
+ initialDpr: 0,
803
+ dpr: 0,
804
+ width: 0,
805
+ height: 0,
806
+ top: 0,
807
+ left: 0,
808
+ aspect: 0,
809
+ distance: 0,
810
+ factor: 0,
811
+ getCurrentViewport
812
+ },
813
+ setEvents: (events) => set((state2) => ({ events: { ...state2.events, ...events } })),
814
+ setSize: (width, height, top, left) => {
815
+ const state2 = get();
816
+ if (width === void 0) {
817
+ if (state2._sizeProps) {
818
+ const { width: propW, height: propH } = state2._sizeProps;
819
+ if (propW !== void 0 || propH !== void 0) {
820
+ const currentSize = state2.size;
821
+ const newSize = {
822
+ width: propW ?? currentSize.width,
823
+ height: propH ?? currentSize.height,
824
+ top: currentSize.top,
825
+ left: currentSize.left
826
+ };
827
+ updateSize(state2, newSize, false);
828
+ return;
829
+ }
830
+ }
831
+ set({ _sizeImperative: false });
832
+ return;
833
+ }
834
+ const w = width;
835
+ const h = height ?? width;
836
+ const t = top ?? state2.size.top;
837
+ const l = left ?? state2.size.left;
838
+ const size = { width: w, height: h, top: t, left: l };
839
+ updateSize(state2, size, true);
840
+ },
841
+ setDpr: (dpr) => set((state2) => {
842
+ const resolved = calculateDpr(dpr);
843
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
844
+ }),
845
+ setFrameloop: (frameloop = "always") => {
846
+ const rootId = get().internal.rootId;
847
+ if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
848
+ set(() => ({ frameloop }));
849
+ },
850
+ setError: (error) => set(() => ({ error })),
851
+ error: null,
852
+ //* Texture registry (useTextures) ==============================
853
+ // TSL fields (uniforms, nodes, ..., renderPipeline) are added by @react-three/tsl's root extension.
854
+ textures: /* @__PURE__ */ new Map(),
855
+ _textureRefs: /* @__PURE__ */ new Map(),
856
+ _sizeImperative: false,
857
+ _sizeProps: null,
858
+ previousRoot: void 0,
859
+ internal: {
860
+ // Events
861
+ interaction: [],
862
+ subscribers: [],
863
+ // Per-pointer state (new unified structure)
864
+ pointerMap: /* @__PURE__ */ new Map(),
865
+ pointerDirty: /* @__PURE__ */ new Map(),
866
+ lastEvent: React__namespace.createRef(),
867
+ // Deprecated but kept for backwards compatibility
868
+ hovered: /* @__PURE__ */ new Map(),
869
+ initialClick: [0, 0],
870
+ initialHits: [],
871
+ capturedMap: /* @__PURE__ */ new Map(),
872
+ // Visibility tracking (onFramed, onOccluded, onVisible)
873
+ visibilityRegistry: /* @__PURE__ */ new Map(),
874
+ // Occlusion system (WebGPU only)
875
+ occlusionEnabled: false,
876
+ occlusionObserver: null,
877
+ occlusionCache: /* @__PURE__ */ new Map(),
878
+ helperGroup: null,
879
+ // Updates
880
+ active: false,
881
+ frames: 0,
882
+ priority: 0,
883
+ subscribe: (ref, priority, store) => {
884
+ const internal = get().internal;
885
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
886
+ internal.subscribers.push({ ref, priority, store });
887
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
888
+ return () => {
889
+ const internal2 = get().internal;
890
+ if (internal2?.subscribers) {
891
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
892
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
893
+ }
894
+ };
895
+ },
896
+ // Renderer Storage (single source of truth)
897
+ actualRenderer: null,
898
+ // The renderer support configure() loads for this root
899
+ support: null,
900
+ // Scheduler for useFrameNext (initialized in loop.ts)
901
+ scheduler: null,
902
+ // Replaces the default render call when set (see setRenderOverride)
903
+ renderOverride: null
900
904
  }
901
905
  };
902
- }, [store]);
903
- const subscribe = selector ? () => selector(registry) : (state) => state.textures;
904
- const selected = extensions.useThree(subscribe);
905
- return selector ? selected : registry;
906
- }
907
-
908
- function useRenderTarget(widthOrOptions, heightOrOptions, options) {
909
- const support = extensions.useThree((s) => s.internal.support);
910
- const size = extensions.useThree((s) => s.size);
911
- let width;
912
- let height;
913
- let opts;
914
- if (typeof widthOrOptions === "object") {
915
- opts = widthOrOptions;
916
- } else if (typeof widthOrOptions === "number") {
917
- width = widthOrOptions;
918
- if (typeof heightOrOptions === "object") {
919
- height = widthOrOptions;
920
- opts = heightOrOptions;
921
- } else if (typeof heightOrOptions === "number") {
922
- height = heightOrOptions;
923
- opts = options;
924
- } else {
925
- height = widthOrOptions;
906
+ return rootState;
907
+ });
908
+ const state = rootStore.getState();
909
+ textureStaging.defineRendererAccessors(state);
910
+ let oldScene = state.scene;
911
+ rootStore.subscribe(() => {
912
+ const currentState = rootStore.getState();
913
+ const { scene, rootScene, set } = currentState;
914
+ if (scene !== oldScene) {
915
+ oldScene = scene;
916
+ if (scene?.isScene && scene !== rootScene) {
917
+ set({ rootScene: scene });
918
+ }
926
919
  }
927
- }
928
- return React.useMemo(() => {
929
- const w = width ?? size.width;
930
- const h = height ?? size.height;
931
- const Target = support.RenderTarget;
932
- return new Target(w, h, opts);
933
- }, [width, height, size.width, size.height, opts, support]);
934
- }
935
-
936
- const presetsObj = {
937
- apartment: "lebombo_1k.hdr",
938
- city: "potsdamer_platz_1k.hdr",
939
- dawn: "kiara_1_dawn_1k.hdr",
940
- forest: "forest_slope_1k.hdr",
941
- lobby: "st_fagans_interior_1k.hdr",
942
- night: "dikhololo_night_1k.hdr",
943
- park: "rooitou_park_1k.hdr",
944
- studio: "studio_small_03_1k.hdr",
945
- sunset: "venice_sunset_1k.hdr",
946
- warehouse: "empty_warehouse_01_1k.hdr"
920
+ });
921
+ let oldSize = state.size;
922
+ let oldDpr = state.viewport.dpr;
923
+ let oldCamera = state.camera;
924
+ rootStore.subscribe(() => {
925
+ const { camera, size, viewport, set, internal } = rootStore.getState();
926
+ const actualRenderer = internal.actualRenderer;
927
+ const canvasTarget = internal.canvasTarget;
928
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
929
+ oldSize = size;
930
+ oldDpr = viewport.dpr;
931
+ updateCamera(camera, size);
932
+ const resizeTarget = canvasTarget ?? actualRenderer;
933
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
934
+ resizeTarget.setSize(size.width, size.height, false);
935
+ const activeTarget = actualRenderer.getCanvasTarget?.();
936
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
937
+ }
938
+ if (camera !== oldCamera) {
939
+ oldCamera = camera;
940
+ const { rootScene } = rootStore.getState();
941
+ if (camera && rootScene && !camera.parent) {
942
+ rootScene.add(camera);
943
+ }
944
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
945
+ const currentState = rootStore.getState();
946
+ if (currentState.autoUpdateFrustum && camera) {
947
+ updateFrustum(camera, currentState.frustum);
948
+ }
949
+ }
950
+ });
951
+ rootStore.subscribe((state2) => {
952
+ if (state2.internal.active) invalidate(state2);
953
+ });
954
+ return rootStore;
947
955
  };
948
956
 
949
- const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
950
- const isArray = (arr) => Array.isArray(arr);
951
- const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
952
- const loadedLoaders = /* @__PURE__ */ new Map();
953
- const pendingLoaders = /* @__PURE__ */ new Map();
954
- function loadLoader(format) {
955
- let pending = pendingLoaders.get(format);
956
- if (!pending) {
957
- pending = importLoader(format).then(
958
- (loader) => {
959
- loadedLoaders.set(format, loader);
960
- return loader;
957
+ const shallowLoose$1 = { objects: "shallow", strict: false };
958
+ function configureScheduler(store, canvasId, schedulerConfig, previous) {
959
+ const state = store.getState();
960
+ const scheduler$1 = scheduler.getScheduler();
961
+ const rootId = state.internal.rootId;
962
+ const ordersItself = schedulerConfig?.before !== void 0 || schedulerConfig?.after !== void 0;
963
+ const rootBefore = schedulerConfig?.before;
964
+ const rootAfter = ordersItself ? schedulerConfig?.after : state.internal.sharedAfter;
965
+ if (!rootId) {
966
+ const newRootId = state.internal.pendingRootId ?? canvasId ?? scheduler$1.generateRootId();
967
+ const unregisterRoot = scheduler$1.registerRoot(newRootId, {
968
+ getState: () => store.getState(),
969
+ onError: (err) => store.getState().setError(err),
970
+ frameloop: store.getState().frameloop,
971
+ before: rootBefore,
972
+ after: rootAfter,
973
+ order: schedulerConfig?.order
974
+ });
975
+ const unregisterCanvasTarget = scheduler$1.register(
976
+ () => {
977
+ const state2 = store.getState();
978
+ const canvasTarget = state2.internal.canvasTarget;
979
+ if (canvasTarget) {
980
+ const renderer = state2.internal.actualRenderer;
981
+ if (renderer.getCanvasTarget() !== canvasTarget) renderer.setCanvasTarget(canvasTarget);
982
+ if (state2.internal.canvasTargetSizeDirty) {
983
+ state2.internal.canvasTargetSizeDirty = false;
984
+ renderer.backend?.updateSize?.();
985
+ }
986
+ }
961
987
  },
962
- (error) => {
963
- pendingLoaders.delete(format);
964
- throw error;
988
+ {
989
+ id: `${newRootId}_canvasTarget`,
990
+ rootId: newRootId,
991
+ phase: "start",
992
+ system: true
965
993
  }
966
994
  );
967
- pendingLoaders.set(format, pending);
968
- }
969
- return pending;
970
- }
971
- async function importLoader(format) {
972
- let loader;
973
- switch (format) {
974
- case "cube":
975
- loader = getThree().CubeTextureLoader;
976
- break;
977
- case "hdr-cube":
978
- loader = (await import('three/examples/jsm/loaders/HDRCubeTextureLoader.js')).HDRCubeTextureLoader;
979
- break;
980
- case "hdr":
981
- loader = (await import('three/examples/jsm/loaders/HDRLoader.js')).HDRLoader;
982
- break;
983
- case "exr":
984
- loader = (await import('three/examples/jsm/loaders/EXRLoader.js')).EXRLoader;
985
- break;
986
- case "jpg":
987
- loader = (await import('three/examples/jsm/loaders/UltraHDRLoader.js')).UltraHDRLoader;
988
- break;
989
- }
990
- return loader;
991
- }
992
- const LOADER_KEY = Symbol("r3f-environment-loader");
993
- function useEnvironmentLoader(format) {
994
- return suspendReact.suspend(() => loadLoader(format), [LOADER_KEY, format]);
995
- }
996
- function useEnvironment({
997
- files = defaultFiles,
998
- path = "",
999
- preset = void 0,
1000
- colorSpace = void 0,
1001
- extensions
1002
- } = {}) {
1003
- if (preset) {
1004
- validatePreset(preset);
1005
- files = presetsObj[preset];
1006
- path = CUBEMAP_ROOT;
1007
- }
1008
- const multiFile = isArray(files);
1009
- const { format, isCubemap } = getFormat(files);
1010
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1011
- const loader = useEnvironmentLoader(format);
1012
- const loaderResult = useLoader(
1013
- loader,
1014
- multiFile ? [files] : files,
1015
- (loader2) => {
1016
- loader2.setPath?.(path);
1017
- if (extensions) extensions(loader2);
1018
- }
1019
- );
1020
- const texture = multiFile ? (
1021
- // @ts-ignore
1022
- loaderResult[0]
1023
- ) : loaderResult;
1024
- const three = getThree();
1025
- texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
1026
- texture.colorSpace = colorSpace ?? (isCubemap && format !== "hdr-cube" ? "srgb" : "srgb-linear");
1027
- return texture;
1028
- }
1029
- const preloadDefaultOptions = {
1030
- files: defaultFiles,
1031
- path: "",
1032
- preset: void 0,
1033
- extensions: void 0
1034
- };
1035
- useEnvironment.preload = (preloadOptions) => {
1036
- const options = { ...preloadDefaultOptions, ...preloadOptions };
1037
- let { files, path = "" } = options;
1038
- const { preset, extensions } = options;
1039
- if (preset) {
1040
- validatePreset(preset);
1041
- files = presetsObj[preset];
1042
- path = CUBEMAP_ROOT;
1043
- }
1044
- const { format } = getFormat(files);
1045
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1046
- const input = isArray(files) ? [files] : files;
1047
- loadLoader(format).then((loader) => {
1048
- useLoader.preload(loader, input, (loader2) => {
1049
- loader2.setPath?.(path);
1050
- if (extensions) extensions(loader2);
1051
- });
1052
- });
1053
- };
1054
- const clearDefaultOptins = {
1055
- files: defaultFiles,
1056
- preset: void 0
1057
- };
1058
- useEnvironment.clear = (clearOptions) => {
1059
- const options = { ...clearDefaultOptins, ...clearOptions };
1060
- let { files } = options;
1061
- const { preset } = options;
1062
- if (preset) {
1063
- validatePreset(preset);
1064
- files = presetsObj[preset];
1065
- }
1066
- const { format } = getFormat(files);
1067
- if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1068
- const input = isArray(files) ? [files] : files;
1069
- const clear = (loader2) => useLoader.clear(loader2, input);
1070
- const loader = loadedLoaders.get(format);
1071
- if (loader) {
1072
- clear(loader);
995
+ const unregisterEventsFlush = scheduler$1.register(
996
+ () => {
997
+ const state2 = store.getState();
998
+ state2.events.flush?.();
999
+ },
1000
+ {
1001
+ id: `${newRootId}_events`,
1002
+ rootId: newRootId,
1003
+ phase: "input",
1004
+ system: true
1005
+ }
1006
+ );
1007
+ const unregisterFrustum = scheduler$1.register(
1008
+ () => {
1009
+ const state2 = store.getState();
1010
+ if (state2.autoUpdateFrustum && state2.camera) {
1011
+ updateFrustum(state2.camera, state2.frustum);
1012
+ }
1013
+ },
1014
+ {
1015
+ id: `${newRootId}_frustum`,
1016
+ rootId: newRootId,
1017
+ before: "render",
1018
+ system: true
1019
+ }
1020
+ );
1021
+ const unregisterVisibility = scheduler$1.register(
1022
+ () => {
1023
+ const state2 = store.getState();
1024
+ checkVisibility(state2);
1025
+ },
1026
+ {
1027
+ id: `${newRootId}_visibility`,
1028
+ rootId: newRootId,
1029
+ before: "render",
1030
+ system: true,
1031
+ after: `${newRootId}_frustum`
1032
+ }
1033
+ );
1034
+ const unregisterRender = scheduler$1.register(
1035
+ () => {
1036
+ const state2 = store.getState();
1037
+ const renderer = state2.internal.actualRenderer;
1038
+ const userHandlesRender = scheduler$1.hasUserJobsInPhase("render", newRootId);
1039
+ if (userHandlesRender || state2.internal.priority) return;
1040
+ try {
1041
+ const renderOverride = state2.internal.renderOverride;
1042
+ if (renderOverride) renderOverride();
1043
+ else if (renderer?.render) renderer.render(state2.scene, state2.camera);
1044
+ } catch (error) {
1045
+ state2.setError(error instanceof Error ? error : new Error(String(error)));
1046
+ }
1047
+ },
1048
+ {
1049
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
1050
+ id: canvasId || `${newRootId}_render`,
1051
+ rootId: newRootId,
1052
+ phase: "render",
1053
+ system: true,
1054
+ // Internal flag: this is a system job, not user-controlled
1055
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
1056
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
1057
+ }
1058
+ );
1059
+ state.set((state2) => ({
1060
+ internal: {
1061
+ ...state2.internal,
1062
+ rootId: newRootId,
1063
+ pendingRootId: void 0,
1064
+ unregisterRoot: () => {
1065
+ unregisterRoot();
1066
+ unregisterCanvasTarget();
1067
+ unregisterEventsFlush();
1068
+ unregisterFrustum();
1069
+ unregisterVisibility();
1070
+ unregisterRender();
1071
+ },
1072
+ scheduler: scheduler$1
1073
+ }
1074
+ }));
1073
1075
  } else {
1074
- const pending = pendingLoaders.get(format);
1075
- if (pending) void pending.then(clear, () => {
1076
- });
1076
+ const constraintsChanged = !is.equ(schedulerConfig?.before, previous?.before, shallowLoose$1) || !is.equ(schedulerConfig?.after, previous?.after, shallowLoose$1);
1077
+ if (constraintsChanged) {
1078
+ scheduler$1.setRootConstraints(rootId, {
1079
+ before: rootBefore,
1080
+ after: rootAfter
1081
+ });
1082
+ }
1083
+ if (schedulerConfig?.order !== previous?.order) {
1084
+ scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
1085
+ }
1077
1086
  }
1078
- };
1079
- function validatePreset(preset) {
1080
- if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
1081
- }
1082
- function getFormat(files) {
1083
- const isCubemap = isArray(files) && files.length === 6;
1084
- const firstEntry = isArray(files) ? files[0] : files;
1085
- const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1086
- let format;
1087
- if (isCubemap) format = firstExtension === "hdr" ? "hdr-cube" : "cube";
1088
- else if (firstEntry.startsWith("data:application/exr")) format = "exr";
1089
- else if (firstEntry.startsWith("data:application/hdr")) format = "hdr";
1090
- else if (firstEntry.startsWith("data:image/jpeg")) format = "jpg";
1091
- else if (firstExtension === "hdr" || firstExtension === "exr") format = firstExtension;
1092
- else if (firstExtension === "jpg" || firstExtension === "jpeg") format = "jpg";
1093
- return { format, isCubemap };
1094
- }
1095
-
1096
- function useInstanceHandle(ref) {
1097
- const instance = React.useRef(null);
1098
- React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1099
- return instance;
1100
- }
1101
- function useGraph(object) {
1102
- return React.useMemo(() => buildGraph(object), [object]);
1103
1087
  }
1104
-
1105
1088
  let effectId = 0;
1106
1089
  function addEffect(callback) {
1107
- extensions.notifyDepreciated({
1090
+ textureStaging.notifyDepreciated({
1108
1091
  heading: "addEffect is deprecated",
1109
1092
  body: 'Use useFrame(callback, { phase: "start" }) instead.\naddEffect will be removed in a future version.',
1110
1093
  link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
@@ -1113,7 +1096,7 @@ function addEffect(callback) {
1113
1096
  return scheduler.getScheduler().registerGlobal("before", id, callback);
1114
1097
  }
1115
1098
  function addAfterEffect(callback) {
1116
- extensions.notifyDepreciated({
1099
+ textureStaging.notifyDepreciated({
1117
1100
  heading: "addAfterEffect is deprecated",
1118
1101
  body: 'Use useFrame(callback, { phase: "finish" }) instead.\naddAfterEffect will be removed in a future version.',
1119
1102
  link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
@@ -1122,7 +1105,7 @@ function addAfterEffect(callback) {
1122
1105
  return scheduler.getScheduler().registerGlobal("after", id, callback);
1123
1106
  }
1124
1107
  function addTail(callback) {
1125
- extensions.notifyDepreciated({
1108
+ textureStaging.notifyDepreciated({
1126
1109
  heading: "addTail is deprecated",
1127
1110
  body: "Use scheduler.onIdle(callback) instead.\naddTail will be removed in a future version.",
1128
1111
  link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
@@ -1142,6 +1125,790 @@ function advance(timestamp, runGlobalEffects, state) {
1142
1125
  else scheduler$1.step(timestamp);
1143
1126
  }
1144
1127
 
1128
+ const isPromiseLike = (value) => typeof value?.then === "function";
1129
+ const fulfilled = (value) => Object.assign(Promise.resolve(value), { status: "fulfilled", value });
1130
+ const rejected = (reason) => {
1131
+ const promise = Promise.reject(reason);
1132
+ promise.catch(() => {
1133
+ });
1134
+ return Object.assign(promise, { status: "rejected", reason });
1135
+ };
1136
+ const deferred = () => {
1137
+ let resolve;
1138
+ let reject;
1139
+ const promise = new Promise((yes, no) => {
1140
+ resolve = (value) => {
1141
+ Object.assign(promise, { status: "fulfilled", value });
1142
+ yes(value);
1143
+ };
1144
+ reject = (reason) => {
1145
+ Object.assign(promise, { status: "rejected", reason });
1146
+ no(reason);
1147
+ };
1148
+ });
1149
+ promise.status = "pending";
1150
+ promise.catch(() => {
1151
+ });
1152
+ return { promise, resolve, reject };
1153
+ };
1154
+ function resolveThen(value, apply) {
1155
+ return isPromiseLike(value) ? Promise.resolve(value).then(apply) : apply(value);
1156
+ }
1157
+
1158
+ const isRenderer = (def) => !!def?.render;
1159
+ function ensureStoreThreeObjects(store) {
1160
+ const state = store.getState();
1161
+ if (state.pointer) return;
1162
+ const { Vector2, Frustum } = getThree();
1163
+ const pointer = new Vector2();
1164
+ state.set({ pointer, mouse: pointer, frustum: new Frustum() });
1165
+ }
1166
+ const R3F_RENDERER_SETTINGS = ["textureColorSpace", "shadows"];
1167
+ const NON_APPLIED_RENDERER_PROPS = /* @__PURE__ */ new Set([
1168
+ ...R3F_RENDERER_SETTINGS,
1169
+ "canvas",
1170
+ "antialias",
1171
+ "alpha",
1172
+ "samples",
1173
+ "powerPreference",
1174
+ "depth",
1175
+ "stencil",
1176
+ "logarithmicDepthBuffer",
1177
+ "reversedDepthBuffer",
1178
+ // WebGPURenderer only
1179
+ "forceWebGL",
1180
+ "outputBufferType",
1181
+ "multiview",
1182
+ "getFallback",
1183
+ "trackTimestamp"
1184
+ ]);
1185
+ function settingsBag(config) {
1186
+ return is.obj(config) && !isRenderer(config) ? config : void 0;
1187
+ }
1188
+ function isInstanceOrFactory(config) {
1189
+ return is.fun(config) || isRenderer(config);
1190
+ }
1191
+ function assertNoRemovedConfig(props) {
1192
+ if (props.primaryCanvas !== void 0) {
1193
+ throw new Error(
1194
+ 'R3F: `primaryCanvas` was removed: mark the owner <Canvas primary> and other canvases share it automatically (or use share="id")'
1195
+ );
1196
+ }
1197
+ if (props.shadows !== void 0) {
1198
+ throw new Error(
1199
+ "R3F: the `shadows` Canvas prop moved into the renderer settings: use `renderer={{ shadows }}` (or `gl={{ shadows }}` for WebGL and @react-three/fiber/legacy)"
1200
+ );
1201
+ }
1202
+ for (const name of ["renderer", "gl"]) {
1203
+ const bag = settingsBag(props[name]);
1204
+ if (!bag) continue;
1205
+ if ("primaryCanvas" in bag) {
1206
+ throw new Error(
1207
+ `R3F: \`${name}={{ primaryCanvas }}\` was removed: mark the owner <Canvas primary> and other canvases share it automatically (or use share="id")`
1208
+ );
1209
+ }
1210
+ if ("scheduler" in bag) {
1211
+ throw new Error(`R3F: \`${name}={{ scheduler }}\` moved to <Canvas scheduler>`);
1212
+ }
1213
+ }
1214
+ }
1215
+ function resolveRenderer(config, defaultProps, RendererClass) {
1216
+ if (typeof config === "function") {
1217
+ return resolveThen(config(defaultProps), (renderer) => ({
1218
+ renderer,
1219
+ owned: true
1220
+ }));
1221
+ }
1222
+ if (isRenderer(config)) return { renderer: config, owned: false };
1223
+ const params = { ...defaultProps };
1224
+ for (const key in settingsBag(config)) if (!R3F_RENDERER_SETTINGS.includes(key)) params[key] = config[key];
1225
+ return { renderer: new RendererClass(params), owned: true };
1226
+ }
1227
+ function createOwnedWebGPURenderer(rendererConfig, defaultGPUProps, canvas, propsSize, dpr, support) {
1228
+ return resolveThen(resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer), (resolved) => {
1229
+ const renderer = resolved.renderer;
1230
+ if (!renderer.hasInitialized?.()) {
1231
+ const size = computeInitialSize(canvas, propsSize);
1232
+ if (size.width > 0 && size.height > 0) {
1233
+ renderer.setPixelRatio(calculateDpr(dpr));
1234
+ renderer.setSize(size.width, size.height, false);
1235
+ }
1236
+ return resolveThen(renderer.init(), () => ({ renderer, owned: resolved.owned }));
1237
+ }
1238
+ return { renderer, owned: resolved.owned };
1239
+ });
1240
+ }
1241
+ function initializeRenderer(canvas, provider, store, props) {
1242
+ const {
1243
+ id: canvasId,
1244
+ primary = false,
1245
+ share,
1246
+ gl: glConfig,
1247
+ renderer: rendererConfig,
1248
+ size: propsSize,
1249
+ dpr = [1, 2],
1250
+ _primaryToken
1251
+ } = props;
1252
+ assertNoRemovedConfig(props);
1253
+ const rendererBag = settingsBag(rendererConfig);
1254
+ const state = store.getState();
1255
+ const { webgl, webgpu } = provider;
1256
+ const defaultGLProps = {
1257
+ canvas,
1258
+ powerPreference: "high-performance",
1259
+ antialias: true,
1260
+ alpha: true
1261
+ };
1262
+ const defaultGPUProps = {
1263
+ canvas,
1264
+ antialias: true
1265
+ };
1266
+ if (glConfig && !webgl) {
1267
+ throw new Error(
1268
+ "R3F: the `gl` prop configures WebGLRenderer, which is only available on @react-three/fiber/legacy. Import Canvas from @react-three/fiber/legacy to keep WebGL, or move these settings to the `renderer` prop for WebGPU."
1269
+ );
1270
+ }
1271
+ if (rendererConfig && !webgpu) {
1272
+ throw new Error(
1273
+ "WebGPURenderer (renderer prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
1274
+ );
1275
+ }
1276
+ if (glConfig && rendererConfig) {
1277
+ throw new Error("Cannot use both gl and renderer props at the same time");
1278
+ }
1279
+ if (primary && !webgpu) {
1280
+ throw new Error(
1281
+ "R3F: <Canvas primary> shares a WebGPU renderer and is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
1282
+ );
1283
+ }
1284
+ if (primary && glConfig) {
1285
+ throw new Error(
1286
+ "R3F: <Canvas primary> shares a WebGPU renderer and cannot be used with WebGL. Remove the `gl` prop or use the `renderer` prop."
1287
+ );
1288
+ }
1289
+ if (primary && is.str(share)) {
1290
+ throw new Error(`R3F: a <Canvas primary> owns its renderer; remove share="${share}" from it.`);
1291
+ }
1292
+ if (is.str(share)) {
1293
+ if (!webgpu || glConfig) {
1294
+ throw new Error(
1295
+ `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.`
1296
+ );
1297
+ }
1298
+ if (isInstanceOrFactory(rendererConfig)) {
1299
+ throw new Error(
1300
+ `R3F: share="${share}" borrows the primary's renderer, so it cannot be combined with a renderer instance or factory. Remove one of the two.`
1301
+ );
1302
+ }
1303
+ }
1304
+ const primaryKey = canvasId || DEFAULT_PRIMARY;
1305
+ const primaryToken = _primaryToken ?? store;
1306
+ if (primary && !state.internal.actualRenderer) {
1307
+ announcePrimary(primaryKey, primaryToken);
1308
+ state.internal.unregisterPrimary = () => withdrawPrimary(primaryKey, primaryToken);
1309
+ if (primaryConflict(primaryKey, primaryToken)) {
1310
+ throw new Error(
1311
+ 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\`.`
1312
+ );
1313
+ }
1314
+ }
1315
+ let shareKey;
1316
+ if (!state.internal.actualRenderer && webgpu && !primary && !glConfig && !state.isLegacy) {
1317
+ if (is.str(share)) {
1318
+ shareKey = share;
1319
+ } else if (share !== false && !isInstanceOrFactory(rendererConfig)) {
1320
+ const live = livePrimaryKeys();
1321
+ if (live.length > 1) {
1322
+ throw new Error(
1323
+ `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.`
1324
+ );
1325
+ }
1326
+ shareKey = live[0];
1327
+ }
1328
+ }
1329
+ const explicitShare = is.str(share);
1330
+ const wantsGL = !!webgl && (state.isLegacy || !!glConfig || !webgpu || !rendererConfig && !primary && shareKey === void 0);
1331
+ let renderer = state.internal.actualRenderer;
1332
+ if (!state.internal.actualRenderer) {
1333
+ return resolveThen(wantsGL ? webgl() : webgpu(), (support) => {
1334
+ registerThree(support.three);
1335
+ state.internal.support = support;
1336
+ ensureStoreThreeObjects(store);
1337
+ if (support.kind === "webgl") {
1338
+ return resolveThen(resolveRenderer(glConfig, defaultGLProps, support.Renderer), (resolved) => {
1339
+ renderer = resolved.renderer;
1340
+ state.internal.actualRenderer = renderer;
1341
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1342
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
1343
+ });
1344
+ }
1345
+ const primaryReady = shareKey === void 0 ? null : explicitShare ? waitForPrimary(shareKey) : waitForAnnouncedPrimary(shareKey);
1346
+ return resolveThen(primaryReady, (primaryEntry) => {
1347
+ if (primaryEntry && primaryEntry.store.getState().webGPUSupported) {
1348
+ renderer = primaryEntry.renderer;
1349
+ state.internal.actualRenderer = renderer;
1350
+ state.internal.releaseRenderer = borrowRenderer(renderer);
1351
+ state.internal.sharedAfter = primaryEntry.rootId;
1352
+ const canvasTarget = new support.CanvasTarget(canvas);
1353
+ primaryEntry.store.setState((prev) => ({
1354
+ internal: { ...prev.internal, isMultiCanvas: true }
1355
+ }));
1356
+ state.set((prev) => ({
1357
+ webGPUSupported: primaryEntry.store.getState().webGPUSupported,
1358
+ renderer,
1359
+ primaryStore: primaryEntry.store,
1360
+ internal: {
1361
+ ...prev.internal,
1362
+ canvasTarget,
1363
+ isMultiCanvas: true,
1364
+ isSecondary: true,
1365
+ targetId: shareKey === DEFAULT_PRIMARY ? void 0 : shareKey
1366
+ }
1367
+ }));
1368
+ return;
1369
+ }
1370
+ if (primaryEntry) {
1371
+ return resolveThen(
1372
+ createOwnedWebGPURenderer(
1373
+ { ...rendererBag, forceWebGL: true },
1374
+ defaultGPUProps,
1375
+ canvas,
1376
+ propsSize,
1377
+ dpr,
1378
+ support
1379
+ ),
1380
+ (resolved) => {
1381
+ renderer = resolved.renderer;
1382
+ const backend = renderer.backend;
1383
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
1384
+ state.internal.actualRenderer = renderer;
1385
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1386
+ state.set((prev) => ({
1387
+ webGPUSupported: isWebGPUBackend,
1388
+ renderer,
1389
+ primaryStore: store,
1390
+ internal: {
1391
+ ...prev.internal,
1392
+ canvasTarget: renderer.getCanvasTarget?.(),
1393
+ sharedRendererFallback: true
1394
+ }
1395
+ }));
1396
+ }
1397
+ );
1398
+ }
1399
+ return resolveThen(
1400
+ createOwnedWebGPURenderer(rendererConfig, defaultGPUProps, canvas, propsSize, dpr, support),
1401
+ (resolved) => {
1402
+ var _a;
1403
+ renderer = resolved.renderer;
1404
+ const backend = renderer.backend;
1405
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
1406
+ state.internal.actualRenderer = renderer;
1407
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
1408
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
1409
+ if (primary) {
1410
+ const canvasTarget = renderer.getCanvasTarget?.();
1411
+ (_a = state.internal).pendingRootId ?? (_a.pendingRootId = canvasId || scheduler.getScheduler().generateRootId());
1412
+ registerPrimary(primaryKey, renderer, store, {
1413
+ token: primaryToken,
1414
+ rootId: state.internal.pendingRootId
1415
+ });
1416
+ state.set((prev) => ({ internal: { ...prev.internal, canvasTarget } }));
1417
+ } else if (share !== false && !isInstanceOrFactory(rendererConfig)) {
1418
+ state.internal.untrackStandalone = trackStandalone(store);
1419
+ }
1420
+ }
1421
+ );
1422
+ });
1423
+ });
1424
+ }
1425
+ }
1426
+ const records = /* @__PURE__ */ new WeakMap();
1427
+ function take(renderer, record) {
1428
+ record.leases++;
1429
+ let released = false;
1430
+ return () => {
1431
+ if (released) return;
1432
+ released = true;
1433
+ if (--record.leases > 0) return;
1434
+ records.delete(renderer);
1435
+ if (record.owned) return disposeRenderer(renderer);
1436
+ };
1437
+ }
1438
+ function leaseRenderer(renderer, owned) {
1439
+ let record = records.get(renderer);
1440
+ if (!record) records.set(renderer, record = { owned, leases: 0 });
1441
+ return take(renderer, record);
1442
+ }
1443
+ function borrowRenderer(renderer) {
1444
+ const record = records.get(renderer);
1445
+ if (!record) throw new Error("R3F: cannot share a renderer whose canvas has already unmounted");
1446
+ return take(renderer, record);
1447
+ }
1448
+ function disposeRenderer(renderer) {
1449
+ if (renderer.hasInitialized?.() === false) return;
1450
+ const warn = (error) => console.warn("[R3F] Error disposing renderer", error);
1451
+ const loseContext = () => {
1452
+ try {
1453
+ ;
1454
+ renderer.forceContextLoss?.();
1455
+ } catch (error) {
1456
+ warn(error);
1457
+ }
1458
+ };
1459
+ let disposed;
1460
+ try {
1461
+ disposed = renderer.dispose ? renderer.dispose() : renderer.renderLists?.dispose?.();
1462
+ } catch (error) {
1463
+ warn(error);
1464
+ }
1465
+ if (typeof disposed?.then === "function") {
1466
+ return Promise.resolve(disposed).then(loseContext, (error) => {
1467
+ warn(error);
1468
+ loseContext();
1469
+ });
1470
+ }
1471
+ loseContext();
1472
+ }
1473
+ const DEFAULT_PRIMARY = "\0r3f:primary";
1474
+ const primaryRegistry = /* @__PURE__ */ new Map();
1475
+ const announcements = /* @__PURE__ */ new Map();
1476
+ const withdrawing = /* @__PURE__ */ new Set();
1477
+ const pendingSubscribers = /* @__PURE__ */ new Map();
1478
+ const standaloneRoots = /* @__PURE__ */ new Set();
1479
+ const warnedLatePrimary = /* @__PURE__ */ new WeakSet();
1480
+ function describePrimary(key) {
1481
+ return key === DEFAULT_PRIMARY ? "<Canvas primary>" : `<Canvas id="${key}" primary>`;
1482
+ }
1483
+ function notify(key, entry) {
1484
+ const subscribers = pendingSubscribers.get(key);
1485
+ if (!subscribers) return;
1486
+ pendingSubscribers.delete(key);
1487
+ subscribers.forEach((callback) => callback(entry));
1488
+ }
1489
+ function isLive(key) {
1490
+ const owners = announcements.get(key);
1491
+ if (owners) {
1492
+ for (const owner of owners) if (!withdrawing.has(owner)) return true;
1493
+ }
1494
+ const entry = primaryRegistry.get(key);
1495
+ return !!entry && !(entry.token && withdrawing.has(entry.token));
1496
+ }
1497
+ function announcePrimary(key, owner) {
1498
+ withdrawing.delete(owner);
1499
+ let owners = announcements.get(key);
1500
+ if (!owners) announcements.set(key, owners = /* @__PURE__ */ new Set());
1501
+ if (owners.has(owner)) return;
1502
+ owners.add(owner);
1503
+ if (standaloneRoots.size > 0 && !warnedLatePrimary.has(owner) && textureStaging.isDevelopment()) {
1504
+ warnedLatePrimary.add(owner);
1505
+ console.warn(
1506
+ `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.`
1507
+ );
1508
+ }
1509
+ }
1510
+ function markWithdrawing(owner) {
1511
+ withdrawing.add(owner);
1512
+ }
1513
+ function withdrawPrimary(key, owner) {
1514
+ withdrawing.delete(owner);
1515
+ const owners = announcements.get(key);
1516
+ if (owners) {
1517
+ owners.delete(owner);
1518
+ if (owners.size === 0) announcements.delete(key);
1519
+ }
1520
+ const entry = primaryRegistry.get(key);
1521
+ if (entry?.token === owner) primaryRegistry.delete(key);
1522
+ if (!isLive(key)) notify(key, null);
1523
+ }
1524
+ function primaryConflict(key, owner) {
1525
+ const owners = announcements.get(key);
1526
+ if (owners) {
1527
+ for (const other of owners) {
1528
+ if (other === owner) break;
1529
+ if (!withdrawing.has(other)) return true;
1530
+ }
1531
+ }
1532
+ const entry = primaryRegistry.get(key);
1533
+ return !!entry && entry.token !== owner && !(entry.token && withdrawing.has(entry.token));
1534
+ }
1535
+ function livePrimaryKeys() {
1536
+ const keys = /* @__PURE__ */ new Set([...announcements.keys(), ...primaryRegistry.keys()]);
1537
+ return [...keys].filter(isLive);
1538
+ }
1539
+ function trackStandalone(owner) {
1540
+ standaloneRoots.add(owner);
1541
+ return () => {
1542
+ standaloneRoots.delete(owner);
1543
+ };
1544
+ }
1545
+ function registerPrimary(id, renderer, store, options = {}) {
1546
+ if (options.token && !announcements.get(id)?.has(options.token)) return () => {
1547
+ };
1548
+ if (primaryRegistry.has(id)) {
1549
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
1550
+ }
1551
+ const entry = { renderer, store, rootId: options.rootId, token: options.token };
1552
+ primaryRegistry.set(id, entry);
1553
+ notify(id, entry);
1554
+ return () => {
1555
+ const currentEntry = primaryRegistry.get(id);
1556
+ if (currentEntry?.renderer === renderer) {
1557
+ primaryRegistry.delete(id);
1558
+ }
1559
+ };
1560
+ }
1561
+ function getPrimary(id) {
1562
+ return primaryRegistry.get(id);
1563
+ }
1564
+ function subscribe(id, callback) {
1565
+ if (!pendingSubscribers.has(id)) pendingSubscribers.set(id, []);
1566
+ pendingSubscribers.get(id).push(callback);
1567
+ return () => {
1568
+ const subscribers = pendingSubscribers.get(id);
1569
+ if (!subscribers) return;
1570
+ const index = subscribers.indexOf(callback);
1571
+ if (index !== -1) subscribers.splice(index, 1);
1572
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
1573
+ };
1574
+ }
1575
+ function waitForPrimary(id, timeout = 5e3) {
1576
+ const existing = primaryRegistry.get(id);
1577
+ if (existing) {
1578
+ return Promise.resolve(existing);
1579
+ }
1580
+ return new Promise((resolve, reject) => {
1581
+ let unsubscribe = () => {
1582
+ };
1583
+ const timeoutId = setTimeout(() => {
1584
+ unsubscribe();
1585
+ reject(
1586
+ new Error(
1587
+ `Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}" primary> is mounted for share="${id}".`
1588
+ )
1589
+ );
1590
+ }, timeout);
1591
+ const callback = (entry) => {
1592
+ if (!entry) {
1593
+ unsubscribe = subscribe(id, callback);
1594
+ return;
1595
+ }
1596
+ clearTimeout(timeoutId);
1597
+ resolve(entry);
1598
+ };
1599
+ unsubscribe = subscribe(id, callback);
1600
+ });
1601
+ }
1602
+ function waitForAnnouncedPrimary(key, timeout = 5e3) {
1603
+ const existing = primaryRegistry.get(key);
1604
+ if (existing && isLive(key)) return Promise.resolve(existing);
1605
+ if (!isLive(key)) return Promise.resolve(null);
1606
+ return new Promise((resolve, reject) => {
1607
+ const timeoutId = setTimeout(() => {
1608
+ unsubscribe();
1609
+ reject(
1610
+ new Error(
1611
+ `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.`
1612
+ )
1613
+ );
1614
+ }, timeout);
1615
+ const unsubscribe = subscribe(key, (entry) => {
1616
+ clearTimeout(timeoutId);
1617
+ resolve(entry);
1618
+ });
1619
+ });
1620
+ }
1621
+ function hasPrimary(id) {
1622
+ return primaryRegistry.has(id);
1623
+ }
1624
+ function unregisterPrimary(id) {
1625
+ primaryRegistry.delete(id);
1626
+ }
1627
+ function getPrimaryIds() {
1628
+ return Array.from(primaryRegistry.keys());
1629
+ }
1630
+
1631
+ const shallowLoose = { objects: "shallow", strict: false };
1632
+ function watchDpr(onChange) {
1633
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function" || !window.devicePixelRatio) {
1634
+ return () => {
1635
+ };
1636
+ }
1637
+ let watching = true;
1638
+ let query;
1639
+ const listen = () => {
1640
+ try {
1641
+ query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
1642
+ if (typeof query?.addEventListener === "function") query.addEventListener("change", handleChange);
1643
+ else query?.addListener?.(handleChange);
1644
+ } catch {
1645
+ query = void 0;
1646
+ }
1647
+ };
1648
+ const unlisten = () => {
1649
+ if (typeof query?.removeEventListener === "function") query.removeEventListener("change", handleChange);
1650
+ else query?.removeListener?.(handleChange);
1651
+ query = void 0;
1652
+ };
1653
+ const handleChange = () => {
1654
+ if (!watching) return;
1655
+ unlisten();
1656
+ listen();
1657
+ onChange();
1658
+ };
1659
+ listen();
1660
+ return () => {
1661
+ watching = false;
1662
+ unlisten();
1663
+ };
1664
+ }
1665
+ function createRootConfiguration(store, canvas) {
1666
+ let previous;
1667
+ let lastCamera;
1668
+ const lastConfiguredProps = {};
1669
+ let resolvedDpr = 0;
1670
+ let dprChangedInXR = false;
1671
+ let unwatchDpr;
1672
+ const resolveDpr = () => {
1673
+ const state = store.getState();
1674
+ const xr = state.internal.actualRenderer?.xr;
1675
+ if (xr?.isPresenting || xr?.getSession?.()) {
1676
+ dprChangedInXR = true;
1677
+ return;
1678
+ }
1679
+ dprChangedInXR = false;
1680
+ const dpr = lastConfiguredProps.dpr;
1681
+ if (dpr === void 0 || state.viewport.dpr !== resolvedDpr || calculateDpr(dpr) === resolvedDpr) return;
1682
+ state.setDpr(dpr);
1683
+ resolvedDpr = store.getState().viewport.dpr;
1684
+ };
1685
+ let warnedSharedSettings = false;
1686
+ let configured = false;
1687
+ return (props) => {
1688
+ const {
1689
+ gl: glConfig,
1690
+ renderer: rendererConfig,
1691
+ size: propsSize,
1692
+ scene: sceneOptions,
1693
+ events,
1694
+ orthographic = false,
1695
+ frameloop = "always",
1696
+ dpr = [1, 2],
1697
+ performance,
1698
+ raycaster: raycastOptions,
1699
+ camera: cameraOptions,
1700
+ onPointerMissed,
1701
+ onDragOverMissed,
1702
+ onDropMissed,
1703
+ autoUpdateFrustum = true,
1704
+ occlusion = false,
1705
+ _sizeProps,
1706
+ forceEven
1707
+ } = props;
1708
+ assertNoRemovedConfig(props);
1709
+ const rendererBag = settingsBag(rendererConfig);
1710
+ const settings = settingsBag(glConfig) ?? rendererBag;
1711
+ const requestedTextureColorSpace = settings?.textureColorSpace || void 0;
1712
+ const shadows = isInstanceOrFactory(glConfig ?? rendererConfig) ? void 0 : settings?.shadows ?? false;
1713
+ const next = { ...props, shadows, frameloop, autoUpdateFrustum, occlusion };
1714
+ const last = previous;
1715
+ const changed = (key) => !last || !is.equ(next[key], last[key], { objects: "shallow" });
1716
+ previous = void 0;
1717
+ const state = store.getState();
1718
+ const renderer = state.internal.actualRenderer;
1719
+ const three = getThree();
1720
+ const borrowsRenderer = state.internal.isSecondary === true;
1721
+ if (borrowsRenderer && !warnedSharedSettings && rendererBag && Object.keys(rendererBag).some((key) => key !== "textureColorSpace") && textureStaging.isDevelopment()) {
1722
+ warnedSharedSettings = true;
1723
+ console.warn(
1724
+ "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."
1725
+ );
1726
+ }
1727
+ let raycaster = state.raycaster;
1728
+ if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
1729
+ if (changed("raycaster")) {
1730
+ const { params, ...options } = raycastOptions || {};
1731
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
1732
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
1733
+ applyProps(raycaster, { params: { ...raycaster.params, ...params } });
1734
+ }
1735
+ }
1736
+ let tempCamera = state.camera;
1737
+ if (!state.camera || state.camera === lastCamera && changed("camera")) {
1738
+ lastCamera = cameraOptions;
1739
+ const isCamera = cameraOptions?.isCamera;
1740
+ const camera = isCamera ? cameraOptions : orthographic ? new three.OrthographicCamera(0, 0, 0, 0, 0.1, 1e3) : new three.PerspectiveCamera(50, 0, 0.1, 1e3);
1741
+ if (!isCamera) {
1742
+ camera.position.z = 5;
1743
+ if (cameraOptions) {
1744
+ applyProps(camera, cameraOptions);
1745
+ if (!camera.manual) {
1746
+ const projectionProps = ["aspect", "left", "right", "bottom", "top"];
1747
+ if (projectionProps.some((prop) => prop in cameraOptions)) {
1748
+ camera.manual = true;
1749
+ camera.updateProjectionMatrix();
1750
+ }
1751
+ }
1752
+ }
1753
+ if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
1754
+ }
1755
+ state.set({ camera });
1756
+ tempCamera = camera;
1757
+ raycaster.camera = camera;
1758
+ }
1759
+ if (!state.scene) {
1760
+ let scene;
1761
+ if (sceneOptions?.isScene) {
1762
+ scene = sceneOptions;
1763
+ prepare(scene, store, "", {});
1764
+ } else {
1765
+ scene = new three.Scene();
1766
+ prepare(scene, store, "", {});
1767
+ if (sceneOptions) applyProps(scene, sceneOptions);
1768
+ }
1769
+ state.set((prev) => ({
1770
+ scene,
1771
+ rootScene: scene,
1772
+ internal: { ...prev.internal, container: scene }
1773
+ }));
1774
+ const camera = tempCamera;
1775
+ if (camera && !camera.parent) scene.add(camera);
1776
+ }
1777
+ if (events && !state.events.handlers) {
1778
+ state.set({ events: events(store) });
1779
+ let wasEnabled = true;
1780
+ store.subscribe((state2) => {
1781
+ const { enabled } = state2.events;
1782
+ if (enabled && !wasEnabled) {
1783
+ state2.events.update?.();
1784
+ }
1785
+ wasEnabled = enabled;
1786
+ });
1787
+ }
1788
+ if (_sizeProps !== void 0 && changed("_sizeProps")) {
1789
+ state.set({ _sizeProps });
1790
+ }
1791
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
1792
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
1793
+ }
1794
+ configureSize(store, computeInitialSize(canvas, propsSize));
1795
+ if (dpr !== void 0 && (!last || !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose))) {
1796
+ state.setDpr(dpr);
1797
+ lastConfiguredProps.dpr = dpr;
1798
+ resolvedDpr = store.getState().viewport.dpr;
1799
+ }
1800
+ const internal = store.getState().internal;
1801
+ if (Array.isArray(dpr) && (!unwatchDpr || internal.unwatchDpr !== unwatchDpr)) {
1802
+ internal.unwatchDpr?.();
1803
+ internal.unwatchDpr = unwatchDpr = watchDpr(resolveDpr);
1804
+ }
1805
+ resolveDpr();
1806
+ if (frameloop !== void 0 && changed("frameloop")) {
1807
+ state.setFrameloop(frameloop);
1808
+ }
1809
+ if (changed("onPointerMissed")) state.set({ onPointerMissed });
1810
+ if (changed("onDragOverMissed")) state.set({ onDragOverMissed });
1811
+ if (changed("onDropMissed")) state.set({ onDropMissed });
1812
+ if (changed("autoUpdateFrustum") && state.autoUpdateFrustum !== autoUpdateFrustum) {
1813
+ state.set({ autoUpdateFrustum });
1814
+ }
1815
+ if (occlusion && !state.internal.occlusionEnabled) {
1816
+ enableOcclusion(store);
1817
+ }
1818
+ if (performance && changed("performance")) {
1819
+ state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
1820
+ }
1821
+ if (!state.xr) {
1822
+ const handleXRFrame = (timestamp, _frame) => {
1823
+ const state2 = store.getState();
1824
+ if (state2.frameloop === "never") return;
1825
+ advance(timestamp, true, state2);
1826
+ };
1827
+ const actualRenderer = state.internal.actualRenderer;
1828
+ const handleSessionChange = () => {
1829
+ const state2 = store.getState();
1830
+ const renderer2 = state2.internal.actualRenderer;
1831
+ actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
1832
+ renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
1833
+ if (!renderer2.xr.isPresenting) {
1834
+ if (dprChangedInXR) resolveDpr();
1835
+ invalidate(state2);
1836
+ }
1837
+ };
1838
+ const xr = {
1839
+ connect() {
1840
+ const xrManager = store.getState().renderer.xr;
1841
+ xrManager.addEventListener("sessionstart", handleSessionChange);
1842
+ xrManager.addEventListener("sessionend", handleSessionChange);
1843
+ },
1844
+ disconnect() {
1845
+ const xrManager = store.getState().renderer.xr;
1846
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
1847
+ xrManager.removeEventListener("sessionend", handleSessionChange);
1848
+ }
1849
+ };
1850
+ if (!borrowsRenderer && typeof renderer.xr?.addEventListener === "function") xr.connect();
1851
+ state.set({ xr });
1852
+ }
1853
+ if (!borrowsRenderer && shadows !== void 0 && renderer.shadowMap && changed("shadows")) {
1854
+ const oldEnabled = renderer.shadowMap.enabled;
1855
+ const oldType = renderer.shadowMap.type;
1856
+ renderer.shadowMap.enabled = !!shadows;
1857
+ if (is.boo(shadows)) {
1858
+ renderer.shadowMap.type = three.PCFShadowMap;
1859
+ } else if (is.str(shadows)) {
1860
+ if (shadows === "soft") {
1861
+ textureStaging.notifyDepreciated({
1862
+ heading: 'shadows="soft" is deprecated',
1863
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
1864
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
1865
+ });
1866
+ }
1867
+ const types = {
1868
+ basic: three.BasicShadowMap,
1869
+ percentage: three.PCFShadowMap,
1870
+ soft: three.PCFShadowMap,
1871
+ variance: three.VSMShadowMap
1872
+ };
1873
+ renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
1874
+ } else if (is.obj(shadows)) {
1875
+ Object.assign(renderer.shadowMap, shadows);
1876
+ }
1877
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
1878
+ renderer.shadowMap.needsUpdate = true;
1879
+ }
1880
+ }
1881
+ if (!configured && !borrowsRenderer) {
1882
+ renderer.outputColorSpace = three.SRGBColorSpace;
1883
+ renderer.toneMapping = three.ACESFilmicToneMapping;
1884
+ }
1885
+ const textureColorSpace = requestedTextureColorSpace ?? three.SRGBColorSpace;
1886
+ if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
1887
+ if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
1888
+ lastConfiguredProps.textureColorSpace = textureColorSpace;
1889
+ }
1890
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && changed("gl")) {
1891
+ const glProps = {};
1892
+ for (const key in glConfig) {
1893
+ if (!NON_APPLIED_RENDERER_PROPS.has(key)) glProps[key] = glConfig[key];
1894
+ }
1895
+ applyProps(renderer, glProps);
1896
+ }
1897
+ if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer && !borrowsRenderer) {
1898
+ const currentRenderer = state.renderer;
1899
+ if (changed("renderer")) {
1900
+ const rendererProps = {};
1901
+ for (const key in rendererConfig) {
1902
+ if (!NON_APPLIED_RENDERER_PROPS.has(key)) rendererProps[key] = rendererConfig[key];
1903
+ }
1904
+ applyProps(currentRenderer, rendererProps);
1905
+ }
1906
+ }
1907
+ configured = true;
1908
+ previous = next;
1909
+ };
1910
+ }
1911
+
1145
1912
  const version = "10.0.0-alpha.5";
1146
1913
  const packageData = {
1147
1914
  version: version};
@@ -13340,15 +14107,16 @@ function swapInteractivity(store, object, newObject) {
13340
14107
  pointerState.hovered.set(makeId(next), next);
13341
14108
  }
13342
14109
  });
13343
- const captureData = pointerState.captured.get(object);
13344
- if (captureData) {
13345
- pointerState.captured.delete(object);
13346
- pointerState.captured.set(newObject, {
14110
+ for (const [key, captureData] of Array.from(pointerState.captured)) {
14111
+ const { intersection } = captureData;
14112
+ if (key !== object && intersection.object !== object && intersection.eventObject !== object) continue;
14113
+ if (key === object) pointerState.captured.delete(key);
14114
+ pointerState.captured.set(key === object ? newObject : key, {
13347
14115
  ...captureData,
13348
14116
  intersection: {
13349
- ...captureData.intersection,
13350
- object: captureData.intersection.object === object ? newObject : captureData.intersection.object,
13351
- eventObject: captureData.intersection.eventObject === object ? newObject : captureData.intersection.eventObject
14117
+ ...intersection,
14118
+ object: intersection.object === object ? newObject : intersection.object,
14119
+ eventObject: intersection.eventObject === object ? newObject : intersection.eventObject
13352
14120
  }
13353
14121
  });
13354
14122
  }
@@ -13877,10 +14645,11 @@ const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
13877
14645
  const R3F_EXTEND_ID = Symbol.for("@react-three/fiber.extendId");
13878
14646
  const ids = globalThis;
13879
14647
  const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
13880
- const isConstructor = (object) => typeof object === "function";
14648
+ const isConstructor$1 = (object) => typeof object === "function";
13881
14649
  function extend(objects) {
13882
- if (isConstructor(objects)) {
13883
- const id = ids[R3F_EXTEND_ID] ?? 0;
14650
+ if (isConstructor$1(objects)) {
14651
+ let id = ids[R3F_EXTEND_ID] ?? 0;
14652
+ while (`${id}` in catalogue) id++;
13884
14653
  ids[R3F_EXTEND_ID] = id + 1;
13885
14654
  const Component = `${id}`;
13886
14655
  catalogue[Component] = objects;
@@ -13888,15 +14657,14 @@ function extend(objects) {
13888
14657
  } else {
13889
14658
  for (const name in objects) {
13890
14659
  const object = objects[name];
13891
- if (isConstructor(object)) catalogue[name] = object;
14660
+ if (isConstructor$1(object)) catalogue[name] = object;
13892
14661
  }
13893
14662
  }
13894
14663
  }
13895
-
13896
14664
  function resolveConstructor(name, root) {
13897
14665
  if (Object.prototype.hasOwnProperty.call(catalogue, name)) return catalogue[name];
13898
14666
  const object = root.getState().internal.support?.three[name];
13899
- return isConstructor(object) ? object : void 0;
14667
+ return isConstructor$1(object) ? object : void 0;
13900
14668
  }
13901
14669
 
13902
14670
  function createReconciler(config) {
@@ -13933,6 +14701,7 @@ function hideInstance(instance) {
13933
14701
  if (instance.props.attach && instance.parent?.object) {
13934
14702
  detach(instance.parent, instance);
13935
14703
  } else if (isObject3D(instance.object)) {
14704
+ instance.previousVisible = instance.object.visible;
13936
14705
  instance.object.visible = false;
13937
14706
  }
13938
14707
  instance.isHidden = true;
@@ -13943,9 +14712,10 @@ function unhideInstance(instance) {
13943
14712
  if (instance.isHidden) {
13944
14713
  if (instance.props.attach && instance.parent?.object) {
13945
14714
  attach(instance.parent, instance);
13946
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
13947
- instance.object.visible = true;
14715
+ } else if (isObject3D(instance.object)) {
14716
+ instance.object.visible = instance.previousVisible ?? true;
13948
14717
  }
14718
+ instance.previousVisible = void 0;
13949
14719
  instance.isHidden = false;
13950
14720
  invalidateInstance(instance);
13951
14721
  }
@@ -13958,6 +14728,7 @@ function handleContainerEffects(parent, child, beforeChild) {
13958
14728
  child.object = child.props.object ?? new target(...child.props.args ?? []);
13959
14729
  child.object.__r3f = child;
13960
14730
  }
14731
+ warnGLSLMaterialUnsupported(state, child.object);
13961
14732
  applyProps(child.object, child.props);
13962
14733
  if (child.props.attach) {
13963
14734
  attach(parent, child);
@@ -14450,945 +15221,738 @@ const reconciler = /* @__PURE__ */ createReconciler({
14450
15221
  // https://github.com/facebook/react/pull/34522
14451
15222
  getSuspendedCommitReason: (_state, _rootContainer) => null
14452
15223
  });
14453
-
14454
- const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
14455
- function isInternalRendererAccess(stack, callerDepth = 2) {
14456
- if (!stack) return false;
14457
- const caller = stack.split("\n")[callerDepth] ?? "";
14458
- return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
15224
+ function flushSync(fn) {
15225
+ return reconciler.flushSyncFromReconciler(fn);
14459
15226
  }
14460
15227
 
14461
- const createStore = (invalidate, advance) => {
14462
- const rootStore = traditional.createWithEqualityFn((set, get) => {
14463
- let position;
14464
- let defaultTarget;
14465
- let tempTarget;
14466
- function getCurrentViewport(camera = get().camera, target = void 0, size = get().size) {
14467
- const { Vector3 } = getThree();
14468
- position ?? (position = new Vector3());
14469
- defaultTarget ?? (defaultTarget = new Vector3());
14470
- tempTarget ?? (tempTarget = new Vector3());
14471
- target ?? (target = defaultTarget);
14472
- const { width, height, top, left } = size;
14473
- const aspect = width / height;
14474
- if (target.isVector3) tempTarget.copy(target);
14475
- else tempTarget.set(...target);
14476
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
14477
- if (isOrthographicCamera(camera)) {
14478
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
14479
- } else {
14480
- const fov = camera.fov * Math.PI / 180;
14481
- const h = 2 * Math.tan(fov / 2) * distance;
14482
- const w = h * (width / height);
14483
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
14484
- }
15228
+ const noop = () => {
15229
+ };
15230
+ const _roots = /* @__PURE__ */ new Map();
15231
+ function transitionRoot(root, event) {
15232
+ const state = root.state;
15233
+ switch (event.type) {
15234
+ case "use":
15235
+ if (state.status === "disposed") return false;
15236
+ if (state.status === "closing") {
15237
+ root.store.getState().internal.active = true;
15238
+ state.settle();
15239
+ }
15240
+ root.state = { status: "open" };
15241
+ return true;
15242
+ case "unmount": {
15243
+ if (state.status === "disposed") return false;
15244
+ if (state.status === "closing") state.settle();
15245
+ const { promise, resolve } = deferred();
15246
+ root.state = { status: "closing", token: event.token, settled: promise, settle: resolve };
15247
+ return true;
14485
15248
  }
14486
- let performanceTimeout = void 0;
14487
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
14488
- const rootState = {
14489
- set,
14490
- get,
14491
- // Mock objects that have to be configured
14492
- // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
14493
- primaryStore: null,
14494
- gl: null,
14495
- renderer: null,
14496
- camera: null,
14497
- // frustum, pointer and mouse are three objects; configure() creates them with the renderer
14498
- frustum: null,
14499
- autoUpdateFrustum: true,
14500
- raycaster: null,
14501
- events: {
14502
- priority: 1,
14503
- enabled: true,
14504
- connected: false,
14505
- frameTimedRaycasts: true,
14506
- alwaysFireOnScroll: true,
14507
- updateOnFrame: false
14508
- },
14509
- scene: null,
14510
- rootScene: null,
14511
- xr: null,
14512
- inspector: null,
14513
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
14514
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
14515
- textureColorSpace: "srgb",
14516
- // THREE.SRGBColorSpace
14517
- isLegacy: false,
14518
- webGPUSupported: false,
14519
- isNative: false,
14520
- controls: null,
14521
- pointer: null,
14522
- mouse: null,
14523
- frameloop: "always",
14524
- onPointerMissed: void 0,
14525
- onDragOverMissed: void 0,
14526
- onDropMissed: void 0,
14527
- performance: {
14528
- current: 1,
14529
- min: 0.5,
14530
- max: 1,
14531
- debounce: 200,
14532
- regress: () => {
14533
- const state2 = get();
14534
- if (performanceTimeout) clearTimeout(performanceTimeout);
14535
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
14536
- performanceTimeout = setTimeout(
14537
- () => setPerformanceCurrent(get().performance.max),
14538
- state2.performance.debounce
14539
- );
14540
- }
14541
- },
14542
- size: { width: 0, height: 0, top: 0, left: 0 },
14543
- viewport: {
14544
- initialDpr: 0,
14545
- dpr: 0,
14546
- width: 0,
14547
- height: 0,
14548
- top: 0,
14549
- left: 0,
14550
- aspect: 0,
14551
- distance: 0,
14552
- factor: 0,
14553
- getCurrentViewport
14554
- },
14555
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
14556
- setSize: (width, height, top, left) => {
14557
- const state2 = get();
14558
- if (width === void 0) {
14559
- set({ _sizeImperative: false });
14560
- if (state2._sizeProps) {
14561
- const { width: propW, height: propH } = state2._sizeProps;
14562
- if (propW !== void 0 || propH !== void 0) {
14563
- const currentSize = state2.size;
14564
- const newSize = {
14565
- width: propW ?? currentSize.width,
14566
- height: propH ?? currentSize.height,
14567
- top: currentSize.top,
14568
- left: currentSize.left
14569
- };
14570
- set((s) => ({
14571
- size: newSize,
14572
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
14573
- }));
14574
- get().invalidate();
14575
- }
14576
- }
14577
- return;
14578
- }
14579
- const w = width;
14580
- const h = height ?? width;
14581
- const t = top ?? state2.size.top;
14582
- const l = left ?? state2.size.left;
14583
- const size = { width: w, height: h, top: t, left: l };
14584
- set((s) => ({
14585
- size,
14586
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
14587
- _sizeImperative: true
14588
- }));
14589
- get().invalidate();
14590
- },
14591
- setDpr: (dpr) => set((state2) => {
14592
- const resolved = calculateDpr(dpr);
14593
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
14594
- }),
14595
- setFrameloop: (frameloop = "always") => {
14596
- const rootId = get().internal.rootId;
14597
- if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
14598
- set(() => ({ frameloop }));
14599
- },
14600
- setError: (error) => set(() => ({ error })),
14601
- error: null,
14602
- //* Texture registry (useTextures) ==============================
14603
- // TSL fields (uniforms, nodes, ..., renderPipeline) are added by @react-three/tsl's root extension.
14604
- textures: /* @__PURE__ */ new Map(),
14605
- _textureRefs: /* @__PURE__ */ new Map(),
14606
- _sizeImperative: false,
14607
- _sizeProps: null,
14608
- previousRoot: void 0,
14609
- internal: {
14610
- // Events
14611
- interaction: [],
14612
- subscribers: [],
14613
- // Per-pointer state (new unified structure)
14614
- pointerMap: /* @__PURE__ */ new Map(),
14615
- pointerDirty: /* @__PURE__ */ new Map(),
14616
- lastEvent: React__namespace.createRef(),
14617
- // Deprecated but kept for backwards compatibility
14618
- hovered: /* @__PURE__ */ new Map(),
14619
- initialClick: [0, 0],
14620
- initialHits: [],
14621
- capturedMap: /* @__PURE__ */ new Map(),
14622
- // Visibility tracking (onFramed, onOccluded, onVisible)
14623
- visibilityRegistry: /* @__PURE__ */ new Map(),
14624
- // Occlusion system (WebGPU only)
14625
- occlusionEnabled: false,
14626
- occlusionObserver: null,
14627
- occlusionCache: /* @__PURE__ */ new Map(),
14628
- helperGroup: null,
14629
- // Updates
14630
- active: false,
14631
- frames: 0,
14632
- priority: 0,
14633
- subscribe: (ref, priority, store) => {
14634
- const internal = get().internal;
14635
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
14636
- internal.subscribers.push({ ref, priority, store });
14637
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
14638
- return () => {
14639
- const internal2 = get().internal;
14640
- if (internal2?.subscribers) {
14641
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
14642
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
14643
- }
14644
- };
14645
- },
14646
- // Renderer Storage (single source of truth)
14647
- actualRenderer: null,
14648
- // The renderer support configure() loads for this root
14649
- support: null,
14650
- // Scheduler for useFrameNext (initialized in renderer.tsx)
14651
- scheduler: null,
14652
- // Replaces the default render call when set (see setRenderOverride)
14653
- renderOverride: null
14654
- }
14655
- };
14656
- return rootState;
14657
- });
14658
- const state = rootStore.getState();
14659
- Object.defineProperty(state, "gl", {
14660
- get() {
14661
- const currentState = rootStore.getState();
14662
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
14663
- const stack = new Error().stack || "";
14664
- if (!isInternalRendererAccess(stack)) {
14665
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
14666
- extensions.notifyDepreciated({
14667
- heading: "Accessing state.gl in WebGPU mode",
14668
- 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
14669
- });
14670
- }
14671
- }
14672
- return currentState.internal.actualRenderer;
14673
- },
14674
- set(value) {
14675
- rootStore.getState().internal.actualRenderer = value;
15249
+ case "dispose":
15250
+ if (state.status !== "closing" || state.token !== event.token) return false;
15251
+ root.state = { status: "disposed" };
15252
+ state.settle();
15253
+ return true;
15254
+ }
15255
+ }
15256
+ function createRoot(canvas, provider) {
15257
+ const prevRoot = _roots.get(canvas);
15258
+ const prevFiber = prevRoot?.fiber;
15259
+ const prevStore = prevRoot?.store;
15260
+ if (prevRoot) console.warn("R3F.createRoot should only be called once!");
15261
+ const logRecoverableError = typeof reportError === "function" ? (
15262
+ // In modern browsers, reportError will dispatch an error event,
15263
+ // emulating an uncaught JavaScript error.
15264
+ reportError
15265
+ ) : (
15266
+ // In older browsers and test environments, fallback to console.error.
15267
+ console.error
15268
+ );
15269
+ const store = prevStore || createStore(invalidate, advance);
15270
+ const fiber = prevFiber || reconciler.createContainer(
15271
+ store,
15272
+ // container
15273
+ t,
15274
+ // tag
15275
+ null,
15276
+ // hydration callbacks
15277
+ false,
15278
+ // isStrictMode
15279
+ null,
15280
+ // concurrentUpdatesByDefaultOverride
15281
+ "",
15282
+ // identifierPrefix
15283
+ logRecoverableError,
15284
+ // onUncaughtError
15285
+ logRecoverableError,
15286
+ // onCaughtError
15287
+ logRecoverableError,
15288
+ // onRecoverableError
15289
+ null
15290
+ // transitionCallbacks
15291
+ );
15292
+ const root = prevRoot || {
15293
+ fiber,
15294
+ store,
15295
+ state: { status: "open" },
15296
+ ready: fulfilled(void 0),
15297
+ configuration: {}
15298
+ };
15299
+ if (!prevRoot) _roots.set(canvas, root);
15300
+ if (!prevRoot) root.configuration.apply = createRootConfiguration(store, canvas);
15301
+ let mounted = false;
15302
+ return {
15303
+ get ready() {
15304
+ return root.ready;
14676
15305
  },
14677
- enumerable: true,
14678
- configurable: true
14679
- });
14680
- Object.defineProperty(state, "renderer", {
14681
- get() {
14682
- return rootStore.getState().internal.actualRenderer;
15306
+ configure(props = {}) {
15307
+ if (!transitionRoot(root, { type: "use" })) {
15308
+ return rejected(new Error("R3F: Cannot configure a root after it has unmounted (disposal has started)."));
15309
+ }
15310
+ const previous = root.ready;
15311
+ const { promise, resolve, reject } = deferred();
15312
+ root.ready = promise;
15313
+ const run = () => {
15314
+ try {
15315
+ const initialized = initializeRenderer(canvas, provider, store, props);
15316
+ const apply = () => {
15317
+ if (props.id) {
15318
+ for (const other of _roots.values()) {
15319
+ if (other !== root && other.state.status === "closing" && other.store.getState().internal.rootId === props.id) {
15320
+ other.state.settled.then(apply).catch(reject);
15321
+ return;
15322
+ }
15323
+ }
15324
+ }
15325
+ const previous2 = root.configuration.previous;
15326
+ root.configuration.apply(props);
15327
+ configureScheduler(store, props.id, props.scheduler, previous2?.scheduler);
15328
+ textureStaging.attachRootExtensions(store);
15329
+ root.configuration.previous = props;
15330
+ resolve(this);
15331
+ };
15332
+ if (isPromiseLike(initialized)) Promise.resolve(initialized).then(apply).catch(reject);
15333
+ else apply();
15334
+ } catch (error) {
15335
+ reject(error);
15336
+ }
15337
+ };
15338
+ if (previous.status === "pending") previous.then(run, reject);
15339
+ else run();
15340
+ return promise;
14683
15341
  },
14684
- set(value) {
14685
- rootStore.getState().internal.actualRenderer = value;
15342
+ render(children) {
15343
+ if (!transitionRoot(root, { type: "use" })) return store;
15344
+ if (!store.getState().internal.actualRenderer && root.ready.status === "fulfilled") this.configure();
15345
+ if (root.ready.status === "rejected") throw root.ready.reason;
15346
+ const commit = () => {
15347
+ if (root.state.status !== "open" || root.ready.status === "rejected") return;
15348
+ if (root.ready.status === "pending") {
15349
+ root.ready.then(commit, logRecoverableError);
15350
+ return;
15351
+ }
15352
+ const onCreated = root.configuration.previous?.onCreated;
15353
+ const element = /* @__PURE__ */ jsxRuntime.jsx(Provider, { store, children, onCreated, rootElement: canvas });
15354
+ if (mounted) {
15355
+ reconciler.updateContainer(element, fiber, null, noop);
15356
+ return;
15357
+ }
15358
+ mounted = true;
15359
+ reconciler.updateContainerSync(element, fiber, null, noop);
15360
+ reconciler.flushSyncWork();
15361
+ };
15362
+ commit();
15363
+ return store;
14686
15364
  },
14687
- enumerable: true,
14688
- configurable: true
14689
- });
14690
- let oldScene = state.scene;
14691
- rootStore.subscribe(() => {
14692
- const currentState = rootStore.getState();
14693
- const { scene, rootScene, set } = currentState;
14694
- if (scene !== oldScene) {
14695
- oldScene = scene;
14696
- if (scene?.isScene && scene !== rootScene) {
14697
- set({ rootScene: scene });
14698
- }
15365
+ unmount() {
15366
+ if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
14699
15367
  }
15368
+ };
15369
+ }
15370
+ function Provider({
15371
+ store,
15372
+ children,
15373
+ onCreated,
15374
+ rootElement
15375
+ }) {
15376
+ textureStaging.useIsomorphicLayoutEffect(() => {
15377
+ const state = store.getState();
15378
+ state.set((state2) => ({ internal: { ...state2.internal, active: true } }));
15379
+ if (onCreated) onCreated(state);
15380
+ if (!store.getState().events.connected) state.events.connect?.(rootElement);
15381
+ }, []);
15382
+ return /* @__PURE__ */ jsxRuntime.jsx(textureStaging.context.Provider, { value: store, children });
15383
+ }
15384
+ function unmountComponentAtNode(canvas, callback) {
15385
+ const claim = Symbol("unmount");
15386
+ const root = _roots.get(canvas);
15387
+ if (!root || !transitionRoot(root, { type: "unmount", token: claim })) return;
15388
+ root.store.getState().internal.active = false;
15389
+ reconciler.updateContainer(null, root.fiber, null, () => {
15390
+ if (root.state.status !== "closing" || root.state.token !== claim) return;
15391
+ reconciler.updateContainer(null, root.fiber, null, () => {
15392
+ if (root.state.status !== "closing" || root.state.token !== claim) return;
15393
+ if (root.ready.status === "pending") root.ready.then(teardown, teardown);
15394
+ else teardown();
15395
+ });
14700
15396
  });
14701
- let oldSize = state.size;
14702
- let oldDpr = state.viewport.dpr;
14703
- let oldCamera = state.camera;
14704
- rootStore.subscribe(() => {
14705
- const { camera, size, viewport, set, internal } = rootStore.getState();
14706
- const actualRenderer = internal.actualRenderer;
14707
- const canvasTarget = internal.canvasTarget;
14708
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
14709
- oldSize = size;
14710
- oldDpr = viewport.dpr;
14711
- updateCamera(camera, size);
14712
- const resizeTarget = canvasTarget ?? actualRenderer;
14713
- if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
14714
- resizeTarget.setSize(size.width, size.height, false);
14715
- const activeTarget = actualRenderer.getCanvasTarget?.();
14716
- if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
15397
+ function teardown() {
15398
+ if (root.ready.status === "pending") {
15399
+ root.ready.then(teardown, teardown);
15400
+ return;
14717
15401
  }
14718
- if (camera !== oldCamera) {
14719
- oldCamera = camera;
14720
- const { rootScene } = rootStore.getState();
14721
- if (camera && rootScene && !camera.parent) {
14722
- rootScene.add(camera);
15402
+ if (!transitionRoot(root, { type: "dispose", token: claim })) return;
15403
+ _roots.delete(canvas);
15404
+ const state = root.store.getState();
15405
+ const internal = state.internal;
15406
+ internal.active = false;
15407
+ const attempt = (step) => {
15408
+ try {
15409
+ return step();
15410
+ } catch (error) {
15411
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
14723
15412
  }
14724
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
14725
- const currentState = rootStore.getState();
14726
- if (currentState.autoUpdateFrustum && camera) {
14727
- updateFrustum(camera, currentState.frustum);
15413
+ };
15414
+ attempt(() => internal.unwatchDpr?.());
15415
+ internal.unwatchDpr = void 0;
15416
+ attempt(() => internal.unregisterRoot?.());
15417
+ internal.unregisterRoot = void 0;
15418
+ attempt(() => textureStaging.detachRootExtensions(root.store));
15419
+ attempt(() => internal.unregisterPrimary?.());
15420
+ attempt(() => internal.untrackStandalone?.());
15421
+ attempt(() => state.events.disconnect?.());
15422
+ if (!internal.isSecondary && internal.actualRenderer?.xr && state.xr) attempt(() => state.xr.disconnect());
15423
+ attempt(() => cleanupHelperGroup(root.store));
15424
+ if (state.scene) attempt(() => dispose(state.scene));
15425
+ const canvasTarget = internal.canvasTarget;
15426
+ if (internal.isSecondary && canvasTarget?.dispose) attempt(() => canvasTarget.dispose());
15427
+ const released = attempt(() => internal.releaseRenderer?.());
15428
+ internal.releaseRenderer = void 0;
15429
+ if (callback) {
15430
+ if (released) {
15431
+ released.then(() => callback(canvas)).catch((error) => console.warn("[R3F] Error in unmount callback", error));
15432
+ } else {
15433
+ callback(canvas);
14728
15434
  }
14729
15435
  }
14730
- });
14731
- rootStore.subscribe((state2) => invalidate(state2));
14732
- return rootStore;
14733
- };
14734
-
14735
- const primaryRegistry = /* @__PURE__ */ new Map();
14736
- const pendingSubscribers = /* @__PURE__ */ new Map();
14737
- function registerPrimary(id, renderer, store) {
14738
- if (primaryRegistry.has(id)) {
14739
- console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
14740
15436
  }
14741
- const entry = { renderer, store };
14742
- primaryRegistry.set(id, entry);
14743
- const subscribers = pendingSubscribers.get(id);
14744
- if (subscribers) {
14745
- subscribers.forEach((callback) => callback(entry));
14746
- pendingSubscribers.delete(id);
15437
+ }
15438
+
15439
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
15440
+ const isConstructor = (value) => typeof value === "function" && value?.prototype?.constructor === value;
15441
+ function getLoader(Proto) {
15442
+ if (isConstructor(Proto)) {
15443
+ let loader = memoizedLoaders.get(Proto);
15444
+ if (!loader) {
15445
+ loader = new Proto();
15446
+ memoizedLoaders.set(Proto, loader);
15447
+ }
15448
+ return loader;
14747
15449
  }
14748
- return () => {
14749
- const currentEntry = primaryRegistry.get(id);
14750
- if (currentEntry?.renderer === renderer) {
14751
- primaryRegistry.delete(id);
15450
+ return Proto;
15451
+ }
15452
+ function loadingFn(extensions, onProgress) {
15453
+ return function(Proto, input) {
15454
+ const loader = getLoader(Proto);
15455
+ if (extensions) extensions(loader);
15456
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
15457
+ return loader.loadAsync(input, onProgress).then((data) => {
15458
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
15459
+ return data;
15460
+ });
14752
15461
  }
15462
+ return new Promise(
15463
+ (res, reject) => loader.load(
15464
+ input,
15465
+ (data) => {
15466
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
15467
+ res(data);
15468
+ },
15469
+ onProgress,
15470
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
15471
+ )
15472
+ );
14753
15473
  };
14754
15474
  }
14755
- function getPrimary(id) {
14756
- return primaryRegistry.get(id);
15475
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
15476
+ const inputs = Array.isArray(input) ? input : [input];
15477
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15478
+ const fn = loadingFn(extensions, onProgress);
15479
+ const results = cacheKeys.map(
15480
+ (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
15481
+ );
15482
+ const stableRef = React.useRef(null);
15483
+ const previous = stableRef.current;
15484
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
15485
+ stableRef.current = stable;
15486
+ return Array.isArray(input) ? stable : stable[0];
14757
15487
  }
14758
- function waitForPrimary(id, timeout = 5e3) {
14759
- const existing = primaryRegistry.get(id);
14760
- if (existing) {
14761
- return Promise.resolve(existing);
15488
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
15489
+ const inputs = Array.isArray(input) ? input : [input];
15490
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15491
+ cacheKeys.forEach(
15492
+ (key, index) => suspendReact.preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
15493
+ );
15494
+ };
15495
+ useLoader.clear = function(loader, input, cacheKey) {
15496
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
15497
+ cacheKeys.forEach((key) => suspendReact.clear([loader, key]));
15498
+ };
15499
+ useLoader.loader = getLoader;
15500
+
15501
+ const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
15502
+ function getUrls(input) {
15503
+ if (typeof input === "string") return [input];
15504
+ if (Array.isArray(input)) return input;
15505
+ return Object.values(input);
15506
+ }
15507
+ function inputSignature(input) {
15508
+ if (typeof input === "string") return input;
15509
+ if (Array.isArray(input)) return input.join("\0");
15510
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
15511
+ }
15512
+ function allUrlsCached(urls, textureCache) {
15513
+ return urls.every((url) => textureCache.has(url));
15514
+ }
15515
+ function buildFromCache(input, textureCache) {
15516
+ if (typeof input === "string") {
15517
+ return textureCache.get(input);
15518
+ } else if (Array.isArray(input)) {
15519
+ return input.map((url) => textureCache.get(url));
15520
+ } else {
15521
+ const result = {};
15522
+ const objInput = input;
15523
+ for (const key in objInput) {
15524
+ result[key] = textureCache.get(objInput[key]);
15525
+ }
15526
+ return result;
14762
15527
  }
14763
- return new Promise((resolve, reject) => {
14764
- const timeoutId = setTimeout(() => {
14765
- const subscribers = pendingSubscribers.get(id);
14766
- if (subscribers) {
14767
- const index = subscribers.indexOf(callback);
14768
- if (index !== -1) subscribers.splice(index, 1);
14769
- if (subscribers.length === 0) pendingSubscribers.delete(id);
15528
+ }
15529
+ function useTexture(input, optionsOrOnLoad) {
15530
+ const renderer = textureStaging.useThree((state) => state.internal.actualRenderer);
15531
+ const store = textureStaging.useStore();
15532
+ const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
15533
+ const { onLoad, cache = true } = options;
15534
+ const onLoadRef = React.useRef(onLoad);
15535
+ onLoadRef.current = onLoad;
15536
+ const onLoadCalledForRef = React.useRef(null);
15537
+ const inputKey = React.useMemo(() => inputSignature(input), [input]);
15538
+ const stableInput = React.useMemo(() => input, [inputKey]);
15539
+ const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
15540
+ const cachedResult = React.useMemo(() => {
15541
+ if (!cache) return null;
15542
+ const textures = store.getState().textures;
15543
+ if (!allUrlsCached(urls, textures)) return null;
15544
+ return buildFromCache(stableInput, textures);
15545
+ }, [cache, urls, stableInput, store]);
15546
+ const loadedTextures = useLoader(
15547
+ getThree().TextureLoader,
15548
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
15549
+ );
15550
+ const mappedTextures = React.useMemo(() => {
15551
+ if (cachedResult) return cachedResult;
15552
+ if (IsObject(stableInput)) {
15553
+ const keyed = {};
15554
+ const textureArray = loadedTextures;
15555
+ let i = 0;
15556
+ for (const key in stableInput) keyed[key] = textureArray[i++];
15557
+ return keyed;
15558
+ } else {
15559
+ return loadedTextures;
15560
+ }
15561
+ }, [stableInput, loadedTextures, cachedResult]);
15562
+ React.useLayoutEffect(() => {
15563
+ if (cachedResult) return;
15564
+ if (onLoadCalledForRef.current === inputKey) return;
15565
+ onLoadCalledForRef.current = inputKey;
15566
+ onLoadRef.current?.(mappedTextures);
15567
+ }, [cachedResult, mappedTextures, inputKey]);
15568
+ React.useEffect(() => {
15569
+ if (cachedResult) return;
15570
+ if ("initTexture" in renderer) {
15571
+ let textureArray = [];
15572
+ if (Array.isArray(loadedTextures)) {
15573
+ textureArray = loadedTextures;
15574
+ } else if (loadedTextures.isTexture) {
15575
+ textureArray = [loadedTextures];
15576
+ } else if (IsObject(loadedTextures)) {
15577
+ textureArray = Object.values(loadedTextures);
15578
+ }
15579
+ textureArray.forEach((texture) => {
15580
+ if (texture?.isTexture) {
15581
+ renderer.initTexture(texture);
15582
+ }
15583
+ });
15584
+ }
15585
+ }, [renderer, loadedTextures, cachedResult]);
15586
+ React.useEffect(() => {
15587
+ if (!cache) return;
15588
+ const urlTextureMap = urlTextureEntries(stableInput, mappedTextures);
15589
+ store.setState((state) => {
15590
+ const refs = new Map(state._textureRefs);
15591
+ let textures = state.textures;
15592
+ let added = false;
15593
+ for (const [url, texture] of urlTextureMap) {
15594
+ if (!textures.has(url)) {
15595
+ if (!added) {
15596
+ textures = new Map(textures);
15597
+ added = true;
15598
+ }
15599
+ textures.set(url, texture);
15600
+ }
15601
+ refs.set(url, (refs.get(url) ?? 0) + 1);
14770
15602
  }
14771
- reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
14772
- }, timeout);
14773
- const callback = (entry) => {
14774
- clearTimeout(timeoutId);
14775
- resolve(entry);
14776
- };
14777
- if (!pendingSubscribers.has(id)) {
14778
- pendingSubscribers.set(id, []);
14779
- }
14780
- pendingSubscribers.get(id).push(callback);
14781
- });
14782
- }
14783
- function hasPrimary(id) {
14784
- return primaryRegistry.has(id);
14785
- }
14786
- function unregisterPrimary(id) {
14787
- primaryRegistry.delete(id);
15603
+ return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
15604
+ });
15605
+ textureStaging.unstageTextures(
15606
+ store,
15607
+ urlTextureMap.map(([url]) => url)
15608
+ );
15609
+ return () => store.setState((state) => {
15610
+ const refs = new Map(state._textureRefs);
15611
+ for (const [url] of urlTextureMap) {
15612
+ const next = (refs.get(url) ?? 0) - 1;
15613
+ if (next <= 0) refs.delete(url);
15614
+ else refs.set(url, next);
15615
+ }
15616
+ return { _textureRefs: refs };
15617
+ });
15618
+ }, [cache, stableInput, mappedTextures, store]);
15619
+ if (cache && !cachedResult) textureStaging.stageTextures(store, urlTextureEntries(stableInput, mappedTextures));
15620
+ return mappedTextures;
14788
15621
  }
14789
- function getPrimaryIds() {
14790
- return Array.from(primaryRegistry.keys());
15622
+ function urlTextureEntries(input, textures) {
15623
+ if (typeof input === "string") return [[input, textures]];
15624
+ if (Array.isArray(input)) return input.map((url, i) => [url, textures[i]]);
15625
+ const record = textures;
15626
+ return Object.keys(input).map((key) => [input[key], record[key]]);
14791
15627
  }
15628
+ useTexture.preload = (url) => {
15629
+ whenThree().then((three) => useLoader.preload(three.TextureLoader, url));
15630
+ };
15631
+ useTexture.clear = (input) => {
15632
+ if (hasThree()) useLoader.clear(getThree().TextureLoader, input);
15633
+ };
15634
+ const Texture = ({
15635
+ children,
15636
+ input,
15637
+ onLoad,
15638
+ cache
15639
+ }) => {
15640
+ const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
15641
+ const ret = useTexture(input, options);
15642
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children?.(ret) });
15643
+ };
14792
15644
 
14793
- const isRenderer = (def) => !!def?.render;
14794
- const _roots = /* @__PURE__ */ new Map();
14795
- function ensureStoreThreeObjects(store) {
14796
- const state = store.getState();
14797
- if (state.pointer) return;
14798
- const { Vector2, Frustum } = getThree();
14799
- const pointer = new Vector2();
14800
- state.set({ pointer, mouse: pointer, frustum: new Frustum() });
14801
- }
14802
- const shallowLoose = { objects: "shallow", strict: false };
14803
- async function resolveRenderer(config, defaultProps, RendererClass) {
14804
- if (typeof config === "function") return await config(defaultProps);
14805
- if (isRenderer(config)) return config;
14806
- return new RendererClass({ ...defaultProps, ...config });
14807
- }
14808
- function computeInitialSize(canvas, size) {
14809
- if (!size && typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
14810
- const { width, height, top, left } = canvas.parentElement.getBoundingClientRect();
14811
- return { width, height, top, left };
14812
- } else if (!size && typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) {
15645
+ function useTextures(selector) {
15646
+ const store = textureStaging.useStore();
15647
+ const registry = React.useMemo(() => {
15648
+ const getState = store.getState;
15649
+ const setState = store.setState;
15650
+ const getOne = (key) => getState().textures.get(key);
14813
15651
  return {
14814
- width: canvas.width,
14815
- height: canvas.height,
14816
- top: 0,
14817
- left: 0
15652
+ get all() {
15653
+ return getState().textures;
15654
+ },
15655
+ get(input) {
15656
+ if (typeof input === "string") return getOne(input);
15657
+ if (Array.isArray(input)) return input.map(getOne);
15658
+ const out = {};
15659
+ for (const name in input) out[name] = getOne(input[name]);
15660
+ return out;
15661
+ },
15662
+ has: (key) => getState().textures.has(key),
15663
+ add(keyOrRecord, texture) {
15664
+ setState((state) => {
15665
+ const textures = new Map(state.textures);
15666
+ if (typeof keyOrRecord === "string") {
15667
+ textures.set(keyOrRecord, texture);
15668
+ } else {
15669
+ for (const key in keyOrRecord) textures.set(key, keyOrRecord[key]);
15670
+ }
15671
+ return { textures };
15672
+ });
15673
+ },
15674
+ dispose(key, options) {
15675
+ const state = getState();
15676
+ const refs = state._textureRefs.get(key) ?? 0;
15677
+ if (refs > 0 && !options?.force) {
15678
+ console.warn(
15679
+ `[useTextures] "${key}" still has ${refs} active reference(s); skipping dispose. Pass { force: true } to override.`
15680
+ );
15681
+ return false;
15682
+ }
15683
+ state.textures.get(key)?.dispose();
15684
+ setState((s) => {
15685
+ const textures = new Map(s.textures);
15686
+ textures.delete(key);
15687
+ const nextRefs = new Map(s._textureRefs);
15688
+ nextRefs.delete(key);
15689
+ return { textures, _textureRefs: nextRefs };
15690
+ });
15691
+ return true;
15692
+ },
15693
+ disposeAll() {
15694
+ for (const texture of getState().textures.values()) texture.dispose();
15695
+ setState({ textures: /* @__PURE__ */ new Map(), _textureRefs: /* @__PURE__ */ new Map() });
15696
+ }
14818
15697
  };
15698
+ }, [store]);
15699
+ const subscribe = selector ? () => selector(registry) : (state) => state.textures;
15700
+ const selected = textureStaging.useThree(subscribe);
15701
+ return selector ? selected : registry;
15702
+ }
15703
+
15704
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
15705
+ const support = textureStaging.useThree((s) => s.internal.support);
15706
+ const size = textureStaging.useThree((s) => s.size);
15707
+ let width;
15708
+ let height;
15709
+ let opts;
15710
+ if (typeof widthOrOptions === "object") {
15711
+ opts = widthOrOptions;
15712
+ } else if (typeof widthOrOptions === "number") {
15713
+ width = widthOrOptions;
15714
+ if (typeof heightOrOptions === "object") {
15715
+ height = widthOrOptions;
15716
+ opts = heightOrOptions;
15717
+ } else if (typeof heightOrOptions === "number") {
15718
+ height = heightOrOptions;
15719
+ opts = options;
15720
+ } else {
15721
+ height = widthOrOptions;
15722
+ }
14819
15723
  }
14820
- return { width: 0, height: 0, top: 0, left: 0, ...size };
15724
+ return React.useMemo(() => {
15725
+ const w = width ?? size.width;
15726
+ const h = height ?? size.height;
15727
+ const Target = support.RenderTarget;
15728
+ return new Target(w, h, opts);
15729
+ }, [width, height, size.width, size.height, opts, support]);
14821
15730
  }
14822
- function createRoot(canvas, provider) {
14823
- const prevRoot = _roots.get(canvas);
14824
- const prevFiber = prevRoot?.fiber;
14825
- const prevStore = prevRoot?.store;
14826
- if (prevRoot) console.warn("R3F.createRoot should only be called once!");
14827
- const logRecoverableError = typeof reportError === "function" ? (
14828
- // In modern browsers, reportError will dispatch an error event,
14829
- // emulating an uncaught JavaScript error.
14830
- reportError
14831
- ) : (
14832
- // In older browsers and test environments, fallback to console.error.
14833
- console.error
14834
- );
14835
- const store = prevStore || createStore(invalidate, advance);
14836
- const fiber = prevFiber || reconciler.createContainer(
14837
- store,
14838
- // container
14839
- t,
14840
- // tag
14841
- null,
14842
- // hydration callbacks
14843
- false,
14844
- // isStrictMode
14845
- null,
14846
- // concurrentUpdatesByDefaultOverride
14847
- "",
14848
- // identifierPrefix
14849
- logRecoverableError,
14850
- // onUncaughtError
14851
- logRecoverableError,
14852
- // onCaughtError
14853
- logRecoverableError,
14854
- // onRecoverableError
14855
- null
14856
- // transitionCallbacks
14857
- );
14858
- if (!prevRoot) _roots.set(canvas, { fiber, store });
14859
- let onCreated;
14860
- let lastCamera;
14861
- const lastConfiguredProps = {};
14862
- let configured = false;
14863
- let pending = null;
14864
- let rendererSetup = null;
14865
- return {
14866
- async configure(props = {}) {
14867
- let resolve;
14868
- pending = new Promise((_resolve) => resolve = _resolve);
14869
- const {
14870
- id: canvasId,
14871
- primaryCanvas,
14872
- scheduler: schedulerConfig,
14873
- gl: glConfig,
14874
- renderer: rendererConfig,
14875
- size: propsSize,
14876
- scene: sceneOptions,
14877
- events,
14878
- onCreated: onCreatedCallback,
14879
- shadows = false,
14880
- orthographic = false,
14881
- frameloop = "always",
14882
- dpr = [1, 2],
14883
- performance,
14884
- raycaster: raycastOptions,
14885
- camera: cameraOptions,
14886
- onPointerMissed,
14887
- onDragOverMissed,
14888
- onDropMissed,
14889
- autoUpdateFrustum = true,
14890
- occlusion = false,
14891
- _sizeProps,
14892
- forceEven
14893
- } = props;
14894
- const requestedTextureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || void 0;
14895
- let state = store.getState();
14896
- const { webgl, webgpu } = provider;
14897
- const defaultGLProps = {
14898
- canvas,
14899
- powerPreference: "high-performance",
14900
- antialias: true,
14901
- alpha: true
14902
- };
14903
- const defaultGPUProps = {
14904
- canvas,
14905
- antialias: true
14906
- };
14907
- if (glConfig && !webgl) {
14908
- throw new Error(
14909
- "WebGLRenderer (gl prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/legacy instead."
14910
- );
14911
- }
14912
- if (rendererConfig && !webgpu) {
14913
- throw new Error(
14914
- "WebGPURenderer (renderer prop) is not available on this entry. Use @react-three/fiber or @react-three/fiber/webgpu instead."
14915
- );
14916
- }
14917
- const wantsGL = !!webgl && (state.isLegacy || !!glConfig || !webgpu || !rendererConfig);
14918
- if (glConfig && rendererConfig) {
14919
- throw new Error("Cannot use both gl and renderer props at the same time");
14920
- }
14921
- if (webgl && webgpu && !state.isLegacy && wantsGL) {
14922
- extensions.notifyDepreciated({
14923
- heading: "WebGlRenderer Usage",
14924
- body: "WebGlRenderer usage is deprecated in favor of WebGPU. Import from /legacy directly or upgrade to WebGPU.",
14925
- link: "https://docs.pmnd.rs/react-three-fiber/api/renderer"
14926
- });
14927
- }
14928
- let renderer = state.internal.actualRenderer;
14929
- if (primaryCanvas && !webgpu) {
14930
- throw new Error(
14931
- "The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
14932
- );
14933
- }
14934
- if (primaryCanvas && wantsGL) {
14935
- throw new Error(
14936
- "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
14937
- );
14938
- }
14939
- if (!state.internal.actualRenderer) {
14940
- if (!rendererSetup) {
14941
- rendererSetup = (async () => {
14942
- const support = wantsGL ? await webgl() : await webgpu();
14943
- registerThree(support.three);
14944
- state.internal.support = support;
14945
- ensureStoreThreeObjects(store);
14946
- if (support.kind === "webgl") {
14947
- renderer = await resolveRenderer(glConfig, defaultGLProps, support.Renderer);
14948
- state.internal.actualRenderer = renderer;
14949
- state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14950
- } else if (primaryCanvas) {
14951
- const primary = await waitForPrimary(primaryCanvas);
14952
- renderer = primary.renderer;
14953
- state.internal.actualRenderer = renderer;
14954
- const canvasTarget = new support.CanvasTarget(canvas);
14955
- primary.store.setState((prev) => ({
14956
- internal: { ...prev.internal, isMultiCanvas: true }
14957
- }));
14958
- state.set((prev) => ({
14959
- webGPUSupported: primary.store.getState().webGPUSupported,
14960
- renderer,
14961
- primaryStore: primary.store,
14962
- internal: {
14963
- ...prev.internal,
14964
- canvasTarget,
14965
- isMultiCanvas: true,
14966
- isSecondary: true,
14967
- targetId: primaryCanvas
14968
- }
14969
- }));
14970
- } else {
14971
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer);
14972
- if (!renderer.hasInitialized?.()) {
14973
- const size2 = computeInitialSize(canvas, propsSize);
14974
- if (size2.width > 0 && size2.height > 0) {
14975
- renderer.setPixelRatio(calculateDpr(dpr));
14976
- renderer.setSize(size2.width, size2.height, false);
14977
- }
14978
- await renderer.init();
14979
- }
14980
- const backend = renderer.backend;
14981
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14982
- state.internal.actualRenderer = renderer;
14983
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14984
- if (canvasId && !state.internal.isSecondary) {
14985
- const canvasTarget = renderer.getCanvasTarget?.();
14986
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14987
- state.set((prev) => ({
14988
- internal: {
14989
- ...prev.internal,
14990
- canvasTarget,
14991
- unregisterPrimary
14992
- }
14993
- }));
14994
- }
14995
- }
14996
- })().catch((err) => {
14997
- rendererSetup = null;
14998
- throw err;
14999
- });
15000
- }
15001
- await rendererSetup;
15002
- state = store.getState();
15003
- renderer = state.internal.actualRenderer;
15731
+
15732
+ const presetsObj = {
15733
+ apartment: "lebombo_1k.hdr",
15734
+ city: "potsdamer_platz_1k.hdr",
15735
+ dawn: "kiara_1_dawn_1k.hdr",
15736
+ forest: "forest_slope_1k.hdr",
15737
+ lobby: "st_fagans_interior_1k.hdr",
15738
+ night: "dikhololo_night_1k.hdr",
15739
+ park: "rooitou_park_1k.hdr",
15740
+ studio: "studio_small_03_1k.hdr",
15741
+ sunset: "venice_sunset_1k.hdr",
15742
+ warehouse: "empty_warehouse_01_1k.hdr"
15743
+ };
15744
+
15745
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
15746
+ const isArray = (arr) => Array.isArray(arr);
15747
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
15748
+ const loadedLoaders = /* @__PURE__ */ new Map();
15749
+ const pendingLoaders = /* @__PURE__ */ new Map();
15750
+ function loadLoader(format) {
15751
+ let pending = pendingLoaders.get(format);
15752
+ if (!pending) {
15753
+ pending = importLoader(format).then(
15754
+ (loader) => {
15755
+ loadedLoaders.set(format, loader);
15756
+ return loader;
15757
+ },
15758
+ (error) => {
15759
+ pendingLoaders.delete(format);
15760
+ throw error;
15004
15761
  }
15762
+ );
15763
+ pendingLoaders.set(format, pending);
15764
+ }
15765
+ return pending;
15766
+ }
15767
+ async function importLoader(format) {
15768
+ let loader;
15769
+ switch (format) {
15770
+ case "cube":
15771
+ loader = getThree().CubeTextureLoader;
15772
+ break;
15773
+ case "hdr-cube":
15774
+ loader = (await import('three/examples/jsm/loaders/HDRCubeTextureLoader.js')).HDRCubeTextureLoader;
15775
+ break;
15776
+ case "hdr":
15777
+ loader = (await import('three/examples/jsm/loaders/HDRLoader.js')).HDRLoader;
15778
+ break;
15779
+ case "exr":
15780
+ loader = (await import('three/examples/jsm/loaders/EXRLoader.js')).EXRLoader;
15781
+ break;
15782
+ case "jpg":
15783
+ loader = createJpegLoader((await import('three/examples/jsm/loaders/UltraHDRLoader.js')).UltraHDRLoader);
15784
+ break;
15785
+ case "ldr":
15786
+ loader = getThree().TextureLoader;
15787
+ break;
15788
+ }
15789
+ return loader;
15790
+ }
15791
+ function createJpegLoader(UltraHDR) {
15792
+ return class JpegEnvironmentLoader extends UltraHDR {
15793
+ // @ts-expect-error A plain JPEG resolves to a Texture, not the DataTexture UltraHDRLoader declares
15794
+ load(url, onLoad, onProgress, onError) {
15005
15795
  const three = getThree();
15006
- let raycaster = state.raycaster;
15007
- if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
15008
- const { params, ...options } = raycastOptions || {};
15009
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
15010
- if (!is.equ(params, raycaster.params, shallowLoose)) {
15011
- applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15012
- }
15013
- let tempCamera = state.camera;
15014
- if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
15015
- lastCamera = cameraOptions;
15016
- const isCamera = cameraOptions?.isCamera;
15017
- const camera = isCamera ? cameraOptions : orthographic ? new three.OrthographicCamera(0, 0, 0, 0, 0.1, 1e3) : new three.PerspectiveCamera(50, 0, 0.1, 1e3);
15018
- if (!isCamera) {
15019
- camera.position.z = 5;
15020
- if (cameraOptions) {
15021
- applyProps(camera, cameraOptions);
15022
- if (!camera.manual) {
15023
- const projectionProps = ["aspect", "left", "right", "bottom", "top"];
15024
- if (projectionProps.some((prop) => prop in cameraOptions)) {
15025
- camera.manual = true;
15026
- camera.updateProjectionMatrix();
15027
- }
15028
- }
15029
- }
15030
- if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
15031
- }
15032
- state.set({ camera });
15033
- tempCamera = camera;
15034
- raycaster.camera = camera;
15035
- }
15036
- if (!state.scene) {
15037
- let scene;
15038
- if (sceneOptions?.isScene) {
15039
- scene = sceneOptions;
15040
- prepare(scene, store, "", {});
15041
- } else {
15042
- scene = new three.Scene();
15043
- prepare(scene, store, "", {});
15044
- if (sceneOptions) applyProps(scene, sceneOptions);
15045
- }
15046
- state.set((prev) => ({
15047
- scene,
15048
- rootScene: scene,
15049
- internal: { ...prev.internal, container: scene }
15050
- }));
15051
- const camera = tempCamera;
15052
- if (camera && !camera.parent) scene.add(camera);
15053
- }
15054
- if (events && !state.events.handlers) {
15055
- state.set({ events: events(store) });
15056
- let wasEnabled = true;
15057
- store.subscribe((state2) => {
15058
- const { enabled } = state2.events;
15059
- if (enabled && !wasEnabled) {
15060
- state2.events.update?.();
15061
- }
15062
- wasEnabled = enabled;
15063
- });
15064
- }
15065
- if (_sizeProps !== void 0) {
15066
- state.set({ _sizeProps });
15067
- }
15068
- if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15069
- state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15070
- }
15071
- const size = computeInitialSize(canvas, propsSize);
15072
- if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15073
- const wasImperative = state._sizeImperative;
15074
- state.setSize(size.width, size.height, size.top, size.left);
15075
- if (!wasImperative) state.set({ _sizeImperative: false });
15076
- }
15077
- if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
15078
- state.setDpr(dpr);
15079
- lastConfiguredProps.dpr = dpr;
15080
- }
15081
- if (frameloop !== void 0 && frameloop !== lastConfiguredProps.frameloop) {
15082
- state.setFrameloop(frameloop);
15083
- lastConfiguredProps.frameloop = frameloop;
15084
- }
15085
- if (!state.onPointerMissed) state.set({ onPointerMissed });
15086
- if (!state.onDragOverMissed) state.set({ onDragOverMissed });
15087
- if (!state.onDropMissed) state.set({ onDropMissed });
15088
- if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
15089
- if (occlusion && !state.internal.occlusionEnabled) {
15090
- enableOcclusion(store);
15091
- }
15092
- if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
15093
- state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
15094
- lastConfiguredProps.performance = performance;
15095
- }
15096
- if (!state.xr) {
15097
- const handleXRFrame = (timestamp, _frame) => {
15098
- const state2 = store.getState();
15099
- if (state2.frameloop === "never") return;
15100
- advance(timestamp, true, state2);
15101
- };
15102
- const actualRenderer = state.internal.actualRenderer;
15103
- const handleSessionChange = () => {
15104
- const state2 = store.getState();
15105
- const renderer2 = state2.internal.actualRenderer;
15106
- actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
15107
- renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
15108
- if (!renderer2.xr.isPresenting) invalidate(state2);
15109
- };
15110
- const xr = {
15111
- connect() {
15112
- const { gl, renderer: renderer2 } = store.getState();
15113
- const xrManager = (renderer2 || gl).xr;
15114
- xrManager.addEventListener("sessionstart", handleSessionChange);
15115
- xrManager.addEventListener("sessionend", handleSessionChange);
15116
- },
15117
- disconnect() {
15118
- const { gl, renderer: renderer2 } = store.getState();
15119
- const xrManager = (renderer2 || gl).xr;
15120
- xrManager.removeEventListener("sessionstart", handleSessionChange);
15121
- xrManager.removeEventListener("sessionend", handleSessionChange);
15122
- }
15123
- };
15124
- if (typeof renderer.xr?.addEventListener === "function") xr.connect();
15125
- state.set({ xr });
15126
- }
15127
- if (renderer.shadowMap && !is.equ(shadows, lastConfiguredProps.shadows, shallowLoose)) {
15128
- lastConfiguredProps.shadows = shadows;
15129
- const oldEnabled = renderer.shadowMap.enabled;
15130
- const oldType = renderer.shadowMap.type;
15131
- renderer.shadowMap.enabled = !!shadows;
15132
- if (is.boo(shadows)) {
15133
- renderer.shadowMap.type = three.PCFShadowMap;
15134
- } else if (is.str(shadows)) {
15135
- if (shadows === "soft") {
15136
- extensions.notifyDepreciated({
15137
- heading: 'shadows="soft" is deprecated',
15138
- body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15139
- link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15796
+ const file = new three.FileLoader(this.manager);
15797
+ file.setResponseType("arraybuffer");
15798
+ file.setRequestHeader(this.requestHeader);
15799
+ file.setPath(this.path);
15800
+ file.setWithCredentials(this.withCredentials);
15801
+ file.load(
15802
+ url,
15803
+ (buffer) => {
15804
+ try {
15805
+ this.parse(buffer, (texData) => {
15806
+ const { data } = texData;
15807
+ const texture = new three.DataTexture(data, texData.width, texData.height, three.RGBAFormat, texData.type);
15808
+ texture.minFilter = three.LinearMipMapLinearFilter;
15809
+ texture.magFilter = three.LinearFilter;
15810
+ texture.generateMipmaps = true;
15811
+ texture.flipY = true;
15812
+ texture.needsUpdate = true;
15813
+ onLoad?.(texture);
15140
15814
  });
15141
- }
15142
- const types = {
15143
- basic: three.BasicShadowMap,
15144
- percentage: three.PCFShadowMap,
15145
- soft: three.PCFShadowMap,
15146
- variance: three.VSMShadowMap
15147
- };
15148
- renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
15149
- } else if (is.obj(shadows)) {
15150
- Object.assign(renderer.shadowMap, shadows);
15151
- }
15152
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
15153
- renderer.shadowMap.needsUpdate = true;
15154
- }
15155
- }
15156
- if (!configured) {
15157
- renderer.outputColorSpace = three.SRGBColorSpace;
15158
- renderer.toneMapping = three.ACESFilmicToneMapping;
15159
- }
15160
- const textureColorSpace = requestedTextureColorSpace ?? three.SRGBColorSpace;
15161
- if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
15162
- if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15163
- lastConfiguredProps.textureColorSpace = textureColorSpace;
15164
- }
15165
- const r3fProps = ["textureColorSpace"];
15166
- const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15167
- const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15168
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15169
- const glProps = {};
15170
- for (const key in glConfig) {
15171
- if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15172
- }
15173
- applyProps(renderer, glProps);
15174
- }
15175
- if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15176
- const currentRenderer = state.renderer;
15177
- if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15178
- const rendererProps = {};
15179
- for (const key in rendererConfig) {
15180
- if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15181
- }
15182
- applyProps(currentRenderer, rendererProps);
15183
- }
15184
- }
15185
- const scheduler$1 = scheduler.getScheduler();
15186
- const rootId = state.internal.rootId;
15187
- if (!rootId) {
15188
- const newRootId = canvasId || scheduler$1.generateRootId();
15189
- const unregisterRoot = scheduler$1.registerRoot(newRootId, {
15190
- getState: () => store.getState(),
15191
- onError: (err) => store.getState().setError(err),
15192
- frameloop: store.getState().frameloop,
15193
- before: schedulerConfig?.before,
15194
- after: schedulerConfig?.after,
15195
- order: schedulerConfig?.order
15196
- });
15197
- const unregisterCanvasTarget = scheduler$1.register(
15198
- () => {
15199
- const state2 = store.getState();
15200
- const canvasTarget = state2.internal.canvasTarget;
15201
- if (canvasTarget) {
15202
- const renderer2 = state2.internal.actualRenderer;
15203
- if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
15204
- if (state2.internal.canvasTargetSizeDirty) {
15205
- state2.internal.canvasTargetSizeDirty = false;
15206
- renderer2.backend?.updateSize?.();
15815
+ } catch {
15816
+ const objectURL = URL.createObjectURL(new Blob([buffer], { type: "image/jpeg" }));
15817
+ const settle = () => URL.revokeObjectURL(objectURL);
15818
+ new three.ImageLoader().load(
15819
+ objectURL,
15820
+ (image) => {
15821
+ settle();
15822
+ const texture = new three.Texture(image);
15823
+ texture.needsUpdate = true;
15824
+ onLoad?.(texture);
15825
+ },
15826
+ void 0,
15827
+ (error) => {
15828
+ settle();
15829
+ onError?.(error);
15207
15830
  }
15208
- }
15209
- },
15210
- {
15211
- id: `${newRootId}_canvasTarget`,
15212
- rootId: newRootId,
15213
- phase: "start",
15214
- system: true
15215
- }
15216
- );
15217
- const unregisterEventsFlush = scheduler$1.register(
15218
- () => {
15219
- const state2 = store.getState();
15220
- state2.events.flush?.();
15221
- },
15222
- {
15223
- id: `${newRootId}_events`,
15224
- rootId: newRootId,
15225
- phase: "input",
15226
- system: true
15227
- }
15228
- );
15229
- const unregisterFrustum = scheduler$1.register(
15230
- () => {
15231
- const state2 = store.getState();
15232
- if (state2.autoUpdateFrustum && state2.camera) {
15233
- updateFrustum(state2.camera, state2.frustum);
15234
- }
15235
- },
15236
- {
15237
- id: `${newRootId}_frustum`,
15238
- rootId: newRootId,
15239
- before: "render",
15240
- system: true
15241
- }
15242
- );
15243
- const unregisterVisibility = scheduler$1.register(
15244
- () => {
15245
- const state2 = store.getState();
15246
- checkVisibility(state2);
15247
- },
15248
- {
15249
- id: `${newRootId}_visibility`,
15250
- rootId: newRootId,
15251
- before: "render",
15252
- system: true,
15253
- after: `${newRootId}_frustum`
15254
- }
15255
- );
15256
- const unregisterRender = scheduler$1.register(
15257
- () => {
15258
- const state2 = store.getState();
15259
- const renderer2 = state2.internal.actualRenderer;
15260
- const userHandlesRender = scheduler$1.hasUserJobsInPhase("render", newRootId);
15261
- if (userHandlesRender || state2.internal.priority) return;
15262
- try {
15263
- const renderOverride = state2.internal.renderOverride;
15264
- if (renderOverride) renderOverride();
15265
- else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
15266
- } catch (error) {
15267
- state2.setError(error instanceof Error ? error : new Error(String(error)));
15268
- }
15269
- },
15270
- {
15271
- // Use canvas ID directly as job ID if available, otherwise use generated rootId
15272
- id: canvasId || `${newRootId}_render`,
15273
- rootId: newRootId,
15274
- phase: "render",
15275
- system: true,
15276
- // Internal flag: this is a system job, not user-controlled
15277
- // FPS throttles only the default render job; Canvas ordering belongs to the root.
15278
- ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
15279
- }
15280
- );
15281
- state.set((state2) => ({
15282
- internal: {
15283
- ...state2.internal,
15284
- rootId: newRootId,
15285
- unregisterRoot: () => {
15286
- unregisterRoot();
15287
- unregisterCanvasTarget();
15288
- unregisterEventsFlush();
15289
- unregisterFrustum();
15290
- unregisterVisibility();
15291
- unregisterRender();
15292
- },
15293
- scheduler: scheduler$1
15831
+ );
15294
15832
  }
15295
- }));
15296
- } else {
15297
- const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
15298
- if (constraintsChanged) {
15299
- scheduler$1.setRootConstraints(rootId, {
15300
- before: schedulerConfig?.before,
15301
- after: schedulerConfig?.after
15302
- });
15303
- }
15304
- if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
15305
- scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
15306
- }
15307
- }
15308
- lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
15309
- lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
15310
- lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
15311
- extensions.attachRootExtensions(store);
15312
- onCreated = onCreatedCallback;
15313
- configured = true;
15314
- resolve();
15315
- return this;
15316
- },
15317
- render(children) {
15318
- if (!configured && !pending) this.configure();
15319
- pending.then(() => {
15320
- reconciler.updateContainer(
15321
- /* @__PURE__ */ jsxRuntime.jsx(Provider, { store, children, onCreated, rootElement: canvas }),
15322
- fiber,
15323
- null,
15324
- () => void 0
15325
- );
15326
- });
15327
- return store;
15328
- },
15329
- unmount() {
15330
- unmountComponentAtNode(canvas);
15833
+ },
15834
+ onProgress,
15835
+ onError
15836
+ );
15331
15837
  }
15332
15838
  };
15333
15839
  }
15334
- function Provider({
15335
- store,
15336
- children,
15337
- onCreated,
15338
- rootElement
15339
- }) {
15340
- extensions.useIsomorphicLayoutEffect(() => {
15341
- const state = store.getState();
15342
- state.set((state2) => ({ internal: { ...state2.internal, active: true } }));
15343
- if (onCreated) onCreated(state);
15344
- if (!store.getState().events.connected) state.events.connect?.(rootElement);
15345
- }, []);
15346
- return /* @__PURE__ */ jsxRuntime.jsx(extensions.context.Provider, { value: store, children });
15840
+ const LOADER_KEY = Symbol("r3f-environment-loader");
15841
+ function useEnvironmentLoader(format) {
15842
+ return suspendReact.suspend(() => loadLoader(format), [LOADER_KEY, format]);
15347
15843
  }
15348
- function unmountComponentAtNode(canvas, callback) {
15349
- const root = _roots.get(canvas);
15350
- const fiber = root?.fiber;
15351
- if (fiber) {
15352
- const state = root?.store.getState();
15353
- if (state) {
15354
- state.internal.active = false;
15355
- const unregisterRoot = state.internal.unregisterRoot;
15356
- if (unregisterRoot) {
15357
- unregisterRoot();
15358
- state.internal.unregisterRoot = void 0;
15359
- }
15844
+ function useEnvironment({
15845
+ files = defaultFiles,
15846
+ path = "",
15847
+ preset = void 0,
15848
+ colorSpace = void 0,
15849
+ extensions
15850
+ } = {}) {
15851
+ if (preset) {
15852
+ validatePreset(preset);
15853
+ files = presetsObj[preset];
15854
+ path = CUBEMAP_ROOT;
15855
+ }
15856
+ const multiFile = isArray(files);
15857
+ const { format, isCubemap } = getFormat(files);
15858
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15859
+ const loader = useEnvironmentLoader(format);
15860
+ const loaderResult = useLoader(
15861
+ loader,
15862
+ multiFile ? [files] : files,
15863
+ (loader2) => {
15864
+ loader2.setPath?.(path);
15865
+ if (extensions) extensions(loader2);
15360
15866
  }
15361
- reconciler.updateContainer(null, fiber, null, () => {
15362
- if (root?.store) extensions.detachRootExtensions(root.store);
15363
- if (state) {
15364
- setTimeout(() => {
15365
- try {
15366
- const renderer = state.internal.actualRenderer;
15367
- const unregisterPrimary = state.internal.unregisterPrimary;
15368
- if (unregisterPrimary) unregisterPrimary();
15369
- const canvasTarget = state.internal.canvasTarget;
15370
- if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
15371
- state.events.disconnect?.();
15372
- cleanupHelperGroup(root.store);
15373
- if (state.isLegacy && renderer) {
15374
- ;
15375
- renderer.renderLists?.dispose?.();
15376
- renderer.forceContextLoss?.();
15377
- }
15378
- if (!state.internal.isSecondary) {
15379
- if (renderer?.xr) state.xr.disconnect();
15380
- }
15381
- dispose(state.scene);
15382
- _roots.delete(canvas);
15383
- if (callback) callback(canvas);
15384
- } catch (error) {
15385
- console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
15386
- }
15387
- }, 500);
15388
- }
15867
+ );
15868
+ const texture = multiFile ? (
15869
+ // @ts-ignore
15870
+ loaderResult[0]
15871
+ ) : loaderResult;
15872
+ const three = getThree();
15873
+ texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
15874
+ const hdr = format === "hdr-cube" || texture.isDataTexture === true;
15875
+ texture.colorSpace = colorSpace ?? (hdr ? "srgb-linear" : "srgb");
15876
+ return texture;
15877
+ }
15878
+ const preloadDefaultOptions = {
15879
+ files: defaultFiles,
15880
+ path: "",
15881
+ preset: void 0,
15882
+ extensions: void 0
15883
+ };
15884
+ useEnvironment.preload = (preloadOptions) => {
15885
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
15886
+ let { files, path = "" } = options;
15887
+ const { preset, extensions } = options;
15888
+ if (preset) {
15889
+ validatePreset(preset);
15890
+ files = presetsObj[preset];
15891
+ path = CUBEMAP_ROOT;
15892
+ }
15893
+ const { format } = getFormat(files);
15894
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15895
+ const input = isArray(files) ? [files] : files;
15896
+ loadLoader(format).then((loader) => {
15897
+ useLoader.preload(loader, input, (loader2) => {
15898
+ loader2.setPath?.(path);
15899
+ if (extensions) extensions(loader2);
15900
+ });
15901
+ });
15902
+ };
15903
+ const clearDefaultOptins = {
15904
+ files: defaultFiles,
15905
+ preset: void 0
15906
+ };
15907
+ useEnvironment.clear = (clearOptions) => {
15908
+ const options = { ...clearDefaultOptins, ...clearOptions };
15909
+ let { files } = options;
15910
+ const { preset } = options;
15911
+ if (preset) {
15912
+ validatePreset(preset);
15913
+ files = presetsObj[preset];
15914
+ }
15915
+ const { format } = getFormat(files);
15916
+ if (!format) throw new Error("useEnvironment: Unrecognized file extension: " + files);
15917
+ const input = isArray(files) ? [files] : files;
15918
+ const clear = (loader2) => useLoader.clear(loader2, input);
15919
+ const loader = loadedLoaders.get(format);
15920
+ if (loader) {
15921
+ clear(loader);
15922
+ } else {
15923
+ const pending = pendingLoaders.get(format);
15924
+ if (pending) void pending.then(clear, () => {
15389
15925
  });
15390
15926
  }
15927
+ };
15928
+ function validatePreset(preset) {
15929
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
15930
+ }
15931
+ function getFormat(files) {
15932
+ const isCubemap = isArray(files) && files.length === 6;
15933
+ const firstEntry = isArray(files) ? files[0] : files;
15934
+ const firstExtension = firstEntry.split(/[?#]/)[0].split(".").pop()?.toLowerCase();
15935
+ let format;
15936
+ if (isCubemap) format = firstExtension === "hdr" ? "hdr-cube" : "cube";
15937
+ else if (firstEntry.startsWith("data:application/exr")) format = "exr";
15938
+ else if (firstEntry.startsWith("data:application/hdr")) format = "hdr";
15939
+ else if (firstEntry.startsWith("data:image/jpeg")) format = "jpg";
15940
+ else if (firstExtension === "hdr" || firstExtension === "exr") format = firstExtension;
15941
+ else if (firstExtension === "jpg" || firstExtension === "jpeg") format = "jpg";
15942
+ else if (/^data:image\/(png|webp|avif|gif)[;,]/.test(firstEntry)) format = "ldr";
15943
+ else if (["png", "webp", "avif", "gif"].includes(firstExtension)) format = "ldr";
15944
+ return { format, isCubemap };
15945
+ }
15946
+
15947
+ function useInstanceHandle(ref) {
15948
+ const instance = React.useRef(null);
15949
+ React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
15950
+ return instance;
15951
+ }
15952
+ function useGraph(object) {
15953
+ return React.useMemo(() => buildGraph(object), [object]);
15391
15954
  }
15955
+
15392
15956
  function createPortal(children, container, state) {
15393
15957
  return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
15394
15958
  }
@@ -15419,30 +15983,28 @@ function Portal({ children, container, state }) {
15419
15983
  }
15420
15984
  function PortalInner({ state = {}, children, container }) {
15421
15985
  const { events, size, injectScene = true, ...rest } = state;
15422
- const previousRoot = extensions.useStore();
15986
+ const previousRoot = textureStaging.useStore();
15423
15987
  const [raycaster] = React.useState(() => new (getThree()).Raycaster());
15424
15988
  const [pointer] = React.useState(() => new (getThree()).Vector2());
15425
15989
  const [portalScene] = React.useState(() => {
15426
15990
  if ("isScene" in container && container.isScene) return container;
15427
15991
  if (!injectScene) return container;
15428
- const scene = new (getThree()).Scene();
15429
- container.add(scene);
15430
- return scene;
15992
+ return new (getThree()).Scene();
15431
15993
  });
15432
- extensions.useIsomorphicLayoutEffect(() => {
15994
+ textureStaging.useIsomorphicLayoutEffect(() => {
15995
+ if (portalScene === container || !injectScene) return;
15996
+ container.add(portalScene);
15433
15997
  return () => {
15434
- if (portalScene !== container && injectScene) {
15435
- container.remove(portalScene);
15436
- dispose(portalScene);
15437
- }
15998
+ container.remove(portalScene);
15438
15999
  };
15439
16000
  }, [portalScene, container, injectScene]);
15440
16001
  const lastRootState = React.useRef(null);
15441
- const inject = extensions.useMutableCallback((rootState, injectState) => {
16002
+ const inject = textureStaging.useMutableCallback((rootState, injectState) => {
15442
16003
  const followed = {};
15443
16004
  const lastRoot = lastRootState.current;
15444
16005
  if (lastRoot) {
15445
16006
  for (const key in rootState) {
16007
+ if (textureStaging.isAccessor(rootState, key)) continue;
15446
16008
  const field = key;
15447
16009
  const changed = rootState[field] !== lastRoot[field];
15448
16010
  const inherited = !(key in rest) && injectState[field] === lastRoot[field];
@@ -15459,11 +16021,7 @@ function PortalInner({ state = {}, children, container }) {
15459
16021
  if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
15460
16022
  }
15461
16023
  const set = injectState.set;
15462
- return {
15463
- // The intersect consists of the previous root state
15464
- ...rootState,
15465
- ...injectState,
15466
- ...followed,
16024
+ return Object.assign(textureStaging.cloneState(rootState, injectState, followed), {
15467
16025
  // Portals have their own scene - always a real THREE.Scene (injected if needed)
15468
16026
  scene: portalScene,
15469
16027
  // rootScene always points to the actual THREE.Scene, even inside portals
@@ -15478,18 +16036,24 @@ function PortalInner({ state = {}, children, container }) {
15478
16036
  size: resolvedSize,
15479
16037
  viewport: { ...rootState.viewport, ...viewport },
15480
16038
  // Layers are allowed to override events
15481
- setEvents: (events2) => set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
15482
- // Container for child attachment - the portalScene (injected or container itself)
15483
- internal: { ...rootState.internal, ...injectState.internal, container: portalScene }
15484
- };
16039
+ setEvents: (events2) => set((state2) => ({ events: { ...state2.events, ...events2 } })),
16040
+ // Container for child attachment - the portalScene (injected or container itself). A portal
16041
+ // renders with its parent's renderer, which `renderer` / `gl` read from `actualRenderer`
16042
+ internal: {
16043
+ ...rootState.internal,
16044
+ ...injectState.internal,
16045
+ actualRenderer: rootState.internal.actualRenderer,
16046
+ container: portalScene
16047
+ }
16048
+ });
15485
16049
  });
15486
16050
  const usePortalStore = React.useMemo(() => {
15487
- const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
16051
+ const store = traditional.createWithEqualityFn(textureStaging.keepAccessors((set, get) => ({ ...rest, set, get })));
15488
16052
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
15489
16053
  onMutate(previousRoot.getState());
15490
16054
  return store;
15491
16055
  }, [previousRoot, container]);
15492
- extensions.useIsomorphicLayoutEffect(() => {
16056
+ textureStaging.useIsomorphicLayoutEffect(() => {
15493
16057
  const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15494
16058
  const unsubscribe = previousRoot.subscribe(onMutate);
15495
16059
  return unsubscribe;
@@ -15497,15 +16061,12 @@ function PortalInner({ state = {}, children, container }) {
15497
16061
  return (
15498
16062
  // @ts-ignore, reconciler types are not maintained
15499
16063
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
15500
- /* @__PURE__ */ jsxRuntime.jsx(extensions.context.Provider, { value: usePortalStore, children }),
16064
+ /* @__PURE__ */ jsxRuntime.jsx(textureStaging.context.Provider, { value: usePortalStore, children }),
15501
16065
  usePortalStore,
15502
16066
  null
15503
16067
  ) })
15504
16068
  );
15505
16069
  }
15506
- function flushSync(fn) {
15507
- return reconciler.flushSyncFromReconciler(fn);
15508
- }
15509
16070
 
15510
16071
  const isRef = (obj) => obj.current && obj.current.isScene;
15511
16072
  const resolveScene = (scene) => isRef(scene) ? scene.current : scene;
@@ -15538,7 +16099,7 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
15538
16099
  };
15539
16100
  }
15540
16101
  function EnvironmentMap({ scene, background = false, map, ...config }) {
15541
- const defaultScene = extensions.useThree((state) => state.scene);
16102
+ const defaultScene = textureStaging.useThree((state) => state.scene);
15542
16103
  React__namespace.useLayoutEffect(() => {
15543
16104
  if (map) return setEnvProps(background, scene, defaultScene, map, config);
15544
16105
  });
@@ -15556,7 +16117,7 @@ function EnvironmentCube({
15556
16117
  ...rest
15557
16118
  }) {
15558
16119
  const texture = useEnvironment(rest);
15559
- const defaultScene = extensions.useThree((state) => state.scene);
16120
+ const defaultScene = textureStaging.useThree((state) => state.scene);
15560
16121
  React__namespace.useLayoutEffect(() => {
15561
16122
  return setEnvProps(background, scene, defaultScene, texture, {
15562
16123
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -15591,11 +16152,11 @@ function EnvironmentPortal({
15591
16152
  files,
15592
16153
  path,
15593
16154
  preset = void 0,
15594
- extensions: extensions$1
16155
+ extensions
15595
16156
  }) {
15596
- const renderer = extensions.useThree((state) => state.renderer);
15597
- const support = extensions.useThree((state) => state.internal.support);
15598
- const defaultScene = extensions.useThree((state) => state.scene);
16157
+ const renderer = textureStaging.useThree((state) => state.renderer);
16158
+ const support = textureStaging.useThree((state) => state.internal.support);
16159
+ const defaultScene = textureStaging.useThree((state) => state.scene);
15599
16160
  const camera = React__namespace.useRef(null);
15600
16161
  const [virtualScene] = React__namespace.useState(() => new (getThree()).Scene());
15601
16162
  const fbo = React__namespace.useMemo(() => {
@@ -15639,7 +16200,7 @@ function EnvironmentPortal({
15639
16200
  environmentRotation
15640
16201
  ]);
15641
16202
  let count = 1;
15642
- extensions.useFrame(() => {
16203
+ textureStaging.useFrame(() => {
15643
16204
  if (frames === Infinity || count < frames) {
15644
16205
  const autoClear = renderer.autoClear;
15645
16206
  renderer.autoClear = true;
@@ -15652,7 +16213,7 @@ function EnvironmentPortal({
15652
16213
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15653
16214
  children,
15654
16215
  /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
15655
- files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions: extensions$1 }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions: extensions$1 }) : null
16216
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
15656
16217
  ] }),
15657
16218
  virtualScene
15658
16219
  ) });
@@ -15682,7 +16243,7 @@ function EnvironmentGround(props) {
15682
16243
  ] });
15683
16244
  }
15684
16245
  function EnvironmentColor({ color, scene }) {
15685
- const defaultScene = extensions.useThree((state) => state.scene);
16246
+ const defaultScene = textureStaging.useThree((state) => state.scene);
15686
16247
  React__namespace.useLayoutEffect(() => {
15687
16248
  if (color === void 0) return;
15688
16249
  const target = resolveScene(scene || defaultScene);
@@ -15697,12 +16258,12 @@ function EnvironmentColor({ color, scene }) {
15697
16258
  function EnvironmentDualSource(props) {
15698
16259
  const { backgroundFiles, ...envProps } = props;
15699
16260
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15700
- /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
15701
- /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
16261
+ (envProps.files || envProps.preset) && /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
16262
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, preset: void 0, background: "only" })
15702
16263
  ] });
15703
16264
  }
15704
16265
  function Environment(props) {
15705
- if (props.color && !props.files && !props.preset && !props.map) {
16266
+ if (props.color !== void 0 && !props.files && !props.preset && !props.map) {
15706
16267
  return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
15707
16268
  }
15708
16269
  if (props.backgroundFiles && props.backgroundFiles !== props.files) {
@@ -15712,7 +16273,7 @@ function Environment(props) {
15712
16273
  }
15713
16274
 
15714
16275
  function parseBackground(background) {
15715
- if (!background) return null;
16276
+ if (background === void 0 || background === null || background === "") return null;
15716
16277
  if (typeof background === "object" && !background.isColor) {
15717
16278
  const { backgroundMap, envMap, files, preset, ...rest } = background;
15718
16279
  return {
@@ -15730,7 +16291,7 @@ function parseBackground(background) {
15730
16291
  if (background in presetsObj) {
15731
16292
  return { preset: background, background: true };
15732
16293
  }
15733
- if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
16294
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/|data:)|\.(hdr|exr|jpg|jpeg|png|webp|avif|gif)([?#].*)?$/i.test(background)) {
15734
16295
  return { files: background, background: true };
15735
16296
  }
15736
16297
  return { color: background, background: true };
@@ -15741,19 +16302,6 @@ function parseBackground(background) {
15741
16302
  return null;
15742
16303
  }
15743
16304
 
15744
- function parseRendererConfig(rendererProp) {
15745
- const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15746
- if (isRendererConfig) {
15747
- const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15748
- return {
15749
- primaryCanvas: pc,
15750
- scheduler: sc,
15751
- renderer: Object.keys(rest).length > 0 ? rest : rendererProp
15752
- };
15753
- }
15754
- return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
15755
- }
15756
-
15757
16305
  function CanvasImpl({
15758
16306
  ref,
15759
16307
  children,
@@ -15762,11 +16310,13 @@ function CanvasImpl({
15762
16310
  style,
15763
16311
  id,
15764
16312
  gl,
15765
- renderer: rendererProp,
16313
+ renderer,
16314
+ primary,
16315
+ share,
16316
+ scheduler,
15766
16317
  events = createPointerEvents,
15767
16318
  eventSource,
15768
16319
  eventPrefix,
15769
- shadows,
15770
16320
  orthographic,
15771
16321
  frameloop,
15772
16322
  dpr,
@@ -15788,8 +16338,12 @@ function CanvasImpl({
15788
16338
  provider,
15789
16339
  ...props
15790
16340
  }) {
15791
- const { primaryCanvas, scheduler, renderer } = parseRendererConfig(rendererProp);
15792
- const Bridge = extensions.useBridge();
16341
+ const {
16342
+ shadows: removedShadows,
16343
+ primaryCanvas: removedPrimaryCanvas,
16344
+ ...divProps
16345
+ } = props;
16346
+ const Bridge = textureStaging.useBridge();
15793
16347
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
15794
16348
  const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
15795
16349
  const measureConfig = React__namespace.useMemo(() => {
@@ -15825,119 +16379,131 @@ function CanvasImpl({
15825
16379
  const canvasRef = React__namespace.useRef(null);
15826
16380
  const divRef = React__namespace.useRef(null);
15827
16381
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
15828
- const handlePointerMissed = extensions.useMutableCallback(onPointerMissed);
15829
- const handleDragOverMissed = extensions.useMutableCallback(onDragOverMissed);
15830
- const handleDropMissed = extensions.useMutableCallback(onDropMissed);
16382
+ const handlePointerMissed = textureStaging.useMutableCallback(onPointerMissed);
16383
+ const handleDragOverMissed = textureStaging.useMutableCallback(onDragOverMissed);
16384
+ const handleDropMissed = textureStaging.useMutableCallback(onDropMissed);
15831
16385
  const [block, setBlock] = React__namespace.useState(false);
15832
16386
  const [error, setError] = React__namespace.useState(false);
15833
16387
  const [fallbackVisible, setFallbackVisible] = React__namespace.useState(false);
15834
16388
  if (block) throw block;
15835
16389
  if (error) throw error;
15836
16390
  const root = React__namespace.useRef(null);
15837
- const effectActiveRef = React__namespace.useRef(true);
15838
- const unsubscribeErrorRef = React__namespace.useRef(null);
15839
- extensions.useIsomorphicLayoutEffect(() => {
15840
- effectActiveRef.current = true;
16391
+ const [gate, waitFor] = textureStaging.useGate();
16392
+ const rootState = React__namespace.useRef(null);
16393
+ const eventTarget = () => eventSource ? isRef$1(eventSource) ? eventSource.current : eventSource : divRef.current;
16394
+ const pointerMissed = React__namespace.useCallback(
16395
+ (...args) => handlePointerMissed.current?.(...args),
16396
+ [handlePointerMissed]
16397
+ );
16398
+ const dragOverMissed = React__namespace.useCallback(
16399
+ (...args) => handleDragOverMissed.current?.(...args),
16400
+ [handleDragOverMissed]
16401
+ );
16402
+ const dropMissed = React__namespace.useCallback(
16403
+ (...args) => handleDropMissed.current?.(...args),
16404
+ [handleDropMissed]
16405
+ );
16406
+ textureStaging.useIsomorphicLayoutEffect(() => {
15841
16407
  if (fallbackVisible) return;
15842
16408
  const canvas = canvasRef.current;
15843
16409
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15844
16410
  if (!root.current) {
15845
16411
  root.current = createRoot(canvas, provider);
15846
- extensions.notifyAlpha({
16412
+ textureStaging.notifyAlpha({
15847
16413
  message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
15848
16414
  link: "https://github.com/pmndrs/react-three-fiber/discussions"
15849
16415
  });
15850
- const rootEntry = _roots.get(canvas);
15851
- if (rootEntry?.store) {
15852
- if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
15853
- unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
15854
- if (state.error && effectActiveRef.current) {
15855
- setError(state.error);
15856
- }
15857
- });
15858
- }
15859
16416
  }
15860
- async function run() {
15861
- if (!effectActiveRef.current || !root.current) return;
15862
- const configured = await root.current.configure({
15863
- id,
15864
- primaryCanvas,
15865
- scheduler,
15866
- gl,
15867
- renderer,
15868
- scene,
15869
- events,
15870
- shadows,
15871
- orthographic,
15872
- frameloop,
15873
- dpr,
15874
- performance,
15875
- raycaster,
15876
- camera,
15877
- autoUpdateFrustum,
15878
- occlusion,
15879
- size: effectiveSize,
15880
- // Store size props for reset functionality
15881
- _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15882
- forceEven,
15883
- // Pass mutable reference to onPointerMissed so it's free to update
15884
- onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15885
- onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
15886
- onDropMissed: (...args) => handleDropMissed.current?.(...args),
15887
- onCreated: (state) => {
15888
- state.events.connect?.(
15889
- eventSource ? isRef$1(eventSource) ? eventSource.current : eventSource : divRef.current
15890
- );
15891
- if (eventPrefix) {
15892
- state.setEvents({
15893
- compute: (event, state2) => {
15894
- const x = event[eventPrefix + "X"];
15895
- const y = event[eventPrefix + "Y"];
15896
- state2.pointer.set(x / state2.size.width * 2 - 1, -(y / state2.size.height) * 2 + 1);
15897
- state2.raycaster.setFromCamera(state2.pointer, state2.camera);
15898
- }
15899
- });
15900
- }
15901
- onCreated?.(state);
15902
- }
15903
- }).then(
15904
- () => true,
15905
- // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15906
- // inside <canvas>, which browsers don't display, so surface it as visible DOM
15907
- // instead; with no fallback, rethrow to an external error boundary. (#3757)
15908
- (setupError) => {
15909
- if (effectActiveRef.current) {
15910
- if (fallback != null) setFallbackVisible(true);
15911
- else setError(setupError);
15912
- }
15913
- return false;
16417
+ const current = root.current;
16418
+ current.configure({
16419
+ id,
16420
+ primary,
16421
+ share,
16422
+ scheduler,
16423
+ gl,
16424
+ renderer,
16425
+ scene,
16426
+ events,
16427
+ _primaryToken: primaryToken,
16428
+ ...{ shadows: removedShadows, primaryCanvas: removedPrimaryCanvas },
16429
+ orthographic,
16430
+ frameloop,
16431
+ dpr,
16432
+ performance,
16433
+ raycaster,
16434
+ camera,
16435
+ autoUpdateFrustum,
16436
+ occlusion,
16437
+ size: effectiveSize,
16438
+ // Store size props for reset functionality
16439
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
16440
+ forceEven,
16441
+ // Pass mutable reference to onPointerMissed so it's free to update
16442
+ onPointerMissed: pointerMissed,
16443
+ onDragOverMissed: dragOverMissed,
16444
+ onDropMissed: dropMissed,
16445
+ onCreated: (state) => {
16446
+ rootState.current = state;
16447
+ state.events.connect?.(eventTarget() ?? divRef.current);
16448
+ if (eventPrefix) {
16449
+ state.setEvents({
16450
+ compute: (event, state2) => {
16451
+ const x = event[eventPrefix + "X"];
16452
+ const y = event[eventPrefix + "Y"];
16453
+ state2.pointer.set(x / state2.size.width * 2 - 1, -(y / state2.size.height) * 2 + 1);
16454
+ state2.raycaster.setFromCamera(state2.pointer, state2.camera);
16455
+ }
16456
+ });
15914
16457
  }
15915
- );
15916
- if (!configured || !effectActiveRef.current || !root.current) return;
15917
- root.current.render(
15918
- /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(extensions.ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(extensions.Block, { set: setBlock }), children: [
16458
+ onCreated?.(state);
16459
+ }
16460
+ }).catch((setupError) => {
16461
+ if (root.current !== current) return;
16462
+ if (fallback != null) setFallbackVisible(true);
16463
+ else setError(setupError);
16464
+ });
16465
+ if (current.ready.status === "fulfilled") {
16466
+ current.render(
16467
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(textureStaging.ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(textureStaging.Block, { set: setBlock }), children: [
15919
16468
  backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15920
16469
  children ?? null
15921
16470
  ] }) }) })
15922
16471
  );
16472
+ } else if (current.ready.status === "pending") {
16473
+ waitFor(current.ready);
15923
16474
  }
15924
- run();
15925
16475
  }
15926
- return () => {
15927
- effectActiveRef.current = false;
15928
- if (unsubscribeErrorRef.current) {
15929
- unsubscribeErrorRef.current();
15930
- unsubscribeErrorRef.current = null;
15931
- }
16476
+ });
16477
+ textureStaging.useIsomorphicLayoutEffect(() => {
16478
+ const store = canvasRef.current && _roots.get(canvasRef.current)?.store;
16479
+ if (!store) return;
16480
+ const report = () => {
16481
+ const error2 = store.getState().error;
16482
+ if (error2) setError(error2);
15932
16483
  };
16484
+ report();
16485
+ return store.subscribe(report);
16486
+ });
16487
+ React__namespace.useEffect(() => {
16488
+ const state = rootState.current?.get();
16489
+ const target = eventTarget();
16490
+ if (state && target && state.events.connected !== target) state.events.connect?.(target);
15933
16491
  });
15934
16492
  const insertionMounted = React__namespace.useRef(false);
16493
+ const [primaryToken] = React__namespace.useState(() => ({}));
16494
+ const primaryKey = React__namespace.useRef(null);
15935
16495
  React__namespace.useInsertionEffect(() => {
15936
16496
  insertionMounted.current = true;
16497
+ if (primary && provider.webgpu) {
16498
+ primaryKey.current ?? (primaryKey.current = id || DEFAULT_PRIMARY);
16499
+ announcePrimary(primaryKey.current, primaryToken);
16500
+ }
15937
16501
  return () => {
15938
16502
  insertionMounted.current = false;
16503
+ if (primaryKey.current) markWithdrawing(primaryToken);
15939
16504
  queueMicrotask(() => {
15940
16505
  if (insertionMounted.current) return;
16506
+ if (primaryKey.current) withdrawPrimary(primaryKey.current, primaryToken);
15941
16507
  const current = root.current;
15942
16508
  root.current = null;
15943
16509
  current?.unmount();
@@ -15962,10 +16528,10 @@ function CanvasImpl({
15962
16528
  const handleHMR = () => {
15963
16529
  queueMicrotask(() => {
15964
16530
  const rootEntry = _roots.get(canvas);
15965
- if (rootEntry?.store) extensions.notifyRootExtensionsHmr(rootEntry.store);
16531
+ if (rootEntry?.store) textureStaging.notifyRootExtensionsHmr(rootEntry.store);
15966
16532
  });
15967
16533
  };
15968
- if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/fiber.EDf0d92E.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
16534
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/fiber.nDnBf1np.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15969
16535
  const hot = undefined;
15970
16536
  hot.on("vite:afterUpdate", handleHMR);
15971
16537
  return () => hot.off?.("vite:afterUpdate", handleHMR);
@@ -15978,7 +16544,7 @@ function CanvasImpl({
15978
16544
  }
15979
16545
  }, [hmr]);
15980
16546
  const pointerEvents = eventSource ? "none" : "auto";
15981
- return /* @__PURE__ */ jsxRuntime.jsx(
16547
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15982
16548
  "div",
15983
16549
  {
15984
16550
  ref: divRef,
@@ -15990,21 +16556,24 @@ function CanvasImpl({
15990
16556
  pointerEvents,
15991
16557
  ...style
15992
16558
  },
15993
- ...props,
15994
- children: fallbackVisible ? (
15995
- // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15996
- // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15997
- fallback
15998
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
15999
- "canvas",
16000
- {
16001
- ref: canvasRef,
16002
- id,
16003
- className: "r3f-canvas",
16004
- style: { display: "block", width: "100%", height: "100%" },
16005
- children: fallback
16006
- }
16007
- ) })
16559
+ ...divProps,
16560
+ children: [
16561
+ fallbackVisible ? (
16562
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
16563
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
16564
+ fallback
16565
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
16566
+ "canvas",
16567
+ {
16568
+ ref: canvasRef,
16569
+ id,
16570
+ className: "r3f-canvas",
16571
+ style: { display: "block", width: "100%", height: "100%" },
16572
+ children: fallback
16573
+ }
16574
+ ) }),
16575
+ gate
16576
+ ]
16008
16577
  }
16009
16578
  );
16010
16579
  }