@react-three/fiber 10.0.0-canary.3f8df2b → 10.0.0-canary.42caa10

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