@react-three/fiber 10.0.0-canary.20de6b2 → 10.0.0-canary.27df622

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.
@@ -10,6 +10,7 @@ const scheduler = require('@pmndrs/scheduler');
10
10
  const Tb = require('scheduler');
11
11
  const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
12
12
  const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
13
+ const HDRCubeTextureLoader_js = require('three/examples/jsm/loaders/HDRCubeTextureLoader.js');
13
14
  const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
14
15
  const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
15
16
  const gainmapJs = require('@monogrid/gainmap-js');
@@ -539,8 +540,10 @@ function hasVisibilityHandlers(handlers) {
539
540
  }
540
541
 
541
542
  const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
542
- function fromRef(ref) {
543
- return { [FROM_REF]: ref };
543
+ function fromRef(ref, transform) {
544
+ const marker = { [FROM_REF]: ref };
545
+ if (transform) marker.transform = transform;
546
+ return marker;
544
547
  }
545
548
  function isFromRef(value) {
546
549
  return value !== null && typeof value === "object" && FROM_REF in value;
@@ -641,11 +644,9 @@ function diffProps(instance, newProps) {
641
644
  for (const prop in instance.props) {
642
645
  if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
643
646
  const { root, key } = resolve(instance.object, prop);
644
- if (root.constructor && root.constructor.length === 0) {
647
+ if (typeof root?.constructor === "function") {
645
648
  const ctor = getMemoizedPrototype(root);
646
649
  if (!is.und(ctor)) changedProps[prop] = ctor[key];
647
- } else {
648
- changedProps[prop] = 0;
649
650
  }
650
651
  }
651
652
  return changedProps;
@@ -935,7 +936,7 @@ const createStore = (invalidate, advance) => {
935
936
  size: newSize,
936
937
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
937
938
  }));
938
- scheduler.getScheduler().invalidate();
939
+ get().invalidate();
939
940
  }
940
941
  }
941
942
  return;
@@ -950,15 +951,16 @@ const createStore = (invalidate, advance) => {
950
951
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
951
952
  _sizeImperative: true
952
953
  }));
953
- scheduler.getScheduler().invalidate();
954
+ get().invalidate();
954
955
  },
955
956
  setDpr: (dpr) => set((state2) => {
956
957
  const resolved = calculateDpr(dpr);
957
958
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
958
959
  }),
959
960
  setFrameloop: (frameloop = "always") => {
961
+ const rootId = get().internal.rootId;
962
+ if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
960
963
  set(() => ({ frameloop }));
961
- scheduler.getScheduler().frameloop = frameloop;
962
964
  },
963
965
  setError: (error) => set(() => ({ error })),
964
966
  error: null,
@@ -1077,7 +1079,8 @@ const createStore = (invalidate, advance) => {
1077
1079
  const resizeTarget = canvasTarget ?? actualRenderer;
1078
1080
  if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1079
1081
  resizeTarget.setSize(size.width, size.height, false);
1080
- internal.canvasTargetSizeDirty = true;
1082
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1083
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1081
1084
  }
1082
1085
  if (camera !== oldCamera) {
1083
1086
  oldCamera = camera;
@@ -1263,6 +1266,13 @@ function useFrame$1(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$1(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.
@@ -1351,12 +1369,24 @@ function useTexture(input, optionsOrOnLoad) {
1351
1369
  webgpu.TextureLoader,
1352
1370
  IsObject(stableInput) ? Object.values(stableInput) : stableInput
1353
1371
  );
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]);
1354
1384
  React.useLayoutEffect(() => {
1355
1385
  if (cachedResult) return;
1356
1386
  if (onLoadCalledForRef.current === inputKey) return;
1357
1387
  onLoadCalledForRef.current = inputKey;
1358
- onLoadRef.current?.(loadedTextures);
1359
- }, [cachedResult, loadedTextures, inputKey]);
1388
+ onLoadRef.current?.(mappedTextures);
1389
+ }, [cachedResult, mappedTextures, inputKey]);
1360
1390
  React.useEffect(() => {
1361
1391
  if (cachedResult) return;
1362
1392
  if ("initTexture" in renderer) {
@@ -1375,18 +1405,6 @@ function useTexture(input, optionsOrOnLoad) {
1375
1405
  });
1376
1406
  }
1377
1407
  }, [renderer, loadedTextures, cachedResult]);
