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

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/index.mjs CHANGED
@@ -1,7 +1,6 @@
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 } from 'three/webgpu';
3
3
  import { WebGLCubeRenderTarget, WebGLRenderTarget, WebGLRenderer } from 'three';
4
- import { Inspector } from 'three/addons/inspector/Inspector.js';
5
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
5
  import * as React from 'react';
7
6
  import React__default, { useRef, useMemo, useLayoutEffect, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
@@ -32,18 +31,22 @@ function _mergeNamespaces(n, m) {
32
31
 
33
32
  const R3F_BUILD_LEGACY = true;
34
33
  const R3F_BUILD_WEBGPU = true;
34
+ async function loadInspector() {
35
+ const { Inspector } = await import('three/addons/inspector/Inspector.js');
36
+ return Inspector;
37
+ }
35
38
 
36
39
  const THREE = /*#__PURE__*/_mergeNamespaces({
37
40
  __proto__: null,
38
41
  CubeRenderTarget: CubeRenderTarget,
39
42
  CubeRenderTargetCompat: CubeRenderTarget,
40
- Inspector: Inspector,
41
43
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
42
44
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
43
45
  RenderTargetCompat: RenderTarget,
44
46
  WebGLCubeRenderTarget: WebGLCubeRenderTarget,
45
47
  WebGLRenderTarget: WebGLRenderTarget,
46
- WebGLRenderer: WebGLRenderer
48
+ WebGLRenderer: WebGLRenderer,
49
+ loadInspector: loadInspector
47
50
  }, [webgpu]);
48
51
 
49
52
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -902,7 +905,7 @@ const createStore = (invalidate, advance) => {
902
905
  size: newSize,
903
906
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
904
907
  }));
905
- getScheduler().invalidate();
908
+ get().invalidate();
906
909
  }
907
910
  }
908
911
  return;
@@ -917,15 +920,16 @@ const createStore = (invalidate, advance) => {
917
920
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
918
921
  _sizeImperative: true
919
922
  }));
920
- getScheduler().invalidate();
923
+ get().invalidate();
921
924
  },
922
925
  setDpr: (dpr) => set((state2) => {
923
926
  const resolved = calculateDpr(dpr);
924
927
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
925
928
  }),
926
929
  setFrameloop: (frameloop = "always") => {
930
+ const rootId = get().internal.rootId;
931
+ if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
927
932
  set(() => ({ frameloop }));
928
- getScheduler().frameloop = frameloop;
929
933
  },
930
934
  setError: (error) => set(() => ({ error })),
931
935
  error: null,
@@ -1041,17 +1045,11 @@ const createStore = (invalidate, advance) => {
1041
1045
  oldSize = size;
1042
1046
  oldDpr = viewport.dpr;
1043
1047
  updateCamera(camera, size);
1044
- if (internal.isSecondary && canvasTarget) {
1045
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1046
- canvasTarget.setSize(size.width, size.height, false);
1047
- } else {
1048
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1049
- actualRenderer.setSize(size.width, size.height, false);
1050
- if (canvasTarget) {
1051
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1052
- canvasTarget.setSize(size.width, size.height, false);
1053
- }
1054
- }
1048
+ const resizeTarget = canvasTarget ?? actualRenderer;
1049
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1050
+ resizeTarget.setSize(size.width, size.height, false);
1051
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1052
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1055
1053
  }
1056
1054
  if (camera !== oldCamera) {
1057
1055
  oldCamera = camera;
@@ -1113,7 +1111,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1113
1111
  const results = cacheKeys.map(
1114
1112
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1115
1113
  );
1116
- return Array.isArray(input) ? results : results[0];
1114
+ const stableRef = useRef(null);
1115
+ const previous = stableRef.current;
1116
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1117
+ stableRef.current = stable;
1118
+ return Array.isArray(input) ? stable : stable[0];
1117
1119
  }
1118
1120
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1119
1121
  const inputs = Array.isArray(input) ? input : [input];
