@react-three/fiber 10.0.0-canary.164c7be → 10.0.0-canary.1be335a

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.
@@ -1,5 +1,5 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, Vector4, PostProcessing } from 'three/webgpu';
2
+ import { CubeRenderTarget, RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, ShaderMaterial, Uniform, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, Vector4, RenderPipeline } from 'three/webgpu';
3
3
  import { Inspector } from 'three/addons/inspector/Inspector.js';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
  import * as React from 'react';
@@ -12,7 +12,7 @@ import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
12
12
  import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
13
13
  import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
14
14
  import { GainMapLoader } from '@monogrid/gainmap-js';
15
- import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
15
+ import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
16
16
  import { createWithEqualityFn } from 'zustand/traditional';
17
17
  import { getScheduler } from '@pmndrs/scheduler';
18
18
  export { Scheduler, getScheduler } from '@pmndrs/scheduler';
@@ -43,13 +43,16 @@ const WebGLRenderer = class WebGLRenderer2 {
43
43
  }
44
44
  };
45
45
  const WebGLRenderTarget = null;
46
+ const WebGLCubeRenderTarget = null;
46
47
 
47
48
  const THREE = /*#__PURE__*/_mergeNamespaces({
48
49
  __proto__: null,
50
+ CubeRenderTargetCompat: CubeRenderTarget,
49
51
  Inspector: Inspector,
50
52
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
51
53
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
52
54
  RenderTargetCompat: RenderTarget,
55
+ WebGLCubeRenderTarget: WebGLCubeRenderTarget,
53
56
  WebGLRenderTarget: WebGLRenderTarget,
54
57
  WebGLRenderer: WebGLRenderer
55
58
  }, [webgpu]);
