@react-three/fiber 10.0.0-canary.8f5b4d3 → 10.0.0-canary.ac30da2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/legacy.cjs CHANGED
@@ -50,11 +50,9 @@ const Tb__default = /*#__PURE__*/_interopDefaultCompat(Tb);
50
50
 
51
51
  const R3F_BUILD_LEGACY = true;
52
52
  const R3F_BUILD_WEBGPU = false;
53
- const Inspector = class Inspector2 {
54
- constructor() {
55
- throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
56
- }
57
- };
53
+ async function loadInspector() {
54
+ throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
55
+ }
58
56
  const WebGPURenderer = class WebGPURenderer2 {
59
57
  constructor() {
60
58
  throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
@@ -67,12 +65,12 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
67
65
  __proto__: null,
68
66
  CubeRenderTarget: CubeRenderTarget,
69
67
  CubeRenderTargetCompat: three.WebGLCubeRenderTarget,
70
- Inspector: Inspector,
71
68
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
72
69
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
73
70
  RenderTarget: RenderTarget,
74
71
  RenderTargetCompat: three.WebGLRenderTarget,
75
- WebGPURenderer: WebGPURenderer
72
+ WebGPURenderer: WebGPURenderer,
73
+ loadInspector: loadInspector
76
74
  }, [three__namespace]);
77
75
 
78
76
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -1070,17 +1068,10 @@ const createStore = (invalidate, advance) => {
1070
1068
  oldSize = size;
1071
1069
  oldDpr = viewport.dpr;
1072
1070
  updateCamera(camera, size);
1073
- if (internal.isSecondary && canvasTarget) {
1074
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1075
- canvasTarget.setSize(size.width, size.height, false);
1076
- } else {
1077
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1078
- actualRenderer.setSize(size.width, size.height, false);
1079
- if (canvasTarget) {
1080
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1081
- canvasTarget.setSize(size.width, size.height, false);
1082
- }
1083
- }
1071
+ const resizeTarget = canvasTarget ?? actualRenderer;
1072
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1073
+ resizeTarget.setSize(size.width, size.height, false);
1074
+ internal.canvasTargetSizeDirty = true;
1084
1075
  }
1085
1076
  if (camera !== oldCamera) {
1086
1077
  oldCamera = camera;
@@ -1142,7 +1133,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1142
1133
  const results = cacheKeys.map(
1143
1134
  (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1144
1135
  );
1145
- return Array.isArray(input) ? results : results[0];
1136
+ const stableRef = React.useRef(null);
1137
+ const previous = stableRef.current;
1138
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1139
+ stableRef.current = stable;
1140
+ return Array.isArray(input) ? stable : stable[0];
1146
1141
  }
1147
1142
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1148
1143
  const inputs = Array.isArray(input) ? input : [input];
@@ -1307,6 +1302,11 @@ function getUrls(input) {
1307
1302
  if (Array.isArray(input)) return input;
1308
1303
  return Object.values(input);
1309
1304
  }
1305
+ function inputSignature(input) {
1306
+ if (typeof input === "string") return input;
1307
+ if (Array.isArray(input)) return input.join("\0");
1308
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1309
+ }
1310
1310
  function allUrlsCached(urls, textureCache) {
1311
1311
  return urls.every((url) => textureCache.has(url));
1312
1312
  }
@@ -1332,18 +1332,19 @@ function useTexture(input, optionsOrOnLoad) {
1332
1332
  const onLoadRef = React.useRef(onLoad);
1333
1333
  onLoadRef.current = onLoad;
1334
1334
  const onLoadCalledForRef = React.useRef(null);
1335
- const urls = React.useMemo(() => getUrls(input), [input]);
1335
+ const inputKey = React.useMemo(() => inputSignature(input), [input]);
1336
+ const stableInput = React.useMemo(() => input, [inputKey]);
1337
+ const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
1336
1338
  const cachedResult = React.useMemo(() => {
1337
1339
  if (!cache) return null;
1338
1340
  const textures = store.getState().textures;
1339
1341
  if (!allUrlsCached(urls, textures)) return null;
1340
- return buildFromCache(input, textures);
1341
- }, [cache, urls, input, store]);
1342
+ return buildFromCache(stableInput, textures);
1343
+ }, [cache, urls, stableInput, store]);
1342
1344
  const loadedTextures = useLoader(
1343
1345
  three.TextureLoader,
1344
- IsObject(input) ? Object.values(input) : input
1346
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1345
1347
  );