1378
- const mappedTextures = React.useMemo(() => {
1379
- if (cachedResult) return cachedResult;
1380
- if (IsObject(stableInput)) {
1381
- const keyed = {};
1382
- const textureArray = loadedTextures;
1383
- let i = 0;
1384
- for (const key in stableInput) keyed[key] = textureArray[i++];
1385
- return keyed;
1386
- } else {
1387
- return loadedTextures;
1388
- }
1389
- }, [stableInput, loadedTextures, cachedResult]);
1390
1408
  React.useEffect(() => {
1391
1409
  if (!cache) return;
1392
1410
  const urlTextureMap = [];
@@ -1588,7 +1606,7 @@ function useEnvironment({
1588
1606
  texture = texture.renderTarget?.texture;
1589
1607
  }
1590
1608
  texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
1591
- texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
1609
+ texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
1592
1610
  return texture;
1593
1611
  }
1594
1612
  const preloadDefaultOptions = {
@@ -1641,11 +1659,12 @@ function getExtension(files) {
1641
1659
  const isCubemap = isArray(files) && files.length === 6;
1642
1660
  const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
1643
1661
  const firstEntry = isArray(files) ? files[0] : files;
1644
- 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;
1645
1664
  return { extension, isCubemap, isGainmap };
1646
1665
  }
1647
1666
  function getLoader(extension) {
1648
- 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;
1649
1668
  return loader;
1650
1669
  }
1651
1670
 
@@ -1696,22 +1715,28 @@ function addTail(callback) {
1696
1715
  return scheduler.getScheduler().onIdle(callback);
1697
1716
  }
1698
1717
  function invalidate(state, frames = 1, stackFrames = false) {
1699
- 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);
1700
1722
  }
1701
- function advance(timestamp) {
1702
- 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);
1703
1728
  }
1704
1729
 
1705
- const version = "10.0.0-alpha.3";
1730
+ const version = "10.0.0-alpha.5";
1706
1731
  const packageData = {
1707
1732
  version: version};
1708
1733
 
1709
1734
  function Xb(Tt) {
1710
1735
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
1711
1736
  }
1712
- var Rm = { exports: {} }, Og = { exports: {} }, _b;
1737
+ var Rm = { exports: {} }, Og = { exports: {} }, _b$1;
1713
1738
  function Kb() {
1714
- return _b || (_b = 1, (function(Tt) {
1739
+ return _b$1 || (_b$1 = 1, (function(Tt) {
1715
1740
  Tt.exports = function(m) {
1716
1741
  function Yn(t, r, a, l) {
1717
1742
  return new uc(t, r, a, l);
@@ -13561,7 +13586,10 @@ function extend(objects) {
13561
13586
  catalogue[Component] = objects;
13562
13587
  return Component;
13563
13588
  } else {
13564
- Object.assign(catalogue, objects);
13589
+ for (const name in objects) {
13590
+ const object = objects[name];
13591
+ if (isConstructor(object)) catalogue[name] = object;
13592
+ }
13565
13593
  }
13566
13594
  }
13567
13595
  function validateInstance(type, props) {
@@ -13909,7 +13937,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
13909
13937
  getRootHostContext: () => NO_CONTEXT,
13910
13938
  getChildHostContext: () => NO_CONTEXT,
13911
13939
  commitUpdate(instance, type, oldProps, newProps, fiber) {
13912
- validateInstance(type, newProps);
13940
+ validateInstance(instance.type, newProps);
13913
13941
  let reconstruct = false;
13914
13942
  if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
13915
13943
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
@@ -13936,8 +13964,8 @@ const reconciler = /* @__PURE__ */ createReconciler({
13936
13964
  for (const prop in instance.props) {
13937
13965
  const value = instance.props[prop];
13938
13966
  if (isFromRef(value)) {
13939
- const ref = value[FROM_REF];
13940
- 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;
13941
13969
  }
13942
13970
  }
13943
13971
  if (Object.keys(resolved).length) applyProps(instance.object, resolved);
@@ -14218,9 +14246,8 @@ function createRoot(canvas) {
14218
14246
  if (!renderer.hasInitialized?.()) {
14219
14247
  const size2 = computeInitialSize(canvas, propsSize);
14220
14248
  if (size2.width > 0 && size2.height > 0) {
14221
- const pixelRatio = calculateDpr(dpr);
14222
- canvas.width = size2.width * pixelRatio;
14223
- canvas.height = size2.height * pixelRatio;
14249
+ renderer.setPixelRatio(calculateDpr(dpr));
14250
+ renderer.setSize(size2.width, size2.height, false);
14224
14251
  }
14225
14252
  await renderer.init();
14226
14253
  }
@@ -14229,7 +14256,7 @@ function createRoot(canvas) {
14229
14256
  state.internal.actualRenderer = renderer;
14230
14257
  state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14231
14258
  if (canvasId && !state.internal.isSecondary) {
14232
- const canvasTarget = new webgpu.CanvasTarget(canvas);
14259
+ const canvasTarget = renderer.getCanvasTarget?.();
14233
14260
  const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14234
14261
  state.set((prev) => ({
14235
14262
  internal: {
@@ -14343,7 +14370,7 @@ function createRoot(canvas) {
14343
14370
  const handleXRFrame = (timestamp, _frame) => {
14344
14371
  const state2 = store.getState();
14345
14372
  if (state2.frameloop === "never") return;
14346
- advance(timestamp);
14373
+ advance(timestamp, true, state2);
14347
14374
  };
14348
14375
  const actualRenderer = state.internal.actualRenderer;
14349
14376
  const handleSessionChange = () => {
@@ -14351,7 +14378,7 @@ function createRoot(canvas) {
14351
14378
  const renderer2 = state2.internal.actualRenderer;
14352
14379
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14353
14380
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14354
- if (!renderer2.xr.isPresenting) invalidate();
14381
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14355
14382
  };
14356
14383
  const xr = {
14357
14384
  connect() {
@@ -14433,14 +14460,19 @@ function createRoot(canvas) {
14433
14460
  const newRootId = canvasId || scheduler$1.generateRootId();
14434
14461
  const unregisterRoot = scheduler$1.registerRoot(newRootId, {
14435
14462
  getState: () => store.getState(),
14436
- 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
14437
14468
  });
14438
14469
  const unregisterCanvasTarget = scheduler$1.register(
14439
14470
  () => {
14440
14471
  const state2 = store.getState();
14441
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14472
+ const canvasTarget = state2.internal.canvasTarget;
14473
+ if (canvasTarget) {
14442
14474
  const renderer2 = state2.internal.actualRenderer;
14443
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14475
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14444
14476
  if (state2.internal.canvasTargetSizeDirty) {
14445
14477
  state2.internal.canvasTargetSizeDirty = false;
14446
14478
  renderer2.backend?.updateSize?.();
@@ -14513,8 +14545,7 @@ function createRoot(canvas) {
14513
14545
  phase: "render",
14514
14546
  system: true,
14515
14547
  // Internal flag: this is a system job, not user-controlled
14516
- // Apply scheduler config for render ordering and rate limiting
14517
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14548
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14518
14549
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14519
14550
  }
14520
14551
  );
@@ -14533,8 +14564,21 @@ function createRoot(canvas) {
14533
14564
  scheduler: scheduler$1
14534
14565
  }
14535
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
+ }
14536
14578
  }
14537
- scheduler$1.frameloop = frameloop;
14579
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14580
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14581
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14538
14582
  onCreated = onCreatedCallback;
14539
14583
  configured = true;
14540
14584
  resolve();
@@ -14576,18 +14620,23 @@ function unmountComponentAtNode(canvas, callback) {
14576
14620
  const fiber = root?.fiber;
14577
14621
  if (fiber) {
14578
14622
  const state = root?.store.getState();
14579
- 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
+ }
14580
14631
  reconciler.updateContainer(null, fiber, null, () => {
14581
14632
  if (state) {
14582
14633
  setTimeout(() => {
14583
14634
  try {
14584
14635
  const renderer = state.internal.actualRenderer;
14585
- const unregisterRoot = state.internal.unregisterRoot;
14586
- if (unregisterRoot) unregisterRoot();
14587
14636
  const unregisterPrimary = state.internal.unregisterPrimary;
14588
14637
  if (unregisterPrimary) unregisterPrimary();
14589
14638
  const canvasTarget = state.internal.canvasTarget;
14590
- if (canvasTarget?.dispose) canvasTarget.dispose();
14639
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14591
14640
  state.events.disconnect?.();
14592
14641
  cleanupHelperGroup(root.store);
14593
14642
  if (state.isLegacy && renderer) {
@@ -14728,16 +14777,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14728
14777
  const oldbg = target.background;
14729
14778
  const oldenv = target.environment;
14730
14779
  const oldSceneProps = {
14731
- // @ts-ignore
14732
14780
  backgroundBlurriness: target.backgroundBlurriness,
14733
- // @ts-ignore
14734
14781
  backgroundIntensity: target.backgroundIntensity,
14735
- // @ts-ignore
14736
- backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
14737
- // @ts-ignore
14782
+ backgroundRotation: target.backgroundRotation.clone(),
14738
14783
  environmentIntensity: target.environmentIntensity,
14739
- // @ts-ignore
14740
- environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
14784
+ environmentRotation: target.environmentRotation.clone()
14741
14785
  };
14742
14786
  if (background !== "only") target.environment = texture;
14743
14787
  if (background) target.background = texture;
@@ -15128,9 +15172,9 @@ function CanvasImpl({
15128
15172
  React__namespace.useMemo(() => extend(THREE), []);
15129
15173
  const Bridge = useBridge();
15130
15174
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
15131
- const hasInitialSizeRef = React__namespace.useRef(false);
15175
+ const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
15132
15176
  const measureConfig = React__namespace.useMemo(() => {
15133
- if (!hasInitialSizeRef.current) {
15177
+ if (!hasInitialSize) {
15134
15178
  return {
15135
15179
  ...resize,
15136
15180
  scroll: resize?.scroll ?? true,
@@ -15142,7 +15186,7 @@ function CanvasImpl({
15142
15186
  debounce: { scroll: 50, resize: 0 },
15143
15187
  ...resize
15144
15188
  };
15145
- }, [resize, hasInitialSizeRef.current]);
15189
+ }, [hasInitialSize, resize]);
15146
15190
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15147
15191
  const effectiveSize = React__namespace.useMemo(() => {
15148
15192
  let w = width ?? containerRect.width;
@@ -15158,9 +15202,7 @@ function CanvasImpl({
15158
15202
  left: containerRect.left
15159
15203
  };
15160
15204
  }, [width, height, containerRect, forceEven]);
15161
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15162
- hasInitialSizeRef.current = true;
15163
- }
15205
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15164
15206
  const canvasRef = React__namespace.useRef(null);
15165
15207
  const divRef = React__namespace.useRef(null);
15166
15208
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
@@ -15274,6 +15316,7 @@ function CanvasImpl({
15274
15316
  const canvas = canvasRef.current;
15275
15317
  if (canvas) {
15276
15318
  return () => {
15319
+ if (canvas.isConnected) return;
15277
15320
  unmountComponentAtNode(canvas);
15278
15321
  root.current = null;
15279
15322
  };
@@ -15332,20 +15375,36 @@ function CanvasImpl({
15332
15375
  }
15333
15376
  );
15334
15377
  }
15335
- function Canvas(props) {
15378
+ function Canvas$1(props) {
15336
15379
  return /* @__PURE__ */ jsxRuntime.jsx(itsFine.FiberProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(CanvasImpl, { ...props }) });
15337
15380
  }
15338
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
+
15339
15395
  var __defProp = Object.defineProperty;
15340
15396
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15341
15397
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
15342
- var _a;
15398
+ var _a, _b;
15343
15399
  const INTERNAL_DATA = Symbol("ScopedStore.data");
15344
- _a = INTERNAL_DATA;
15400
+ const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
15401
+ _b = INTERNAL_DATA, _a = INTERNAL_IS_LEAF;
15345
15402
  const _ScopedStore = class _ScopedStore {
15346
- constructor(data) {
15403
+ constructor(data, isLeaf) {
15404
+ __publicField(this, _b);
15347
15405
  __publicField(this, _a);
15348
15406
  this[INTERNAL_DATA] = data;
15407
+ this[INTERNAL_IS_LEAF] = isLeaf;
15349
15408
  return new Proxy(this, {
15350
15409
  get(target, prop, receiver) {
15351
15410
  if (typeof prop === "string") {
@@ -15379,10 +15438,10 @@ const _ScopedStore = class _ScopedStore {
15379
15438
  * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
15380
15439
  */
15381
15440
  scope(key) {
15382
- const data = this[INTERNAL_DATA][key];
15383
- return new _ScopedStore(
15384
- data && typeof data === "object" ? data : {}
15385
- );
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);
15386
15445
  }
15387
15446
  /**
15388
15447
  * Check if a key exists in the store.
@@ -15398,8 +15457,8 @@ const _ScopedStore = class _ScopedStore {
15398
15457
  }
15399
15458
  };
15400
15459
  let ScopedStore = _ScopedStore;
15401
- function createScopedStore(data) {
15402
- return new ScopedStore(data);
15460
+ function createScopedStore(data, isLeaf) {
15461
+ return new ScopedStore(data, isLeaf);
15403
15462
  }
15404
15463
  function createLazyCreatorState(state, store) {
15405
15464
  let _uniforms = null;
@@ -15410,22 +15469,22 @@ function createLazyCreatorState(state, store) {
15410
15469
  return Object.create(state, {
15411
15470
  uniforms: {
15412
15471
  get() {
15413
- return _uniforms ?? (_uniforms = createScopedStore(view("uniforms")));
15472
+ return _uniforms ?? (_uniforms = createScopedStore(view("uniforms"), isUniformNode));
15414
15473
  }
15415
15474
  },
15416
15475
  nodes: {
15417
15476
  get() {
15418
- return _nodes ?? (_nodes = createScopedStore(view("nodes")));
15477
+ return _nodes ?? (_nodes = createScopedStore(view("nodes"), isTSLNode));
15419
15478
  }
15420
15479
  },
15421
15480
  buffers: {
15422
15481
  get() {
15423
- return _buffers ?? (_buffers = createScopedStore(view("buffers")));
15482
+ return _buffers ?? (_buffers = createScopedStore(view("buffers"), isBufferLike));
15424
15483
  }
15425
15484
  },
15426
15485
  gpuStorage: {
15427
15486
  get() {
15428
- return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage")));
15487
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage"), isStorageLike));
15429
15488
  }
15430
15489
  }
15431
15490
  });
@@ -15526,7 +15585,7 @@ function vectorize(inObject) {
15526
15585
  return extractTSLValue(inObject);
15527
15586
  }
15528
15587
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15529
- if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15588
+ if (obj.isMatrix2 || obj.isMatrix3 || obj.isMatrix4) return inObject;
15530
15589
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
15531
15590
  if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
15532
15591
  const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
@@ -15584,22 +15643,21 @@ function useScopedResource(options) {
15584
15643
  return entries;
15585
15644
  }
15586
15645
 
15587
- const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15588
15646
  function useUniforms(creatorOrScope, scope) {
15589
15647
  const store = usePrimaryStore();
15590
15648
  const removeUniforms2 = React.useCallback(
15591
- (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),
15592
15650
  [store]
15593
15651
  );
15594
15652
  const clearUniforms = React.useCallback(
15595
- (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode$1),
15653
+ (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode),
15596
15654
  [store]
15597
15655
  );
15598
15656
  const rebuildUniforms = React.useCallback(
15599
15657
  (targetScope) => rebuildResource(store, "uniforms", targetScope),
15600
15658
  [store]
15601
15659
  );
15602
- const inputForMemoization = React.useMemo(() => {
15660
+ const processedInput = React.useMemo(() => {
15603
15661
  let raw = creatorOrScope;
15604
15662
  if (is.fun(creatorOrScope)) {
15605
15663
  const wrappedState = createLazyCreatorState(store.getState(), store);
@@ -15610,23 +15668,24 @@ function useUniforms(creatorOrScope, scope) {
15610
15668
  for (const [key, value] of Object.entries(raw)) {
15611
15669
  normalized[key] = vectorize(value);
15612
15670
  }
15613
- return normalized;
15671
+ return { candidates: raw, normalized };
15614
15672
  }
15615
- return raw;
15673
+ return { candidates: raw, normalized: raw };
15616
15674
  }, [creatorOrScope, store]);
15617
- const memoizedInput = useCompareMemoize(inputForMemoization);
15675
+ const memoizedInput = useCompareMemoize(processedInput.normalized);
15618
15676
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15619
15677
  const storeUniforms = usePrimaryThree((s) => s.uniforms);
15620
15678
  const created = useScopedResource({
15621
15679
  store,
15622
15680
  kind: "uniforms",
15623
15681
  scope: isReader ? void 0 : scope,
15624
- isLeaf: isUniformNode$1,
15682
+ isLeaf: isUniformNode,
15625
15683
  input: memoizedInput,
15626
15684
  create: () => {
15627
15685
  if (isReader) return {};
15628
- if (typeof memoizedInput !== "object" || memoizedInput === null) throw new Error("Invalid uniform input");
15629
- return memoizedInput;
15686
+ if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
15687
+ throw new Error("Invalid uniform input");
15688
+ return processedInput.candidates;
15630
15689
  },
15631
15690
  prepare: (name, candidate) => createUniform(name, candidate, scope),
15632
15691
  reconcile: (existing, candidate) => {
@@ -15648,7 +15707,7 @@ function useUniforms(creatorOrScope, scope) {
15648
15707
  uniforms = storeUniforms;
15649
15708
  } else if (typeof memoizedInput === "string") {
15650
15709
  const scopeData = storeUniforms[memoizedInput];
15651
- uniforms = scopeData && !isUniformNode$1(scopeData) ? scopeData : {};
15710
+ uniforms = scopeData && !isUniformNode(scopeData) ? scopeData : {};
15652
15711
  }
15653
15712
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15654
15713
  }
@@ -15663,7 +15722,7 @@ function removeUniforms(set, names, scope) {
15663
15722
  return { uniforms: { ...state.uniforms, [scope]: currentScope } };
15664
15723
  }
15665
15724
  const uniforms = { ...state.uniforms };
15666
- 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];
15667
15726
  return { uniforms };
15668
15727
  });
15669
15728
  }
@@ -15677,7 +15736,7 @@ function clearRootUniforms(set) {
15677
15736
  set((state) => {
15678
15737
  const uniforms = {};
15679
15738
  for (const [key, value] of Object.entries(state.uniforms)) {
15680
- if (!isUniformNode$1(value)) uniforms[key] = value;
15739
+ if (!isUniformNode(value)) uniforms[key] = value;
15681
15740
  }
15682
15741
  return { uniforms };
15683
15742
  });
@@ -15713,12 +15772,10 @@ function hasEqualsMethod(value) {
15713
15772
  function isSameThreeType(a, b) {
15714
15773
  const obj_a = a;
15715
15774
  const obj_b = b;
15716
- 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;
15717
15776
  }
15718
15777
 
15719
15778
  const uniformOf = tsl.uniform;
15720
- const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
15721
- const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15722
15779
  function useUniform(name, value) {
15723
15780
  const store = useStore();
15724
15781
  const registered = useScopedResource({
@@ -15730,7 +15787,7 @@ function useUniform(name, value) {
15730
15787
  create: () => value === void 0 ? {} : { [name]: value },
15731
15788
  prepare: createNamedUniform,
15732
15789
  reconcile: (existing2) => {
15733
- if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
15790
+ if (value !== void 0 && !isTSLNode(value) && !isUniformNode(value)) {
15734
15791
  existing2.value = typeof value === "string" ? new webgpu.Color(value) : value;
15735
15792
  }
15736
15793
  }
@@ -15746,7 +15803,7 @@ function createNamedUniform(name, value) {
15746
15803
  let node;
15747
15804
  if (isUniformNode(value)) {
15748
15805
  node = value;
15749
- } else if (isTSLNode$1(value)) {
15806
+ } else if (isTSLNode(value)) {
15750
15807
  node = uniformOf(value);
15751
15808
  } else if (typeof value === "string") {
15752
15809
  node = tsl.uniform(new webgpu.Color(value));
@@ -15759,7 +15816,6 @@ function createNamedUniform(name, value) {
15759
15816
  return node;
15760
15817
  }
15761
15818
 
15762
- const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
15763
15819
  function useNodes(creatorOrScope, scope) {
15764
15820
  const store = usePrimaryStore();
15765
15821
  const removeNodes2 = React.useCallback(
@@ -15786,7 +15842,8 @@ function useNodes(creatorOrScope, scope) {
15786
15842
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15787
15843
  },
15788
15844
  prepare: (name, node) => {
15789
- node.setName?.(scopedNodeName(scope, name));
15845
+ const setName = Reflect.get(node, "setName");
15846
+ if (typeof setName === "function") setName.call(node, scopedNodeName(scope, name));
15790
15847
  return node;
15791
15848
  }
15792
15849
  });
@@ -15841,13 +15898,6 @@ function useLocalNodes(creator) {
15841
15898
  }, [store, creator, uniforms, nodes, textures, hmrVersion]);
15842
15899
  }
15843
15900
 
15844
- const isBufferLike = (value) => {
15845
- if (value === null || typeof value !== "object") return false;
15846
- if (ArrayBuffer.isView(value)) return true;
15847
- if ("isBufferAttribute" in value) return true;
15848
- if ("uuid" in value || "nodeType" in value) return true;
15849
- return false;
15850
- };
15851
15901
  const disposeBuffer = (buffer) => {
15852
15902
  if (buffer === null || typeof buffer !== "object") return;
15853
15903
  if ("dispose" in buffer && typeof buffer.dispose === "function") {
@@ -15894,9 +15944,8 @@ function useBuffers(creatorOrScope, scope) {
15894
15944
  return creatorOrScope(createLazyCreatorState(store.getState(), store));
15895
15945
  },
15896
15946
  prepare: (name, buffer) => {
15897
- if ("setName" in buffer && typeof buffer.setName === "function") {
15898
- buffer.setName(scopedNodeName(scope, name));
15899
- }
15947
+ const setName = Reflect.get(buffer, "setName");
15948
+ if (typeof setName === "function") setName.call(buffer, scopedNodeName(scope, name));
15900
15949
  return buffer;
15901
15950
  }
15902
15951
  });
@@ -15913,13 +15962,6 @@ function rebuildAllBuffers(store, scope) {
15913
15962
  rebuildResource(store, "buffers", scope);
15914
15963
  }
15915
15964
 
15916
- const isStorageLike = (value) => {
15917
- if (value === null || typeof value !== "object") return false;
15918
- if ("isTexture" in value) return true;
15919
- if ("isData3DTexture" in value) return true;
15920
- if ("uuid" in value || "nodeType" in value) return true;
15921
- return false;
15922
- };
15923
15965
  const disposeStorage = (storage) => {
15924
15966
  if (storage === null || typeof storage !== "object") return;
15925
15967
  if ("dispose" in storage && typeof storage.dispose === "function") {
@@ -15967,9 +16009,8 @@ function useGPUStorage(creatorOrScope, scope) {
15967
16009
  },
15968
16010
  prepare: (name, storage) => {
15969
16011
  const label = scopedNodeName(scope, name);
15970
- if ("setName" in storage && typeof storage.setName === "function") {
15971
- storage.setName(label);
15972
- }
16012
+ const setName = Reflect.get(storage, "setName");
16013
+ if (typeof setName === "function") setName.call(storage, label);
15973
16014
  if ("name" in storage && typeof storage.name === "string") {
15974
16015
  storage.name = label;
15975
16016
  }
@@ -16000,6 +16041,7 @@ function useRenderPipeline(mainCB, setupCB) {
16000
16041
  const { scene, camera, renderer, isLegacy } = useThree$1();
16001
16042
  const callbacksRanRef = React.useRef(false);
16002
16043
  const scenePassCacheRef = React.useRef(null);
16044
+ const forceScenePassRebuildRef = React.useRef(false);
16003
16045
  const mainCBRef = React.useRef(mainCB);
16004
16046
  const setupCBRef = React.useRef(setupCB);
16005
16047
  mainCBRef.current = mainCB;
@@ -16007,7 +16049,7 @@ function useRenderPipeline(mainCB, setupCB) {
16007
16049
  const [rebuildVersion, setRebuildVersion] = React.useState(0);
16008
16050
  React.useEffect(() => {
16009
16051
  callbacksRanRef.current = false;
16010
- scenePassCacheRef.current = null;
16052
+ forceScenePassRebuildRef.current = true;
16011
16053
  }, []);
16012
16054
  const clearPasses = React.useCallback(() => {
16013
16055
  store.setState({ passes: {} });
@@ -16017,12 +16059,14 @@ function useRenderPipeline(mainCB, setupCB) {
16017
16059
  renderPipeline: null,
16018
16060
  passes: {}
16019
16061
  });
16062
+ scenePassCacheRef.current?.scenePass.dispose();
16020
16063
  callbacksRanRef.current = false;
16021
16064
  scenePassCacheRef.current = null;
16065
+ forceScenePassRebuildRef.current = false;
16022
16066
  }, [store]);
16023
16067
  const rebuild = React.useCallback(() => {
16024
16068
  callbacksRanRef.current = false;
16025
- scenePassCacheRef.current = null;
16069
+ forceScenePassRebuildRef.current = true;
16026
16070
  setRebuildVersion((v) => v + 1);
16027
16071
  }, []);
16028
16072
  React.useLayoutEffect(() => {
@@ -16030,6 +16074,9 @@ function useRenderPipeline(mainCB, setupCB) {
16030
16074
  throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
16031
16075
  }
16032
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
+ }
16033
16080
  const state = store.getState();
16034
16081
  const set = store.setState;
16035
16082
  try {
@@ -16040,21 +16087,33 @@ function useRenderPipeline(mainCB, setupCB) {
16040
16087
  pipeline = new webgpu.RenderPipeline(renderer);
16041
16088
  justCreatedPipeline = true;
16042
16089
  }
16043
- 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;
16044
16092
  let scenePass;
16045
16093
  if (cacheValid) {
16046
16094
  scenePass = scenePassCacheRef.current.scenePass;
16047
16095
  } else {
16096
+ outgoingScenePass = scenePassCacheRef.current?.scenePass ?? null;
16048
16097
  scenePass = tsl.pass(scene, camera);
16049
16098
  scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
16099
+ forceScenePassRebuildRef.current = false;
16050
16100
  }
16051
16101
  currentPasses.scenePass = scenePass;
16052
- 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
+ }
16053
16107
  set({ renderPipeline: pipeline, passes: currentPasses });
16054
16108
  const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
16055
16109
  if (shouldRunCallbacks) {
16056
16110
  if (setupCBRef.current) {
16057
- const freshState = store.getState();
16111
+ const freshState = Object.assign({}, store.getState(), {
16112
+ renderPipeline: pipeline,
16113
+ passes: { ...currentPasses, scenePass },
16114
+ renderer,
16115
+ isLegacy: false
16116
+ });
16058
16117
  const setupResult = setupCBRef.current(freshState);
16059
16118
  if (setupResult && typeof setupResult === "object") {
16060
16119
  currentPasses = { ...currentPasses, ...setupResult };
@@ -16062,35 +16121,38 @@ function useRenderPipeline(mainCB, setupCB) {
16062
16121
  }
16063
16122
  }
16064
16123
  if (mainCBRef.current) {
16065
- const freshState = store.getState();
16124
+ const freshState = Object.assign({}, store.getState(), {
16125
+ renderPipeline: pipeline,
16126
+ passes: { ...currentPasses, scenePass },
16127
+ renderer,
16128
+ isLegacy: false
16129
+ });
16066
16130
  const mainResult = mainCBRef.current(freshState);
16067
16131
  if (mainResult && typeof mainResult === "object") {
16068
16132
  currentPasses = { ...currentPasses, ...mainResult };
16069
16133
  set({ passes: currentPasses });
16070
16134
  }
16071
16135
  }
16136
+ outputNodeChanged = true;
16072
16137
  callbacksRanRef.current = true;
16073
16138
  }
16139
+ if (outputNodeChanged) pipeline.needsUpdate = true;
16140
+ if (outgoingScenePass && outgoingScenePass !== scenePass) outgoingScenePass.dispose();
16074
16141
  } catch (error) {
16075
16142
  console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
16076
16143
  }
16077
16144
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
16078
16145
  const passes = useThree$1((s) => s.passes);
16079
16146
  const renderPipeline = useThree$1((s) => s.renderPipeline);
16080
- return {
16081
- passes,
16082
- renderPipeline,
16083
- clearPasses,
16084
- reset,
16085
- rebuild,
16086
- // isReady indicates if RenderPipeline is configured and ready for rendering
16087
- isReady: renderPipeline !== null
16088
- };
16147
+ const actions = { passes, clearPasses, reset, rebuild };
16148
+ if (renderPipeline) return { ...actions, isReady: true, renderPipeline };
16149
+ return { ...actions, isReady: false, renderPipeline: null };
16089
16150
  }
16090
16151
 
16091
16152
  extend(THREE);
16092
16153
  const useThree = useThree$1;
16093
16154
  const useFrame = useFrame$1;
16155
+ const Canvas = Canvas$1;
16094
16156
 
16095
16157
  exports.Scheduler = scheduler.Scheduler;
16096
16158
  exports.getScheduler = scheduler.getScheduler;