@react-three/fiber 10.0.0-canary.ba354c7 → 10.0.0-canary.c385e9f

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,10 @@ 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
+ internal.canvasTargetSizeDirty = true;
1055
1052
  }
1056
1053
  if (camera !== oldCamera) {
1057
1054
  oldCamera = camera;
@@ -1113,7 +1110,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1113
1110
  const results = cacheKeys.map(
1114
1111
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1115
1112
  );
1116
- return Array.isArray(input) ? results : results[0];
1113
+ const stableRef = useRef(null);
1114
+ const previous = stableRef.current;
1115
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1116
+ stableRef.current = stable;
1117
+ return Array.isArray(input) ? stable : stable[0];
1117
1118
  }
1118
1119
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1119
1120
  const inputs = Array.isArray(input) ? input : [input];
@@ -1233,6 +1234,13 @@ function useFrame(callback, priorityOrOptions) {
1233
1234
  * Use for controlling the entire frame loop, adding phases, etc.
1234
1235
  */
1235
1236
  scheduler: scheduler2,
1237
+ /**
1238
+ * The root that currently owns this job. Resolve on access because ambient
1239
+ * jobs can be adopted by a Canvas after the controls object is created.
1240
+ */
1241
+ get rootId() {
1242
+ return getScheduler().getJobRootId(id);
1243
+ },
1236
1244
  /**
1237
1245
  * Manually step this job only.
1238
1246
  * Bypasses FPS limiting - always runs.
@@ -1249,6 +1257,14 @@ function useFrame(callback, priorityOrOptions) {
1249
1257
  stepAll: (timestamp) => {
1250
1258
  getScheduler().step(timestamp);
1251
1259
  },
1260
+ /**
1261
+ * Request frames for this job's owning root without waking sibling roots.
1262
+ */
1263
+ invalidate: (frames, stackFrames) => {
1264
+ const scheduler3 = getScheduler();
1265
+ const rootId = scheduler3.getJobRootId(id);
1266
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1267
+ },
1252
1268
  /**
1253
1269
  * Pause this job (set enabled=false).
1254
1270
  * Job remains registered but won't run.
@@ -1278,6 +1294,11 @@ function getUrls(input) {
1278
1294
  if (Array.isArray(input)) return input;
1279
1295
  return Object.values(input);
1280
1296
  }
1297
+ function inputSignature(input) {
1298
+ if (typeof input === "string") return input;
1299
+ if (Array.isArray(input)) return input.join("\0");
1300
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1301
+ }
1281
1302
  function allUrlsCached(urls, textureCache) {
1282
1303
  return urls.every((url) => textureCache.has(url));
1283
1304
  }
@@ -1303,18 +1324,19 @@ function useTexture(input, optionsOrOnLoad) {
1303
1324
  const onLoadRef = useRef(onLoad);
1304
1325
  onLoadRef.current = onLoad;
1305
1326
  const onLoadCalledForRef = useRef(null);
1306
- const urls = useMemo(() => getUrls(input), [input]);
1327
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1328
+ const stableInput = useMemo(() => input, [inputKey]);
1329
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1307
1330
  const cachedResult = useMemo(() => {
1308
1331
  if (!cache) return null;
1309
1332
  const textures = store.getState().textures;
1310
1333
  if (!allUrlsCached(urls, textures)) return null;
1311
- return buildFromCache(input, textures);
1312
- }, [cache, urls, input, store]);
1334
+ return buildFromCache(stableInput, textures);
1335
+ }, [cache, urls, stableInput, store]);
1313
1336
  const loadedTextures = useLoader(
1314
1337
  TextureLoader,
1315
- IsObject(input) ? Object.values(input) : input
1338
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1316
1339
  );
1317
- const inputKey = urls.join("\0");
1318
1340
  useLayoutEffect(() => {
1319
1341
  if (cachedResult) return;
1320
1342
  if (onLoadCalledForRef.current === inputKey) return;
@@ -1341,28 +1363,28 @@ function useTexture(input, optionsOrOnLoad) {
1341
1363
  }, [renderer, loadedTextures, cachedResult]);
1342
1364
  const mappedTextures = useMemo(() => {
1343
1365
  if (cachedResult) return cachedResult;
1344
- if (IsObject(input)) {
1366
+ if (IsObject(stableInput)) {
1345
1367
  const keyed = {};
1346
1368
  const textureArray = loadedTextures;
1347
1369
  let i = 0;
1348
- for (const key in input) keyed[key] = textureArray[i++];
1370
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1349
1371
  return keyed;
1350
1372
  } else {
1351
1373
  return loadedTextures;
1352
1374
  }
1353
- }, [input, loadedTextures, cachedResult]);
1375
+ }, [stableInput, loadedTextures, cachedResult]);
1354
1376
  useEffect(() => {
1355
1377
  if (!cache) return;
1356
1378
  const urlTextureMap = [];
1357
- if (typeof input === "string") {
1358
- urlTextureMap.push([input, mappedTextures]);
1359
- } else if (Array.isArray(input)) {
1379
+ if (typeof stableInput === "string") {
1380
+ urlTextureMap.push([stableInput, mappedTextures]);
1381
+ } else if (Array.isArray(stableInput)) {
1360
1382
  const textureArray = mappedTextures;
1361
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1362
- } else if (IsObject(input)) {
1383
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1384
+ } else if (IsObject(stableInput)) {
1363
1385
  const textureRecord = mappedTextures;
1364
- for (const key in input) {
1365
- const url = input[key];
1386
+ for (const key in stableInput) {
1387
+ const url = stableInput[key];
1366
1388
  urlTextureMap.push([url, textureRecord[key]]);
1367
1389
  }
1368
1390
  }
@@ -1391,7 +1413,7 @@ function useTexture(input, optionsOrOnLoad) {
1391
1413
  }
1392
1414
  return { _textureRefs: refs };
1393
1415
  });
1394
- }, [cache, input, mappedTextures, store]);
1416
+ }, [cache, stableInput, mappedTextures, store]);
1395
1417
  return mappedTextures;
1396
1418
  }
1397
1419
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -1662,13 +1684,19 @@ function addTail(callback) {
1662
1684
  return getScheduler().onIdle(callback);
1663
1685
  }
1664
1686
  function invalidate(state, frames = 1, stackFrames = false) {
1665
- getScheduler().invalidate(frames, stackFrames);
1687
+ const rootId = state?.internal?.rootId;
1688
+ const scheduler = getScheduler();
1689
+ if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
1690
+ else scheduler.invalidate(frames, stackFrames);
1666
1691
  }
1667
- function advance(timestamp) {
1668
- getScheduler().step(timestamp);
1692
+ function advance(timestamp, runGlobalEffects, state) {
1693
+ const rootId = state?.internal?.rootId;
1694
+ const scheduler = getScheduler();
1695
+ if (rootId) scheduler.stepRoot(rootId, timestamp);
1696
+ else scheduler.step(timestamp);
1669
1697
  }
1670
1698
 
1671
- const version = "10.0.0-alpha.3";
1699
+ const version = "10.0.0-alpha.4";
1672
1700
  const packageData = {
1673
1701
  version: version};
1674
1702
 
@@ -14327,7 +14355,7 @@ function createRoot(canvas) {
14327
14355
  const handleXRFrame = (timestamp, _frame) => {
14328
14356
  const state2 = store.getState();
14329
14357
  if (state2.frameloop === "never") return;
14330
- advance(timestamp);
14358
+ advance(timestamp, true, state2);
14331
14359
  };
14332
14360
  const actualRenderer = state.internal.actualRenderer;
14333
14361
  const handleSessionChange = () => {
@@ -14335,7 +14363,7 @@ function createRoot(canvas) {
14335
14363
  const renderer2 = state2.internal.actualRenderer;
14336
14364
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14337
14365
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14338
- if (!renderer2.xr.isPresenting) invalidate();
14366
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14339
14367
  };
14340
14368
  const xr = {
14341
14369
  connect() {
@@ -14417,7 +14445,11 @@ function createRoot(canvas) {
14417
14445
  const newRootId = canvasId || scheduler.generateRootId();
14418
14446
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14419
14447
  getState: () => store.getState(),
14420
- 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
14421
14453
  });
14422
14454
  const unregisterCanvasTarget = scheduler.register(
14423
14455
  () => {
@@ -14425,6 +14457,10 @@ function createRoot(canvas) {
14425
14457
  if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14426
14458
  const renderer2 = state2.internal.actualRenderer;
14427
14459
  renderer2.setCanvasTarget(state2.internal.canvasTarget);
14460
+ if (state2.internal.canvasTargetSizeDirty) {
14461
+ state2.internal.canvasTargetSizeDirty = false;
14462
+ renderer2.backend?.updateSize?.();
14463
+ }
14428
14464
  }
14429
14465
  },
14430
14466
  {
@@ -14493,8 +14529,7 @@ function createRoot(canvas) {
14493
14529
  phase: "render",
14494
14530
  system: true,
14495
14531
  // Internal flag: this is a system job, not user-controlled
14496
- // Apply scheduler config for render ordering and rate limiting
14497
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14532
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14498
14533
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14499
14534
  }
14500
14535
  );
@@ -14513,8 +14548,21 @@ function createRoot(canvas) {
14513
14548
  scheduler
14514
14549
  }
14515
14550
  }));
14551
+ } else {
14552
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14553
+ if (constraintsChanged) {
14554
+ scheduler.setRootConstraints(rootId, {
14555
+ before: schedulerConfig?.before,
14556
+ after: schedulerConfig?.after
14557
+ });
14558
+ }
14559
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14560
+ scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14561
+ }
14516
14562
  }
14517
- scheduler.frameloop = frameloop;
14563
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14564
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14565
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14518
14566
  onCreated = onCreatedCallback;
14519
14567
  configured = true;
14520
14568
  resolve();
@@ -14556,14 +14604,19 @@ function unmountComponentAtNode(canvas, callback) {
14556
14604
  const fiber = root?.fiber;
14557
14605
  if (fiber) {
14558
14606
  const state = root?.store.getState();
14559
- if (state) state.internal.active = false;
14607
+ if (state) {
14608
+ state.internal.active = false;
14609
+ const unregisterRoot = state.internal.unregisterRoot;
14610
+ if (unregisterRoot) {
14611
+ unregisterRoot();
14612
+ state.internal.unregisterRoot = void 0;
14613
+ }
14614
+ }
14560
14615
  reconciler.updateContainer(null, fiber, null, () => {
14561
14616
  if (state) {
14562
14617
  setTimeout(() => {
14563
14618
  try {
14564
14619
  const renderer = state.internal.actualRenderer;
14565
- const unregisterRoot = state.internal.unregisterRoot;
14566
- if (unregisterRoot) unregisterRoot();
14567
14620
  const unregisterPrimary = state.internal.unregisterPrimary;
14568
14621
  if (unregisterPrimary) unregisterPrimary();
14569
14622
  const canvasTarget = state.internal.canvasTarget;
@@ -15011,9 +15064,9 @@ function CanvasImpl({
15011
15064
  React.useMemo(() => extend(THREE), []);
15012
15065
  const Bridge = useBridge();
15013
15066
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
15014
- const hasInitialSizeRef = React.useRef(false);
15067
+ const [hasInitialSize, setHasInitialSize] = React.useState(false);
15015
15068
  const measureConfig = React.useMemo(() => {
15016
- if (!hasInitialSizeRef.current) {
15069
+ if (!hasInitialSize) {
15017
15070
  return {
15018
15071
  ...resize,
15019
15072
  scroll: resize?.scroll ?? true,
@@ -15025,7 +15078,7 @@ function CanvasImpl({
15025
15078
  debounce: { scroll: 50, resize: 0 },
15026
15079
  ...resize
15027
15080
  };
15028
- }, [resize, hasInitialSizeRef.current]);
15081
+ }, [hasInitialSize, resize]);
15029
15082
  const [containerRef, containerRect] = useMeasure(measureConfig);
15030
15083
  const effectiveSize = React.useMemo(() => {
15031
15084
  let w = width ?? containerRect.width;
@@ -15041,9 +15094,7 @@ function CanvasImpl({
15041
15094
  left: containerRect.left
15042
15095
  };
15043
15096
  }, [width, height, containerRect, forceEven]);
15044
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15045
- hasInitialSizeRef.current = true;
15046
- }
15097
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15047
15098
  const canvasRef = React.useRef(null);
15048
15099
  const divRef = React.useRef(null);
15049
15100
  React.useImperativeHandle(ref, () => canvasRef.current);
@@ -15157,6 +15208,7 @@ function CanvasImpl({
15157
15208
  const canvas = canvasRef.current;
15158
15209
  if (canvas) {
15159
15210
  return () => {
15211
+ if (canvas.isConnected) return;
15160
15212
  unmountComponentAtNode(canvas);
15161
15213
  root.current = null;
15162
15214
  };