@@ -1233,6 +1235,13 @@ function useFrame(callback, priorityOrOptions) {
1233
1235
  * Use for controlling the entire frame loop, adding phases, etc.
1234
1236
  */
1235
1237
  scheduler: scheduler2,
1238
+ /**
1239
+ * The root that currently owns this job. Resolve on access because ambient
1240
+ * jobs can be adopted by a Canvas after the controls object is created.
1241
+ */
1242
+ get rootId() {
1243
+ return getScheduler().getJobRootId(id);
1244
+ },
1236
1245
  /**
1237
1246
  * Manually step this job only.
1238
1247
  * Bypasses FPS limiting - always runs.
@@ -1249,6 +1258,14 @@ function useFrame(callback, priorityOrOptions) {
1249
1258
  stepAll: (timestamp) => {
1250
1259
  getScheduler().step(timestamp);
1251
1260
  },
1261
+ /**
1262
+ * Request frames for this job's owning root without waking sibling roots.
1263
+ */
1264
+ invalidate: (frames, stackFrames) => {
1265
+ const scheduler3 = getScheduler();
1266
+ const rootId = scheduler3.getJobRootId(id);
1267
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1268
+ },
1252
1269
  /**
1253
1270
  * Pause this job (set enabled=false).
1254
1271
  * Job remains registered but won't run.
@@ -1278,6 +1295,11 @@ function getUrls(input) {
1278
1295
  if (Array.isArray(input)) return input;
1279
1296
  return Object.values(input);
1280
1297
  }
1298
+ function inputSignature(input) {
1299
+ if (typeof input === "string") return input;
1300
+ if (Array.isArray(input)) return input.join("\0");
1301
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1302
+ }
1281
1303
  function allUrlsCached(urls, textureCache) {
1282
1304
  return urls.every((url) => textureCache.has(url));
1283
1305
  }
@@ -1303,18 +1325,19 @@ function useTexture(input, optionsOrOnLoad) {
1303
1325
  const onLoadRef = useRef(onLoad);
1304
1326
  onLoadRef.current = onLoad;
1305
1327
  const onLoadCalledForRef = useRef(null);
1306
- const urls = useMemo(() => getUrls(input), [input]);
1328
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1329
+ const stableInput = useMemo(() => input, [inputKey]);
1330
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1307
1331
  const cachedResult = useMemo(() => {
1308
1332
  if (!cache) return null;
1309
1333
  const textures = store.getState().textures;
1310
1334
  if (!allUrlsCached(urls, textures)) return null;
1311
- return buildFromCache(input, textures);
1312
- }, [cache, urls, input, store]);
1335
+ return buildFromCache(stableInput, textures);
1336
+ }, [cache, urls, stableInput, store]);
1313
1337
  const loadedTextures = useLoader(
1314
1338
  TextureLoader,
1315
- IsObject(input) ? Object.values(input) : input
1339
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1316
1340
  );
1317
- const inputKey = urls.join("\0");
1318
1341
  useLayoutEffect(() => {
1319
1342
  if (cachedResult) return;
1320
1343
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1341,28 +1364,28 @@ function useTexture(input, optionsOrOnLoad) {
1341
1364
  }, [renderer, loadedTextures, cachedResult]);
1342
1365
  const mappedTextures = useMemo(() => {
1343
1366
  if (cachedResult) return cachedResult;
1344
- if (IsObject(input)) {
1367
+ if (IsObject(stableInput)) {
1345
1368
  const keyed = {};
1346
1369
  const textureArray = loadedTextures;
1347
1370
  let i = 0;
1348
- for (const key in input) keyed[key] = textureArray[i++];
1371
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1349
1372
  return keyed;
1350
1373
  } else {
1351
1374
  return loadedTextures;
1352
1375
  }
1353
- }, [input, loadedTextures, cachedResult]);
1376
+ }, [stableInput, loadedTextures, cachedResult]);
1354
1377
  useEffect(() => {
1355
1378
  if (!cache) return;
1356
1379
  const urlTextureMap = [];
1357
- if (typeof input === "string") {
1358
- urlTextureMap.push([input, mappedTextures]);
1359
- } else if (Array.isArray(input)) {
1380
+ if (typeof stableInput === "string") {
1381
+ urlTextureMap.push([stableInput, mappedTextures]);
1382
+ } else if (Array.isArray(stableInput)) {
1360
1383
  const textureArray = mappedTextures;
1361
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1362
- } else if (IsObject(input)) {
1384
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1385
+ } else if (IsObject(stableInput)) {
1363
1386
  const textureRecord = mappedTextures;
1364
- for (const key in input) {
1365
- const url = input[key];
1387
+ for (const key in stableInput) {
1388
+ const url = stableInput[key];
1366
1389
  urlTextureMap.push([url, textureRecord[key]]);
1367
1390
  }
1368
1391
  }
@@ -1391,7 +1414,7 @@ function useTexture(input, optionsOrOnLoad) {
1391
1414
  }
1392
1415
  return { _textureRefs: refs };
1393
1416
  });
1394
- }, [cache, input, mappedTextures, store]);
1417
+ }, [cache, stableInput, mappedTextures, store]);
1395
1418
  return mappedTextures;
1396
1419
  }
1397
1420
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -1662,13 +1685,19 @@ function addTail(callback) {
1662
1685
  return getScheduler().onIdle(callback);
1663
1686
  }
1664
1687
  function invalidate(state, frames = 1, stackFrames = false) {
1665
- getScheduler().invalidate(frames, stackFrames);
1688
+ const rootId = state?.internal?.rootId;
1689
+ const scheduler = getScheduler();
1690
+ if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
1691
+ else scheduler.invalidate(frames, stackFrames);
1666
1692
  }
1667
- function advance(timestamp) {
1668
- getScheduler().step(timestamp);
1693
+ function advance(timestamp, runGlobalEffects, state) {
1694
+ const rootId = state?.internal?.rootId;
1695
+ const scheduler = getScheduler();
1696
+ if (rootId) scheduler.stepRoot(rootId, timestamp);
1697
+ else scheduler.step(timestamp);
1669
1698
  }
1670
1699
 
1671
- const version = "10.0.0-alpha.3";
1700
+ const version = "10.0.0-alpha.4";
1672
1701
  const packageData = {
1673
1702
  version: version};
1674
1703
 
@@ -12951,7 +12980,10 @@ function releaseInternalPointerCapture(internal, obj, pointerId) {
12951
12980
  const captureData = pointerState.captured.get(obj);
12952
12981
  if (captureData) {
12953
12982
  pointerState.captured.delete(obj);
12954
- captureData.target.releasePointerCapture(pointerId);
12983
+ try {
12984
+ captureData.target.releasePointerCapture?.(pointerId);
12985
+ } catch {
12986
+ }
12955
12987
  }
12956
12988
  }
12957
12989
  function swapInteractivity(store, object, newObject) {
@@ -13045,7 +13077,9 @@ function createEvents(store) {
13045
13077
  state2.events.compute?.(event, state2, state2.previousRoot?.getState());
13046
13078
  if (state2.raycaster.camera === void 0) state2.raycaster.camera = null;
13047
13079
  }
13048
- return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
13080
+ if (!state2.raycaster.camera) return [];
13081
+ obj.updateWorldMatrix(true, true);
13082
+ return state2.raycaster.intersectObject(obj, true);
13049
13083
  }
13050
13084
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
13051
13085
  const aInteractivePriority = a.object.userData?.interactivePriority;
@@ -14197,9 +14231,8 @@ function createRoot(canvas) {
14197
14231
  if (!renderer.hasInitialized?.()) {
14198
14232
  const size2 = computeInitialSize(canvas, propsSize);
14199
14233
  if (size2.width > 0 && size2.height > 0) {
14200
- const pixelRatio = calculateDpr(dpr);
14201
- canvas.width = size2.width * pixelRatio;
14202
- canvas.height = size2.height * pixelRatio;
14234
+ renderer.setPixelRatio(calculateDpr(dpr));
14235
+ renderer.setSize(size2.width, size2.height, false);
14203
14236
  }
14204
14237
  await renderer.init();
14205
14238
  }
@@ -14208,7 +14241,7 @@ function createRoot(canvas) {
14208
14241
  state.internal.actualRenderer = renderer;
14209
14242
  state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14210
14243
  if (canvasId && !state.internal.isSecondary) {
14211
- const canvasTarget = new CanvasTarget(canvas);
14244
+ const canvasTarget = renderer.getCanvasTarget?.();
14212
14245
  const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14213
14246
  state.set((prev) => ({
14214
14247
  internal: {
@@ -14322,7 +14355,7 @@ function createRoot(canvas) {
14322
14355
  const handleXRFrame = (timestamp, _frame) => {
14323
14356
  const state2 = store.getState();
14324
14357
  if (state2.frameloop === "never") return;
14325
- advance(timestamp);
14358
+ advance(timestamp, true, state2);
14326
14359
  };
14327
14360
  const actualRenderer = state.internal.actualRenderer;
14328
14361
  const handleSessionChange = () => {
@@ -14330,7 +14363,7 @@ function createRoot(canvas) {
14330
14363
  const renderer2 = state2.internal.actualRenderer;
14331
14364
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14332
14365
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14333
- if (!renderer2.xr.isPresenting) invalidate();
14366
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14334
14367
  };
14335
14368
  const xr = {
14336
14369
  connect() {
@@ -14412,14 +14445,23 @@ function createRoot(canvas) {
14412
14445
  const newRootId = canvasId || scheduler.generateRootId();
14413
14446
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14414
14447
  getState: () => store.getState(),
14415
- onError: (err) => store.getState().setError(err)
14448
+ onError: (err) => store.getState().setError(err),
14449
+ frameloop: store.getState().frameloop,
14450
+ before: schedulerConfig?.before,
14451
+ after: schedulerConfig?.after,
14452
+ order: schedulerConfig?.order
14416
14453
  });
14417
14454
  const unregisterCanvasTarget = scheduler.register(
14418
14455
  () => {
14419
14456
  const state2 = store.getState();
14420
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14457
+ const canvasTarget = state2.internal.canvasTarget;
14458
+ if (canvasTarget) {
14421
14459
  const renderer2 = state2.internal.actualRenderer;
14422
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14460
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14461
+ if (state2.internal.canvasTargetSizeDirty) {
14462
+ state2.internal.canvasTargetSizeDirty = false;
14463
+ renderer2.backend?.updateSize?.();
14464
+ }
14423
14465
  }
14424
14466
  },
14425
14467
  {
@@ -14488,8 +14530,7 @@ function createRoot(canvas) {
14488
14530
  phase: "render",
14489
14531
  system: true,
14490
14532
  // Internal flag: this is a system job, not user-controlled
14491
- // Apply scheduler config for render ordering and rate limiting
14492
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14533
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14493
14534
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14494
14535
  }
14495
14536
  );
@@ -14508,8 +14549,21 @@ function createRoot(canvas) {
14508
14549
  scheduler
14509
14550
  }
14510
14551
  }));
14552
+ } else {
14553
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14554
+ if (constraintsChanged) {
14555
+ scheduler.setRootConstraints(rootId, {
14556
+ before: schedulerConfig?.before,
14557
+ after: schedulerConfig?.after
14558
+ });
14559
+ }
14560
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14561
+ scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14562
+ }
14511
14563
  }
14512
- scheduler.frameloop = frameloop;
14564
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14565
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14566
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14513
14567
  onCreated = onCreatedCallback;
14514
14568
  configured = true;
14515
14569
  resolve();
@@ -14551,18 +14605,23 @@ function unmountComponentAtNode(canvas, callback) {
14551
14605
  const fiber = root?.fiber;
14552
14606
  if (fiber) {
14553
14607
  const state = root?.store.getState();
14554
- if (state) state.internal.active = false;
14608
+ if (state) {
14609
+ state.internal.active = false;
14610
+ const unregisterRoot = state.internal.unregisterRoot;
14611
+ if (unregisterRoot) {
14612
+ unregisterRoot();
14613
+ state.internal.unregisterRoot = void 0;
14614
+ }
14615
+ }
14555
14616
  reconciler.updateContainer(null, fiber, null, () => {
14556
14617
  if (state) {
14557
14618
  setTimeout(() => {
14558
14619
  try {
14559
14620
  const renderer = state.internal.actualRenderer;
14560
- const unregisterRoot = state.internal.unregisterRoot;
14561
- if (unregisterRoot) unregisterRoot();
14562
14621
  const unregisterPrimary = state.internal.unregisterPrimary;
14563
14622
  if (unregisterPrimary) unregisterPrimary();
14564
14623
  const canvasTarget = state.internal.canvasTarget;
14565
- if (canvasTarget?.dispose) canvasTarget.dispose();
14624
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14566
14625
  state.events.disconnect?.();
14567
14626
  cleanupHelperGroup(root.store);
14568
14627
  if (state.isLegacy && renderer) {
@@ -15006,9 +15065,9 @@ function CanvasImpl({
15006
15065
  React.useMemo(() => extend(THREE), []);
15007
15066
  const Bridge = useBridge();
15008
15067
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
15009
- const hasInitialSizeRef = React.useRef(false);
15068
+ const [hasInitialSize, setHasInitialSize] = React.useState(false);
15010
15069
  const measureConfig = React.useMemo(() => {
15011
- if (!hasInitialSizeRef.current) {
15070
+ if (!hasInitialSize) {
15012
15071
  return {
15013
15072
  ...resize,
15014
15073
  scroll: resize?.scroll ?? true,
@@ -15020,7 +15079,7 @@ function CanvasImpl({
15020
15079
  debounce: { scroll: 50, resize: 0 },
15021
15080
  ...resize
15022
15081
  };
15023
- }, [resize, hasInitialSizeRef.current]);
15082
+ }, [hasInitialSize, resize]);
15024
15083
  const [containerRef, containerRect] = useMeasure(measureConfig);
15025
15084
  const effectiveSize = React.useMemo(() => {
15026
15085
  let w = width ?? containerRect.width;
@@ -15036,9 +15095,7 @@ function CanvasImpl({
15036
15095
  left: containerRect.left
15037
15096
  };
15038
15097
  }, [width, height, containerRect, forceEven]);
15039
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15040
- hasInitialSizeRef.current = true;
15041
- }
15098
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15042
15099
  const canvasRef = React.useRef(null);
15043
15100
  const divRef = React.useRef(null);
15044
15101
  React.useImperativeHandle(ref, () => canvasRef.current);
@@ -15152,6 +15209,7 @@ function CanvasImpl({
15152
15209
  const canvas = canvasRef.current;
15153
15210
  if (canvas) {
15154
15211
  return () => {
15212
+ if (canvas.isConnected) return;
15155
15213
  unmountComponentAtNode(canvas);
15156
15214
  root.current = null;
15157
15215
  };