@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.
package/dist/legacy.cjs CHANGED
@@ -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');
@@ -53,11 +54,11 @@ const R3F_BUILD_WEBGPU = false;
53
54
  async function loadInspector() {
54
55
  throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
55
56
  }
56
- const WebGPURenderer = class WebGPURenderer2 {
57
+ class WebGPURenderer {
57
58
  constructor() {
58
59
  throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
59
60
  }
60
- };
61
+ }
61
62
  const RenderTarget = null;
62
63
  const CubeRenderTarget = null;
63
64
 
@@ -533,8 +534,10 @@ function hasVisibilityHandlers(handlers) {
533
534
  }
534
535
 
535
536
  const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
536
- function fromRef(ref) {
537
- return { [FROM_REF]: ref };
537
+ function fromRef(ref, transform) {
538
+ const marker = { [FROM_REF]: ref };
539
+ if (transform) marker.transform = transform;
540
+ return marker;
538
541
  }
539
542
  function isFromRef(value) {
540
543
  return value !== null && typeof value === "object" && FROM_REF in value;
@@ -635,11 +638,9 @@ function diffProps(instance, newProps) {
635
638
  for (const prop in instance.props) {
636
639
  if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
637
640
  const { root, key } = resolve(instance.object, prop);
638
- if (root.constructor && root.constructor.length === 0) {
641
+ if (typeof root?.constructor === "function") {
639
642
  const ctor = getMemoizedPrototype(root);
640
643
  if (!is.und(ctor)) changedProps[prop] = ctor[key];
641
- } else {
642
- changedProps[prop] = 0;
643
644
  }
644
645
  }
645
646
  return changedProps;
@@ -929,7 +930,7 @@ const createStore = (invalidate, advance) => {
929
930
  size: newSize,
930
931
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
931
932
  }));
932
- scheduler.getScheduler().invalidate();
933
+ get().invalidate();
933
934
  }
934
935
  }
935
936
  return;
@@ -944,15 +945,16 @@ const createStore = (invalidate, advance) => {
944
945
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
945
946
  _sizeImperative: true
946
947
  }));
947
- scheduler.getScheduler().invalidate();
948
+ get().invalidate();
948
949
  },
949
950
  setDpr: (dpr) => set((state2) => {
950
951
  const resolved = calculateDpr(dpr);
951
952
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
952
953
  }),
953
954
  setFrameloop: (frameloop = "always") => {
955
+ const rootId = get().internal.rootId;
956
+ if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
954
957
  set(() => ({ frameloop }));
955
- scheduler.getScheduler().frameloop = frameloop;
956
958
  },
957
959
  setError: (error) => set(() => ({ error })),
958
960
  error: null,
@@ -1071,7 +1073,8 @@ const createStore = (invalidate, advance) => {
1071
1073
  const resizeTarget = canvasTarget ?? actualRenderer;
1072
1074
  if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1073
1075
  resizeTarget.setSize(size.width, size.height, false);
1074
- internal.canvasTargetSizeDirty = true;
1076
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1077
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1075
1078
  }