1346
- const inputKey = urls.join("\0");
1347
1348
  React.useLayoutEffect(() => {
1348
1349
  if (cachedResult) return;
1349
1350
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1370,28 +1371,28 @@ function useTexture(input, optionsOrOnLoad) {
1370
1371
  }, [renderer, loadedTextures, cachedResult]);
1371
1372
  const mappedTextures = React.useMemo(() => {
1372
1373
  if (cachedResult) return cachedResult;
1373
- if (IsObject(input)) {
1374
+ if (IsObject(stableInput)) {
1374
1375
  const keyed = {};
1375
1376
  const textureArray = loadedTextures;
1376
1377
  let i = 0;
1377
- for (const key in input) keyed[key] = textureArray[i++];
1378
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1378
1379
  return keyed;
1379
1380
  } else {
1380
1381
  return loadedTextures;
1381
1382
  }
1382
- }, [input, loadedTextures, cachedResult]);
1383
+ }, [stableInput, loadedTextures, cachedResult]);
1383
1384
  React.useEffect(() => {
1384
1385
  if (!cache) return;
1385
1386
  const urlTextureMap = [];
1386
- if (typeof input === "string") {
1387
- urlTextureMap.push([input, mappedTextures]);
1388
- } else if (Array.isArray(input)) {
1387
+ if (typeof stableInput === "string") {
1388
+ urlTextureMap.push([stableInput, mappedTextures]);
1389
+ } else if (Array.isArray(stableInput)) {
1389
1390
  const textureArray = mappedTextures;
1390
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1391
- } else if (IsObject(input)) {
1391
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1392
+ } else if (IsObject(stableInput)) {
1392
1393
  const textureRecord = mappedTextures;
1393
- for (const key in input) {
1394
- const url = input[key];
1394
+ for (const key in stableInput) {
1395
+ const url = stableInput[key];
1395
1396
  urlTextureMap.push([url, textureRecord[key]]);
1396
1397
  }
1397
1398
  }
@@ -1420,7 +1421,7 @@ function useTexture(input, optionsOrOnLoad) {
1420
1421
  }
1421
1422
  return { _textureRefs: refs };
1422
1423
  });
1423
- }, [cache, input, mappedTextures, store]);
1424
+ }, [cache, stableInput, mappedTextures, store]);
1424
1425
  return mappedTextures;
1425
1426
  }
1426
1427
  useTexture.preload = (url) => useLoader.preload(three.TextureLoader, url);
@@ -12978,7 +12979,10 @@ function releaseInternalPointerCapture(internal, obj, pointerId) {
12978
12979
  const captureData = pointerState.captured.get(obj);
12979
12980
  if (captureData) {
12980
12981
  pointerState.captured.delete(obj);
12981
- captureData.target.releasePointerCapture(pointerId);
12982
+ try {
12983
+ captureData.target.releasePointerCapture?.(pointerId);
12984
+ } catch {
12985
+ }
12982
12986
  }
12983
12987
  }
12984
12988
  function swapInteractivity(store, object, newObject) {
@@ -13072,7 +13076,9 @@ function createEvents(store) {
13072
13076
  state2.events.compute?.(event, state2, state2.previousRoot?.getState());
13073
13077
  if (state2.raycaster.camera === void 0) state2.raycaster.camera = null;
13074
13078
  }
13075
- return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
13079
+ if (!state2.raycaster.camera) return [];
13080
+ obj.updateWorldMatrix(true, true);
13081
+ return state2.raycaster.intersectObject(obj, true);
13076
13082
  }
13077
13083
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
13078
13084
  const aInteractivePriority = a.object.userData?.interactivePriority;
@@ -14400,6 +14406,10 @@ function createRoot(canvas) {
14400
14406
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14401
14407
  const renderer2 = state2.internal.actualRenderer;
14402
14408
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14409
+ if (state2.internal.canvasTargetSizeDirty) {
14410
+ state2.internal.canvasTargetSizeDirty = false;
14411
+ renderer2.backend?.updateSize?.();
14412
+ }
14403
14413
  }
14404
14414
  },
