@react-three/fiber 10.0.0-alpha.3 → 10.0.0-alpha.5

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,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const webgpu = require('three/webgpu');
4
- const Inspector_js = require('three/addons/inspector/Inspector.js');
5
4
  const jsxRuntime = require('react/jsx-runtime');
6
5
  const React = require('react');
7
6
  const useMeasure = require('react-use-measure');
@@ -11,6 +10,7 @@ const scheduler = require('@pmndrs/scheduler');
11
10
  const Tb = require('scheduler');
12
11
  const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
13
12
  const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
13
+ const HDRCubeTextureLoader_js = require('three/examples/jsm/loaders/HDRCubeTextureLoader.js');
14
14
  const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
15
15
  const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
16
16
  const gainmapJs = require('@monogrid/gainmap-js');
@@ -54,6 +54,10 @@ const Tb__default = /*#__PURE__*/_interopDefaultCompat(Tb);
54
54
 
55
55
  const R3F_BUILD_LEGACY = false;
56
56
  const R3F_BUILD_WEBGPU = true;
57
+ async function loadInspector() {
58
+ const { Inspector } = await import('three/addons/inspector/Inspector.js');
59
+ return Inspector;
60
+ }
57
61
  const WebGLRenderer = class WebGLRenderer2 {
58
62
  constructor() {
59
63
  throw new Error(
@@ -67,13 +71,13 @@ const WebGLCubeRenderTarget = null;
67
71
  const THREE = /*#__PURE__*/_mergeNamespaces({
68
72
  __proto__: null,
69
73
  CubeRenderTargetCompat: webgpu.CubeRenderTarget,
70
- Inspector: Inspector_js.Inspector,
71
74
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
72
75
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
73
76
  RenderTargetCompat: webgpu.RenderTarget,
74
77
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
75
78
  WebGLRenderTarget: WebGLRenderTarget,
76
- WebGLRenderer: WebGLRenderer
79
+ WebGLRenderer: WebGLRenderer,
80
+ loadInspector: loadInspector
77
81
  }, [webgpu__namespace]);
78
82
 
79
83
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -536,8 +540,10 @@ function hasVisibilityHandlers(handlers) {
536
540
  }
537
541
 
538
542
  const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
539
- function fromRef(ref) {
540
- return { [FROM_REF]: ref };
543
+ function fromRef(ref, transform) {
544
+ const marker = { [FROM_REF]: ref };
545
+ if (transform) marker.transform = transform;
546
+ return marker;
541
547
  }
542
548
  function isFromRef(value) {
543
549
  return value !== null && typeof value === "object" && FROM_REF in value;
@@ -638,11 +644,9 @@ function diffProps(instance, newProps) {
638
644
  for (const prop in instance.props) {
639
645
  if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
640
646
  const { root, key } = resolve(instance.object, prop);
641
- if (root.constructor && root.constructor.length === 0) {
647
+ if (typeof root?.constructor === "function") {
642
648
  const ctor = getMemoizedPrototype(root);
643
649
  if (!is.und(ctor)) changedProps[prop] = ctor[key];
644
- } else {
645
- changedProps[prop] = 0;
646
650
  }
647
651
  }
648
652
  return changedProps;
@@ -932,7 +936,7 @@ const createStore = (invalidate, advance) => {
932
936
  size: newSize,
933
937
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
934
938
  }));
935
- scheduler.getScheduler().invalidate();
939
+ get().invalidate();
936
940
  }
937
941
  }
938
942
  return;
@@ -947,15 +951,16 @@ const createStore = (invalidate, advance) => {
947
951
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
948
952
  _sizeImperative: true
949
953
  }));
950
- scheduler.getScheduler().invalidate();
954
+ get().invalidate();
951
955
  },
952
956
  setDpr: (dpr) => set((state2) => {
953
957
  const resolved = calculateDpr(dpr);
954
958
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
955
959
  }),
956
960
  setFrameloop: (frameloop = "always") => {
961
+ const rootId = get().internal.rootId;
962
+ if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
957
963
  set(() => ({ frameloop }));
958
- scheduler.getScheduler().frameloop = frameloop;
959
964
  },
960
965
  setError: (error) => set(() => ({ error })),
961
966
  error: null,
@@ -1071,17 +1076,11 @@ const createStore = (invalidate, advance) => {
1071
1076
  oldSize = size;
1072
1077
  oldDpr = viewport.dpr;
1073
1078
  updateCamera(camera, size);
1074
- if (internal.isSecondary && canvasTarget) {
1075
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1076
- canvasTarget.setSize(size.width, size.height, false);
1077
- } else {
1078
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1079
- actualRenderer.setSize(size.width, size.height, false);
1080
- if (canvasTarget) {
1081
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1082
- canvasTarget.setSize(size.width, size.height, false);
1083
- }
1084
- }
1079
+ const resizeTarget = canvasTarget ?? actualRenderer;
1080
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1081
+ resizeTarget.setSize(size.width, size.height, false);
1082
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1083
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1085
1084
  }
