@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,6 +1,5 @@
1
1
  import * as webgpu from 'three/webgpu';
2
2
  import { CubeRenderTarget, RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, ShaderMaterial, Uniform, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, HalfFloatType, Color, Vector4, RenderPipeline } from 'three/webgpu';
3
- import { Inspector } from 'three/addons/inspector/Inspector.js';
4
3
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
4
  import * as React from 'react';
6
5
  import React__default, { useRef, useMemo, useLayoutEffect, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
@@ -12,6 +11,7 @@ export { Scheduler, getScheduler } from '@pmndrs/scheduler';
12
11
  import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
13
12
  import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
14
13
  import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
14
+ import { HDRCubeTextureLoader } from 'three/examples/jsm/loaders/HDRCubeTextureLoader.js';
15
15
  import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
16
16
  import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
17
17
  import { GainMapLoader } from '@monogrid/gainmap-js';
@@ -34,6 +34,10 @@ function _mergeNamespaces(n, m) {
34
34
 
35
35
  const R3F_BUILD_LEGACY = false;
36
36
  const R3F_BUILD_WEBGPU = true;
37
+ async function loadInspector() {
38
+ const { Inspector } = await import('three/addons/inspector/Inspector.js');
39
+ return Inspector;
40
+ }
37
41
  const WebGLRenderer = class WebGLRenderer2 {
38
42
  constructor() {
39
43
  throw new Error(
@@ -47,13 +51,13 @@ const WebGLCubeRenderTarget = null;
47
51
  const THREE = /*#__PURE__*/_mergeNamespaces({
48
52
  __proto__: null,
49
53
  CubeRenderTargetCompat: CubeRenderTarget,
50
- Inspector: Inspector,
51
54
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
52
55
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
53
56
  RenderTargetCompat: RenderTarget,
54
57
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
55
58
  WebGLRenderTarget: WebGLRenderTarget,
56
- WebGLRenderer: WebGLRenderer
59
+ WebGLRenderer: WebGLRenderer,
60
+ loadInspector: loadInspector
57
61
  }, [webgpu]);
58
62
 
59
63
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -516,8 +520,10 @@ function hasVisibilityHandlers(handlers) {
516
520
  }
517
521
 
518
522
  const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
519
- function fromRef(ref) {
520
- return { [FROM_REF]: ref };
523
+ function fromRef(ref, transform) {
524
+ const marker = { [FROM_REF]: ref };
525
+ if (transform) marker.transform = transform;
526
+ return marker;
521
527
  }
522
528
  function isFromRef(value) {
523
529
  return value !== null && typeof value === "object" && FROM_REF in value;
@@ -618,11 +624,9 @@ function diffProps(instance, newProps) {
618
624
  for (const prop in instance.props) {
619
625
  if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
620
626
  const { root, key } = resolve(instance.object, prop);
621
- if (root.constructor && root.constructor.length === 0) {
627
+ if (typeof root?.constructor === "function") {
622
628
  const ctor = getMemoizedPrototype(root);
623
629
  if (!is.und(ctor)) changedProps[prop] = ctor[key];
624
- } else {
625
- changedProps[prop] = 0;
626
630
  }
627
631
  }
628
632
  return changedProps;
@@ -912,7 +916,7 @@ const createStore = (invalidate, advance) => {
912
916
  size: newSize,
913
917
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
914
918
  }));
915
- getScheduler().invalidate();
919
+ get().invalidate();
916
920
  }
917
921
  }
918
922
  return;
@@ -927,15 +931,16 @@ const createStore = (invalidate, advance) => {
927
931
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
928
932
  _sizeImperative: true
929
933
  }));
930
- getScheduler().invalidate();
934
+ get().invalidate();
931
935
  },
932
936
  setDpr: (dpr) => set((state2) => {
933
937
  const resolved = calculateDpr(dpr);
934
938
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
935
939
  }),
936
940
  setFrameloop: (frameloop = "always") => {
941
+ const rootId = get().internal.rootId;
942
+ if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
937
943
  set(() => ({ frameloop }));
938
- getScheduler().frameloop = frameloop;
939
944
  },
940
945
  setError: (error) => set(() => ({ error })),
941
946
  error: null,
@@ -1051,17 +1056,11 @@ const createStore = (invalidate, advance) => {
1051
1056
  oldSize = size;
1052
1057
  oldDpr = viewport.dpr;
1053
1058
  updateCamera(camera, size);
1054
- if (internal.isSecondary && canvasTarget) {
1055
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1056
- canvasTarget.setSize(size.width, size.height, false);
1057
- } else {
1058
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1059
- actualRenderer.setSize(size.width, size.height, false);
1060
- if (canvasTarget) {
1061
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1062
- canvasTarget.setSize(size.width, size.height, false);
1063
- }
1064
- }
1059
+ const resizeTarget = canvasTarget ?? actualRenderer;
1060
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1061
+ resizeTarget.setSize(size.width, size.height, false);
1062
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1063
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1065
1064
  }
1066
1065
  if (camera !== oldCamera) {
1067
1066
  oldCamera = camera;
@@ -1123,7 +1122,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1123
1122
  const results = cacheKeys.map(
1124
1123
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1125
1124
  );
1126
- return Array.isArray(input) ? results : results[0];
1125
+ const stableRef = useRef(null);
1126
+ const previous = stableRef.current;
1127
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1128
+ stableRef.current = stable;
1129
+ return Array.isArray(input) ? stable : stable[0];
1127
1130
  }
1128
1131
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1129
1132
  const inputs = Array.isArray(input) ? input : [input];
@@ -1138,7 +1141,7 @@ useLoader.clear = function(loader, input, cacheKey) {
1138
1141
  };
1139
1142
  useLoader.loader = getLoader$1;
1140
1143
 
1141
- function useFrame(callback, priorityOrOptions) {
1144
+ function useFrame$1(callback, priorityOrOptions) {
1142
1145
  const store = React.useContext(context);
1143
1146
  const isInsideCanvas = store !== null;
1144
1147
  const scheduler = getScheduler();
@@ -1243,6 +1246,13 @@ function useFrame(callback, priorityOrOptions) {
1243
1246
  * Use for controlling the entire frame loop, adding phases, etc.
1244
1247
  */
1245
1248
  scheduler: scheduler2,
1249
+ /**
1250
+ * The root that currently owns this job. Resolve on access because ambient
1251
+ * jobs can be adopted by a Canvas after the controls object is created.
1252
+ */
1253
+ get rootId() {
1254
+ return getScheduler().getJobRootId(id);
1255
+ },
1246
1256
  /**
1247
1257
  * Manually step this job only.
1248
1258
  * Bypasses FPS limiting - always runs.
@@ -1259,6 +1269,14 @@ function useFrame(callback, priorityOrOptions) {
1259
1269
  stepAll: (timestamp) => {
1260
1270
  getScheduler().step(timestamp);
1261
1271
  },
1272
+ /**
1273
+ * Request frames for this job's owning root without waking sibling roots.
1274
+ */
1275
+ invalidate: (frames, stackFrames) => {
1276
+ const scheduler3 = getScheduler();
1277
+ const rootId = scheduler3.getJobRootId(id);
1278
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1279
+ },
1262
1280
  /**
1263
1281
  * Pause this job (set enabled=false).
1264
1282
  * Job remains registered but won't run.
@@ -1288,6 +1306,11 @@ function getUrls(input) {
1288
1306
  if (Array.isArray(input)) return input;
1289
1307
  return Object.values(input);
1290
1308
  }
1309
+ function inputSignature(input) {
1310
+ if (typeof input === "string") return input;
1311
+ if (Array.isArray(input)) return input.join("\0");
1312
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1313
+ }
1291
1314
  function allUrlsCached(urls, textureCache) {
1292
1315
  return urls.every((url) => textureCache.has(url));
1293
1316
  }
@@ -1306,31 +1329,44 @@ function buildFromCache(input, textureCache) {
1306
1329
  }
1307
1330
  }
1308
1331
  function useTexture(input, optionsOrOnLoad) {
1309
- const renderer = useThree((state) => state.internal.actualRenderer);
1332
+ const renderer = useThree$1((state) => state.internal.actualRenderer);
1310
1333
  const store = useStore();
1311
1334
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
1312
1335
  const { onLoad, cache = true } = options;
1313
1336
  const onLoadRef = useRef(onLoad);
1314
1337
  onLoadRef.current = onLoad;
1315
1338
  const onLoadCalledForRef = useRef(null);
1316
- const urls = useMemo(() => getUrls(input), [input]);
1339
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1340
+ const stableInput = useMemo(() => input, [inputKey]);
1341
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1317
1342
  const cachedResult = useMemo(() => {
1318
1343
  if (!cache) return null;
1319
1344
  const textures = store.getState().textures;
1320
1345
  if (!allUrlsCached(urls, textures)) return null;
1321
- return buildFromCache(input, textures);
1322
- }, [cache, urls, input, store]);
1346
+ return buildFromCache(stableInput, textures);
1347
+ }, [cache, urls, stableInput, store]);
1323
1348
  const loadedTextures = useLoader(
1324
1349
  TextureLoader,
1325
- IsObject(input) ? Object.values(input) : input
1350
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1326
1351
  );
1327
- const inputKey = urls.join("\0");
1352
+ const mappedTextures = useMemo(() => {
1353
+ if (cachedResult) return cachedResult;
1354
+ if (IsObject(stableInput)) {
1355
+ const keyed = {};
1356
+ const textureArray = loadedTextures;
1357
+ let i = 0;
1358
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1359
+ return keyed;
1360
+ } else {
1361
+ return loadedTextures;
1362
+ }
1363
+ }, [stableInput, loadedTextures, cachedResult]);
1328
1364
  useLayoutEffect(() => {
1329
1365
  if (cachedResult) return;
1330
1366
  if (onLoadCalledForRef.current === inputKey) return;
1331
1367
  onLoadCalledForRef.current = inputKey;
1332
- onLoadRef.current?.(loadedTextures);
1333
- }, [cachedResult, loadedTextures, inputKey]);
1368
+ onLoadRef.current?.(mappedTextures);
1369
+ }, [cachedResult, mappedTextures, inputKey]);
1334
1370
  useEffect(() => {
1335
1371
  if (cachedResult) return;
1336
1372
  if ("initTexture" in renderer) {
@@ -1349,30 +1385,18 @@ function useTexture(input, optionsOrOnLoad) {
1349
1385
  });
1350
1386
  }
1351
1387
  }, [renderer, loadedTextures, cachedResult]);
1352
- const mappedTextures = useMemo(() => {
1353
- if (cachedResult) return cachedResult;
1354
- if (IsObject(input)) {
1355
- const keyed = {};
1356
- const textureArray = loadedTextures;
1357
- let i = 0;
1358
- for (const key in input) keyed[key] = textureArray[i++];
1359
- return keyed;
1360
- } else {
1361
- return loadedTextures;
1362
- }
1363
- }, [input, loadedTextures, cachedResult]);
1364
1388
  useEffect(() => {
1365
1389
  if (!cache) return;
1366
1390
  const urlTextureMap = [];
1367
- if (typeof input === "string") {
1368
- urlTextureMap.push([input, mappedTextures]);
1369
- } else if (Array.isArray(input)) {
1391
+ if (typeof stableInput === "string") {
1392
+ urlTextureMap.push([stableInput, mappedTextures]);
1393
+ } else if (Array.isArray(stableInput)) {
1370
1394
  const textureArray = mappedTextures;
1371
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1372
- } else if (IsObject(input)) {
1395
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1396
+ } else if (IsObject(stableInput)) {
1373
1397
  const textureRecord = mappedTextures;
1374
- for (const key in input) {
1375
- const url = input[key];
1398
+ for (const key in stableInput) {
1399
+ const url = stableInput[key];
1376
1400
  urlTextureMap.push([url, textureRecord[key]]);
1377
1401
  }
1378
1402
  }
@@ -1401,7 +1425,7 @@ function useTexture(input, optionsOrOnLoad) {
1401
1425
  }
1402
1426
  return { _textureRefs: refs };
1403
1427
  });
1404
- }, [cache, input, mappedTextures, store]);
1428
+ }, [cache, stableInput, mappedTextures, store]);
1405
1429
  return mappedTextures;
1406
1430
  }
1407
1431
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -1472,13 +1496,13 @@ function useTextures(selector) {
1472
1496
  };
1473
1497
  }, [store]);
1474
1498
  const subscribe = selector ? () => selector(registry) : (state) => state.textures;
1475
- const selected = useThree(subscribe);
1499
+ const selected = useThree$1(subscribe);
1476
1500
  return selector ? selected : registry;
1477
1501
  }
1478
1502
 
1479
1503
  function useRenderTarget(widthOrOptions, heightOrOptions, options) {
1480
- const isLegacy = useThree((s) => s.isLegacy);
1481
- const size = useThree((s) => s.size);
1504
+ const isLegacy = useThree$1((s) => s.isLegacy);
1505
+ const size = useThree$1((s) => s.size);
1482
1506
  let width;
1483
1507
  let height;
1484
1508
  let opts;
@@ -1535,7 +1559,7 @@ function useEnvironment({
1535
1559
  const { extension, isCubemap } = getExtension(files);
1536
1560
  const loader = getLoader(extension);
1537
1561
  if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
1538
- const renderer = useThree((state) => state.renderer);
1562
+ const renderer = useThree$1((state) => state.renderer);
1539
1563
  useLayoutEffect(() => {
1540
1564
  if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
1541
1565
  function clearGainmapTexture() {
@@ -1562,7 +1586,7 @@ function useEnvironment({
1562
1586
  texture = texture.renderTarget?.texture;
1563
1587
  }
1564
1588
  texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
1565
- texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
1589
+ texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
1566
1590
  return texture;
1567
1591
  }
1568
1592
  const preloadDefaultOptions = {
@@ -1615,11 +1639,12 @@ function getExtension(files) {
1615
1639
  const isCubemap = isArray(files) && files.length === 6;
1616
1640
  const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
1617
1641
  const firstEntry = isArray(files) ? files[0] : files;
1618
- 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();
1642
+ const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1643
+ 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;
1619
1644
  return { extension, isCubemap, isGainmap };
1620
1645
  }
1621
1646
  function getLoader(extension) {
1622
- const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
1647
+ const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr-cube" ? HDRCubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
1623
1648
  return loader;
1624
1649
  }
1625
1650
 
@@ -1628,7 +1653,7 @@ function useStore() {
1628
1653
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
1629
1654
  return store;
1630
1655
  }
1631
- function useThree(selector = (state) => state, equalityFn) {
1656
+ function useThree$1(selector = (state) => state, equalityFn) {
1632
1657
  return useStore()(selector, equalityFn);
1633
1658
  }
1634
1659
  function useInstanceHandle(ref) {
@@ -1670,22 +1695,28 @@ function addTail(callback) {
1670
1695
  return getScheduler().onIdle(callback);
1671
1696
  }
1672
1697
  function invalidate(state, frames = 1, stackFrames = false) {
1673
- getScheduler().invalidate(frames, stackFrames);
1698
+ const rootId = state?.internal?.rootId;
1699
+ const scheduler = getScheduler();
1700
+ if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
1701
+ else scheduler.invalidate(frames, stackFrames);
1674
1702
  }
1675
- function advance(timestamp) {
1676
- getScheduler().step(timestamp);
1703
+ function advance(timestamp, runGlobalEffects, state) {
1704
+ const rootId = state?.internal?.rootId;
1705
+ const scheduler = getScheduler();
1706
+ if (rootId) scheduler.stepRoot(rootId, timestamp);
1707
+ else scheduler.step(timestamp);
1677
1708
  }
1678
1709
 
1679
- const version = "10.0.0-alpha.3";
1710
+ const version = "10.0.0-alpha.5";
1680
1711
  const packageData = {
1681
1712
  version: version};
1682
1713
 
1683
1714
  function Xb(Tt) {
1684
1715
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
1685
1716
  }
1686
- var Rm = { exports: {} }, Og = { exports: {} }, _b;
1717
+ var Rm = { exports: {} }, Og = { exports: {} }, _b$1;
1687
1718
  function Kb() {
1688
- return _b || (_b = 1, (function(Tt) {
1719
+ return _b$1 || (_b$1 = 1, (function(Tt) {
1689
1720
  Tt.exports = function(m) {
1690
1721
  function Yn(t, r, a, l) {
1691
1722
  return new uc(t, r, a, l);
@@ -13535,7 +13566,10 @@ function extend(objects) {
13535
13566
  catalogue[Component] = objects;
13536
13567
  return Component;
13537
13568
  } else {
13538
- Object.assign(catalogue, objects);
13569
+ for (const name in objects) {
13570
+ const object = objects[name];
13571
+ if (isConstructor(object)) catalogue[name] = object;
13572
+ }
13539
13573
  }
13540
13574
  }
13541
13575
  function validateInstance(type, props) {
@@ -13883,7 +13917,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
13883
13917
  getRootHostContext: () => NO_CONTEXT,
13884
13918
  getChildHostContext: () => NO_CONTEXT,
13885
13919
  commitUpdate(instance, type, oldProps, newProps, fiber) {
13886
- validateInstance(type, newProps);
13920
+ validateInstance(instance.type, newProps);
13887
13921
  let reconstruct = false;
13888
13922
  if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
13889
13923
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
@@ -13910,8 +13944,8 @@ const reconciler = /* @__PURE__ */ createReconciler({
13910
13944
  for (const prop in instance.props) {
13911
13945
  const value = instance.props[prop];
13912
13946
  if (isFromRef(value)) {
13913
- const ref = value[FROM_REF];
13914
- if (ref.current != null) resolved[prop] = ref.current;
13947
+ const { [FROM_REF]: ref, transform } = value;
13948
+ if (ref.current != null) resolved[prop] = transform ? transform(ref.current) : ref.current;
13915
13949
  }
13916
13950
  }
13917
13951
  if (Object.keys(resolved).length) applyProps(instance.object, resolved);
@@ -14192,9 +14226,8 @@ function createRoot(canvas) {
14192
14226
  if (!renderer.hasInitialized?.()) {
14193
14227
  const size2 = computeInitialSize(canvas, propsSize);
14194
14228
  if (size2.width > 0 && size2.height > 0) {
14195
- const pixelRatio = calculateDpr(dpr);
14196
- canvas.width = size2.width * pixelRatio;
14197
- canvas.height = size2.height * pixelRatio;
14229
+ renderer.setPixelRatio(calculateDpr(dpr));
14230
+ renderer.setSize(size2.width, size2.height, false);
14198
14231
  }
14199
14232
  await renderer.init();
14200
14233
  }
@@ -14203,7 +14236,7 @@ function createRoot(canvas) {
14203
14236
  state.internal.actualRenderer = renderer;
14204
14237
  state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14205
14238
  if (canvasId && !state.internal.isSecondary) {
14206
- const canvasTarget = new CanvasTarget(canvas);
14239
+ const canvasTarget = renderer.getCanvasTarget?.();
14207
14240
  const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14208
14241
  state.set((prev) => ({
14209
14242
  internal: {
@@ -14317,7 +14350,7 @@ function createRoot(canvas) {
14317
14350
  const handleXRFrame = (timestamp, _frame) => {
14318
14351
  const state2 = store.getState();
14319
14352
  if (state2.frameloop === "never") return;
14320
- advance(timestamp);
14353
+ advance(timestamp, true, state2);
14321
14354
  };
14322
14355
  const actualRenderer = state.internal.actualRenderer;
14323
14356
  const handleSessionChange = () => {
@@ -14325,7 +14358,7 @@ function createRoot(canvas) {
14325
14358
  const renderer2 = state2.internal.actualRenderer;
14326
14359
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14327
14360
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14328
- if (!renderer2.xr.isPresenting) invalidate();
14361
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14329
14362
  };
14330
14363
  const xr = {
14331
14364
  connect() {
@@ -14407,14 +14440,23 @@ function createRoot(canvas) {
14407
14440
  const newRootId = canvasId || scheduler.generateRootId();
14408
14441
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14409
14442
  getState: () => store.getState(),
14410
- onError: (err) => store.getState().setError(err)
14443
+ onError: (err) => store.getState().setError(err),
14444
+ frameloop: store.getState().frameloop,
14445
+ before: schedulerConfig?.before,
14446
+ after: schedulerConfig?.after,
14447
+ order: schedulerConfig?.order
14411
14448
  });
14412
14449
  const unregisterCanvasTarget = scheduler.register(
14413
14450
  () => {
14414
14451
  const state2 = store.getState();
14415
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14452
+ const canvasTarget = state2.internal.canvasTarget;
14453
+ if (canvasTarget) {
14416
14454
  const renderer2 = state2.internal.actualRenderer;
14417
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14455
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14456
+ if (state2.internal.canvasTargetSizeDirty) {
14457
+ state2.internal.canvasTargetSizeDirty = false;
14458
+ renderer2.backend?.updateSize?.();
14459
+ }
14418
14460
  }
14419
14461
  },
14420
14462
  {
@@ -14483,8 +14525,7 @@ function createRoot(canvas) {
14483
14525
  phase: "render",
14484
14526
  system: true,
14485
14527
  // Internal flag: this is a system job, not user-controlled
14486
- // Apply scheduler config for render ordering and rate limiting
14487
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14528
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14488
14529
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14489
14530
  }
14490
14531
  );
@@ -14503,8 +14544,21 @@ function createRoot(canvas) {
14503
14544
  scheduler
14504
14545
  }
14505
14546
  }));
14547
+ } else {
14548
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14549
+ if (constraintsChanged) {
14550
+ scheduler.setRootConstraints(rootId, {
14551
+ before: schedulerConfig?.before,
14552
+ after: schedulerConfig?.after
14553
+ });
14554
+ }
14555
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14556
+ scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14557
+ }
14506
14558
  }
14507
- scheduler.frameloop = frameloop;
14559
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14560
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14561
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14508
14562
  onCreated = onCreatedCallback;
14509
14563
  configured = true;
14510
14564
  resolve();
@@ -14546,18 +14600,23 @@ function unmountComponentAtNode(canvas, callback) {
14546
14600
  const fiber = root?.fiber;
14547
14601
  if (fiber) {
14548
14602
  const state = root?.store.getState();
14549
- if (state) state.internal.active = false;
14603
+ if (state) {
14604
+ state.internal.active = false;
14605
+ const unregisterRoot = state.internal.unregisterRoot;
14606
+ if (unregisterRoot) {
14607
+ unregisterRoot();
14608
+ state.internal.unregisterRoot = void 0;
14609
+ }
14610
+ }
14550
14611
  reconciler.updateContainer(null, fiber, null, () => {
14551
14612
  if (state) {
14552
14613
  setTimeout(() => {
14553
14614
  try {
14554
14615
  const renderer = state.internal.actualRenderer;
14555
- const unregisterRoot = state.internal.unregisterRoot;
14556
- if (unregisterRoot) unregisterRoot();
14557
14616
  const unregisterPrimary = state.internal.unregisterPrimary;
14558
14617
  if (unregisterPrimary) unregisterPrimary();
14559
14618
  const canvasTarget = state.internal.canvasTarget;
14560
- if (canvasTarget?.dispose) canvasTarget.dispose();
14619
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14561
14620
  state.events.disconnect?.();
14562
14621
  cleanupHelperGroup(root.store);
14563
14622
  if (state.isLegacy && renderer) {
@@ -14698,16 +14757,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14698
14757
  const oldbg = target.background;
14699
14758
  const oldenv = target.environment;
14700
14759
  const oldSceneProps = {
14701
- // @ts-ignore
14702
14760
  backgroundBlurriness: target.backgroundBlurriness,
14703
- // @ts-ignore
14704
14761
  backgroundIntensity: target.backgroundIntensity,
14705
- // @ts-ignore
14706
- backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
14707
- // @ts-ignore
14762
+ backgroundRotation: target.backgroundRotation.clone(),
14708
14763
  environmentIntensity: target.environmentIntensity,
14709
- // @ts-ignore
14710
- environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
14764
+ environmentRotation: target.environmentRotation.clone()
14711
14765
  };
14712
14766
  if (background !== "only") target.environment = texture;
14713
14767
  if (background) target.background = texture;
@@ -14719,7 +14773,7 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14719
14773
  };
14720
14774
  }
14721
14775
  function EnvironmentMap({ scene, background = false, map, ...config }) {
14722
- const defaultScene = useThree((state) => state.scene);
14776
+ const defaultScene = useThree$1((state) => state.scene);
14723
14777
  React.useLayoutEffect(() => {
14724
14778
  if (map) return setEnvProps(background, scene, defaultScene, map, config);
14725
14779
  });
@@ -14737,7 +14791,7 @@ function EnvironmentCube({
14737
14791
  ...rest
14738
14792
  }) {
14739
14793
  const texture = useEnvironment(rest);
14740
- const defaultScene = useThree((state) => state.scene);
14794
+ const defaultScene = useThree$1((state) => state.scene);
14741
14795
  React.useLayoutEffect(() => {
14742
14796
  return setEnvProps(background, scene, defaultScene, texture, {
14743
14797
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -14774,9 +14828,9 @@ function EnvironmentPortal({
14774
14828
  preset = void 0,
14775
14829
  extensions
14776
14830
  }) {
14777
- const renderer = useThree((state) => state.renderer);
14778
- const isLegacy = useThree((state) => state.isLegacy);
14779
- const defaultScene = useThree((state) => state.scene);
14831
+ const renderer = useThree$1((state) => state.renderer);
14832
+ const isLegacy = useThree$1((state) => state.isLegacy);
14833
+ const defaultScene = useThree$1((state) => state.scene);
14780
14834
  const camera = React.useRef(null);
14781
14835
  const [virtualScene] = React.useState(() => new Scene());
14782
14836
  const fbo = React.useMemo(() => {
@@ -14820,7 +14874,7 @@ function EnvironmentPortal({
14820
14874
  environmentRotation
14821
14875
  ]);
14822
14876
  let count = 1;
14823
- useFrame(() => {
14877
+ useFrame$1(() => {
14824
14878
  if (frames === Infinity || count < frames) {
14825
14879
  const autoClear = renderer.autoClear;
14826
14880
  renderer.autoClear = true;
@@ -14860,7 +14914,7 @@ function EnvironmentGround(props) {
14860
14914
  ] });
14861
14915
  }
14862
14916
  function EnvironmentColor({ color, scene }) {
14863
- const defaultScene = useThree((state) => state.scene);
14917
+ const defaultScene = useThree$1((state) => state.scene);
14864
14918
  React.useLayoutEffect(() => {
14865
14919
  if (color === void 0) return;
14866
14920
  const target = resolveScene(scene || defaultScene);
@@ -15098,9 +15152,9 @@ function CanvasImpl({
15098
15152
  React.useMemo(() => extend(THREE), []);
15099
15153
  const Bridge = useBridge();
15100
15154
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
15101
- const hasInitialSizeRef = React.useRef(false);
15155
+ const [hasInitialSize, setHasInitialSize] = React.useState(false);
15102
15156
  const measureConfig = React.useMemo(() => {
15103
- if (!hasInitialSizeRef.current) {
15157
+ if (!hasInitialSize) {
15104
15158
  return {
15105
15159
  ...resize,
15106
15160
  scroll: resize?.scroll ?? true,
@@ -15112,7 +15166,7 @@ function CanvasImpl({
15112
15166
  debounce: { scroll: 50, resize: 0 },
15113
15167
  ...resize
15114
15168
  };
15115
- }, [resize, hasInitialSizeRef.current]);
15169
+ }, [hasInitialSize, resize]);
15116
15170
  const [containerRef, containerRect] = useMeasure(measureConfig);
15117
15171
  const effectiveSize = React.useMemo(() => {
15118
15172
  let w = width ?? containerRect.width;
@@ -15128,9 +15182,7 @@ function CanvasImpl({
15128
15182
  left: containerRect.left
15129
15183
  };
15130
15184
  }, [width, height, containerRect, forceEven]);
15131
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15132
- hasInitialSizeRef.current = true;
15133
- }
15185
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15134
15186
  const canvasRef = React.useRef(null);
15135
15187
  const divRef = React.useRef(null);
15136
15188
  React.useImperativeHandle(ref, () => canvasRef.current);
@@ -15244,6 +15296,7 @@ function CanvasImpl({
15244
15296
  const canvas = canvasRef.current;
15245
15297
  if (canvas) {
15246
15298
  return () => {
15299
+ if (canvas.isConnected) return;
15247
15300
  unmountComponentAtNode(canvas);
15248
15301
  root.current = null;
15249
15302
  };
@@ -15302,20 +15355,36 @@ function CanvasImpl({
15302
15355
  }
15303
15356
  );
15304
15357
  }
15305
- function Canvas(props) {
15358
+ function Canvas$1(props) {
15306
15359
  return /* @__PURE__ */ jsx(FiberProvider, { children: /* @__PURE__ */ jsx(CanvasImpl, { ...props }) });
15307
15360
  }
15308
15361
 
15362
+ const isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
15363
+ const hasValidLegacyNodeMarkers = (value) => {
15364
+ const hasUuid = "uuid" in value;
15365
+ const hasNodeType = "nodeType" in value;
15366
+ const uuid = Reflect.get(value, "uuid");
15367
+ const nodeType = Reflect.get(value, "nodeType");
15368
+ return (hasUuid || hasNodeType) && (!hasUuid || uuid === void 0 || typeof uuid === "string") && (!hasNodeType || nodeType === void 0 || nodeType === null || typeof nodeType === "string");
15369
+ };
15370
+ const isUniformNode = (value) => isObjectLike(value) && "isUniformNode" in value && value.isUniformNode === true;
15371
+ const isTSLNode = (value) => isObjectLike(value) && (Reflect.get(value, "isNode") === true || Reflect.get(value, "shaderNode") !== void 0 && typeof Reflect.get(value, "id") === "number" || hasValidLegacyNodeMarkers(value));
15372
+ const isBufferLike = (value) => ArrayBuffer.isView(value) && !(value instanceof DataView) || isObjectLike(value) && ("isBufferAttribute" in value || "isInterleavedBufferAttribute" in value || isTSLNode(value));
15373
+ const isStorageLike = (value) => isObjectLike(value) && ("isTexture" in value || "isData3DTexture" in value || isTSLNode(value));
15374
+
15309
15375
  var __defProp = Object.defineProperty;
15310
15376
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15311
15377
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
15312
- var _a;
15378
+ var _a, _b;
15313
15379
  const INTERNAL_DATA = Symbol("ScopedStore.data");
15314
- _a = INTERNAL_DATA;
15380
+ const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
15381
+ _b = INTERNAL_DATA, _a = INTERNAL_IS_LEAF;
15315
15382
  const _ScopedStore = class _ScopedStore {
15316
- constructor(data) {
15383
+ constructor(data, isLeaf) {
15384
+ __publicField(this, _b);
15317
15385
  __publicField(this, _a);
15318
15386
  this[INTERNAL_DATA] = data;
15387
+ this[INTERNAL_IS_LEAF] = isLeaf;
15319
15388
  return new Proxy(this, {
15320
15389
  get(target, prop, receiver) {
15321
15390
  if (typeof prop === "string") {
@@ -15349,10 +15418,10 @@ const _ScopedStore = class _ScopedStore {
15349
15418
  * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
15350
15419
  */
15351
15420
  scope(key) {
15352
- const data = this[INTERNAL_DATA][key];
15353
- return new _ScopedStore(
15354
- data && typeof data === "object" ? data : {}
15355
- );
15421
+ const value = this[INTERNAL_DATA][key];
15422
+ const isLeaf = this[INTERNAL_IS_LEAF];
15423
+ const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
15424
+ return new _ScopedStore(scope, isLeaf);
15356
15425
  }
15357
15426
  /**
15358
15427
  * Check if a key exists in the store.
@@ -15368,8 +15437,8 @@ const _ScopedStore = class _ScopedStore {
15368
15437
  }
15369
15438
  };
15370
15439
  let ScopedStore = _ScopedStore;
15371
- function createScopedStore(data) {
15372
- return new ScopedStore(data);
15440
+ function createScopedStore(data, isLeaf) {
15441
+ return new ScopedStore(data, isLeaf);
15373
15442
  }
15374
15443
  function createLazyCreatorState(state, store) {
15375
15444
  let _uniforms = null;
@@ -15380,22 +15449,22 @@ function createLazyCreatorState(state, store) {
15380
15449
  return Object.create(state, {
15381
15450
  uniforms: {
15382
15451
  get() {
15383
- return _uniforms ?? (_uniforms = createScopedStore(view("uniforms")));
15452
+ return _uniforms ?? (_uniforms = createScopedStore(view("uniforms"), isUniformNode));
15384
15453
  }
15385
15454
  },
15386
15455
  nodes: {
15387
15456
  get() {
15388
- return _nodes ?? (_nodes = createScopedStore(view("nodes")));
15457
+ return _nodes ?? (_nodes = createScopedStore(view("nodes"), isTSLNode));
15389
15458
  }
15390
15459
  },
15391
15460
  buffers: {
15392
15461
  get() {
15393
- return _buffers ?? (_buffers = createScopedStore(view("buffers")));
15462
+ return _buffers ?? (_buffers = createScopedStore(view("buffers"), isBufferLike));
15394
15463
  }
15395
15464
  },
15396
15465
  gpuStorage: {
15397
15466
  get() {
15398
- return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage")));
15467
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage"), isStorageLike));
15399
15468
  }
15400
15469
  }
15401
15470
  });
@@ -15454,6 +15523,11 @@ function createTextureOperations(set) {
15454
15523
  removeMultiple: (keys) => removeTextures(set, keys)
15455
15524
  };
15456
15525
  }
15526
+ function scopedNodeName(scope, name) {
15527
+ const raw = scope ? `${scope}_${name}` : name;
15528
+ const safe = raw.replace(/[^A-Za-z0-9_]/g, "_");
15529
+ return /^[A-Za-z_]/.test(safe) ? safe : `_${safe}`;
15530
+ }
15457
15531
  function extractTSLValue(value) {
15458
15532
  if (value === null || value === void 0) return value;
15459
15533
  if (typeof value !== "object") return value;
@@ -15491,7 +15565,7 @@ function vectorize(inObject) {
15491
15565
  return extractTSLValue(inObject);
15492
15566
  }
15493
15567
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15494
- if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15568
+ if (obj.isMatrix2 || obj.isMatrix3 || obj.isMatrix4) return inObject;
15495
15569
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
15496
15570
  if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
15497
15571
  const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
@@ -15549,22 +15623,21 @@ function useScopedResource(options) {
15549
15623
  return entries;
15550
15624
  }
15551
15625
 
15552
- const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15553
15626
  function useUniforms(creatorOrScope, scope) {
15554
15627
  const store = usePrimaryStore();
15555
15628
  const removeUniforms2 = useCallback(
15556
- (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode$1),
15629
+ (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode),
15557
15630
  [store]
15558
15631
  );
15559
15632
  const clearUniforms = useCallback(
15560
- (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode$1),
15633
+ (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode),
15561
15634
  [store]
15562
15635
  );
15563
15636
  const rebuildUniforms = useCallback(
15564
15637
  (targetScope) => rebuildResource(store, "uniforms", targetScope),
15565
15638
  [store]
15566
15639
  );
15567
- const inputForMemoization = useMemo(() => {
15640
+ const processedInput = useMemo(() => {
15568
15641
  let raw = creatorOrScope;
15569
15642
  if (is.fun(creatorOrScope)) {
15570
15643
  const wrappedState = createLazyCreatorState(store.getState(), store);
@@ -15575,23 +15648,24 @@ function useUniforms(creatorOrScope, scope) {
15575
15648
  for (const [key, value] of Object.entries(raw)) {
15576
15649
  normalized[key] = vectorize(value);
15577
15650
  }
15578
- return normalized;
15651
+ return { candidates: raw, normalized };
15579
15652
  }
15580
- return raw;
15653
+ return { candidates: raw, normalized: raw };
15581
15654
  }, [creatorOrScope, store]);
15582
- const memoizedInput = useCompareMemoize(inputForMemoization);
15655
+ const memoizedInput = useCompareMemoize(processedInput.normalized);
15583
15656
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15584
15657
  const storeUniforms = usePrimaryThree((s) => s.uniforms);
15585
15658
  const created = useScopedResource({
15586
15659
  store,
15587
15660
  kind: "uniforms",
15588
15661
  scope: isReader ? void 0 : scope,
15589
- isLeaf: isUniformNode$1,
15662
+ isLeaf: isUniformNode,
15590
15663
  input: memoizedInput,
15591
15664
  create: () => {
15592
15665
  if (isReader) return {};
15593
- if (typeof memoizedInput !== "object" || memoizedInput === null) throw new Error("Invalid uniform input");
15594
- return memoizedInput;
15666
+ if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
15667
+ throw new Error("Invalid uniform input");
15668
+ return processedInput.candidates;
15595
15669
  },
15596
15670
  prepare: (name, candidate) => createUniform(name, candidate, scope),
15597
15671
  reconcile: (existing, candidate) => {
@@ -15613,7 +15687,7 @@ function useUniforms(creatorOrScope, scope) {
15613
15687
  uniforms = storeUniforms;
15614
15688
  } else if (typeof memoizedInput === "string") {
15615
15689
  const scopeData = storeUniforms[memoizedInput];
15616
- uniforms = scopeData && !isUniformNode$1(scopeData) ? scopeData : {};
15690
+ uniforms = scopeData && !isUniformNode(scopeData) ? scopeData : {};
15617
15691
  }
15618
15692
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15619
15693
  }
@@ -15628,7 +15702,7 @@ function removeUniforms(set, names, scope) {
15628
15702
  return { uniforms: { ...state.uniforms, [scope]: currentScope } };
15629
15703
  }
15630
15704
  const uniforms = { ...state.uniforms };
15631
- for (const name of names) if (isUniformNode$1(uniforms[name])) delete uniforms[name];
15705
+ for (const name of names) if (isUniformNode(uniforms[name])) delete uniforms[name];
15632
15706
  return { uniforms };
15633
15707
  });
15634
15708
  }
@@ -15642,7 +15716,7 @@ function clearRootUniforms(set) {
15642
15716
  set((state) => {
15643
15717
  const uniforms = {};
15644
15718
  for (const [key, value] of Object.entries(state.uniforms)) {
15645
- if (!isUniformNode$1(value)) uniforms[key] = value;
15719
+ if (!isUniformNode(value)) uniforms[key] = value;
15646
15720
  }
15647
15721
  return { uniforms };
15648
15722
  });
@@ -15652,8 +15726,7 @@ function createUniform(inName, node, scope) {
15652
15726
  const inValue = vectorize(node);
15653
15727
  const newUniform = uniform(inValue);
15654
15728
  if (typeof newUniform.setName === "function") {
15655
- const name = scope ? `${scope}_${inName}` : inName;
15656
- newUniform.setName(name);
15729
+ newUniform.setName(scopedNodeName(scope, inName));
15657
15730
  }
15658
15731
  return newUniform;
15659
15732
  }
@@ -15679,12 +15752,10 @@ function hasEqualsMethod(value) {
15679
15752
  function isSameThreeType(a, b) {
15680
15753
  const obj_a = a;
15681
15754
  const obj_b = b;
15682
- 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;
15755
+ 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;
15683
15756
  }
15684
15757
 
15685
15758
  const uniformOf = uniform;
15686
- const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15687
- const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15688
15759
  function useUniform(name, value) {
15689
15760
  const store = useStore();
15690
15761
  const registered = useScopedResource({
@@ -15696,7 +15767,7 @@ function useUniform(name, value) {
15696
15767
  create: () => value === void 0 ? {} : { [name]: value },
15697
15768
  prepare: createNamedUniform,
15698
15769
  reconcile: (existing2) => {
15699
- if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
15770
+ if (value !== void 0 && !isTSLNode(value) && !isUniformNode(value)) {
15700
15771
  existing2.value = typeof value === "string" ? new Color(value) : value;
15701
15772
  }
15702
15773
  }
@@ -15712,7 +15783,7 @@ function createNamedUniform(name, value) {
15712
15783
  let node;
15713
15784
  if (isUniformNode(value)) {
15714
15785
  node = value;
15715
- } else if (isTSLNode$1(value)) {
15786
+ } else if (isTSLNode(value)) {
15716
15787
  node = uniformOf(value);
15717
15788
  } else if (typeof value === "string") {
15718
15789
  node = uniform(new Color(value));
@@ -15725,7 +15796,6 @@ function createNamedUniform(name, value) {
15725
15796
  return node;
15726
15797
  }
15727
15798
 
15728
- const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
15729
15799
  function useNodes(creatorOrScope, scope) {
15730
15800
  const store = usePrimaryStore();
15731
15801
  const removeNodes2 = useCallback(
@@ -15752,7 +15822,8 @@ function useNodes(creatorOrScope, scope) {
15752
15822
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15753
15823
  },
15754
15824
  prepare: (name, node) => {
15755
- node.setName?.(scope ? `${scope}.${name}` : name);
15825
+ const setName = Reflect.get(node, "setName");
15826
+ if (typeof setName === "function") setName.call(node, scopedNodeName(scope, name));
15756
15827
  return node;
15757
15828
  }
15758
15829
  });
@@ -15807,13 +15878,6 @@ function useLocalNodes(creator) {
15807
15878
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
15808
15879
  }
15809
15880
 
15810
- const isBufferLike = (value) => {
15811
- if (value === null || typeof value !== "object") return false;
15812
- if (ArrayBuffer.isView(value)) return true;
15813
- if ("isBufferAttribute" in value) return true;
15814
- if ("uuid" in value || "nodeType" in value) return true;
15815
- return false;
15816
- };
15817
15881
  const disposeBuffer = (buffer) => {
15818
15882
  if (buffer === null || typeof buffer !== "object") return;
15819
15883
  if ("dispose" in buffer && typeof buffer.dispose === "function") {
@@ -15860,9 +15924,8 @@ function useBuffers(creatorOrScope, scope) {
15860
15924
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15861
15925
  },
15862
15926
  prepare: (name, buffer) => {
15863
- if ("setName" in buffer && typeof buffer.setName === "function") {
15864
- buffer.setName(scope ? `${scope}.${name}` : name);
15865
- }
15927
+ const setName = Reflect.get(buffer, "setName");
15928
+ if (typeof setName === "function") setName.call(buffer, scopedNodeName(scope, name));
15866
15929
  return buffer;
15867
15930
  }
15868
15931
  });
@@ -15879,13 +15942,6 @@ function rebuildAllBuffers(store, scope) {
15879
15942
  rebuildResource(store, "buffers", scope);
15880
15943
  }
15881
15944
 
15882
- const isStorageLike = (value) => {
15883
- if (value === null || typeof value !== "object") return false;
15884
- if ("isTexture" in value) return true;
15885
- if ("isData3DTexture" in value) return true;
15886
- if ("uuid" in value || "nodeType" in value) return true;
15887
- return false;
15888
- };
15889
15945
  const disposeStorage = (storage) => {
15890
15946
  if (storage === null || typeof storage !== "object") return;
15891
15947
  if ("dispose" in storage && typeof storage.dispose === "function") {
@@ -15932,10 +15988,9 @@ function useGPUStorage(creatorOrScope, scope) {
15932
15988
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15933
15989
  },
15934
15990
  prepare: (name, storage) => {
15935
- const label = scope ? `${scope}.${name}` : name;
15936
- if ("setName" in storage && typeof storage.setName === "function") {
15937
- storage.setName(label);
15938
- }
15991
+ const label = scopedNodeName(scope, name);
15992
+ const setName = Reflect.get(storage, "setName");
15993
+ if (typeof setName === "function") setName.call(storage, label);
15939
15994
  if ("name" in storage && typeof storage.name === "string") {
15940
15995
  storage.name = label;
15941
15996
  }
@@ -15963,9 +16018,10 @@ function rebuildAllStorage(store, scope) {
15963
16018
 
15964
16019
  function useRenderPipeline(mainCB, setupCB) {
15965
16020
  const store = useStore();
15966
- const { scene, camera, renderer, isLegacy } = useThree();
16021
+ const { scene, camera, renderer, isLegacy } = useThree$1();
15967
16022
  const callbacksRanRef = useRef(false);
15968
16023
  const scenePassCacheRef = useRef(null);
16024
+ const forceScenePassRebuildRef = useRef(false);
15969
16025
  const mainCBRef = useRef(mainCB);
15970
16026
  const setupCBRef = useRef(setupCB);
15971
16027
  mainCBRef.current = mainCB;
@@ -15973,7 +16029,7 @@ function useRenderPipeline(mainCB, setupCB) {
15973
16029
  const [rebuildVersion, setRebuildVersion] = useState(0);
15974
16030
  useEffect(() => {
15975
16031
  callbacksRanRef.current = false;
15976
- scenePassCacheRef.current = null;
16032
+ forceScenePassRebuildRef.current = true;
15977
16033
  }, []);
15978
16034
  const clearPasses = useCallback(() => {
15979
16035
  store.setState({ passes: {} });
@@ -15983,12 +16039,14 @@ function useRenderPipeline(mainCB, setupCB) {
15983
16039
  renderPipeline: null,
15984
16040
  passes: {}
15985
16041
  });
16042
+ scenePassCacheRef.current?.scenePass.dispose();
15986
16043
  callbacksRanRef.current = false;
15987
16044
  scenePassCacheRef.current = null;
16045
+ forceScenePassRebuildRef.current = false;
15988
16046
  }, [store]);
15989
16047
  const rebuild = useCallback(() => {
15990
16048
  callbacksRanRef.current = false;
15991
- scenePassCacheRef.current = null;
16049
+ forceScenePassRebuildRef.current = true;
15992
16050
  setRebuildVersion((v) => v + 1);
15993
16051
  }, []);
15994
16052
  useLayoutEffect(() => {
@@ -15996,6 +16054,9 @@ function useRenderPipeline(mainCB, setupCB) {
15996
16054
  throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
15997
16055
  }
15998
16056
  if (!renderer || !scene || !camera) return;
16057
+ if (!("isWebGPURenderer" in renderer)) {
16058
+ throw new Error("useRenderPipeline: renderer is not a WebGPURenderer but isLegacy is false.");
16059
+ }
15999
16060
  const state = store.getState();
16000
16061
  const set = store.setState;
16001
16062
  try {
@@ -16006,21 +16067,33 @@ function useRenderPipeline(mainCB, setupCB) {
16006
16067
  pipeline = new RenderPipeline(renderer);
16007
16068
  justCreatedPipeline = true;
16008
16069
  }
16009
- const cacheValid = scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16070
+ const cacheValid = !forceScenePassRebuildRef.current && scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
16071
+ let outgoingScenePass = null;
16010
16072
  let scenePass;
16011
16073
  if (cacheValid) {
16012
16074
  scenePass = scenePassCacheRef.current.scenePass;
16013
16075
  } else {
16076
+ outgoingScenePass = scenePassCacheRef.current?.scenePass ?? null;
16014
16077
  scenePass = pass(scene, camera);
16015
16078
  scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
16079
+ forceScenePassRebuildRef.current = false;
16016
16080
  }
16017
16081
  currentPasses.scenePass = scenePass;
16018
- if (!pipeline.outputNode || justCreatedPipeline) pipeline.outputNode = scenePass;
16082
+ let outputNodeChanged = false;
16083
+ if (!pipeline.outputNode || justCreatedPipeline || pipeline.outputNode === outgoingScenePass) {
16084
+ pipeline.outputNode = scenePass;
16085
+ outputNodeChanged = true;
16086
+ }
16019
16087
  set({ renderPipeline: pipeline, passes: currentPasses });
16020
16088
  const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
16021
16089
  if (shouldRunCallbacks) {
16022
16090
  if (setupCBRef.current) {
16023
- const freshState = store.getState();
16091
+ const freshState = Object.assign({}, store.getState(), {
16092
+ renderPipeline: pipeline,
16093
+ passes: { ...currentPasses, scenePass },
16094
+ renderer,
16095
+ isLegacy: false
16096
+ });
16024
16097
  const setupResult = setupCBRef.current(freshState);
16025
16098
  if (setupResult && typeof setupResult === "object") {
16026
16099
  currentPasses = { ...currentPasses, ...setupResult };
@@ -16028,32 +16101,37 @@ function useRenderPipeline(mainCB, setupCB) {
16028
16101
  }
16029
16102
  }
16030
16103
  if (mainCBRef.current) {
16031
- const freshState = store.getState();
16104
+ const freshState = Object.assign({}, store.getState(), {
16105
+ renderPipeline: pipeline,
16106
+ passes: { ...currentPasses, scenePass },
16107
+ renderer,
16108
+ isLegacy: false
16109
+ });
16032
16110
  const mainResult = mainCBRef.current(freshState);
16033
16111
  if (mainResult && typeof mainResult === "object") {
16034
16112
  currentPasses = { ...currentPasses, ...mainResult };
16035
16113
  set({ passes: currentPasses });
16036
16114
  }
16037
16115
  }
16116
+ outputNodeChanged = true;
16038
16117
  callbacksRanRef.current = true;
16039
16118
  }
16119
+ if (outputNodeChanged) pipeline.needsUpdate = true;
16120
+ if (outgoingScenePass && outgoingScenePass !== scenePass) outgoingScenePass.dispose();
16040
16121
  } catch (error) {
16041
16122
  console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
16042
16123
  }
16043
16124
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16044
- const passes = useThree((s) => s.passes);
16045
- const renderPipeline = useThree((s) => s.renderPipeline);
16046
- return {
16047
- passes,
16048
- renderPipeline,
16049
- clearPasses,
16050
- reset,
16051
- rebuild,
16052
- // isReady indicates if RenderPipeline is configured and ready for rendering
16053
- isReady: renderPipeline !== null
16054
- };
16125
+ const passes = useThree$1((s) => s.passes);
16126
+ const renderPipeline = useThree$1((s) => s.renderPipeline);
16127
+ const actions = { passes, clearPasses, reset, rebuild };
16128
+ if (renderPipeline) return { ...actions, isReady: true, renderPipeline };
16129
+ return { ...actions, isReady: false, renderPipeline: null };
16055
16130
  }
16056
16131
 
16057
16132
  extend(THREE);
16133
+ const useThree = useThree$1;
16134
+ const useFrame = useFrame$1;
16135
+ const Canvas = Canvas$1;
16058
16136
 
16059
16137
  export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Texture, _roots, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef$1 as isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, rebuildAllBuffers, rebuildAllNodes, rebuildAllStorage, rebuildAllUniforms, reconciler, registerPrimary, removeInteractivity, removeNodes, removeUniforms, resolve, swapInteractivity, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useBuffers, useEnvironment, useFrame, useGPUStorage, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, useRenderPipeline, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms, waitForPrimary };