14405
14415
  {
@@ -15132,6 +15142,7 @@ function CanvasImpl({
15132
15142
  const canvas = canvasRef.current;
15133
15143
  if (canvas) {
15134
15144
  return () => {
15145
+ if (canvas.isConnected) return;
15135
15146
  unmountComponentAtNode(canvas);
15136
15147
  root.current = null;
15137
15148
  };
package/dist/legacy.d.cts CHANGED
@@ -38,9 +38,9 @@ function _mergeNamespaces(n, m) {
38
38
  declare const R3F_BUILD_LEGACY = true;
39
39
  declare const R3F_BUILD_WEBGPU = false;
40
40
 
41
- declare const Inspector: {
42
- new (): {};
43
- };
41
+ type Inspector = never;
42
+ /** Not available on the legacy (WebGL-only) entry. */
43
+ declare function loadInspector(): Promise<never>;
44
44
  declare const WebGPURenderer: {
45
45
  new (): {};
46
46
  };
@@ -58,7 +58,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
59
59
  RenderTarget: RenderTarget,
60
60
  RenderTargetCompat: WebGLRenderTarget,
61
- WebGPURenderer: WebGPURenderer
61
+ WebGPURenderer: WebGPURenderer,
62
+ loadInspector: loadInspector
62
63
  }, [THREE$1]);
63
64
 
64
65
  //* Utility Types ==============================
@@ -496,6 +497,18 @@ interface InternalState {
496
497
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
498
  */
498
499
  canvasTarget?: CanvasTarget
500
+ /**
501
+ * Set when this root's canvas target has been resized and the backend's cached render pass
502
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
503
+ *
504
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
505
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
506
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
507
+ * invalidate some other canvas's descriptor instead.
508
+ *
509
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
510
+ */
511
+ canvasTargetSizeDirty?: boolean
499
512
  /**
500
513
  * Whether multi-canvas rendering is active.
501
514
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
package/dist/legacy.d.mts CHANGED
@@ -38,9 +38,9 @@ function _mergeNamespaces(n, m) {
38
38
  declare const R3F_BUILD_LEGACY = true;
39
39
  declare const R3F_BUILD_WEBGPU = false;
40
40
 
41
- declare const Inspector: {
42
- new (): {};
43
- };
41
+ type Inspector = never;
42
+ /** Not available on the legacy (WebGL-only) entry. */
43
+ declare function loadInspector(): Promise<never>;
44
44
  declare const WebGPURenderer: {
45
45
  new (): {};
46
46
  };
@@ -58,7 +58,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
59
59
  RenderTarget: RenderTarget,
60
60
  RenderTargetCompat: WebGLRenderTarget,
61
- WebGPURenderer: WebGPURenderer
61
+ WebGPURenderer: WebGPURenderer,
62
+ loadInspector: loadInspector
62
63
  }, [THREE$1]);
63
64
 
64
65
  //* Utility Types ==============================
@@ -496,6 +497,18 @@ interface InternalState {
496
497
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
498
  */
498
499
  canvasTarget?: CanvasTarget
500
+ /**
501
+ * Set when this root's canvas target has been resized and the backend's cached render pass
502
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
503
+ *
504
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
505
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
506
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
507
+ * invalidate some other canvas's descriptor instead.
508
+ *
509
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
510
+ */
511
+ canvasTargetSizeDirty?: boolean
499
512
  /**
500
513
  * Whether multi-canvas rendering is active.
501
514
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
package/dist/legacy.d.ts CHANGED
@@ -38,9 +38,9 @@ function _mergeNamespaces(n, m) {
38
38
  declare const R3F_BUILD_LEGACY = true;
39
39
  declare const R3F_BUILD_WEBGPU = false;
40
40
 
41
- declare const Inspector: {
42
- new (): {};
43
- };
41
+ type Inspector = never;
42
+ /** Not available on the legacy (WebGL-only) entry. */
43
+ declare function loadInspector(): Promise<never>;
44
44
  declare const WebGPURenderer: {
45
45
  new (): {};
46
46
  };
@@ -58,7 +58,8 @@ var THREE = /*#__PURE__*/_mergeNamespaces({
58
58
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
59
59
  RenderTarget: RenderTarget,
60
60
  RenderTargetCompat: WebGLRenderTarget,
61
- WebGPURenderer: WebGPURenderer
61
+ WebGPURenderer: WebGPURenderer,
62
+ loadInspector: loadInspector
62
63
  }, [THREE$1]);
63
64
 
64
65
  //* Utility Types ==============================
@@ -496,6 +497,18 @@ interface InternalState {
496
497
  * @see https://threejs.org/docs/#api/en/renderers/common/CanvasTarget
497
498
  */
498
499
  canvasTarget?: CanvasTarget
500
+ /**
501
+ * Set when this root's canvas target has been resized and the backend's cached render pass
502
+ * descriptor (which holds a depth-stencil view built once per canvas) is therefore stale.
503
+ *
504
+ * Flushed by the canvas-target job in the `start` phase, which is the only place this root's
505
+ * target is guaranteed to be the renderer's active one — `backend.updateSize()` operates on
506
+ * whatever `getCanvasTarget()` returns, so calling it from the resize subscription would
507
+ * invalidate some other canvas's descriptor instead.
508
+ *
509
+ * @see https://github.com/pmndrs/react-three-fiber/issues/3847
510
+ */
511
+ canvasTargetSizeDirty?: boolean
499
512
  /**
500
513
  * Whether multi-canvas rendering is active.
501
514
  * True when any canvas uses `renderer={{ primaryCanvas: 'id' }}` to share a renderer.
package/dist/legacy.mjs CHANGED
@@ -30,11 +30,9 @@ function _mergeNamespaces(n, m) {
30
30
 
31
31
  const R3F_BUILD_LEGACY = true;
32
32
  const R3F_BUILD_WEBGPU = false;
33
- const Inspector = class Inspector2 {
34
- constructor() {
35
- throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
36
- }
37
- };
33
+ async function loadInspector() {
34
+ throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
35
+ }
38
36
  const WebGPURenderer = class WebGPURenderer2 {
39
37
  constructor() {
40
38
  throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
@@ -47,12 +45,12 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
47
45
  __proto__: null,
48
46
  CubeRenderTarget: CubeRenderTarget,
49
47
  CubeRenderTargetCompat: WebGLCubeRenderTarget,
50
- Inspector: Inspector,
51
48
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
52
49
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
53
50
  RenderTarget: RenderTarget,
54
51
  RenderTargetCompat: WebGLRenderTarget,
55
- WebGPURenderer: WebGPURenderer
52
+ WebGPURenderer: WebGPURenderer,
53
+ loadInspector: loadInspector
56
54
  }, [three]);
57
55
 
58
56
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -1050,17 +1048,10 @@ const createStore = (invalidate, advance) => {
1050
1048
  oldSize = size;
1051
1049
  oldDpr = viewport.dpr;
1052
1050
  updateCamera(camera, size);
1053
- if (internal.isSecondary && canvasTarget) {
1054
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1055
- canvasTarget.setSize(size.width, size.height, false);
1056
- } else {
1057
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1058
- actualRenderer.setSize(size.width, size.height, false);
1059
- if (canvasTarget) {
1060
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1061
- canvasTarget.setSize(size.width, size.height, false);
1062
- }
1063
- }
1051
+ const resizeTarget = canvasTarget ?? actualRenderer;
1052
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1053
+ resizeTarget.setSize(size.width, size.height, false);
1054
+ internal.canvasTargetSizeDirty = true;
1064
1055
  }
1065
1056
  if (camera !== oldCamera) {
1066
1057
  oldCamera = camera;
@@ -1122,7 +1113,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1122
1113
  const results = cacheKeys.map(
1123
1114
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1124
1115
  );
1125
- return Array.isArray(input) ? results : results[0];
1116
+ const stableRef = useRef(null);
1117
+ const previous = stableRef.current;
1118
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1119
+ stableRef.current = stable;
1120
+ return Array.isArray(input) ? stable : stable[0];
1126
1121
  }
1127
1122
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1128
1123
  const inputs = Array.isArray(input) ? input : [input];
@@ -1287,6 +1282,11 @@ function getUrls(input) {
1287
1282
  if (Array.isArray(input)) return input;
1288
1283
  return Object.values(input);
1289
1284
  }
1285
+ function inputSignature(input) {
1286
+ if (typeof input === "string") return input;
1287
+ if (Array.isArray(input)) return input.join("\0");
1288
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1289
+ }
1290
1290
  function allUrlsCached(urls, textureCache) {
1291
1291
  return urls.every((url) => textureCache.has(url));
1292
1292
  }
@@ -1312,18 +1312,19 @@ function useTexture(input, optionsOrOnLoad) {
1312
1312
  const onLoadRef = useRef(onLoad);
1313
1313
  onLoadRef.current = onLoad;
1314
1314
  const onLoadCalledForRef = useRef(null);
1315
- const urls = useMemo(() => getUrls(input), [input]);
1315
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1316
+ const stableInput = useMemo(() => input, [inputKey]);
1317
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1316
1318
  const cachedResult = useMemo(() => {
1317
1319
  if (!cache) return null;
1318
1320
  const textures = store.getState().textures;
1319
1321
  if (!allUrlsCached(urls, textures)) return null;
1320
- return buildFromCache(input, textures);
1321
- }, [cache, urls, input, store]);
1322
+ return buildFromCache(stableInput, textures);
1323
+ }, [cache, urls, stableInput, store]);
1322
1324
  const loadedTextures = useLoader(
1323
1325
  TextureLoader,
1324
- IsObject(input) ? Object.values(input) : input
1326
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1325
1327
  );
1326
- const inputKey = urls.join("\0");
1327
1328
  useLayoutEffect(() => {
1328
1329
  if (cachedResult) return;
1329
1330
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1350,28 +1351,28 @@ function useTexture(input, optionsOrOnLoad) {
1350
1351
  }, [renderer, loadedTextures, cachedResult]);
1351
1352
  const mappedTextures = useMemo(() => {
1352
1353
  if (cachedResult) return cachedResult;
1353
- if (IsObject(input)) {
1354
+ if (IsObject(stableInput)) {
1354
1355
  const keyed = {};
1355
1356
  const textureArray = loadedTextures;
1356
1357
  let i = 0;
1357
- for (const key in input) keyed[key] = textureArray[i++];
1358
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1358
1359
  return keyed;
1359
1360
  } else {
1360
1361
  return loadedTextures;
1361
1362
  }
1362
- }, [input, loadedTextures, cachedResult]);
1363
+ }, [stableInput, loadedTextures, cachedResult]);
1363
1364
  useEffect(() => {
1364
1365
  if (!cache) return;
1365
1366
  const urlTextureMap = [];
1366
- if (typeof input === "string") {
1367
- urlTextureMap.push([input, mappedTextures]);
1368
- } else if (Array.isArray(input)) {
1367
+ if (typeof stableInput === "string") {
1368
+ urlTextureMap.push([stableInput, mappedTextures]);
1369
+ } else if (Array.isArray(stableInput)) {
1369
1370
  const textureArray = mappedTextures;
1370
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1371
- } else if (IsObject(input)) {
1371
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1372
+ } else if (IsObject(stableInput)) {
1372
1373
  const textureRecord = mappedTextures;
1373
- for (const key in input) {
1374
- const url = input[key];
1374
+ for (const key in stableInput) {
1375
+ const url = stableInput[key];
1375
1376
  urlTextureMap.push([url, textureRecord[key]]);
1376
1377
  }
1377
1378
  }
@@ -1400,7 +1401,7 @@ function useTexture(input, optionsOrOnLoad) {
1400
1401
  }
1401
1402
  return { _textureRefs: refs };
1402
1403
  });
1403
- }, [cache, input, mappedTextures, store]);
1404
+ }, [cache, stableInput, mappedTextures, store]);
1404
1405
  return mappedTextures;
1405
1406
  }
1406
1407
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -12958,7 +12959,10 @@ function releaseInternalPointerCapture(internal, obj, pointerId) {
12958
12959
  const captureData = pointerState.captured.get(obj);
12959
12960
  if (captureData) {
12960
12961
  pointerState.captured.delete(obj);
12961
- captureData.target.releasePointerCapture(pointerId);
12962
+ try {
12963
+ captureData.target.releasePointerCapture?.(pointerId);
12964
+ } catch {
12965
+ }
12962
12966
  }
12963
12967
  }
12964
12968
  function swapInteractivity(store, object, newObject) {
@@ -13052,7 +13056,9 @@ function createEvents(store) {
13052
13056
  state2.events.compute?.(event, state2, state2.previousRoot?.getState());
13053
13057
  if (state2.raycaster.camera === void 0) state2.raycaster.camera = null;
13054
13058
  }
13055
- return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
13059
+ if (!state2.raycaster.camera) return [];
13060
+ obj.updateWorldMatrix(true, true);
13061
+ return state2.raycaster.intersectObject(obj, true);
13056
13062
  }
13057
13063
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
13058
13064
  const aInteractivePriority = a.object.userData?.interactivePriority;
@@ -14380,6 +14386,10 @@ function createRoot(canvas) {
14380
14386
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14381
14387
  const renderer2 = state2.internal.actualRenderer;
14382
14388
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14389
+ if (state2.internal.canvasTargetSizeDirty) {
14390
+ state2.internal.canvasTargetSizeDirty = false;
14391
+ renderer2.backend?.updateSize?.();
14392
+ }
14383
14393
  }
14384
14394
  },
14385
14395
  {
@@ -15112,6 +15122,7 @@ function CanvasImpl({
15112
15122
  const canvas = canvasRef.current;
15113
15123
  if (canvas) {
15114
15124
  return () => {
15125
+ if (canvas.isConnected) return;
15115
15126
  unmountComponentAtNode(canvas);
15116
15127
  root.current = null;
15117
15128
  };