1086
1085
  if (camera !== oldCamera) {
1087
1086
  oldCamera = camera;
@@ -1143,7 +1142,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1143
1142
  const results = cacheKeys.map(
1144
1143
  (key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1145
1144
  );
1146
- return Array.isArray(input) ? results : results[0];
1145
+ const stableRef = React.useRef(null);
1146
+ const previous = stableRef.current;
1147
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1148
+ stableRef.current = stable;
1149
+ return Array.isArray(input) ? stable : stable[0];
1147
1150
  }
1148
1151
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1149
1152
  const inputs = Array.isArray(input) ? input : [input];
@@ -1158,7 +1161,7 @@ useLoader.clear = function(loader, input, cacheKey) {
1158
1161
  };
1159
1162
  useLoader.loader = getLoader$1;
1160
1163
 
1161
- function useFrame(callback, priorityOrOptions) {
1164
+ function useFrame$1(callback, priorityOrOptions) {
1162
1165
  const store = React__namespace.useContext(context);
1163
1166
  const isInsideCanvas = store !== null;
1164
1167
  const scheduler$1 = scheduler.getScheduler();
@@ -1263,6 +1266,13 @@ function useFrame(callback, priorityOrOptions) {
1263
1266
  * Use for controlling the entire frame loop, adding phases, etc.
1264
1267
  */
1265
1268
  scheduler: scheduler2,
1269
+ /**
1270
+ * The root that currently owns this job. Resolve on access because ambient
1271
+ * jobs can be adopted by a Canvas after the controls object is created.
1272
+ */
1273
+ get rootId() {
1274
+ return scheduler.getScheduler().getJobRootId(id);
1275
+ },
1266
1276
  /**
1267
1277
  * Manually step this job only.
1268
1278
  * Bypasses FPS limiting - always runs.
@@ -1279,6 +1289,14 @@ function useFrame(callback, priorityOrOptions) {
1279
1289
  stepAll: (timestamp) => {
1280
1290
  scheduler.getScheduler().step(timestamp);
1281
1291
  },
1292
+ /**
1293
+ * Request frames for this job's owning root without waking sibling roots.
1294
+ */
1295
+ invalidate: (frames, stackFrames) => {
1296
+ const scheduler3 = scheduler.getScheduler();
1297
+ const rootId = scheduler3.getJobRootId(id);
1298
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1299
+ },
1282
1300
  /**
1283
1301
  * Pause this job (set enabled=false).
1284
1302
  * Job remains registered but won't run.
@@ -1308,6 +1326,11 @@ function getUrls(input) {
1308
1326
  if (Array.isArray(input)) return input;
1309
1327
  return Object.values(input);
1310
1328
  }
1329
+ function inputSignature(input) {
1330
+ if (typeof input === "string") return input;
1331
+ if (Array.isArray(input)) return input.join("\0");
1332
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1333
+ }
1311
1334
  function allUrlsCached(urls, textureCache) {
1312
1335
  return urls.every((url) => textureCache.has(url));
1313
1336
  }
@@ -1326,31 +1349,44 @@ function buildFromCache(input, textureCache) {
1326
1349
  }
1327
1350
  }
1328
1351
  function useTexture(input, optionsOrOnLoad) {
1329
- const renderer = useThree((state) => state.internal.actualRenderer);
1352
+ const renderer = useThree$1((state) => state.internal.actualRenderer);
1330
1353
  const store = useStore();
1331
1354
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
1332
1355
  const { onLoad, cache = true } = options;
1333
1356
  const onLoadRef = React.useRef(onLoad);
1334
1357
  onLoadRef.current = onLoad;
1335
1358
  const onLoadCalledForRef = React.useRef(null);
1336
- const urls = React.useMemo(() => getUrls(input), [input]);
1359
+ const inputKey = React.useMemo(() => inputSignature(input), [input]);
1360
+ const stableInput = React.useMemo(() => input, [inputKey]);
1361
+ const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
1337
1362
  const cachedResult = React.useMemo(() => {
1338
1363
  if (!cache) return null;
1339
1364
  const textures = store.getState().textures;
1340
1365
  if (!allUrlsCached(urls, textures)) return null;
1341
- return buildFromCache(input, textures);
1342
- }, [cache, urls, input, store]);
1366
+ return buildFromCache(stableInput, textures);
1367
+ }, [cache, urls, stableInput, store]);
1343
1368
  const loadedTextures = useLoader(
1344
1369
  webgpu.TextureLoader,
1345
- IsObject(input) ? Object.values(input) : input
1370
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1346
1371
  );
1347
- const inputKey = urls.join("\0");
1372
+ const mappedTextures = React.useMemo(() => {
1373
+ if (cachedResult) return cachedResult;
1374
+ if (IsObject(stableInput)) {
1375
+ const keyed = {};
1376
+ const textureArray = loadedTextures;
1377
+ let i = 0;
1378
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1379
+ return keyed;
1380
+ } else {
1381
+ return loadedTextures;
1382
+ }
1383
+ }, [stableInput, loadedTextures, cachedResult]);
1348
1384
  React.useLayoutEffect(() => {
1349
1385
  if (cachedResult) return;
1350
1386
  if (onLoadCalledForRef.current === inputKey) return;
1351
1387
  onLoadCalledForRef.current = inputKey;
1352
- onLoadRef.current?.(loadedTextures);
1353
- }, [cachedResult, loadedTextures, inputKey]);
1388
+ onLoadRef.current?.(mappedTextures);
1389
+ }, [cachedResult, mappedTextures, inputKey]);
1354
1390
  React.useEffect(() => {
1355
1391
  if (cachedResult) return;
1356
1392
  if ("initTexture" in renderer) {
@@ -1369,30 +1405,18 @@ function useTexture(input, optionsOrOnLoad) {
1369
1405
  });
1370
1406
  }
1371
1407
  }, [renderer, loadedTextures, cachedResult]);
1372
- const mappedTextures = React.useMemo(() => {
1373
- if (cachedResult) return cachedResult;
1374
- if (IsObject(input)) {
1375
- const keyed = {};
1376
- const textureArray = loadedTextures;
1377
- let i = 0;
1378
- for (const key in input) keyed[key] = textureArray[i++];
1379
- return keyed;
1380
- } else {
1381
- return loadedTextures;
1382
- }
1383
- }, [input, loadedTextures, cachedResult]);
1384
1408
  React.useEffect(() => {
1385
1409
  if (!cache) return;
1386
1410
  const urlTextureMap = [];
1387
- if (typeof input === "string") {
1388
- urlTextureMap.push([input, mappedTextures]);
1389
- } else if (Array.isArray(input)) {
1411
+ if (typeof stableInput === "string") {
1412
+ urlTextureMap.push([stableInput, mappedTextures]);
1413
+ } else if (Array.isArray(stableInput)) {
1390
1414
  const textureArray = mappedTextures;
1391
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1392
- } else if (IsObject(input)) {
1415
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1416
+ } else if (IsObject(stableInput)) {
1393
1417
  const textureRecord = mappedTextures;
1394
- for (const key in input) {
1395
- const url = input[key];
1418
+ for (const key in stableInput) {
1419
+ const url = stableInput[key];
1396
1420
  urlTextureMap.push([url, textureRecord[key]]);
1397
1421
  }
1398
1422
  }
@@ -1421,7 +1445,7 @@ function useTexture(input, optionsOrOnLoad) {
1421
1445
  }
1422
1446
  return { _textureRefs: refs };
1423
1447
  });
1424
- }, [cache, input, mappedTextures, store]);
1448
+ }, [cache, stableInput, mappedTextures, store]);
1425
1449
  return mappedTextures;
1426
1450
  }
1427
1451
  useTexture.preload = (url) => useLoader.preload(webgpu.TextureLoader, url);
@@ -1492,13 +1516,13 @@ function useTextures(selector) {
1492
1516
  };
1493
1517
  }, [store]);
1494
1518
  const subscribe = selector ? () => selector(registry) : (state) => state.textures;
1495
- const selected = useThree(subscribe);
1519
+ const selected = useThree$1(subscribe);
1496
1520
  return selector ? selected : registry;
1497
1521
  }
1498
1522
 
1499
1523
  function useRenderTarget(widthOrOptions, heightOrOptions, options) {
1500
- const isLegacy = useThree((s) => s.isLegacy);
1501
- const size = useThree((s) => s.size);
1524
+ const isLegacy = useThree$1((s) => s.isLegacy);
1525
+ const size = useThree$1((s) => s.size);
1502
1526
  let width;
1503
1527
  let height;
1504
1528
  let opts;
@@ -1555,7 +1579,7 @@ function useEnvironment({
1555
1579
  const { extension, isCubemap } = getExtension(files);
1556
1580
  const loader = getLoader(extension);
1557
1581
  if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1558
- const renderer = useThree((state) => state.renderer);
1582
+ const renderer = useThree$1((state) => state.renderer);
1559
1583
  React.useLayoutEffect(() => {
1560
1584
  if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
1561
1585
  function clearGainmapTexture() {
@@ -1582,7 +1606,7 @@ function useEnvironment({
1582
1606
  texture = texture.renderTarget?.texture;
1583
1607
  }
1584
1608
  texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
1585
- texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
1609
+ texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
1586
1610
  return texture;
1587
1611
  }
1588
1612
  const preloadDefaultOptions = {
@@ -1635,11 +1659,12 @@ function getExtension(files) {
1635
1659
  const isCubemap = isArray(files) && files.length === 6;
1636
1660
  const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
1637
1661
  const firstEntry = isArray(files) ? files[0] : files;
1638
- const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1662
+ const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1663
+ const extension = isCubemap ? firstExtension === "hdr" ? "hdr-cube" : "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstExtension;
1639
1664
  return { extension, isCubemap, isGainmap };
1640
1665
  }
1641
1666
  function getLoader(extension) {
1642
- const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
1667
+ const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr-cube" ? HDRCubeTextureLoader_js.HDRCubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
1643
1668
  return loader;
1644
1669
  }
1645
1670
 
@@ -1648,7 +1673,7 @@ function useStore() {
1648
1673
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
1649
1674
  return store;
1650
1675
  }
1651
- function useThree(selector = (state) => state, equalityFn) {
1676
+ function useThree$1(selector = (state) => state, equalityFn) {
1652
1677
  return useStore()(selector, equalityFn);
1653
1678
  }
1654
1679
  function useInstanceHandle(ref) {
@@ -1690,22 +1715,28 @@ function addTail(callback) {
1690
1715
  return scheduler.getScheduler().onIdle(callback);
1691
1716
  }
1692
1717
  function invalidate(state, frames = 1, stackFrames = false) {
1693
- scheduler.getScheduler().invalidate(frames, stackFrames);
1718
+ const rootId = state?.internal?.rootId;
1719
+ const scheduler$1 = scheduler.getScheduler();
1720
+ if (rootId) scheduler$1.invalidateRoot(rootId, frames, stackFrames);
1721
+ else scheduler$1.invalidate(frames, stackFrames);
1694
1722
  }
1695
- function advance(timestamp) {
1696
- scheduler.getScheduler().step(timestamp);
1723
+ function advance(timestamp, runGlobalEffects, state) {
1724
+ const rootId = state?.internal?.rootId;
1725
+ const scheduler$1 = scheduler.getScheduler();
1726
+ if (rootId) scheduler$1.stepRoot(rootId, timestamp);
1727
+ else scheduler$1.step(timestamp);
1697
1728
  }
1698
1729
 
1699
- const version = "10.0.0-alpha.3";
1730
+ const version = "10.0.0-alpha.5";
1700
1731
  const packageData = {
1701
1732
  version: version};
1702
1733
 
1703
1734
  function Xb(Tt) {
1704
1735
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
1705
1736
  }
1706
- var Rm = { exports: {} }, Og = { exports: {} }, _b;
1737
+ var Rm = { exports: {} }, Og = { exports: {} }, _b$1;
1707
1738
  function Kb() {
1708
- return _b || (_b = 1, (function(Tt) {
1739
+ return _b$1 || (_b$1 = 1, (function(Tt) {
1709
1740
  Tt.exports = function(m) {
1710
1741
  function Yn(t, r, a, l) {
1711
1742
  return new uc(t, r, a, l);
@@ -13555,7 +13586,10 @@ function extend(objects) {
13555
13586
  catalogue[Component] = objects;
13556
13587
  return Component;
13557
13588
  } else {
13558
- Object.assign(catalogue, objects);
13589
+ for (const name in objects) {
13590
+ const object = objects[name];
13591
+ if (isConstructor(object)) catalogue[name] = object;
13592
+ }
13559
13593
  }
13560
13594
  }
13561
13595
  function validateInstance(type, props) {
@@ -13903,7 +13937,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
13903
13937
  getRootHostContext: () => NO_CONTEXT,
13904
13938
  getChildHostContext: () => NO_CONTEXT,
13905
13939
  commitUpdate(instance, type, oldProps, newProps, fiber) {
13906
- validateInstance(type, newProps);
13940
+ validateInstance(instance.type, newProps);
13907
13941
  let reconstruct = false;
13908
13942
  if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
13909
13943
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
@@ -13930,8 +13964,8 @@ const reconciler = /* @__PURE__ */ createReconciler({
13930
13964
  for (const prop in instance.props) {
13931
13965
  const value = instance.props[prop];
13932
13966
  if (isFromRef(value)) {
13933
- const ref = value[FROM_REF];
13934
- if (ref.current != null) resolved[prop] = ref.current;
13967
+ const { [FROM_REF]: ref, transform } = value;
13968
+ if (ref.current != null) resolved[prop] = transform ? transform(ref.current) : ref.current;
13935
13969
  }
13936
13970
  }
13937
13971
  if (Object.keys(resolved).length) applyProps(instance.object, resolved);
@@ -14212,9 +14246,8 @@ function createRoot(canvas) {
14212
14246
  if (!renderer.hasInitialized?.()) {
14213
14247
  const size2 = computeInitialSize(canvas, propsSize);
14214
14248
  if (size2.width > 0 && size2.height > 0) {
14215
- const pixelRatio = calculateDpr(dpr);
14216
- canvas.width = size2.width * pixelRatio;
14217
- canvas.height = size2.height * pixelRatio;
14249
+ renderer.setPixelRatio(calculateDpr(dpr));
14250
+ renderer.setSize(size2.width, size2.height, false);
14218
14251
  }
14219
14252
  await renderer.init();
14220
14253
  }
@@ -14223,7 +14256,7 @@ function createRoot(canvas) {
14223
14256
  state.internal.actualRenderer = renderer;
14224
14257
  state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14225
14258
  if (canvasId && !state.internal.isSecondary) {
14226
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14259
+ const canvasTarget = renderer.getCanvasTarget?.();
14227
14260
  const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14228
14261
  state.set((prev) => ({
14229
14262
  internal: {
@@ -14337,7 +14370,7 @@ function createRoot(canvas) {
14337
14370
  const handleXRFrame = (timestamp, _frame) => {
14338
14371
  const state2 = store.getState();
14339
14372
  if (state2.frameloop === "never") return;
14340
- advance(timestamp);
14373
+ advance(timestamp, true, state2);
14341
14374
  };
14342
14375
  const actualRenderer = state.internal.actualRenderer;
14343
14376
  const handleSessionChange = () => {
@@ -14345,7 +14378,7 @@ function createRoot(canvas) {
14345
14378
  const renderer2 = state2.internal.actualRenderer;
14346
14379
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14347
14380
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14348
- if (!renderer2.xr.isPresenting) invalidate();
14381
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14349
14382
  };
14350
14383
  const xr = {
14351
14384
  connect() {
@@ -14427,14 +14460,23 @@ function createRoot(canvas) {
14427
14460
  const newRootId = canvasId || scheduler$1.generateRootId();
14428
14461
  const unregisterRoot = scheduler$1.registerRoot(newRootId, {
14429
14462
  getState: () => store.getState(),
14430
- onError: (err) => store.getState().setError(err)
14463
+ onError: (err) => store.getState().setError(err),
14464
+ frameloop: store.getState().frameloop,
14465
+ before: schedulerConfig?.before,
14466
+ after: schedulerConfig?.after,
14467
+ order: schedulerConfig?.order
14431
14468
  });
14432
14469
  const unregisterCanvasTarget = scheduler$1.register(
14433
14470
  () => {
14434
14471
  const state2 = store.getState();
14435
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14472
+ const canvasTarget = state2.internal.canvasTarget;
14473
+ if (canvasTarget) {
14436
14474
  const renderer2 = state2.internal.actualRenderer;
14437
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14475
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14476
+ if (state2.internal.canvasTargetSizeDirty) {
14477
+ state2.internal.canvasTargetSizeDirty = false;
14478
+ renderer2.backend?.updateSize?.();
14479
+ }
14438
14480
  }
14439
14481
  },
14440
14482
  {
@@ -14503,8 +14545,7 @@ function createRoot(canvas) {
14503
14545
  phase: "render",
14504
14546
  system: true,
14505
14547
  // Internal flag: this is a system job, not user-controlled
14506
- // Apply scheduler config for render ordering and rate limiting
14507
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14548
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14508
14549
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14509
14550
  }
14510
14551
  );
@@ -14523,8 +14564,21 @@ function createRoot(canvas) {
14523
14564
  scheduler: scheduler$1
14524
14565
  }
14525
14566
  }));
14567
+ } else {
14568
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14569
+ if (constraintsChanged) {
14570
+ scheduler$1.setRootConstraints(rootId, {
14571
+ before: schedulerConfig?.before,
14572
+ after: schedulerConfig?.after
14573
+ });
14574
+ }
14575
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14576
+ scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14577
+ }
14526
14578
  }
14527
- scheduler$1.frameloop = frameloop;
14579
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14580
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14581
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14528
14582
  onCreated = onCreatedCallback;
14529
14583
  configured = true;
14530
14584
  resolve();
@@ -14566,18 +14620,23 @@ function unmountComponentAtNode(canvas, callback) {
14566
14620
  const fiber = root?.fiber;
14567
14621
  if (fiber) {
14568
14622
  const state = root?.store.getState();
14569
- if (state) state.internal.active = false;
14623
+ if (state) {
14624
+ state.internal.active = false;
14625
+ const unregisterRoot = state.internal.unregisterRoot;
14626
+ if (unregisterRoot) {
14627
+ unregisterRoot();
14628
+ state.internal.unregisterRoot = void 0;
14629
+ }
14630
+ }
14570
14631
  reconciler.updateContainer(null, fiber, null, () => {
14571
14632
  if (state) {
14572
14633
  setTimeout(() => {
14573
14634
  try {
14574
14635
  const renderer = state.internal.actualRenderer;
14575
- const unregisterRoot = state.internal.unregisterRoot;
14576
- if (unregisterRoot) unregisterRoot();
14577
14636
  const unregisterPrimary = state.internal.unregisterPrimary;
14578
14637
  if (unregisterPrimary) unregisterPrimary();
14579
14638
  const canvasTarget = state.internal.canvasTarget;
14580
- if (canvasTarget?.dispose) canvasTarget.dispose();
14639
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14581
14640
  state.events.disconnect?.();
14582
14641
  cleanupHelperGroup(root.store);
14583
14642
  if (state.isLegacy && renderer) {
@@ -14718,16 +14777,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14718
14777
  const oldbg = target.background;
14719
14778
  const oldenv = target.environment;
14720
14779
  const oldSceneProps = {
14721
- // @ts-ignore
14722
14780
  backgroundBlurriness: target.backgroundBlurriness,
14723
- // @ts-ignore
14724
14781
  backgroundIntensity: target.backgroundIntensity,
14725
- // @ts-ignore
14726
- backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
14727
- // @ts-ignore
14782
+ backgroundRotation: target.backgroundRotation.clone(),
14728
14783
  environmentIntensity: target.environmentIntensity,
14729
- // @ts-ignore
14730
- environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
14784
+ environmentRotation: target.environmentRotation.clone()
14731
14785
  };
14732
14786
  if (background !== "only") target.environment = texture;
14733
14787
  if (background) target.background = texture;
@@ -14739,7 +14793,7 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14739
14793
  };
14740
14794
  }
14741
14795
  function EnvironmentMap({ scene, background = false, map, ...config }) {
14742
- const defaultScene = useThree((state) => state.scene);
14796
+ const defaultScene = useThree$1((state) => state.scene);
14743
14797
  React__namespace.useLayoutEffect(() => {
14744
14798
  if (map) return setEnvProps(background, scene, defaultScene, map, config);
14745
14799
  });
@@ -14757,7 +14811,7 @@ function EnvironmentCube({
14757
14811
  ...rest
14758
14812
  }) {
14759
14813
  const texture = useEnvironment(rest);
14760
- const defaultScene = useThree((state) => state.scene);
14814
+ const defaultScene = useThree$1((state) => state.scene);
14761
14815
  React__namespace.useLayoutEffect(() => {
14762
14816
  return setEnvProps(background, scene, defaultScene, texture, {
14763
14817
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -14794,9 +14848,9 @@ function EnvironmentPortal({
14794
14848
  preset = void 0,
14795
14849
  extensions
14796
14850
  }) {
14797
- const renderer = useThree((state) => state.renderer);
14798
- const isLegacy = useThree((state) => state.isLegacy);
14799
- const defaultScene = useThree((state) => state.scene);
14851
+ const renderer = useThree$1((state) => state.renderer);
14852
+ const isLegacy = useThree$1((state) => state.isLegacy);
14853
+ const defaultScene = useThree$1((state) => state.scene);
14800
14854
  const camera = React__namespace.useRef(null);
14801
14855
  const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
14802
14856
  const fbo = React__namespace.useMemo(() => {
@@ -14840,7 +14894,7 @@ function EnvironmentPortal({
14840
14894
  environmentRotation
14841
14895
  ]);
14842
14896
  let count = 1;
14843
- useFrame(() => {
14897
+ useFrame$1(() => {
14844
14898
  if (frames === Infinity || count < frames) {
14845
14899
  const autoClear = renderer.autoClear;
14846
14900
  renderer.autoClear = true;
@@ -14880,7 +14934,7 @@ function EnvironmentGround(props) {
14880
14934
  ] });
14881
14935
  }
14882
14936
  function EnvironmentColor({ color, scene }) {
14883
- const defaultScene = useThree((state) => state.scene);
14937
+ const defaultScene = useThree$1((state) => state.scene);
14884
14938
  React__namespace.useLayoutEffect(() => {
14885
14939
  if (color === void 0) return;
14886
14940
  const target = resolveScene(scene || defaultScene);
@@ -15118,9 +15172,9 @@ function CanvasImpl({
15118
15172
  React__namespace.useMemo(() => extend(THREE), []);
15119
15173
  const Bridge = useBridge();
15120
15174
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
15121
- const hasInitialSizeRef = React__namespace.useRef(false);
15175
+ const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
15122
15176
  const measureConfig = React__namespace.useMemo(() => {
15123
- if (!hasInitialSizeRef.current) {
15177
+ if (!hasInitialSize) {
15124
15178
  return {
15125
15179
  ...resize,
15126
15180
  scroll: resize?.scroll ?? true,
@@ -15132,7 +15186,7 @@ function CanvasImpl({
15132
15186
  debounce: { scroll: 50, resize: 0 },
15133
15187
  ...resize
15134
15188
  };
15135
- }, [resize, hasInitialSizeRef.current]);
15189
+ }, [hasInitialSize, resize]);
15136
15190
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15137
15191
  const effectiveSize = React__namespace.useMemo(() => {
15138
15192
  let w = width ?? containerRect.width;
@@ -15148,9 +15202,7 @@ function CanvasImpl({
15148
15202
  left: containerRect.left
15149
15203
  };
15150
15204
  }, [width, height, containerRect, forceEven]);
15151
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15152
- hasInitialSizeRef.current = true;
15153
- }
15205
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15154
15206
  const canvasRef = React__namespace.useRef(null);
15155
15207
  const divRef = React__namespace.useRef(null);
15156
15208
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
@@ -15264,6 +15316,7 @@ function CanvasImpl({
15264
15316
  const canvas = canvasRef.current;
15265
15317
  if (canvas) {
15266
15318
  return () => {
15319
+ if (canvas.isConnected) return;
15267
15320
  unmountComponentAtNode(canvas);
15268
15321
  root.current = null;
15269
15322
  };
@@ -15322,20 +15375,36 @@ function CanvasImpl({
15322
15375
  }
15323
15376
  );
15324
15377
  }
15325
- function Canvas(props) {
15378
+ function Canvas$1(props) {
15326
15379
  return /* @__PURE__ */ jsxRuntime.jsx(itsFine.FiberProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(CanvasImpl, { ...props }) });
15327
15380
  }
15328
15381
 
15382
+ const isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
15383
+ const hasValidLegacyNodeMarkers = (value) => {
15384
+ const hasUuid = "uuid" in value;
15385
+ const hasNodeType = "nodeType" in value;
15386
+ const uuid = Reflect.get(value, "uuid");
15387
+ const nodeType = Reflect.get(value, "nodeType");
15388
+ return (hasUuid || hasNodeType) && (!hasUuid || uuid === void 0 || typeof uuid === "string") && (!hasNodeType || nodeType === void 0 || nodeType === null || typeof nodeType === "string");
15389
+ };
15390
+ const isUniformNode = (value) => isObjectLike(value) && "isUniformNode" in value && value.isUniformNode === true;
15391
+ const isTSLNode = (value) => isObjectLike(value) && (Reflect.get(value, "isNode") === true || Reflect.get(value, "shaderNode") !== void 0 && typeof Reflect.get(value, "id") === "number" || hasValidLegacyNodeMarkers(value));
15392
+ const isBufferLike = (value) => ArrayBuffer.isView(value) && !(value instanceof DataView) || isObjectLike(value) && ("isBufferAttribute" in value || "isInterleavedBufferAttribute" in value || isTSLNode(value));
15393
+ const isStorageLike = (value) => isObjectLike(value) && ("isTexture" in value || "isData3DTexture" in value || isTSLNode(value));
15394
+
15329
15395
  var __defProp = Object.defineProperty;
15330
15396
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15331
15397
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
15332
- var _a;
15398
+ var _a, _b;
15333
15399
  const INTERNAL_DATA = Symbol("ScopedStore.data");
15334
- _a = INTERNAL_DATA;
15400
+ const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
15401
+ _b = INTERNAL_DATA, _a = INTERNAL_IS_LEAF;
15335
15402
  const _ScopedStore = class _ScopedStore {
15336
- constructor(data) {
15403
+ constructor(data, isLeaf) {
15404
+ __publicField(this, _b);
15337
15405
  __publicField(this, _a);
15338
15406
  this[INTERNAL_DATA] = data;
15407
+ this[INTERNAL_IS_LEAF] = isLeaf;
15339
15408
  return new Proxy(this, {
15340
15409
  get(target, prop, receiver) {
15341
15410
  if (typeof prop === "string") {
@@ -15369,10 +15438,10 @@ const _ScopedStore = class _ScopedStore {
15369
15438
  * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
15370
15439
  */
15371
15440
  scope(key) {
15372
- const data = this[INTERNAL_DATA][key];
15373
- return new _ScopedStore(
15374
- data && typeof data === "object" ? data : {}
15375
- );
15441
+ const value = this[INTERNAL_DATA][key];
15442
+ const isLeaf = this[INTERNAL_IS_LEAF];
15443
+ const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
15444
+ return new _ScopedStore(scope, isLeaf);
15376
15445
  }
15377
15446
  /**
15378
15447
  * Check if a key exists in the store.
@@ -15388,8 +15457,8 @@ const _ScopedStore = class _ScopedStore {
15388
15457
  }
15389
15458
  };
15390
15459
  let ScopedStore = _ScopedStore;
15391
- function createScopedStore(data) {
15392
- return new ScopedStore(data);
15460
+ function createScopedStore(data, isLeaf) {
15461
+ return new ScopedStore(data, isLeaf);
15393
15462
  }
15394
15463
  function createLazyCreatorState(state, store) {
15395
15464
  let _uniforms = null;
@@ -15400,22 +15469,22 @@ function createLazyCreatorState(state, store) {
15400
15469
  return Object.create(state, {
15401
15470
  uniforms: {
15402
15471
  get() {
15403
- return _uniforms ?? (_uniforms = createScopedStore(view("uniforms")));
15472
+ return _uniforms ?? (_uniforms = createScopedStore(view("uniforms"), isUniformNode));
15404
15473
  }
15405
15474
  },
15406
15475
  nodes: {
15407
15476
  get() {
15408
- return _nodes ?? (_nodes = createScopedStore(view("nodes")));
15477
+ return _nodes ?? (_nodes = createScopedStore(view("nodes"), isTSLNode));
15409
15478
  }
15410
15479
  },
15411
15480
  buffers: {
15412
15481
  get() {
15413
- return _buffers ?? (_buffers = createScopedStore(view("buffers")));
15482
+ return _buffers ?? (_buffers = createScopedStore(view("buffers"), isBufferLike));
15414
15483
  }
15415
15484
  },
15416
15485
  gpuStorage: {
15417
15486
  get() {
15418
- return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage")));
15487
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage"), isStorageLike));
15419
15488
  }
15420
15489
  }
15421
15490
  });
@@ -15474,6 +15543,11 @@ function createTextureOperations(set) {
15474
15543
  removeMultiple: (keys) => removeTextures(set, keys)
15475
15544
  };
15476
15545
  }
15546
+ function scopedNodeName(scope, name) {
15547
+ const raw = scope ? `${scope}_${name}` : name;
15548
+ const safe = raw.replace(/[^A-Za-z0-9_]/g, "_");
15549
+ return /^[A-Za-z_]/.test(safe) ? safe : `_${safe}`;
15550
+ }
15477
15551
  function extractTSLValue(value) {
15478
15552
  if (value === null || value === void 0) return value;
15479
15553
  if (typeof value !== "object") return value;
@@ -15511,7 +15585,7 @@ function vectorize(inObject) {
15511
15585
  return extractTSLValue(inObject);
15512
15586
  }
15513
15587
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15514
- if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15588
+ if (obj.isMatrix2 || obj.isMatrix3 || obj.isMatrix4) return inObject;
15515
15589
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
15516
15590
  if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
15517
15591
  const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
@@ -15569,22 +15643,21 @@ function useScopedResource(options) {
15569
15643
  return entries;
15570
15644
  }
15571
15645
 
15572
- const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15573
15646
  function useUniforms(creatorOrScope, scope) {
15574
15647
  const store = usePrimaryStore();
15575
15648
  const removeUniforms2 = React.useCallback(
15576
- (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode$1),
15649
+ (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode),
15577
15650
  [store]
15578
15651
  );
15579
15652
  const clearUniforms = React.useCallback(
15580
- (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode$1),
15653
+ (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode),
15581
15654
  [store]
15582
15655
  );
15583
15656
  const rebuildUniforms = React.useCallback(
15584
15657
  (targetScope) => rebuildResource(store, "uniforms", targetScope),
15585
15658
  [store]
15586
15659
  );
15587
- const inputForMemoization = React.useMemo(() => {
15660
+ const processedInput = React.useMemo(() => {
15588
15661
  let raw = creatorOrScope;
15589
15662
  if (is.fun(creatorOrScope)) {
15590
15663
  const wrappedState = createLazyCreatorState(store.getState(), store);
@@ -15595,23 +15668,24 @@ function useUniforms(creatorOrScope, scope) {
15595
15668
  for (const [key, value] of Object.entries(raw)) {
15596
15669
  normalized[key] = vectorize(value);
15597
15670
  }
15598
- return normalized;
15671
+ return { candidates: raw, normalized };
15599
15672
  }
15600
- return raw;
15673
+ return { candidates: raw, normalized: raw };
15601
15674
  }, [creatorOrScope, store]);
15602
- const memoizedInput = useCompareMemoize(inputForMemoization);
15675
+ const memoizedInput = useCompareMemoize(processedInput.normalized);
15603
15676
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15604
15677
  const storeUniforms = usePrimaryThree((s) => s.uniforms);
15605
15678
  const created = useScopedResource({
15606
15679
  store,
15607
15680
  kind: "uniforms",
15608
15681
  scope: isReader ? void 0 : scope,
15609
- isLeaf: isUniformNode$1,
15682
+ isLeaf: isUniformNode,
15610
15683
  input: memoizedInput,
15611
15684
  create: () => {
15612
15685
  if (isReader) return {};
15613
- if (typeof memoizedInput !== "object" || memoizedInput === null) throw new Error("Invalid uniform input");
15614
- return memoizedInput;
15686
+ if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
15687
+ throw new Error("Invalid uniform input");
15688
+ return processedInput.candidates;
15615
15689
  },
15616
15690
  prepare: (name, candidate) => createUniform(name, candidate, scope),
15617
15691
  reconcile: (existing, candidate) => {
@@ -15633,7 +15707,7 @@ function useUniforms(creatorOrScope, scope) {
15633
15707
  uniforms = storeUniforms;
15634
15708
  } else if (typeof memoizedInput === "string") {
15635
15709
  const scopeData = storeUniforms[memoizedInput];
15636
- uniforms = scopeData && !isUniformNode$1(scopeData) ? scopeData : {};
15710
+ uniforms = scopeData && !isUniformNode(scopeData) ? scopeData : {};
15637
15711
  }
15638
15712
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15639
15713
  }
@@ -15648,7 +15722,7 @@ function removeUniforms(set, names, scope) {
15648
15722
  return { uniforms: { ...state.uniforms, [scope]: currentScope } };
15649
15723
  }
15650
15724
  const uniforms = { ...state.uniforms };
15651
- for (const name of names) if (isUniformNode$1(uniforms[name])) delete uniforms[name];
15725
+ for (const name of names) if (isUniformNode(uniforms[name])) delete uniforms[name];
15652
15726
  return { uniforms };
15653
15727
  });
15654
15728
  }
@@ -15662,7 +15736,7 @@ function clearRootUniforms(set) {
15662
15736
  set((state) => {
15663
15737
  const uniforms = {};
15664
15738
  for (const [key, value] of Object.entries(state.uniforms)) {
15665
- if (!isUniformNode$1(value)) uniforms[key] = value;
15739
+ if (!isUniformNode(value)) uniforms[key] = value;
15666
15740
  }
15667
15741
  return { uniforms };
15668
15742
  });
@@ -15672,8 +15746,7 @@ function createUniform(inName, node, scope) {
15672
15746
  const inValue = vectorize(node);
15673
15747
  const newUniform = tsl.uniform(inValue);
15674
15748
  if (typeof newUniform.setName === "function") {
15675
- const name = scope ? `${scope}_${inName}` : inName;
15676
- newUniform.setName(name);
15749
+ newUniform.setName(scopedNodeName(scope, inName));
15677
15750
  }
15678
15751
  return newUniform;
15679
15752
  }
@@ -15699,12 +15772,10 @@ function hasEqualsMethod(value) {
15699
15772
  function isSameThreeType(a, b) {
15700
15773
  const obj_a = a;
15701
15774
  const obj_b = b;
15702
- 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;
15775
+ return obj_a.isColor && obj_b.isColor || obj_a.isMatrix2 && obj_b.isMatrix2 || obj_a.isMatrix3 && obj_b.isMatrix3 || obj_a.isMatrix4 && obj_b.isMatrix4 || obj_a.isEuler && obj_b.isEuler || obj_a.isQuaternion && obj_b.isQuaternion;
15703
15776
  }
15704
15777
 
15705
15778
  const uniformOf = tsl.uniform;
15706
- const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15707
- const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15708
15779
  function useUniform(name, value) {
15709
15780
  const store = useStore();
15710
15781
  const registered = useScopedResource({
@@ -15716,7 +15787,7 @@ function useUniform(name, value) {
15716
15787
  create: () => value === void 0 ? {} : { [name]: value },
15717
15788
  prepare: createNamedUniform,
15718
15789
  reconcile: (existing2) => {
15719
- if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
15790
+ if (value !== void 0 && !isTSLNode(value) && !isUniformNode(value)) {
15720
15791
  existing2.value = typeof value === "string" ? new webgpu.Color(value) : value;
15721
15792
  }
15722
15793
  }
@@ -15732,7 +15803,7 @@ function createNamedUniform(name, value) {
15732
15803
  let node;
15733
15804
  if (isUniformNode(value)) {
15734
15805
  node = value;
15735
- } else if (isTSLNode$1(value)) {
15806
+ } else if (isTSLNode(value)) {
15736
15807
  node = uniformOf(value);
15737
15808
  } else if (typeof value === "string") {
15738
15809
  node = tsl.uniform(new webgpu.Color(value));
@@ -15745,7 +15816,6 @@ function createNamedUniform(name, value) {
15745
15816
  return node;
15746
15817
  }
15747
15818
 
15748
- const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
15749
15819
  function useNodes(creatorOrScope, scope) {
15750
15820
  const store = usePrimaryStore();
15751
15821
  const removeNodes2 = React.useCallback(
@@ -15772,7 +15842,8 @@ function useNodes(creatorOrScope, scope) {
15772
15842
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15773
15843
  },
15774
15844
  prepare: (name, node) => {
15775
- node.setName?.(scope ? `${scope}.${name}` : name);
15845
+ const setName = Reflect.get(node, "setName");
15846
+ if (typeof setName === "function") setName.call(node, scopedNodeName(scope, name));
15776
15847
  return node;
15777
15848
  }
15778
15849
  });
@@ -15827,13 +15898,6 @@ function useLocalNodes(creator) {
15827
15898
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
15828
15899
  }
15829
15900
 
15830
- const isBufferLike = (value) => {
15831
- if (value === null || typeof value !== "object") return false;
15832
- if (ArrayBuffer.isView(value)) return true;
15833
- if ("isBufferAttribute" in value) return true;
15834
- if ("uuid" in value || "nodeType" in value) return true;
15835
- return false;
15836
- };
15837
15901
  const disposeBuffer = (buffer) => {
15838
15902
  if (buffer === null || typeof buffer !== "object") return;
15839
15903
  if ("dispose" in buffer && typeof buffer.dispose === "function") {
@@ -15880,9 +15944,8 @@ function useBuffers(creatorOrScope, scope) {
15880
15944
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15881
15945
  },
15882
15946
  prepare: (name, buffer) => {
15883
- if ("setName" in buffer && typeof buffer.setName === "function") {
15884
- buffer.setName(scope ? `${scope}.${name}` : name);
15885
- }
15947
+ const setName = Reflect.get(buffer, "setName");
15948
+ if (typeof setName === "function") setName.call(buffer, scopedNodeName(scope, name));
15886
15949
  return buffer;
15887
15950
  }
15888
15951
  });
@@ -15899,13 +15962,6 @@ function rebuildAllBuffers(store, scope) {
15899
15962
  rebuildResource(store, "buffers", scope);
15900
15963
  }
15901
15964
 
15902
- const isStorageLike = (value) => {
15903
- if (value === null || typeof value !== "object") return false;
15904
- if ("isTexture" in value) return true;
15905
- if ("isData3DTexture" in value) return true;
15906
- if ("uuid" in value || "nodeType" in value) return true;
15907
- return false;
15908
- };
15909
15965
  const disposeStorage = (storage) => {
15910
15966
  if (storage === null || typeof storage !== "object") return;
15911
15967
  if ("dispose" in storage && typeof storage.dispose === "function") {
@@ -15952,10 +16008,9 @@ function useGPUStorage(creatorOrScope, scope) {
15952
16008
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15953
16009
  },
15954
16010
  prepare: (name, storage) => {
15955
- const label = scope ? `${scope}.${name}` : name;
15956
- if ("setName" in storage && typeof storage.setName === "function") {
15957
- storage.setName(label);
15958
- }
16011
+ const label = scopedNodeName(scope, name);
16012
+ const setName = Reflect.get(storage, "setName");
16013
+ if (typeof setName === "function") setName.call(storage, label);
15959
16014
  if ("name" in storage && typeof storage.name === "string") {
15960
16015
  storage.name = label;
15961
16016
  }
@@ -15983,9 +16038,10 @@ function rebuildAllStorage(store, scope) {
15983
16038
 
15984
16039
  function useRenderPipeline(mainCB, setupCB) {
15985
16040
  const store = useStore();
15986
- const { scene, camera, renderer, isLegacy } = useThree();
16041
+ const { scene, camera, renderer, isLegacy } = useThree$1();
15987
16042
  const callbacksRanRef = React.useRef(false);
15988
16043
  const scenePassCacheRef = React.useRef(null);
16044
+ const forceScenePassRebuildRef = React.useRef(false);
15989
16045
  const mainCBRef = React.useRef(mainCB);
15990
16046
  const setupCBRef = React.useRef(setupCB);
15991
16047
  mainCBRef.current = mainCB;
@@ -15993,7 +16049,7 @@ function useRenderPipeline(mainCB, setupCB) {
15993
16049
  const [rebuildVersion, setRebuildVersion] = React.useState(0);
15994
16050
  React.useEffect(() => {
15995
16051
  callbacksRanRef.current = false;
15996
- scenePassCacheRef.current = null;
16052
+ forceScenePassRebuildRef.current = true;
15997
16053
  }, []);
15998
16054
  const clearPasses = React.useCallback(() => {
15999
16055
  store.setState({ passes: {} });
@@ -16003,12 +16059,14 @@ function useRenderPipeline(mainCB, setupCB) {
16003
16059
  renderPipeline: null,
16004
16060
  passes: {}
16005
16061
  });
16062
+ scenePassCacheRef.current?.scenePass.dispose();
16006
16063
  callbacksRanRef.current = false;
16007
16064
  scenePassCacheRef.current = null;
16065
+ forceScenePassRebuildRef.current = false;
16008
16066
  }, [store]);
16009
16067
  const rebuild = React.useCallback(() => {
16010
16068
  callbacksRanRef.current = false;
16011
- scenePassCacheRef.current = null;
16069
+ forceScenePassRebuildRef.current = true;
16012
16070
  setRebuildVersion((v) => v + 1);
16013
16071
  }, []);
16014
16072
  React.useLayoutEffect(() => {
@@ -16016,6 +16074,9 @@ function useRenderPipeline(mainCB, setupCB) {
16016
16074
  throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
16017
16075
  }
16018
16076
  if (!renderer || !scene || !camera) return;
16077
+ if (!("isWebGPURenderer" in renderer)) {
16078
+ throw new Error("useRenderPipeline: renderer is not a WebGPURenderer but isLegacy is false.");
16079
+ }
16019
16080
  const state = store.getState();
16020
16081
  const set = store.setState;
16021
16082
  try {
@@ -16026,21 +16087,33 @@ function useRenderPipeline(mainCB, setupCB) {
16026
16087
  pipeline = new webgpu.RenderPipeline(renderer);
16027
16088
  justCreatedPipeline = true;
16028
16089
  }
16029
- const cacheValid = scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16090
+ const cacheValid = !forceScenePassRebuildRef.current && scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16091
+ let outgoingScenePass = null;
16030
16092
  let scenePass;
16031
16093
  if (cacheValid) {
16032
16094
  scenePass = scenePassCacheRef.current.scenePass;
16033
16095
  } else {
16096
+ outgoingScenePass = scenePassCacheRef.current?.scenePass ?? null;
16034
16097
  scenePass = tsl.pass(scene, camera);
16035
16098
  scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
16099
+ forceScenePassRebuildRef.current = false;
16036
16100
  }
16037
16101
  currentPasses.scenePass = scenePass;
16038
- if (!pipeline.outputNode || justCreatedPipeline) pipeline.outputNode = scenePass;
16102
+ let outputNodeChanged = false;
16103
+ if (!pipeline.outputNode || justCreatedPipeline || pipeline.outputNode === outgoingScenePass) {
16104
+ pipeline.outputNode = scenePass;
16105
+ outputNodeChanged = true;
16106
+ }
16039
16107
  set({ renderPipeline: pipeline, passes: currentPasses });
16040
16108
  const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
16041
16109
  if (shouldRunCallbacks) {
16042
16110
  if (setupCBRef.current) {
16043
- const freshState = store.getState();
16111
+ const freshState = Object.assign({}, store.getState(), {
16112
+ renderPipeline: pipeline,
16113
+ passes: { ...currentPasses, scenePass },
16114
+ renderer,
16115
+ isLegacy: false
16116
+ });
16044
16117
  const setupResult = setupCBRef.current(freshState);
16045
16118
  if (setupResult && typeof setupResult === "object") {
16046
16119
  currentPasses = { ...currentPasses, ...setupResult };
@@ -16048,33 +16121,38 @@ function useRenderPipeline(mainCB, setupCB) {
16048
16121
  }
16049
16122
  }
16050
16123
  if (mainCBRef.current) {
16051
- const freshState = store.getState();
16124
+ const freshState = Object.assign({}, store.getState(), {
16125
+ renderPipeline: pipeline,
16126
+ passes: { ...currentPasses, scenePass },
16127
+ renderer,
16128
+ isLegacy: false
16129
+ });
16052
16130
  const mainResult = mainCBRef.current(freshState);
16053
16131
  if (mainResult && typeof mainResult === "object") {
16054
16132
  currentPasses = { ...currentPasses, ...mainResult };
16055
16133
  set({ passes: currentPasses });
16056
16134
  }
16057
16135
  }
16136
+ outputNodeChanged = true;
16058
16137
  callbacksRanRef.current = true;
16059
16138
  }
16139
+ if (outputNodeChanged) pipeline.needsUpdate = true;
16140
+ if (outgoingScenePass && outgoingScenePass !== scenePass) outgoingScenePass.dispose();
16060
16141
  } catch (error) {
16061
16142
  console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
16062
16143
  }
16063
16144
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16064
- const passes = useThree((s) => s.passes);
16065
- const renderPipeline = useThree((s) => s.renderPipeline);
16066
- return {
16067
- passes,
16068
- renderPipeline,
16069
- clearPasses,
16070
- reset,
16071
- rebuild,
16072
- // isReady indicates if RenderPipeline is configured and ready for rendering
16073
- isReady: renderPipeline !== null
16074
- };
16145
+ const passes = useThree$1((s) => s.passes);
16146
+ const renderPipeline = useThree$1((s) => s.renderPipeline);
16147
+ const actions = { passes, clearPasses, reset, rebuild };
16148
+ if (renderPipeline) return { ...actions, isReady: true, renderPipeline };
16149
+ return { ...actions, isReady: false, renderPipeline: null };
16075
16150
  }
16076
16151
 
16077
16152
  extend(THREE);
16153
+ const useThree = useThree$1;
16154
+ const useFrame = useFrame$1;
16155
+ const Canvas = Canvas$1;
16078
16156
 
16079
16157
  exports.Scheduler = scheduler.Scheduler;
16080
16158
  exports.getScheduler = scheduler.getScheduler;