1076
1079
  if (camera !== oldCamera) {
1077
1080
  oldCamera = camera;
@@ -1257,6 +1260,13 @@ function useFrame(callback, priorityOrOptions) {
1257
1260
  * Use for controlling the entire frame loop, adding phases, etc.
1258
1261
  */
1259
1262
  scheduler: scheduler2,
1263
+ /**
1264
+ * The root that currently owns this job. Resolve on access because ambient
1265
+ * jobs can be adopted by a Canvas after the controls object is created.
1266
+ */
1267
+ get rootId() {
1268
+ return scheduler.getScheduler().getJobRootId(id);
1269
+ },
1260
1270
  /**
1261
1271
  * Manually step this job only.
1262
1272
  * Bypasses FPS limiting - always runs.
@@ -1273,6 +1283,14 @@ function useFrame(callback, priorityOrOptions) {
1273
1283
  stepAll: (timestamp) => {
1274
1284
  scheduler.getScheduler().step(timestamp);
1275
1285
  },
1286
+ /**
1287
+ * Request frames for this job's owning root without waking sibling roots.
1288
+ */
1289
+ invalidate: (frames, stackFrames) => {
1290
+ const scheduler3 = scheduler.getScheduler();
1291
+ const rootId = scheduler3.getJobRootId(id);
1292
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1293
+ },
1276
1294
  /**
1277
1295
  * Pause this job (set enabled=false).
1278
1296
  * Job remains registered but won't run.
@@ -1345,12 +1363,24 @@ function useTexture(input, optionsOrOnLoad) {
1345
1363
  three.TextureLoader,
1346
1364
  IsObject(stableInput) ? Object.values(stableInput) : stableInput
1347
1365
  );
1366
+ const mappedTextures = React.useMemo(() => {
1367
+ if (cachedResult) return cachedResult;
1368
+ if (IsObject(stableInput)) {
1369
+ const keyed = {};
1370
+ const textureArray = loadedTextures;
1371
+ let i = 0;
1372
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1373
+ return keyed;
1374
+ } else {
1375
+ return loadedTextures;
1376
+ }
1377
+ }, [stableInput, loadedTextures, cachedResult]);
1348
1378
  React.useLayoutEffect(() => {
1349
1379
  if (cachedResult) return;
1350
1380
  if (onLoadCalledForRef.current === inputKey) return;
1351
1381
  onLoadCalledForRef.current = inputKey;
1352
- onLoadRef.current?.(loadedTextures);
1353
- }, [cachedResult, loadedTextures, inputKey]);
1382
+ onLoadRef.current?.(mappedTextures);
1383
+ }, [cachedResult, mappedTextures, inputKey]);
1354
1384
  React.useEffect(() => {
1355
1385
  if (cachedResult) return;
1356
1386
  if ("initTexture" in renderer) {
@@ -1369,18 +1399,6 @@ function useTexture(input, optionsOrOnLoad) {
1369
1399
  });
1370
1400
  }
1371
1401
  }, [renderer, loadedTextures, cachedResult]);
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]);
1384
1402
  React.useEffect(() => {
1385
1403
  if (!cache) return;
1386
1404
  const urlTextureMap = [];
@@ -1582,7 +1600,7 @@ function useEnvironment({
1582
1600
  texture = texture.renderTarget?.texture;
1583
1601
  }
1584
1602
  texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
1585
- texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
1603
+ texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
1586
1604
  return texture;
1587
1605
  }
1588
1606
  const preloadDefaultOptions = {
@@ -1635,11 +1653,12 @@ function getExtension(files) {
1635
1653
  const isCubemap = isArray(files) && files.length === 6;
1636
1654
  const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
1637
1655
  const firstEntry = isArray(files) ? files[0] : files;
1638
- const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1656
+ const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1657
+ const extension = isCubemap ? firstExtension === "hdr" ? "hdr-cube" : "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstExtension;
1639
1658
  return { extension, isCubemap, isGainmap };
1640
1659
  }
1641
1660
  function getLoader(extension) {
1642
- const loader = extension === "cube" ? three.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
1661
+ const loader = extension === "cube" ? three.CubeTextureLoader : extension === "hdr-cube" ? HDRCubeTextureLoader_js.HDRCubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
1643
1662
  return loader;
1644
1663
  }
1645
1664
 
@@ -1690,13 +1709,19 @@ function addTail(callback) {
1690
1709
  return scheduler.getScheduler().onIdle(callback);
1691
1710
  }
1692
1711
  function invalidate(state, frames = 1, stackFrames = false) {
1693
- scheduler.getScheduler().invalidate(frames, stackFrames);
1712
+ const rootId = state?.internal?.rootId;
1713
+ const scheduler$1 = scheduler.getScheduler();
1714
+ if (rootId) scheduler$1.invalidateRoot(rootId, frames, stackFrames);
1715
+ else scheduler$1.invalidate(frames, stackFrames);
1694
1716
  }
1695
- function advance(timestamp) {
1696
- scheduler.getScheduler().step(timestamp);
1717
+ function advance(timestamp, runGlobalEffects, state) {
1718
+ const rootId = state?.internal?.rootId;
1719
+ const scheduler$1 = scheduler.getScheduler();
1720
+ if (rootId) scheduler$1.stepRoot(rootId, timestamp);
1721
+ else scheduler$1.step(timestamp);
1697
1722
  }
1698
1723
 
1699
- const version = "10.0.0-alpha.3";
1724
+ const version = "10.0.0-alpha.5";
1700
1725
  const packageData = {
1701
1726
  version: version};
1702
1727
 
@@ -13555,7 +13580,10 @@ function extend(objects) {
13555
13580
  catalogue[Component] = objects;
13556
13581
  return Component;
13557
13582
  } else {
13558
- Object.assign(catalogue, objects);
13583
+ for (const name in objects) {
13584
+ const object = objects[name];
13585
+ if (isConstructor(object)) catalogue[name] = object;
13586
+ }
13559
13587
  }
13560
13588
  }
13561
13589
  function validateInstance(type, props) {
@@ -13903,7 +13931,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
13903
13931
  getRootHostContext: () => NO_CONTEXT,
13904
13932
  getChildHostContext: () => NO_CONTEXT,
13905
13933
  commitUpdate(instance, type, oldProps, newProps, fiber) {
13906
- validateInstance(type, newProps);
13934
+ validateInstance(instance.type, newProps);
13907
13935
  let reconstruct = false;
13908
13936
  if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
13909
13937
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
@@ -13930,8 +13958,8 @@ const reconciler = /* @__PURE__ */ createReconciler({
13930
13958
  for (const prop in instance.props) {
13931
13959
  const value = instance.props[prop];
13932
13960
  if (isFromRef(value)) {
13933
- const ref = value[FROM_REF];
13934
- if (ref.current != null) resolved[prop] = ref.current;
13961
+ const { [FROM_REF]: ref, transform } = value;
13962
+ if (ref.current != null) resolved[prop] = transform ? transform(ref.current) : ref.current;
13935
13963
  }
13936
13964
  }
13937
13965
  if (Object.keys(resolved).length) applyProps(instance.object, resolved);
@@ -14308,7 +14336,7 @@ function createRoot(canvas) {
14308
14336
  const handleXRFrame = (timestamp, _frame) => {
14309
14337
  const state2 = store.getState();
14310
14338
  if (state2.frameloop === "never") return;
14311
- advance(timestamp);
14339
+ advance(timestamp, true, state2);
14312
14340
  };
14313
14341
  const actualRenderer = state.internal.actualRenderer;
14314
14342
  const handleSessionChange = () => {
@@ -14316,7 +14344,7 @@ function createRoot(canvas) {
14316
14344
  const renderer2 = state2.internal.actualRenderer;
14317
14345
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14318
14346
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14319
- if (!renderer2.xr.isPresenting) invalidate();
14347
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14320
14348
  };
14321
14349
  const xr = {
14322
14350
  connect() {
@@ -14398,14 +14426,19 @@ function createRoot(canvas) {
14398
14426
  const newRootId = canvasId || scheduler$1.generateRootId();
14399
14427
  const unregisterRoot = scheduler$1.registerRoot(newRootId, {
14400
14428
  getState: () => store.getState(),
14401
- onError: (err) => store.getState().setError(err)
14429
+ onError: (err) => store.getState().setError(err),
14430
+ frameloop: store.getState().frameloop,
14431
+ before: schedulerConfig?.before,
14432
+ after: schedulerConfig?.after,
14433
+ order: schedulerConfig?.order
14402
14434
  });
14403
14435
  const unregisterCanvasTarget = scheduler$1.register(
14404
14436
  () => {
14405
14437
  const state2 = store.getState();
14406
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14438
+ const canvasTarget = state2.internal.canvasTarget;
14439
+ if (canvasTarget) {
14407
14440
  const renderer2 = state2.internal.actualRenderer;
14408
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14441
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14409
14442
  if (state2.internal.canvasTargetSizeDirty) {
14410
14443
  state2.internal.canvasTargetSizeDirty = false;
14411
14444
  renderer2.backend?.updateSize?.();
@@ -14478,8 +14511,7 @@ function createRoot(canvas) {
14478
14511
  phase: "render",
14479
14512
  system: true,
14480
14513
  // Internal flag: this is a system job, not user-controlled
14481
- // Apply scheduler config for render ordering and rate limiting
14482
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14514
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14483
14515
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14484
14516
  }
14485
14517
  );
@@ -14498,8 +14530,21 @@ function createRoot(canvas) {
14498
14530
  scheduler: scheduler$1
14499
14531
  }
14500
14532
  }));
14533
+ } else {
14534
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14535
+ if (constraintsChanged) {
14536
+ scheduler$1.setRootConstraints(rootId, {
14537
+ before: schedulerConfig?.before,
14538
+ after: schedulerConfig?.after
14539
+ });
14540
+ }
14541
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14542
+ scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14543
+ }
14501
14544
  }
14502
- scheduler$1.frameloop = frameloop;
14545
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14546
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14547
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14503
14548
  onCreated = onCreatedCallback;
14504
14549
  configured = true;
14505
14550
  resolve();
@@ -14541,18 +14586,23 @@ function unmountComponentAtNode(canvas, callback) {
14541
14586
  const fiber = root?.fiber;
14542
14587
  if (fiber) {
14543
14588
  const state = root?.store.getState();
14544
- if (state) state.internal.active = false;
14589
+ if (state) {
14590
+ state.internal.active = false;
14591
+ const unregisterRoot = state.internal.unregisterRoot;
14592
+ if (unregisterRoot) {
14593
+ unregisterRoot();
14594
+ state.internal.unregisterRoot = void 0;
14595
+ }
14596
+ }
14545
14597
  reconciler.updateContainer(null, fiber, null, () => {
14546
14598
  if (state) {
14547
14599
  setTimeout(() => {
14548
14600
  try {
14549
14601
  const renderer = state.internal.actualRenderer;
14550
- const unregisterRoot = state.internal.unregisterRoot;
14551
- if (unregisterRoot) unregisterRoot();
14552
14602
  const unregisterPrimary = state.internal.unregisterPrimary;
14553
14603
  if (unregisterPrimary) unregisterPrimary();
14554
14604
  const canvasTarget = state.internal.canvasTarget;
14555
- if (canvasTarget?.dispose) canvasTarget.dispose();
14605
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14556
14606
  state.events.disconnect?.();
14557
14607
  cleanupHelperGroup(root.store);
14558
14608
  if (state.isLegacy && renderer) {
@@ -14693,16 +14743,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14693
14743
  const oldbg = target.background;
14694
14744
  const oldenv = target.environment;
14695
14745
  const oldSceneProps = {
14696
- // @ts-ignore
14697
14746
  backgroundBlurriness: target.backgroundBlurriness,
14698
- // @ts-ignore
14699
14747
  backgroundIntensity: target.backgroundIntensity,
14700
- // @ts-ignore
14701
- backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
14702
- // @ts-ignore
14748
+ backgroundRotation: target.backgroundRotation.clone(),
14703
14749
  environmentIntensity: target.environmentIntensity,
14704
- // @ts-ignore
14705
- environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
14750
+ environmentRotation: target.environmentRotation.clone()
14706
14751
  };
14707
14752
  if (background !== "only") target.environment = texture;
14708
14753
  if (background) target.background = texture;
@@ -14996,9 +15041,9 @@ function CanvasImpl({
14996
15041
  React__namespace.useMemo(() => extend(THREE), []);
14997
15042
  const Bridge = useBridge();
14998
15043
  const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
14999
- const hasInitialSizeRef = React__namespace.useRef(false);
15044
+ const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
15000
15045
  const measureConfig = React__namespace.useMemo(() => {
15001
- if (!hasInitialSizeRef.current) {
15046
+ if (!hasInitialSize) {
15002
15047
  return {
15003
15048
  ...resize,
15004
15049
  scroll: resize?.scroll ?? true,
@@ -15010,7 +15055,7 @@ function CanvasImpl({
15010
15055
  debounce: { scroll: 50, resize: 0 },
15011
15056
  ...resize
15012
15057
  };
15013
- }, [resize, hasInitialSizeRef.current]);
15058
+ }, [hasInitialSize, resize]);
15014
15059
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15015
15060
  const effectiveSize = React__namespace.useMemo(() => {
15016
15061
  let w = width ?? containerRect.width;
@@ -15026,9 +15071,7 @@ function CanvasImpl({
15026
15071
  left: containerRect.left
15027
15072
  };
15028
15073
  }, [width, height, containerRect, forceEven]);
15029
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15030
- hasInitialSizeRef.current = true;
15031
- }
15074
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15032
15075
  const canvasRef = React__namespace.useRef(null);
15033
15076
  const divRef = React__namespace.useRef(null);
15034
15077
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
@@ -15142,6 +15185,7 @@ function CanvasImpl({
15142
15185
  const canvas = canvasRef.current;
15143
15186
  if (canvas) {
15144
15187
  return () => {
15188
+ if (canvas.isConnected) return;
15145
15189
  unmountComponentAtNode(canvas);
15146
15190
  root.current = null;
15147
15191
  };