@@ -323,15 +326,16 @@ function EnvironmentPortal({
323
326
  preset = void 0,
324
327
  extensions
325
328
  }) {
326
- const gl = useThree$1((state) => state.gl);
329
+ const renderer = useThree$1((state) => state.renderer);
330
+ const isLegacy = useThree$1((state) => state.isLegacy);
327
331
  const defaultScene = useThree$1((state) => state.scene);
328
332
  const camera = React.useRef(null);
329
333
  const [virtualScene] = React.useState(() => new Scene());
330
334
  const fbo = React.useMemo(() => {
331
- const fbo2 = new WebGLCubeRenderTarget(resolution);
335
+ const fbo2 = isLegacy ? new WebGLCubeRenderTarget(resolution) : new CubeRenderTarget(resolution);
332
336
  fbo2.texture.type = HalfFloatType;
333
337
  return fbo2;
334
- }, [resolution]);
338
+ }, [resolution, isLegacy]);
335
339
  React.useEffect(() => {
336
340
  return () => {
337
341
  fbo.dispose();
@@ -339,10 +343,10 @@ function EnvironmentPortal({
339
343
  }, [fbo]);
340
344
  React.useLayoutEffect(() => {
341
345
  if (frames === 1) {
342
- const autoClear = gl.autoClear;
343
- gl.autoClear = true;
344
- camera.current.update(gl, virtualScene);
345
- gl.autoClear = autoClear;
346
+ const autoClear = renderer.autoClear;
347
+ renderer.autoClear = true;
348
+ camera.current.update(renderer, virtualScene);
349
+ renderer.autoClear = autoClear;
346
350
  }
347
351
  return setEnvProps(background, scene, defaultScene, fbo.texture, {
348
352
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -359,7 +363,7 @@ function EnvironmentPortal({
359
363
  defaultScene,
360
364
  background,
361
365
  frames,
362
- gl,
366
+ renderer,
363
367
  blur,
364
368
  backgroundBlurriness,
365
369
  backgroundIntensity,
@@ -370,10 +374,10 @@ function EnvironmentPortal({
370
374
  let count = 1;
371
375
  useFrame$1(() => {
372
376
  if (frames === Infinity || count < frames) {
373
- const autoClear = gl.autoClear;
374
- gl.autoClear = true;
375
- camera.current.update(gl, virtualScene);
376
- gl.autoClear = autoClear;
377
+ const autoClear = renderer.autoClear;
378
+ renderer.autoClear = true;
379
+ camera.current.update(renderer, virtualScene);
380
+ renderer.autoClear = autoClear;
377
381
  count++;
378
382
  }
379
383
  });
@@ -616,8 +620,7 @@ function prepare(target, root, type, props) {
616
620
  object,
617
621
  eventCount: 0,
618
622
  handlers: {},
619
- isHidden: false,
620
- deferredRefs: []
623
+ isHidden: false
621
624
  };
622
625
  if (object) object.__r3f = instance;
623
626
  }
@@ -774,6 +777,17 @@ function unregisterVisibility(store, object) {
774
777
  internal.visibilityRegistry.delete(object.uuid);
775
778
  internal.occlusionCache.delete(object);
776
779
  }
780
+ function updateVisibilityHandlers(store, object, handlers) {
781
+ const { internal } = store.getState();
782
+ const entry = internal.visibilityRegistry.get(object.uuid);
783
+ if (!entry) return false;
784
+ entry.handlers = handlers;
785
+ if ((handlers.onOccluded || handlers.onVisible) && !internal.occlusionEnabled) {
786
+ object.occlusionTest = true;
787
+ enableOcclusion(store);
788
+ }
789
+ return true;
790
+ }
777
791
  function checkVisibility(state) {
778
792
  const { internal, camera } = state;
779
793
  const registry = internal.visibilityRegistry;
@@ -934,9 +948,9 @@ function diffProps(instance, newProps) {
934
948
  const { root, key } = resolve(instance.object, prop);
935
949
  if (root.constructor && root.constructor.length === 0) {
936
950
  const ctor = getMemoizedPrototype(root);
937
- if (!is.und(ctor)) changedProps[key] = ctor[key];
951
+ if (!is.und(ctor)) changedProps[prop] = ctor[key];
938
952
  } else {
939
- changedProps[key] = 0;
953
+ changedProps[prop] = 0;
940
954
  }
941
955
  }
942
956
  return changedProps;
@@ -961,10 +975,7 @@ function applyProps(object, props) {
961
975
  continue;
962
976
  }
963
977
  if (value === void 0) continue;
964
- if (isFromRef(value)) {
965
- instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
966
- continue;
967
- }
978
+ if (isFromRef(value)) continue;
968
979
  if (isOnce(value)) {
969
980
  if (instance?.appliedOnce?.has(prop)) continue;
970
981
  if (instance) {
@@ -1000,6 +1011,19 @@ function applyProps(object, props) {
1000
1011
  } else if (isVectorLike(target) && is.num(value)) {
1001
1012
  if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
1002
1013
  else target.set(value);
1014
+ } else if (root instanceof ShaderMaterial && key === "uniforms" && is.obj(value)) {
1015
+ if (!is.obj(root.uniforms)) root.uniforms = {};
1016
+ const uniforms = root.uniforms;
1017
+ const nextUniforms = value;
1018
+ for (const name in nextUniforms) {
1019
+ const uniform = nextUniforms[name];
1020
+ const targetUniform = uniforms[name];
1021
+ if (targetUniform) Object.assign(targetUniform, uniform);
1022
+ else {
1023
+ const nextUniform = uniform instanceof Uniform ? uniform.clone() : new Uniform(uniform.value);
1024
+ uniforms[name] = nextUniform;
1025
+ }
1026
+ }
1003
1027
  } else {
1004
1028
  root[key] = value;
1005
1029
  if (key.endsWith("Node") && root.isMaterial) {
@@ -1018,6 +1042,9 @@ function applyProps(object, props) {
1018
1042
  if (instance.eventCount && object2.raycast !== null) {
1019
1043
  rootState.internal.interaction.push(object2);
1020
1044
  }
1045
+ }
1046
+ if (instance?.parent && rootState?.internal && instance.object?.isObject3D) {
1047
+ const object2 = instance.object;
1021
1048
  const root = findInitialRoot(instance);
1022
1049
  const visibilityHandlers = {
1023
1050
  onFramed: instance.handlers.onFramed,
@@ -1025,7 +1052,9 @@ function applyProps(object, props) {
1025
1052
  onVisible: instance.handlers.onVisible
1026
1053
  };
1027
1054
  if (hasVisibilityHandlers(visibilityHandlers)) {
1028
- registerVisibility(root, object2, visibilityHandlers);
1055
+ if (!updateVisibilityHandlers(root, object2, visibilityHandlers)) {
1056
+ registerVisibility(root, object2, visibilityHandlers);
1057
+ }
1029
1058
  } else {
1030
1059
  unregisterVisibility(root, object2);
1031
1060
  }
@@ -1553,6 +1582,7 @@ function createPointerEvents(store) {
1553
1582
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1554
1583
  }
1555
1584
  },
1585
+ // PREVIEW (A8): the XR pointer registration API may change before stable.
1556
1586
  registerPointer: (config) => {
1557
1587
  const pointerId = nextXRPointerId++;
1558
1588
  xrPointers.set(pointerId, config);
@@ -1639,6 +1669,13 @@ function notifyAlpha({ message, link }) {
1639
1669
  }
1640
1670
  }
1641
1671
 
1672
+ const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
1673
+ function isInternalRendererAccess(stack, callerDepth = 2) {
1674
+ if (!stack) return false;
1675
+ const caller = stack.split("\n")[callerDepth] ?? "";
1676
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
1677
+ }
1678
+
1642
1679
  const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1643
1680
  const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1644
1681
  const createStore = (invalidate, advance) => {
@@ -1771,6 +1808,7 @@ const createStore = (invalidate, advance) => {
1771
1808
  }),
1772
1809
  setFrameloop: (frameloop = "always") => {
1773
1810
  set(() => ({ frameloop }));
1811
+ getScheduler().frameloop = frameloop;
1774
1812
  },
1775
1813
  setError: (error) => set(() => ({ error })),
1776
1814
  error: null,
@@ -1838,8 +1876,7 @@ const createStore = (invalidate, advance) => {
1838
1876
  const currentState = rootStore.getState();
1839
1877
  if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1840
1878
  const stack = new Error().stack || "";
1841
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1842
- if (!isInternalAccess) {
1879
+ if (!isInternalRendererAccess(stack)) {
1843
1880
  const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1844
1881
  notifyDepreciated({
1845
1882
  heading: "Accessing state.gl in WebGPU mode",
@@ -1952,19 +1989,25 @@ function loadingFn(extensions, onProgress) {
1952
1989
  );
1953
1990
  };
1954
1991
  }
1955
- function useLoader(loader, input, extensions, onProgress) {
1956
- const keys = Array.isArray(input) ? input : [input];
1992
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1993
+ const inputs = Array.isArray(input) ? input : [input];
1994
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
1957
1995
  const fn = loadingFn(extensions, onProgress);
1958
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1996
+ const results = cacheKeys.map(
1997
+ (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1998
+ );
1959
1999
  return Array.isArray(input) ? results : results[0];
1960
2000
  }
1961
- useLoader.preload = function(loader, input, extensions, onProgress) {
1962
- const keys = Array.isArray(input) ? input : [input];
1963
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
2001
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
2002
+ const inputs = Array.isArray(input) ? input : [input];
2003
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2004
+ cacheKeys.forEach(
2005
+ (key, index) => preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
2006
+ );
1964
2007
  };
1965
- useLoader.clear = function(loader, input) {
1966
- const keys = Array.isArray(input) ? input : [input];
1967
- keys.forEach((key) => clear([loader, key]));
2008
+ useLoader.clear = function(loader, input, cacheKey) {
2009
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2010
+ cacheKeys.forEach((key) => clear([loader, key]));
1968
2011
  };
1969
2012
  useLoader.loader = getLoader;
1970
2013
 
@@ -2384,7 +2427,7 @@ function advance(timestamp) {
2384
2427
  getScheduler().step(timestamp);
2385
2428
  }
2386
2429
 
2387
- const version = "10.0.0-alpha.2";
2430
+ const version = "10.0.0-alpha.3";
2388
2431
  const packageData = {
2389
2432
  version: version};
2390
2433
 
@@ -13640,7 +13683,7 @@ function n0() {
13640
13683
  var t0 = n0();
13641
13684
  const r0 = Xb(t0);
13642
13685
 
13643
- const t = 1, o = 8, r = 32, e = 2;
13686
+ const t = 1, o = 8, r = 32, e = 2, i$1 = 268435456;
13644
13687
 
13645
13688
  function createReconciler(config) {
13646
13689
  const reconciler2 = r0(config);
@@ -13805,7 +13848,15 @@ function setFiberRef(fiber, publicInstance) {
13805
13848
  }
13806
13849
  }
13807
13850
  const reconstructed = [];
13808
- function swapInstances() {
13851
+ function flushReconstructedInstances() {
13852
+ if (reconstructed.length === 0) return;
13853
+ try {
13854
+ swapReconstructedInstances();
13855
+ } finally {
13856
+ reconstructed.length = 0;
13857
+ }
13858
+ }
13859
+ function swapReconstructedInstances() {
13809
13860
  for (const [instance] of reconstructed) {
13810
13861
  const parent = instance.parent;
13811
13862
  if (parent) {
@@ -13823,6 +13874,7 @@ function swapInstances() {
13823
13874
  }
13824
13875
  }
13825
13876
  if (instance.isHidden) unhideInstance(instance);
13877
+ if (isObject3D(instance.object)) removeInteractivity(findInitialRoot(instance), instance.object);
13826
13878
  if (instance.object.__r3f) delete instance.object.__r3f;
13827
13879
  if (instance.type !== "primitive") disposeOnIdle(instance.object);
13828
13880
  }
@@ -13835,6 +13887,8 @@ function swapInstances() {
13835
13887
  instance.object.__r3f = instance;
13836
13888
  setFiberRef(fiber, instance.object);
13837
13889
  delete instance.appliedOnce;
13890
+ instance.eventCount = 0;
13891
+ instance.handlers = {};
13838
13892
  applyProps(instance.object, instance.props);
13839
13893
  if (instance.props.attach) {
13840
13894
  attach(parent, instance);
@@ -13851,14 +13905,114 @@ function swapInstances() {
13851
13905
  invalidateInstance(instance);
13852
13906
  }
13853
13907
  }
13854
- reconstructed.length = 0;
13855
13908
  }
13856
13909
  const handleTextInstance = () => {
13857
13910
  };
13858
13911
  const NO_CONTEXT = {};
13859
13912
  let currentUpdatePriority = NoEventPriority;
13860
- const NoFlags = 0;
13861
- const Update = 4;
13913
+ function getEventPriority(type) {
13914
+ switch (type) {
13915
+ case "beforetoggle":
13916
+ case "cancel":
13917
+ case "click":
13918
+ case "close":
13919
+ case "contextmenu":
13920
+ case "copy":
13921
+ case "cut":
13922
+ case "auxclick":
13923
+ case "dblclick":
13924
+ case "dragend":
13925
+ case "dragstart":
13926
+ case "drop":
13927
+ case "focusin":
13928
+ case "focusout":
13929
+ case "input":
13930
+ case "invalid":
13931
+ case "keydown":
13932
+ case "keypress":
13933
+ case "keyup":
13934
+ case "mousedown":
13935
+ case "mouseup":
13936
+ case "paste":
13937
+ case "pause":
13938
+ case "play":
13939
+ case "pointercancel":
13940
+ case "pointerdown":
13941
+ case "pointerup":
13942
+ case "ratechange":
13943
+ case "reset":
13944
+ case "resize":
13945
+ case "seeked":
13946
+ case "submit":
13947
+ case "touchcancel":
13948
+ case "touchend":
13949
+ case "touchstart":
13950
+ case "volumechange":
13951
+ case "change":
13952
+ case "selectionchange":
13953
+ case "compositionstart":
13954
+ case "compositionend":
13955
+ case "compositionupdate":
13956
+ case "beforeinput":
13957
+ case "blur":
13958
+ case "fullscreenchange":
13959
+ case "focus":
13960
+ case "hashchange":
13961
+ case "popstate":
13962
+ case "select":
13963
+ case "selectstart":
13964
+ return e;
13965
+ case "drag":
13966
+ case "dragenter":
13967
+ case "dragexit":
13968
+ case "dragleave":
13969
+ case "dragover":
13970
+ case "mousemove":
13971
+ case "mouseout":
13972
+ case "mouseover":
13973
+ case "pointermove":
13974
+ case "pointerout":
13975
+ case "pointerover":
13976
+ case "scroll":
13977
+ case "touchmove":
13978
+ case "wheel":
13979
+ case "mouseenter":
13980
+ case "mouseleave":
13981
+ case "pointerenter":
13982
+ case "pointerleave":
13983
+ return o;
13984
+ case "message": {
13985
+ switch (unstable_getCurrentPriorityLevel()) {
13986
+ case unstable_ImmediatePriority:
13987
+ return e;
13988
+ case unstable_UserBlockingPriority:
13989
+ return o;
13990
+ case unstable_NormalPriority:
13991
+ case unstable_LowPriority:
13992
+ return r;
13993
+ case unstable_IdlePriority:
13994
+ return i$1;
13995
+ default:
13996
+ return r;
13997
+ }
13998
+ }
13999
+ default:
14000
+ return r;
14001
+ }
14002
+ }
14003
+ function scheduleMicrotask(callback) {
14004
+ if (typeof queueMicrotask === "function") {
14005
+ queueMicrotask(callback);
14006
+ } else if (typeof Promise !== "undefined") {
14007
+ Promise.resolve().then(callback).catch((error) => {
14008
+ setTimeout(() => {
14009
+ throw error;
14010
+ });
14011
+ });
14012
+ } else {
14013
+ setTimeout(callback);
14014
+ }
14015
+ }
13862
14016
  const reconciler = /* @__PURE__ */ createReconciler({
13863
14017
  isPrimaryRenderer: false,
13864
14018
  warnsIfNotActing: false,
@@ -13897,16 +14051,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
13897
14051
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
13898
14052
  else if (newProps.args?.some((value, index) => value !== oldProps.args?.[index])) reconstruct = true;
13899
14053
  if (reconstruct) {
13900
- reconstructed.push([instance, { ...newProps }, fiber]);
14054
+ reconstructed.push([instance, getInstanceProps(newProps), fiber]);
13901
14055
  } else {
13902
14056
  const changedProps = diffProps(instance, newProps);
13903
- if (Object.keys(changedProps).length) {
13904
- Object.assign(instance.props, changedProps);
13905
- applyProps(instance.object, changedProps);
13906
- }
14057
+ const attach2 = instance.props.attach;
14058
+ instance.props = getInstanceProps(newProps);
14059
+ if (attach2 !== void 0) instance.props.attach = attach2;
14060
+ else delete instance.props.attach;
14061
+ if (Object.keys(changedProps).length) applyProps(instance.object, changedProps);
13907
14062
  }
13908
- const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
13909
- if (isTailSibling) swapInstances();
13910
14063
  },
13911
14064
  finalizeInitialChildren: (instance) => {
13912
14065
  for (const prop in instance.props) {
@@ -13931,8 +14084,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13931
14084
  const target = container.getState().internal.container ?? container.getState().scene;
13932
14085
  return prepare(target, container, "", {});
13933
14086
  },
13934
- resetAfterCommit: () => {
13935
- },
14087
+ // Reconstructed instances are swapped once all mutations are committed,
14088
+ // before layout effects run so refs point to the new objects
14089
+ resetAfterCommit: flushReconstructedInstances,
13936
14090
  shouldSetTextContent: () => false,
13937
14091
  clearContainer: () => false,
13938
14092
  hideInstance,
@@ -13940,6 +14094,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13940
14094
  createTextInstance: handleTextInstance,
13941
14095
  hideTextInstance: handleTextInstance,
13942
14096
  unhideTextInstance: handleTextInstance,
14097
+ // Mirrors react-dom, which flushes discrete work in microtasks
14098
+ supportsMicrotasks: true,
14099
+ scheduleMicrotask,
13943
14100
  scheduleTimeout: typeof setTimeout === "function" ? setTimeout : void 0,
13944
14101
  cancelTimeout: typeof clearTimeout === "function" ? clearTimeout : void 0,
13945
14102
  noTimeout: -1,
@@ -13980,28 +14137,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13980
14137
  },
13981
14138
  resolveUpdatePriority() {
13982
14139
  if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
13983
- switch (typeof window !== "undefined" && window.event?.type) {
13984
- case "click":
13985
- case "contextmenu":
13986
- case "dblclick":
13987
- case "dragenter":
13988
- case "dragleave":
13989
- case "drop":
13990
- case "pointercancel":
13991
- case "pointerdown":
13992
- case "pointerup":
13993
- return e;
13994
- case "dragover":
13995
- case "pointermove":
13996
- case "pointerout":
13997
- case "pointerover":
13998
- case "pointerenter":
13999
- case "pointerleave":
14000
- case "wheel":
14001
- return o;
14002
- default:
14003
- return r;
14004
- }
14140
+ const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
14141
+ if (eventType === void 0) return r;
14142
+ return getEventPriority(eventType);
14005
14143
  },
14006
14144
  resetFormInstance() {
14007
14145
  },
@@ -14050,11 +14188,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
14050
14188
  cloneMutableTextInstance(textInstance) {
14051
14189
  return textInstance;
14052
14190
  },
14191
+ // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
14192
+ // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
14193
+ // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
14194
+ // React never calls them. They stay as no-ops rather than throws: the host config is shared by
14195
+ // every root, and a throw here would turn a React-internal probe into a hard crash for users.
14196
+ // Guarded by a test asserting a full render + unmount never reaches them.
14053
14197
  cloneRootViewTransitionContainer(_rootContainer) {
14054
- throw new Error("Not implemented.");
14055
14198
  },
14056
14199
  removeRootViewTransitionClone(_rootContainer, _clone) {
14057
- throw new Error("Not implemented.");
14058
14200
  },
14059
14201
  // https://github.com/facebook/react/pull/32465
14060
14202
  createFragmentInstance: (_fiber) => null,
@@ -14139,6 +14281,7 @@ function createRoot(canvas) {
14139
14281
  const lastConfiguredProps = {};
14140
14282
  let configured = false;
14141
14283
  let pending = null;
14284
+ let rendererSetup = null;
14142
14285
  return {
14143
14286
  async configure(props = {}) {
14144
14287
  let resolve;
@@ -14169,7 +14312,7 @@ function createRoot(canvas) {
14169
14312
  forceEven
14170
14313
  } = props;
14171
14314
  const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
14172
- const state = store.getState();
14315
+ let state = store.getState();
14173
14316
  const defaultGPUProps = {
14174
14317
  canvas,
14175
14318
  antialias: true
@@ -14183,52 +14326,64 @@ function createRoot(canvas) {
14183
14326
  throw new Error("Cannot use both gl and renderer props at the same time");
14184
14327
  }
14185
14328
  let renderer = state.internal.actualRenderer;
14186
- if (primaryCanvas && !state.internal.actualRenderer) {
14187
- const primary = await waitForPrimary(primaryCanvas);
14188
- renderer = primary.renderer;
14189
- state.internal.actualRenderer = renderer;
14190
- const canvasTarget = new CanvasTarget(canvas);
14191
- primary.store.setState((prev) => ({
14192
- internal: { ...prev.internal, isMultiCanvas: true }
14193
- }));
14194
- state.set((prev) => ({
14195
- webGPUSupported: primary.store.getState().webGPUSupported,
14196
- renderer,
14197
- primaryStore: primary.store,
14198
- internal: {
14199
- ...prev.internal,
14200
- canvasTarget,
14201
- isMultiCanvas: true,
14202
- isSecondary: true,
14203
- targetId: primaryCanvas
14204
- }
14205
- }));
14206
- } else if (!state.internal.actualRenderer) {
14207
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14208
- if (!renderer.hasInitialized?.()) {
14209
- const size2 = computeInitialSize(canvas, propsSize);
14210
- if (size2.width > 0 && size2.height > 0) {
14211
- const pixelRatio = calculateDpr(dpr);
14212
- canvas.width = size2.width * pixelRatio;
14213
- canvas.height = size2.height * pixelRatio;
14214
- }
14215
- await renderer.init();
14216
- }
14217
- const backend = renderer.backend;
14218
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14219
- state.internal.actualRenderer = renderer;
14220
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14221
- if (canvasId && !state.internal.isSecondary) {
14222
- const canvasTarget = new CanvasTarget(canvas);
14223
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14224
- state.set((prev) => ({
14225
- internal: {
14226
- ...prev.internal,
14227
- canvasTarget,
14228
- unregisterPrimary
14329
+ if (!state.internal.actualRenderer) {
14330
+ if (!rendererSetup) {
14331
+ rendererSetup = (async () => {
14332
+ if (primaryCanvas) {
14333
+ const primary = await waitForPrimary(primaryCanvas);
14334
+ renderer = primary.renderer;
14335
+ state.internal.actualRenderer = renderer;
14336
+ const canvasTarget = new CanvasTarget(canvas);
14337
+ primary.store.setState((prev) => ({
14338
+ internal: { ...prev.internal, isMultiCanvas: true }
14339
+ }));
14340
+ state.set((prev) => ({
14341
+ webGPUSupported: primary.store.getState().webGPUSupported,
14342
+ renderer,
14343
+ primaryStore: primary.store,
14344
+ internal: {
14345
+ ...prev.internal,
14346
+ canvasTarget,
14347
+ isMultiCanvas: true,
14348
+ isSecondary: true,
14349
+ targetId: primaryCanvas
14350
+ }
14351
+ }));
14352
+ } else {
14353
+ renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14354
+ if (!renderer.hasInitialized?.()) {
14355
+ const size2 = computeInitialSize(canvas, propsSize);
14356
+ if (size2.width > 0 && size2.height > 0) {
14357
+ const pixelRatio = calculateDpr(dpr);
14358
+ canvas.width = size2.width * pixelRatio;
14359
+ canvas.height = size2.height * pixelRatio;
14360
+ }
14361
+ await renderer.init();
14362
+ }
14363
+ const backend = renderer.backend;
14364
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14365
+ state.internal.actualRenderer = renderer;
14366
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14367
+ if (canvasId && !state.internal.isSecondary) {
14368
+ const canvasTarget = new CanvasTarget(canvas);
14369
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14370
+ state.set((prev) => ({
14371
+ internal: {
14372
+ ...prev.internal,
14373
+ canvasTarget,
14374
+ unregisterPrimary
14375
+ }
14376
+ }));
14377
+ }
14229
14378
  }
14230
- }));
14379
+ })().catch((err) => {
14380
+ rendererSetup = null;
14381
+ throw err;
14382
+ });
14231
14383
  }
14384
+ await rendererSetup;
14385
+ state = store.getState();
14386
+ renderer = state.internal.actualRenderer;
14232
14387
  }
14233
14388
  let raycaster = state.raycaster;
14234
14389
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
@@ -14578,7 +14733,8 @@ function unmountComponentAtNode(canvas, callback) {
14578
14733
  dispose(state.scene);
14579
14734
  _roots.delete(canvas);
14580
14735
  if (callback) callback(canvas);
14581
- } catch {
14736
+ } catch (error) {
14737
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
14582
14738
  }
14583
14739
  }, 500);
14584
14740
  }
@@ -14641,6 +14797,7 @@ function PortalInner({ state = {}, children, container }) {
14641
14797
  viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
14642
14798
  if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14643
14799
  }
14800
+ const set = injectState.set;
14644
14801
  return {
14645
14802
  // The intersect consists of the previous root state
14646
14803
  ...rootState,
@@ -14659,7 +14816,7 @@ function PortalInner({ state = {}, children, container }) {
14659
14816
  size: resolvedSize,
14660
14817
  viewport: { ...rootState.viewport, ...viewport },
14661
14818
  // Layers are allowed to override events
14662
- setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14819
+ setEvents: (events2) => set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14663
14820
  // Container for child attachment - the portalScene (injected or container itself)
14664
14821
  internal: { ...rootState.internal, ...injectState.internal, container: portalScene }
14665
14822
  };
@@ -14718,14 +14875,146 @@ function parseBackground(background) {
14718
14875
  return null;
14719
14876
  }
14720
14877
 
14878
+ function parseRendererConfig(rendererProp) {
14879
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14880
+ if (isRendererConfig) {
14881
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14882
+ return {
14883
+ primaryCanvas: pc,
14884
+ scheduler: sc,
14885
+ renderer: Object.keys(rest).length > 0 ? rest : rendererProp
14886
+ };
14887
+ }
14888
+ return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
14889
+ }
14890
+
14891
+ const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
14892
+ const ROOT_SCOPE = "";
14893
+ const registries = /* @__PURE__ */ new WeakMap();
14894
+ function getKindRegistry(store, kind) {
14895
+ let registry = registries.get(store);
14896
+ if (!registry) {
14897
+ registry = {
14898
+ nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14899
+ uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14900
+ buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14901
+ gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
14902
+ };
14903
+ registries.set(store, registry);
14904
+ }
14905
+ return registry[kind];
14906
+ }
14907
+ function resolvePrimary$1(store) {
14908
+ return store.getState().primaryStore ?? store;
14909
+ }
14910
+ function isScopeValid(store, kind, scopeKey) {
14911
+ return getKindRegistry(store, kind).valid.has(scopeKey);
14912
+ }
14913
+ function peekStaged(store, kind, scopeKey) {
14914
+ return getKindRegistry(store, kind).staged.get(scopeKey);
14915
+ }
14916
+ function stageEntries(store, kind, scopeKey, entries) {
14917
+ const { staged } = getKindRegistry(store, kind);
14918
+ const current = staged.get(scopeKey);
14919
+ if (current) Object.assign(current, entries);
14920
+ else staged.set(scopeKey, { ...entries });
14921
+ }
14922
+ function readCommittedScope(map, scopeKey, isLeaf) {
14923
+ if (scopeKey === ROOT_SCOPE) {
14924
+ const leaves = {};
14925
+ for (const [key, value] of Object.entries(map)) if (isLeaf(value)) leaves[key] = value;
14926
+ return leaves;
14927
+ }
14928
+ const scopeData = map[scopeKey];
14929
+ if (scopeData && typeof scopeData === "object" && !isLeaf(scopeData)) return scopeData;
14930
+ return {};
14931
+ }
14932
+ function withStagedOverlay(store, kind, committed) {
14933
+ const { staged } = getKindRegistry(store, kind);
14934
+ if (staged.size === 0) return committed;
14935
+ const view = { ...committed };
14936
+ for (const [scopeKey, entries] of staged) {
14937
+ if (scopeKey === ROOT_SCOPE) Object.assign(view, entries);
14938
+ else view[scopeKey] = { ...view[scopeKey], ...entries };
14939
+ }
14940
+ return view;
14941
+ }
14942
+ function flushStagedScope(store, kind, scopeKey, isLeaf) {
14943
+ const registry = getKindRegistry(store, kind);
14944
+ const staged = registry.staged.get(scopeKey);
14945
+ if (!staged) return;
14946
+ registry.staged.delete(scopeKey);
14947
+ const replace = !registry.valid.has(scopeKey);
14948
+ store.setState((state) => {
14949
+ const map = state[kind];
14950
+ if (scopeKey === ROOT_SCOPE) {
14951
+ if (!replace) return { [kind]: { ...map, ...staged } };
14952
+ const next = {};
14953
+ for (const [key, value] of Object.entries(map)) if (!isLeaf(value)) next[key] = value;
14954
+ return { [kind]: { ...next, ...staged } };
14955
+ }
14956
+ const current = replace ? {} : map[scopeKey] ?? {};
14957
+ return { [kind]: { ...map, [scopeKey]: { ...current, ...staged } } };
14958
+ });
14959
+ registry.valid.add(scopeKey);
14960
+ }
14961
+ function invalidateResource(store, kind, scopeKey) {
14962
+ const registry = getKindRegistry(store, kind);
14963
+ if (scopeKey === void 0) {
14964
+ registry.valid.clear();
14965
+ registry.staged.clear();
14966
+ } else {
14967
+ registry.valid.delete(scopeKey);
14968
+ registry.staged.delete(scopeKey);
14969
+ }
14970
+ }
14971
+ function rebuildResource(store, kind, userScope) {
14972
+ const primary = resolvePrimary$1(store);
14973
+ invalidateResource(primary, kind, userScope === "root" ? ROOT_SCOPE : userScope);
14974
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
14975
+ }
14976
+ function invalidateAllResources(store) {
14977
+ for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
14978
+ }
14979
+ function removeResourceEntries(store, kind, names, scope, isLeaf) {
14980
+ const staged = peekStaged(store, kind, scope ?? ROOT_SCOPE);
14981
+ if (staged) for (const name of names) delete staged[name];
14982
+ store.setState((state) => {
14983
+ const map = state[kind];
14984
+ if (scope) {
14985
+ const currentScope = { ...map[scope] };
14986
+ for (const name of names) delete currentScope[name];
14987
+ return { [kind]: { ...map, [scope]: currentScope } };
14988
+ }
14989
+ const next = { ...map };
14990
+ for (const name of names) if (isLeaf(next[name])) delete next[name];
14991
+ return { [kind]: next };
14992
+ });
14993
+ }
14994
+ function clearResourceEntries(store, kind, scope, isLeaf) {
14995
+ const registry = getKindRegistry(store, kind);
14996
+ if (scope && scope !== "root") registry.staged.delete(scope);
14997
+ else if (scope === "root") registry.staged.delete(ROOT_SCOPE);
14998
+ else registry.staged.clear();
14999
+ store.setState((state) => {
15000
+ const map = state[kind];
15001
+ if (scope && scope !== "root") {
15002
+ const { [scope]: _, ...rest } = map;
15003
+ return { [kind]: rest };
15004
+ }
15005
+ if (scope === "root") {
15006
+ const next = {};
15007
+ for (const [key, value] of Object.entries(map)) if (!isLeaf(value)) next[key] = value;
15008
+ return { [kind]: next };
15009
+ }
15010
+ return { [kind]: {} };
15011
+ });
15012
+ }
15013
+
14721
15014
  function clearHmrCaches(store) {
14722
- store.setState((state) => ({
14723
- nodes: {},
14724
- uniforms: {},
14725
- buffers: {},
14726
- gpuStorage: {},
14727
- _hmrVersion: state._hmrVersion + 1
14728
- }));
15015
+ const primary = store.getState().primaryStore ?? store;
15016
+ invalidateAllResources(primary);
15017
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
14729
15018
  }
14730
15019
 
14731
15020
  function CanvasImpl({
@@ -14761,18 +15050,7 @@ function CanvasImpl({
14761
15050
  forceEven,
14762
15051
  ...props
14763
15052
  }) {
14764
- const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14765
- let primaryCanvas;
14766
- let scheduler;
14767
- let renderer;
14768
- if (isRendererConfig) {
14769
- const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14770
- primaryCanvas = pc;
14771
- scheduler = sc;
14772
- renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
14773
- } else {
14774
- renderer = rendererProp;
14775
- }
15053
+ const { primaryCanvas, scheduler, renderer } = parseRendererConfig(rendererProp);
14776
15054
  React.useMemo(() => extend(THREE), []);
14777
15055
  const Bridge = useBridge();
14778
15056
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
@@ -14817,6 +15095,7 @@ function CanvasImpl({
14817
15095
  const handleDropMissed = useMutableCallback(onDropMissed);
14818
15096
  const [block, setBlock] = React.useState(false);
14819
15097
  const [error, setError] = React.useState(false);
15098
+ const [fallbackVisible, setFallbackVisible] = React.useState(false);
14820
15099
  if (block) throw block;
14821
15100
  if (error) throw error;
14822
15101
  const root = React.useRef(null);
@@ -14824,6 +15103,7 @@ function CanvasImpl({
14824
15103
  const unsubscribeErrorRef = React.useRef(null);
14825
15104
  useIsomorphicLayoutEffect(() => {
14826
15105
  effectActiveRef.current = true;
15106
+ if (fallbackVisible) return;
14827
15107
  const canvas = canvasRef.current;
14828
15108
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
14829
15109
  if (!root.current) {
@@ -14844,7 +15124,7 @@ function CanvasImpl({
14844
15124
  }
14845
15125
  async function run() {
14846
15126
  if (!effectActiveRef.current || !root.current) return;
14847
- await root.current.configure({
15127
+ const configured = await root.current.configure({
14848
15128
  id,
14849
15129
  primaryCanvas,
14850
15130
  scheduler,
@@ -14885,8 +15165,20 @@ function CanvasImpl({
14885
15165
  }
14886
15166
  onCreated?.(state);
14887
15167
  }
14888
- });
14889
- if (!effectActiveRef.current || !root.current) return;
15168
+ }).then(
15169
+ () => true,
15170
+ // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15171
+ // inside <canvas>, which browsers don't display, so surface it as visible DOM
15172
+ // instead; with no fallback, rethrow to an external error boundary. (#3757)
15173
+ (setupError) => {
15174
+ if (effectActiveRef.current) {
15175
+ if (fallback != null) setFallbackVisible(true);
15176
+ else setError(setupError);
15177
+ }
15178
+ return false;
15179
+ }
15180
+ );
15181
+ if (!configured || !effectActiveRef.current || !root.current) return;
14890
15182
  root.current.render(
14891
15183
  /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
14892
15184
  backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
@@ -14949,7 +15241,11 @@ function CanvasImpl({
14949
15241
  ...style
14950
15242
  },
14951
15243
  ...props,
14952
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
15244
+ children: fallbackVisible ? (
15245
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15246
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15247
+ fallback
15248
+ ) : /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
14953
15249
  "canvas",
14954
15250
  {
14955
15251
  ref: canvasRef,
@@ -15031,30 +15327,31 @@ let ScopedStore = _ScopedStore;
15031
15327
  function createScopedStore(data) {
15032
15328
  return new ScopedStore(data);
15033
15329
  }
15034
- function createLazyCreatorState(state) {
15330
+ function createLazyCreatorState(state, store) {
15035
15331
  let _uniforms = null;
15036
15332
  let _nodes = null;
15037
15333
  let _buffers = null;
15038
15334
  let _gpuStorage = null;
15335
+ const view = (kind) => store ? withStagedOverlay(store, kind, state[kind]) : state[kind];
15039
15336
  return Object.create(state, {
15040
15337
  uniforms: {
15041
15338
  get() {
15042
- return _uniforms ?? (_uniforms = createScopedStore(state.uniforms));
15339
+ return _uniforms ?? (_uniforms = createScopedStore(view("uniforms")));
15043
15340
  }
15044
15341
  },
15045
15342
  nodes: {
15046
15343
  get() {
15047
- return _nodes ?? (_nodes = createScopedStore(state.nodes));
15344
+ return _nodes ?? (_nodes = createScopedStore(view("nodes")));
15048
15345
  }
15049
15346
  },
15050
15347
  buffers: {
15051
15348
  get() {
15052
- return _buffers ?? (_buffers = createScopedStore(state.buffers));
15349
+ return _buffers ?? (_buffers = createScopedStore(view("buffers")));
15053
15350
  }
15054
15351
  },
15055
15352
  gpuStorage: {
15056
15353
  get() {
15057
- return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
15354
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage")));
15058
15355
  }
15059
15356
  }
15060
15357
  });
@@ -15177,65 +15474,56 @@ function useCompareMemoize(value, deep) {
15177
15474
  return ref.current;
15178
15475
  }
15179
15476
 
15477
+ function useScopedResource(options) {
15478
+ const { store, kind, scope, isLeaf, create, prepare, reconcile, input } = options;
15479
+ const scopeKey = scope ?? ROOT_SCOPE;
15480
+ const version = store((s) => s._hmrVersion);
15481
+ const entries = useMemo(() => {
15482
+ const staged = peekStaged(store, kind, scopeKey);
15483
+ const reusable = isScopeValid(store, kind, scopeKey);
15484
+ const map = store.getState()[kind];
15485
+ const committed = reusable ? readCommittedScope(map, scopeKey, isLeaf) : void 0;
15486
+ const result = {};
15487
+ let fresh = null;
15488
+ for (const [name, candidate] of Object.entries(create())) {
15489
+ const existing = staged?.[name] ?? committed?.[name];
15490
+ if (existing !== void 0) {
15491
+ result[name] = existing;
15492
+ reconcile?.(existing, candidate);
15493
+ } else {
15494
+ const value = prepare ? prepare(name, candidate) : candidate;
15495
+ result[name] = value;
15496
+ (fresh ?? (fresh = {}))[name] = value;
15497
+ }
15498
+ }
15499
+ if (fresh) stageEntries(store, kind, scopeKey, fresh);
15500
+ return result;
15501
+ }, [store, kind, scopeKey, version, input]);
15502
+ useIsomorphicLayoutEffect(() => {
15503
+ flushStagedScope(store, kind, scopeKey, isLeaf);
15504
+ });
15505
+ return entries;
15506
+ }
15507
+
15180
15508
  const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15181
15509
  function useUniforms(creatorOrScope, scope) {
15182
15510
  const store = usePrimaryStore();
15183
15511
  const removeUniforms2 = useCallback(
15184
- (names, targetScope) => {
15185
- const nameArray = Array.isArray(names) ? names : [names];
15186
- store.setState((state) => {
15187
- if (targetScope) {
15188
- const currentScope = { ...state.uniforms[targetScope] };
15189
- for (const name of nameArray) delete currentScope[name];
15190
- return { uniforms: { ...state.uniforms, [targetScope]: currentScope } };
15191
- }
15192
- const uniforms2 = { ...state.uniforms };
15193
- for (const name of nameArray) if (isUniformNode$1(uniforms2[name])) delete uniforms2[name];
15194
- return { uniforms: uniforms2 };
15195
- });
15196
- },
15512
+ (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode$1),
15197
15513
  [store]
15198
15514
  );
15199
15515
  const clearUniforms = useCallback(
15200
- (targetScope) => {
15201
- store.setState((state) => {
15202
- if (targetScope && targetScope !== "root") {
15203
- const { [targetScope]: _, ...rest } = state.uniforms;
15204
- return { uniforms: rest };
15205
- }
15206
- if (targetScope === "root") {
15207
- const uniforms2 = {};
15208
- for (const [key, value] of Object.entries(state.uniforms)) {
15209
- if (!isUniformNode$1(value)) uniforms2[key] = value;
15210
- }
15211
- return { uniforms: uniforms2 };
15212
- }
15213
- return { uniforms: {} };
15214
- });
15215
- },
15516
+ (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode$1),
15216
15517
  [store]
15217
15518
  );
15218
15519
  const rebuildUniforms = useCallback(
15219
- (targetScope) => {
15220
- store.setState((state) => {
15221
- let newUniforms = {};
15222
- if (targetScope && targetScope !== "root") {
15223
- const { [targetScope]: _, ...rest } = state.uniforms;
15224
- newUniforms = rest;
15225
- } else if (targetScope === "root") {
15226
- for (const [key, value] of Object.entries(state.uniforms)) {
15227
- if (!isUniformNode$1(value)) newUniforms[key] = value;
15228
- }
15229
- }
15230
- return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15231
- });
15232
- },
15520
+ (targetScope) => rebuildResource(store, "uniforms", targetScope),
15233
15521
  [store]
15234
15522
  );
15235
15523
  const inputForMemoization = useMemo(() => {
15236
15524
  let raw = creatorOrScope;
15237
15525
  if (is.fun(creatorOrScope)) {
15238
- const wrappedState = createLazyCreatorState(store.getState());
15526
+ const wrappedState = createLazyCreatorState(store.getState(), store);
15239
15527
  raw = creatorOrScope(wrappedState);
15240
15528
  }
15241
15529
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
@@ -15250,80 +15538,43 @@ function useUniforms(creatorOrScope, scope) {
15250
15538
  const memoizedInput = useCompareMemoize(inputForMemoization);
15251
15539
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15252
15540
  const storeUniforms = usePrimaryThree((s) => s.uniforms);
15253
- const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
15254
- const readerDep = isReader ? storeUniforms : null;
15255
- const creatorDep = isReader ? null : hmrVersion;
15256
- const uniforms = useMemo(() => {
15257
- if (memoizedInput === void 0) {
15258
- return storeUniforms;
15259
- }
15260
- if (typeof memoizedInput === "string") {
15261
- const scopeData = storeUniforms[memoizedInput];
15262
- if (scopeData && !isUniformNode$1(scopeData)) return scopeData;
15263
- return {};
15264
- }
15265
- const state = store.getState();
15266
- const set = store.setState;
15267
- if (typeof memoizedInput !== "object" || memoizedInput === null) {
15268
- throw new Error("Invalid uniform input");
15269
- }
15270
- const created = memoizedInput;
15271
- const result = {};
15272
- let hasNewUniforms = false;
15273
- let targetRecord = state.uniforms;
15274
- if (scope) {
15275
- if (!state.uniforms[scope]) state.uniforms[scope] = {};
15276
- targetRecord = state.uniforms[scope];
15277
- }
15278
- for (const [name, node] of Object.entries(created)) {
15279
- if (targetRecord[name]) {
15280
- result[name] = targetRecord[name];
15281
- const existingVal = result[name].value;
15282
- const newVal = vectorize(node);
15283
- let equals = newVal === existingVal;
15284
- if (!equals && hasEqualsMethod(existingVal) && hasEqualsMethod(newVal)) {
15285
- if (isThreeVector(existingVal) && isThreeVector(newVal)) {
15286
- equals = vectorEquals(existingVal, newVal);
15287
- } else if (isSameThreeType(existingVal, newVal)) {
15288
- equals = existingVal.equals(newVal);
15289
- }
15290
- }
15291
- if (!equals) result[name].value = newVal;
15292
- } else {
15293
- result[name] = createUniform(name, node, scope);
15294
- hasNewUniforms = true;
15295
- }
15296
- }
15297
- if (hasNewUniforms) {
15298
- if (scope) {
15299
- set((s) => ({
15300
- uniforms: {
15301
- ...s.uniforms,
15302
- [scope]: { ...s.uniforms[scope], ...result }
15303
- }
15304
- }));
15305
- } else {
15306
- set((s) => ({ uniforms: { ...s.uniforms, ...result } }));
15307
- }
15541
+ const created = useScopedResource({
15542
+ store,
15543
+ kind: "uniforms",
15544
+ scope: isReader ? void 0 : scope,
15545
+ isLeaf: isUniformNode$1,
15546
+ input: memoizedInput,
15547
+ create: () => {
15548
+ if (isReader) return {};
15549
+ if (typeof memoizedInput !== "object" || memoizedInput === null) throw new Error("Invalid uniform input");
15550
+ return memoizedInput;
15551
+ },
15552
+ prepare: (name, candidate) => createUniform(name, candidate, scope),
15553
+ reconcile: (existing, candidate) => {
15554
+ const existingVal = existing.value;
15555
+ const newVal = vectorize(candidate);
15556
+ let equals = newVal === existingVal;
15557
+ if (!equals && hasEqualsMethod(existingVal) && hasEqualsMethod(newVal)) {
15558
+ if (isThreeVector(existingVal) && isThreeVector(newVal)) {
15559
+ equals = vectorEquals(existingVal, newVal);
15560
+ } else if (isSameThreeType(existingVal, newVal)) {
15561
+ equals = existingVal.equals(newVal);
15562
+ }
15563
+ }
15564
+ if (!equals) existing.value = newVal;
15308
15565
  }
15309
- return result;
15310
- }, [store, memoizedInput, scope, readerDep, creatorDep]);
15566
+ });
15567
+ let uniforms = created;
15568
+ if (memoizedInput === void 0) {
15569
+ uniforms = storeUniforms;
15570
+ } else if (typeof memoizedInput === "string") {
15571
+ const scopeData = storeUniforms[memoizedInput];
15572
+ uniforms = scopeData && !isUniformNode$1(scopeData) ? scopeData : {};
15573
+ }
15311
15574
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15312
15575
  }
15313
15576
  function rebuildAllUniforms(store, scope) {
15314
- store = store.getState().primaryStore ?? store;
15315
- store.setState((state) => {
15316
- let newUniforms = {};
15317
- if (scope && scope !== "root") {
15318
- const { [scope]: _, ...rest } = state.uniforms;
15319
- newUniforms = rest;
15320
- } else if (scope === "root") {
15321
- for (const [key, value] of Object.entries(state.uniforms)) {
15322
- if (!isUniformNode$1(value)) newUniforms[key] = value;
15323
- }
15324
- }
15325
- return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15326
- });
15577
+ rebuildResource(store, "uniforms", scope);
15327
15578
  }
15328
15579
  function removeUniforms(set, names, scope) {
15329
15580
  set((state) => {
@@ -15387,192 +15638,91 @@ function isSameThreeType(a, b) {
15387
15638
  return obj_a.isColor && obj_b.isColor || obj_a.isMatrix3 && obj_b.isMatrix3 || obj_a.isMatrix4 && obj_b.isMatrix4 || obj_a.isEuler && obj_b.isEuler || obj_a.isQuaternion && obj_b.isQuaternion;
15388
15639
  }
15389
15640
 
15641
+ const uniformOf = uniform;
15390
15642
  const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15391
15643
  const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15392
15644
  function useUniform(name, value) {
15393
15645
  const store = useStore();
15394
- const hmrVersion = useThree((s) => s._hmrVersion);
15395
- return useMemo(() => {
15396
- const state = store.getState();
15397
- const set = store.setState;
15398
- const existing = state.uniforms[name];
15399
- if (existing && isUniformNode(existing)) {
15646
+ const registered = useScopedResource({
15647
+ store,
15648
+ kind: "uniforms",
15649
+ scope: void 0,
15650
+ isLeaf: isUniformNode,
15651
+ input: name,
15652
+ create: () => value === void 0 ? {} : { [name]: value },
15653
+ prepare: createNamedUniform,
15654
+ reconcile: (existing2) => {
15400
15655
  if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
15401
- existing.value = typeof value === "string" ? new Color(value) : value;
15656
+ existing2.value = typeof value === "string" ? new Color(value) : value;
15402
15657
  }
15403
- return existing;
15404
- }
15405
- if (value === void 0) {
15406
- throw new Error(
15407
- `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15408
- );
15409
- }
15410
- if (isUniformNode(value)) {
15411
- const node2 = value;
15412
- if (typeof node2.setName === "function") {
15413
- node2.setName(name);
15414
- }
15415
- set((s) => ({
15416
- uniforms: { ...s.uniforms, [name]: node2 }
15417
- }));
15418
- return node2;
15419
- }
15420
- let node;
15421
- if (isTSLNode$1(value)) {
15422
- node = uniform(value);
15423
- } else if (typeof value === "string") {
15424
- node = uniform(new Color(value));
15425
- } else {
15426
- node = uniform(value);
15427
- }
15428
- if (typeof node.setName === "function") {
15429
- node.setName(name);
15430
15658
  }
15431
- set((s) => ({
15432
- uniforms: {
15433
- ...s.uniforms,
15434
- [name]: node
15435
- }
15436
- }));
15437
- return node;
15438
- }, [store, name, hmrVersion]);
15659
+ });
15660
+ if (registered[name]) return registered[name];
15661
+ const existing = peekStaged(store, "uniforms", ROOT_SCOPE)?.[name] ?? store.getState().uniforms[name];
15662
+ if (existing && isUniformNode(existing)) return existing;
15663
+ throw new Error(
15664
+ `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15665
+ );
15666
+ }
15667
+ function createNamedUniform(name, value) {
15668
+ let node;
15669
+ if (isUniformNode(value)) {
15670
+ node = value;
15671
+ } else if (isTSLNode$1(value)) {
15672
+ node = uniformOf(value);
15673
+ } else if (typeof value === "string") {
15674
+ node = uniform(new Color(value));
15675
+ } else {
15676
+ node = uniformOf(value);
15677
+ }
15678
+ if (typeof node.setName === "function") {
15679
+ node.setName(name);
15680
+ }
15681
+ return node;
15439
15682
  }
15440
15683
 
15441
15684
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
15442
15685
  function useNodes(creatorOrScope, scope) {
15443
15686
  const store = usePrimaryStore();
15444
15687
  const removeNodes2 = useCallback(
15445
- (names, targetScope) => {
15446
- const nameArray = Array.isArray(names) ? names : [names];
15447
- store.setState((state) => {
15448
- if (targetScope) {
15449
- const currentScope = { ...state.nodes[targetScope] };
15450
- for (const name of nameArray) delete currentScope[name];
15451
- return { nodes: { ...state.nodes, [targetScope]: currentScope } };
15452
- }
15453
- const nodes2 = { ...state.nodes };
15454
- for (const name of nameArray) if (isTSLNode(nodes2[name])) delete nodes2[name];
15455
- return { nodes: nodes2 };
15456
- });
15457
- },
15688
+ (names, targetScope) => removeResourceEntries(store, "nodes", Array.isArray(names) ? names : [names], targetScope, isTSLNode),
15458
15689
  [store]
15459
15690
  );
15460
15691
  const clearNodes = useCallback(
15461
- (targetScope) => {
15462
- store.setState((state) => {
15463
- if (targetScope && targetScope !== "root") {
15464
- const { [targetScope]: _, ...rest } = state.nodes;
15465
- return { nodes: rest };
15466
- }
15467
- if (targetScope === "root") {
15468
- const nodes2 = {};
15469
- for (const [key, value] of Object.entries(state.nodes)) {
15470
- if (!isTSLNode(value)) nodes2[key] = value;
15471
- }
15472
- return { nodes: nodes2 };
15473
- }
15474
- return { nodes: {} };
15475
- });
15476
- },
15692
+ (targetScope) => clearResourceEntries(store, "nodes", targetScope, isTSLNode),
15477
15693
  [store]
15478
15694
  );
15479
15695
  const rebuildNodes = useCallback(
15480
- (targetScope) => {
15481
- store.setState((state) => {
15482
- let newNodes = state.nodes;
15483
- if (targetScope && targetScope !== "root") {
15484
- const { [targetScope]: _, ...rest } = state.nodes;
15485
- newNodes = rest;
15486
- } else if (targetScope === "root") {
15487
- newNodes = {};
15488
- for (const [key, value] of Object.entries(state.nodes)) {
15489
- if (!isTSLNode(value)) newNodes[key] = value;
15490
- }
15491
- } else {
15492
- newNodes = {};
15493
- }
15494
- return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
15495
- });
15496
- },
15696
+ (targetScope) => rebuildResource(store, "nodes", targetScope),
15497
15697
  [store]
15498
15698
  );
15499
15699
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15500
15700
  const storeNodes = usePrimaryThree((s) => s.nodes);
15501
- const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
15502
- const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
15503
- const readerDep = isReader ? storeNodes : null;
15504
- const creatorDep = isReader ? null : hmrVersion;
15505
- const nodes = useMemo(() => {
15506
- if (creatorOrScope === void 0) {
15507
- return storeNodes;
15508
- }
15509
- if (typeof creatorOrScope === "string") {
15510
- const scopeData = storeNodes[creatorOrScope];
15511
- if (scopeData && !isTSLNode(scopeData)) return scopeData;
15512
- return {};
15513
- }
15514
- const state = store.getState();
15515
- const set = store.setState;
15516
- const creator = creatorOrScope;
15517
- const wrappedState = createLazyCreatorState(state);
15518
- const created = creator(wrappedState);
15519
- const result = {};
15520
- let hasNewNodes = false;
15521
- if (scope) {
15522
- const currentScope = state.nodes[scope] ?? {};
15523
- for (const [name, node] of Object.entries(created)) {
15524
- if (currentScope[name]) {
15525
- result[name] = currentScope[name];
15526
- } else {
15527
- node.setName?.(`${scope}.${name}`);
15528
- result[name] = node;
15529
- hasNewNodes = true;
15530
- }
15531
- }
15532
- if (hasNewNodes) {
15533
- set((s) => ({
15534
- nodes: {
15535
- ...s.nodes,
15536
- [scope]: { ...s.nodes[scope], ...result }
15537
- }
15538
- }));
15539
- }
15540
- return result;
15541
- }
15542
- for (const [name, node] of Object.entries(created)) {
15543
- const existing = state.nodes[name];
15544
- if (existing && isTSLNode(existing)) {
15545
- result[name] = existing;
15546
- } else {
15547
- node.setName?.(name);
15548
- result[name] = node;
15549
- hasNewNodes = true;
15550
- }
15551
- }
15552
- if (hasNewNodes) {
15553
- set((s) => ({ nodes: { ...s.nodes, ...result } }));
15701
+ const created = useScopedResource({
15702
+ store,
15703
+ kind: "nodes",
15704
+ scope: isReader ? void 0 : scope,
15705
+ isLeaf: isTSLNode,
15706
+ create: () => {
15707
+ if (isReader) return {};
15708
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
15709
+ },
15710
+ prepare: (name, node) => {
15711
+ node.setName?.(scope ? `${scope}.${name}` : name);
15712
+ return node;
15554
15713
  }
15555
- return result;
15556
- }, [store, scopeDep, readerDep, creatorDep]);
15714
+ });
15715
+ let nodes = created;
15716
+ if (creatorOrScope === void 0) {
15717
+ nodes = storeNodes;
15718
+ } else if (typeof creatorOrScope === "string") {
15719
+ const scopeData = storeNodes[creatorOrScope];
15720
+ nodes = scopeData && !isTSLNode(scopeData) ? scopeData : {};
15721
+ }
15557
15722
  return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
15558
15723
  }
15559
15724
  function rebuildAllNodes(store, scope) {
15560
- store = store.getState().primaryStore ?? store;
15561
- store.setState((state) => {
15562
- let newNodes = state.nodes;
15563
- if (scope && scope !== "root") {
15564
- const { [scope]: _, ...rest } = state.nodes;
15565
- newNodes = rest;
15566
- } else if (scope === "root") {
15567
- newNodes = {};
15568
- for (const [key, value] of Object.entries(state.nodes)) {
15569
- if (!isTSLNode(value)) newNodes[key] = value;
15570
- }
15571
- } else {
15572
- newNodes = {};
15573
- }
15574
- return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
15575
- });
15725
+ rebuildResource(store, "nodes", scope);
15576
15726
  }
15577
15727
  function removeNodes(set, names, scope) {
15578
15728
  set((state) => {
@@ -15608,7 +15758,7 @@ function useLocalNodes(creator) {
15608
15758
  const textures = usePrimaryThree((s) => s.textures);
15609
15759
  const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
15610
15760
  return useMemo(() => {
15611
- const wrappedState = createLazyCreatorState(store.getState());
15761
+ const wrappedState = createLazyCreatorState(store.getState(), store);
15612
15762
  return creator(wrappedState);
15613
15763
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
15614
15764
  }
@@ -15629,58 +15779,15 @@ const disposeBuffer = (buffer) => {
15629
15779
  function useBuffers(creatorOrScope, scope) {
15630
15780
  const store = usePrimaryStore();
15631
15781
  const removeBuffers = useCallback(
15632
- (names, targetScope) => {
15633
- const nameArray = Array.isArray(names) ? names : [names];
15634
- store.setState((state) => {
15635
- if (targetScope) {
15636
- const currentScope = { ...state.buffers[targetScope] };
15637
- for (const name of nameArray) delete currentScope[name];
15638
- return { buffers: { ...state.buffers, [targetScope]: currentScope } };
15639
- }
15640
- const buffers2 = { ...state.buffers };
15641
- for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
15642
- return { buffers: buffers2 };
15643
- });
15644
- },
15782
+ (names, targetScope) => removeResourceEntries(store, "buffers", Array.isArray(names) ? names : [names], targetScope, isBufferLike),
15645
15783
  [store]
15646
15784
  );
15647
15785
  const clearBuffers = useCallback(
15648
- (targetScope) => {
15649
- store.setState((state) => {
15650
- if (targetScope && targetScope !== "root") {
15651
- const { [targetScope]: _, ...rest } = state.buffers;
15652
- return { buffers: rest };
15653
- }
15654
- if (targetScope === "root") {
15655
- const buffers2 = {};
15656
- for (const [key, value] of Object.entries(state.buffers)) {
15657
- if (!isBufferLike(value)) buffers2[key] = value;
15658
- }
15659
- return { buffers: buffers2 };
15660
- }
15661
- return { buffers: {} };
15662
- });
15663
- },
15786
+ (targetScope) => clearResourceEntries(store, "buffers", targetScope, isBufferLike),
15664
15787
  [store]
15665
15788
  );
15666
15789
  const rebuildBuffers = useCallback(
15667
- (targetScope) => {
15668
- store.setState((state) => {
15669
- let newBuffers = state.buffers;
15670
- if (targetScope && targetScope !== "root") {
15671
- const { [targetScope]: _, ...rest } = state.buffers;
15672
- newBuffers = rest;
15673
- } else if (targetScope === "root") {
15674
- newBuffers = {};
15675
- for (const [key, value] of Object.entries(state.buffers)) {
15676
- if (!isBufferLike(value)) newBuffers[key] = value;
15677
- }
15678
- } else {
15679
- newBuffers = {};
15680
- }
15681
- return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
15682
- });
15683
- },
15790
+ (targetScope) => rebuildResource(store, "buffers", targetScope),
15684
15791
  [store]
15685
15792
  );
15686
15793
  const disposeBuffers = useCallback(
@@ -15699,85 +15806,33 @@ function useBuffers(creatorOrScope, scope) {
15699
15806
  );
15700
15807
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15701
15808
  const storeBuffers = usePrimaryThree((s) => s.buffers);
15702
- const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
15703
- const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
15704
- const readerDep = isReader ? storeBuffers : null;
15705
- const creatorDep = isReader ? null : hmrVersion;
15706
- const buffers = useMemo(() => {
15707
- if (creatorOrScope === void 0) {
15708
- return storeBuffers;
15709
- }
15710
- if (typeof creatorOrScope === "string") {
15711
- const scopeData = storeBuffers[creatorOrScope];
15712
- if (scopeData && !isBufferLike(scopeData)) return scopeData;
15713
- return {};
15714
- }
15715
- const state = store.getState();
15716
- const set = store.setState;
15717
- const creator = creatorOrScope;
15718
- const wrappedState = createLazyCreatorState(state);
15719
- const created = creator(wrappedState);
15720
- const result = {};
15721
- let hasNewBuffers = false;
15722
- if (scope) {
15723
- const currentScope = state.buffers[scope] ?? {};
15724
- for (const [name, buffer] of Object.entries(created)) {
15725
- if (currentScope[name]) {
15726
- result[name] = currentScope[name];
15727
- } else {
15728
- if ("setName" in buffer && typeof buffer.setName === "function") {
15729
- buffer.setName(`${scope}.${name}`);
15730
- }
15731
- result[name] = buffer;
15732
- hasNewBuffers = true;
15733
- }
15734
- }
15735
- if (hasNewBuffers) {
15736
- set((s) => ({
15737
- buffers: {
15738
- ...s.buffers,
15739
- [scope]: { ...s.buffers[scope], ...result }
15740
- }
15741
- }));
15742
- }
15743
- return result;
15744
- }
15745
- for (const [name, buffer] of Object.entries(created)) {
15746
- const existing = state.buffers[name];
15747
- if (existing && isBufferLike(existing)) {
15748
- result[name] = existing;
15749
- } else {
15750
- if ("setName" in buffer && typeof buffer.setName === "function") {
15751
- buffer.setName(name);
15752
- }
15753
- result[name] = buffer;
15754
- hasNewBuffers = true;
15809
+ const created = useScopedResource({
15810
+ store,
15811
+ kind: "buffers",
15812
+ scope: isReader ? void 0 : scope,
15813
+ isLeaf: isBufferLike,
15814
+ create: () => {
15815
+ if (isReader) return {};
15816
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
15817
+ },
15818
+ prepare: (name, buffer) => {
15819
+ if ("setName" in buffer && typeof buffer.setName === "function") {
15820
+ buffer.setName(scope ? `${scope}.${name}` : name);
15755
15821
  }
15822
+ return buffer;
15756
15823
  }
15757
- if (hasNewBuffers) {
15758
- set((s) => ({ buffers: { ...s.buffers, ...result } }));
15759
- }
15760
- return result;
15761
- }, [store, scopeDep, readerDep, creatorDep]);
15824
+ });
15825
+ let buffers = created;
15826
+ if (creatorOrScope === void 0) {
15827
+ buffers = storeBuffers;
15828
+ } else if (typeof creatorOrScope === "string") {
15829
+ const scopeData = storeBuffers[creatorOrScope];
15830
+ buffers = scopeData && !isBufferLike(scopeData) ? scopeData : {};
15831
+ }
15762
15832
  return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
15763
15833
  }
15764
15834
  function rebuildAllBuffers(store, scope) {
15765
- store = store.getState().primaryStore ?? store;
15766
- store.setState((state) => {
15767
- let newBuffers = state.buffers;
15768
- if (scope && scope !== "root") {
15769
- const { [scope]: _, ...rest } = state.buffers;
15770
- newBuffers = rest;
15771
- } else if (scope === "root") {
15772
- newBuffers = {};
15773
- for (const [key, value] of Object.entries(state.buffers)) {
15774
- if (!isBufferLike(value)) newBuffers[key] = value;
15775
- }
15776
- } else {
15777
- newBuffers = {};
15778
- }
15779
- return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
15780
- });
15835
+ rebuildResource(store, "buffers", scope);
15781
15836
  }
15782
15837
 
15783
15838
  const isStorageLike = (value) => {
@@ -15796,58 +15851,15 @@ const disposeStorage = (storage) => {
15796
15851
  function useGPUStorage(creatorOrScope, scope) {
15797
15852
  const store = usePrimaryStore();
15798
15853
  const removeStorage = useCallback(
15799
- (names, targetScope) => {
15800
- const nameArray = Array.isArray(names) ? names : [names];
15801
- store.setState((state) => {
15802
- if (targetScope) {
15803
- const currentScope = { ...state.gpuStorage[targetScope] };
15804
- for (const name of nameArray) delete currentScope[name];
15805
- return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
15806
- }
15807
- const gpuStorage2 = { ...state.gpuStorage };
15808
- for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
15809
- return { gpuStorage: gpuStorage2 };
15810
- });
15811
- },
15854
+ (names, targetScope) => removeResourceEntries(store, "gpuStorage", Array.isArray(names) ? names : [names], targetScope, isStorageLike),
15812
15855
  [store]
15813
15856
  );
15814
15857
  const clearStorage = useCallback(
15815
- (targetScope) => {
15816
- store.setState((state) => {
15817
- if (targetScope && targetScope !== "root") {
15818
- const { [targetScope]: _, ...rest } = state.gpuStorage;
15819
- return { gpuStorage: rest };
15820
- }
15821
- if (targetScope === "root") {
15822
- const gpuStorage2 = {};
15823
- for (const [key, value] of Object.entries(state.gpuStorage)) {
15824
- if (!isStorageLike(value)) gpuStorage2[key] = value;
15825
- }
15826
- return { gpuStorage: gpuStorage2 };
15827
- }
15828
- return { gpuStorage: {} };
15829
- });
15830
- },
15858
+ (targetScope) => clearResourceEntries(store, "gpuStorage", targetScope, isStorageLike),
15831
15859
  [store]
15832
15860
  );
15833
15861
  const rebuildStorage = useCallback(
15834
- (targetScope) => {
15835
- store.setState((state) => {
15836
- let newStorage = state.gpuStorage;
15837
- if (targetScope && targetScope !== "root") {
15838
- const { [targetScope]: _, ...rest } = state.gpuStorage;
15839
- newStorage = rest;
15840
- } else if (targetScope === "root") {
15841
- newStorage = {};
15842
- for (const [key, value] of Object.entries(state.gpuStorage)) {
15843
- if (!isStorageLike(value)) newStorage[key] = value;
15844
- }
15845
- } else {
15846
- newStorage = {};
15847
- }
15848
- return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
15849
- });
15850
- },
15862
+ (targetScope) => rebuildResource(store, "gpuStorage", targetScope),
15851
15863
  [store]
15852
15864
  );
15853
15865
  const disposeStorageFn = useCallback(
@@ -15866,72 +15878,33 @@ function useGPUStorage(creatorOrScope, scope) {
15866
15878
  );
15867
15879
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15868
15880
  const storeStorage = usePrimaryThree((s) => s.gpuStorage);
15869
- const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
15870
- const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
15871
- const readerDep = isReader ? storeStorage : null;
15872
- const creatorDep = isReader ? null : hmrVersion;
15873
- const gpuStorage = useMemo(() => {
15874
- if (creatorOrScope === void 0) {
15875
- return storeStorage;
15876
- }
15877
- if (typeof creatorOrScope === "string") {
15878
- const scopeData = storeStorage[creatorOrScope];
15879
- if (scopeData && !isStorageLike(scopeData)) return scopeData;
15880
- return {};
15881
- }
15882
- const state = store.getState();
15883
- const set = store.setState;
15884
- const creator = creatorOrScope;
15885
- const wrappedState = createLazyCreatorState(state);
15886
- const created = creator(wrappedState);
15887
- const result = {};
15888
- let hasNewStorage = false;
15889
- if (scope) {
15890
- const currentScope = state.gpuStorage[scope] ?? {};
15891
- for (const [name, storage] of Object.entries(created)) {
15892
- if (currentScope[name]) {
15893
- result[name] = currentScope[name];
15894
- } else {
15895
- if ("setName" in storage && typeof storage.setName === "function") {
15896
- storage.setName(`${scope}.${name}`);
15897
- }
15898
- if ("name" in storage && typeof storage.name === "string") {
15899
- storage.name = `${scope}.${name}`;
15900
- }
15901
- result[name] = storage;
15902
- hasNewStorage = true;
15903
- }
15904
- }
15905
- if (hasNewStorage) {
15906
- set((s) => ({
15907
- gpuStorage: {
15908
- ...s.gpuStorage,
15909
- [scope]: { ...s.gpuStorage[scope], ...result }
15910
- }
15911
- }));
15881
+ const created = useScopedResource({
15882
+ store,
15883
+ kind: "gpuStorage",
15884
+ scope: isReader ? void 0 : scope,
15885
+ isLeaf: isStorageLike,
15886
+ create: () => {
15887
+ if (isReader) return {};
15888
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
15889
+ },
15890
+ prepare: (name, storage) => {
15891
+ const label = scope ? `${scope}.${name}` : name;
15892
+ if ("setName" in storage && typeof storage.setName === "function") {
15893
+ storage.setName(label);
15912
15894
  }
15913
- return result;
15914
- }
15915
- for (const [name, storage] of Object.entries(created)) {
15916
- const existing = state.gpuStorage[name];
15917
- if (existing && isStorageLike(existing)) {
15918
- result[name] = existing;
15919
- } else {
15920
- if ("setName" in storage && typeof storage.setName === "function") {
15921
- storage.setName(name);
15922
- }
15923
- if ("name" in storage && typeof storage.name === "string") {
15924
- storage.name = name;
15925
- }
15926
- result[name] = storage;
15927
- hasNewStorage = true;
15895
+ if ("name" in storage && typeof storage.name === "string") {
15896
+ storage.name = label;
15928
15897
  }
15898
+ return storage;
15929
15899
  }
15930
- if (hasNewStorage) {
15931
- set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
15932
- }
15933
- return result;
15934
- }, [store, scopeDep, readerDep, creatorDep]);
15900
+ });
15901
+ let gpuStorage = created;
15902
+ if (creatorOrScope === void 0) {
15903
+ gpuStorage = storeStorage;
15904
+ } else if (typeof creatorOrScope === "string") {
15905
+ const scopeData = storeStorage[creatorOrScope];
15906
+ gpuStorage = scopeData && !isStorageLike(scopeData) ? scopeData : {};
15907
+ }
15935
15908
  return {
15936
15909
  ...gpuStorage,
15937
15910
  removeStorage,
@@ -15941,22 +15914,7 @@ function useGPUStorage(creatorOrScope, scope) {
15941
15914
  };
15942
15915
  }
15943
15916
  function rebuildAllStorage(store, scope) {
15944
- store = store.getState().primaryStore ?? store;
15945
- store.setState((state) => {
15946
- let newStorage = state.gpuStorage;
15947
- if (scope && scope !== "root") {
15948
- const { [scope]: _, ...rest } = state.gpuStorage;
15949
- newStorage = rest;
15950
- } else if (scope === "root") {
15951
- newStorage = {};
15952
- for (const [key, value] of Object.entries(state.gpuStorage)) {
15953
- if (!isStorageLike(value)) newStorage[key] = value;
15954
- }
15955
- } else {
15956
- newStorage = {};
15957
- }
15958
- return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
15959
- });
15917
+ rebuildResource(store, "gpuStorage", scope);
15960
15918
  }
15961
15919
 
15962
15920
  function useRenderPipeline(mainCB, setupCB) {
@@ -15997,12 +15955,12 @@ function useRenderPipeline(mainCB, setupCB) {
15997
15955
  const state = store.getState();
15998
15956
  const set = store.setState;
15999
15957
  try {
16000
- let pp = state.renderPipeline;
15958
+ let pipeline = state.renderPipeline;
16001
15959
  let currentPasses = { ...state.passes };
16002
- let justCreatedPP = false;
16003
- if (!pp) {
16004
- pp = new PostProcessing(renderer);
16005
- justCreatedPP = true;
15960
+ let justCreatedPipeline = false;
15961
+ if (!pipeline) {
15962
+ pipeline = new RenderPipeline(renderer);
15963
+ justCreatedPipeline = true;
16006
15964
  }
16007
15965
  const cacheValid = scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16008
15966
  let scenePass;
@@ -16013,9 +15971,9 @@ function useRenderPipeline(mainCB, setupCB) {
16013
15971
  scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
16014
15972
  }
16015
15973
  currentPasses.scenePass = scenePass;
16016
- if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
16017
- set({ renderPipeline: pp, passes: currentPasses });
16018
- const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
15974
+ if (!pipeline.outputNode || justCreatedPipeline) pipeline.outputNode = scenePass;
15975
+ set({ renderPipeline: pipeline, passes: currentPasses });
15976
+ const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
16019
15977
  if (shouldRunCallbacks) {
16020
15978
  if (setupCBRef.current) {
16021
15979
  const freshState = store.getState();
@@ -16036,7 +15994,7 @@ function useRenderPipeline(mainCB, setupCB) {
16036
15994
  callbacksRanRef.current = true;
16037
15995
  }
16038
15996
  } catch (error) {
16039
- console.error("[useRenderPipeline] Setup error:", error);
15997
+ console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
16040
15998
  }
16041
15999
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16042
16000
  const passes = useThree((s) => s.passes);