@react-three/fiber 10.0.0-alpha.0 → 10.0.0-alpha.1

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.
@@ -54,12 +54,15 @@ const WebGLRenderer = class WebGLRenderer2 {
54
54
  );
55
55
  }
56
56
  };
57
+ const WebGLRenderTarget = null;
57
58
 
58
59
  const THREE = /*#__PURE__*/_mergeNamespaces({
59
60
  __proto__: null,
60
61
  Inspector: Inspector_js.Inspector,
61
62
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
62
63
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
64
+ RenderTargetCompat: webgpu.RenderTarget,
65
+ WebGLRenderTarget: WebGLRenderTarget,
63
66
  WebGLRenderer: WebGLRenderer
64
67
  }, [webgpu__namespace]);
65
68
 
@@ -168,6 +171,13 @@ function updateCamera(camera, size) {
168
171
  }
169
172
  camera.updateProjectionMatrix();
170
173
  }
174
+ const frustumMatrix = new webgpu.Matrix4();
175
+ function updateFrustum(camera, frustum) {
176
+ const target = frustum ?? new webgpu.Frustum();
177
+ frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
178
+ target.setFromProjectionMatrix(frustumMatrix);
179
+ return target;
180
+ }
171
181
 
172
182
  const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
173
183
  function findInitialRoot(instance) {
@@ -248,6 +258,205 @@ function invalidateInstance(instance) {
248
258
  if (state && state.internal.frames === 0) state.invalidate();
249
259
  }
250
260
 
261
+ const tempFrustum = new webgpu.Frustum();
262
+ let hasWarnedWebGL = false;
263
+ let tslModule = null;
264
+ async function loadTSL() {
265
+ if (tslModule) return tslModule;
266
+ try {
267
+ const tsl = await import('three/tsl');
268
+ tslModule = { uniform: tsl.uniform, nodeObject: tsl.nodeObject };
269
+ return tslModule;
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+ function createOcclusionObserverNode(store, uniform) {
275
+ const node = new webgpu.Node("float");
276
+ node.updateType = webgpu.NodeUpdateType.OBJECT;
277
+ node.update = function(frame) {
278
+ const { internal } = store.getState();
279
+ const registry = internal.visibilityRegistry;
280
+ const cache = internal.occlusionCache;
281
+ for (const entry of registry.values()) {
282
+ const { object, handlers } = entry;
283
+ if (handlers.onOccluded || handlers.onVisible) {
284
+ const isOccluded = frame.renderer.isOccluded(object);
285
+ cache.set(object, isOccluded);
286
+ }
287
+ }
288
+ };
289
+ node.setup = function() {
290
+ return uniform(0);
291
+ };
292
+ return node;
293
+ }
294
+ let occlusionSetupPromise = null;
295
+ function enableOcclusion(store) {
296
+ const state = store.getState();
297
+ const { internal, renderer, rootScene } = state;
298
+ if (internal.occlusionEnabled || occlusionSetupPromise) return;
299
+ const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
300
+ if (!hasOcclusionSupport) {
301
+ if (!hasWarnedWebGL) {
302
+ console.warn(
303
+ "[R3F] Warning: onOccluded/onVisible occlusion queries require WebGPU renderer. Occlusion events will not fire on WebGL."
304
+ );
305
+ hasWarnedWebGL = true;
306
+ }
307
+ return;
308
+ }
309
+ occlusionSetupPromise = setupOcclusion(store);
310
+ }
311
+ async function setupOcclusion(store) {
312
+ const state = store.getState();
313
+ const { internal, rootScene, set } = state;
314
+ const tsl = await loadTSL();
315
+ if (!tsl) {
316
+ console.warn("[R3F] Warning: TSL module not available. Occlusion queries disabled.");
317
+ occlusionSetupPromise = null;
318
+ return;
319
+ }
320
+ const { uniform, nodeObject } = tsl;
321
+ let helperGroup = internal.helperGroup;
322
+ if (!helperGroup) {
323
+ helperGroup = new webgpu.Group();
324
+ helperGroup.name = "__r3fInternal";
325
+ helperGroup.__r3fInternal = true;
326
+ rootScene.add(helperGroup);
327
+ }
328
+ const geometry = new webgpu.BoxGeometry(1, 1, 1);
329
+ const material = new webgpu.MeshBasicNodeMaterial({
330
+ transparent: true,
331
+ opacity: 0
332
+ });
333
+ const observerNode = nodeObject(createOcclusionObserverNode(store, uniform));
334
+ material.colorNode = observerNode;
335
+ material.needsUpdate = true;
336
+ const mesh = new webgpu.Mesh(geometry, material);
337
+ mesh.name = "__r3fOcclusionObserver";
338
+ mesh.scale.setScalar(1e-4);
339
+ mesh.frustumCulled = false;
340
+ mesh.__r3fInternal = true;
341
+ helperGroup.add(mesh);
342
+ set((state2) => ({
343
+ internal: {
344
+ ...state2.internal,
345
+ helperGroup,
346
+ occlusionObserver: mesh,
347
+ occlusionEnabled: true
348
+ }
349
+ }));
350
+ occlusionSetupPromise = null;
351
+ }
352
+ function disableOcclusion(store) {
353
+ const { internal, set } = store.getState();
354
+ if (!internal.occlusionEnabled) return;
355
+ if (internal.occlusionObserver) {
356
+ internal.occlusionObserver.removeFromParent();
357
+ internal.occlusionObserver.geometry.dispose();
358
+ internal.occlusionObserver.material.dispose();
359
+ }
360
+ internal.occlusionCache.clear();
361
+ set((state) => ({
362
+ internal: {
363
+ ...state.internal,
364
+ occlusionObserver: null,
365
+ occlusionEnabled: false
366
+ }
367
+ }));
368
+ }
369
+ function cleanupHelperGroup(store) {
370
+ const { internal, set } = store.getState();
371
+ disableOcclusion(store);
372
+ if (internal.helperGroup) {
373
+ internal.helperGroup.removeFromParent();
374
+ set((state) => ({
375
+ internal: {
376
+ ...state.internal,
377
+ helperGroup: null
378
+ }
379
+ }));
380
+ }
381
+ }
382
+ function registerVisibility(store, object, handlers) {
383
+ const { internal } = store.getState();
384
+ const registry = internal.visibilityRegistry;
385
+ const entry = {
386
+ object,
387
+ handlers,
388
+ lastFramedState: null,
389
+ lastOccludedState: null,
390
+ lastVisibleState: null
391
+ };
392
+ registry.set(object.uuid, entry);
393
+ if (handlers.onOccluded || handlers.onVisible) {
394
+ object.occlusionTest = true;
395
+ if (!internal.occlusionEnabled) {
396
+ enableOcclusion(store);
397
+ }
398
+ }
399
+ }
400
+ function unregisterVisibility(store, object) {
401
+ const { internal } = store.getState();
402
+ internal.visibilityRegistry.delete(object.uuid);
403
+ internal.occlusionCache.delete(object);
404
+ }
405
+ function checkVisibility(state) {
406
+ const { internal, camera } = state;
407
+ const registry = internal.visibilityRegistry;
408
+ if (registry.size === 0) return;
409
+ updateFrustum(camera, tempFrustum);
410
+ for (const entry of registry.values()) {
411
+ const { object, handlers, lastFramedState, lastOccludedState, lastVisibleState } = entry;
412
+ let inFrustum = null;
413
+ const computeFrustum = () => {
414
+ if (inFrustum === null) {
415
+ if (object.geometry?.boundingSphere === null) {
416
+ object.geometry?.computeBoundingSphere();
417
+ }
418
+ inFrustum = tempFrustum.intersectsObject(object);
419
+ }
420
+ return inFrustum;
421
+ };
422
+ if (handlers.onFramed) {
423
+ const currentInFrustum = computeFrustum();
424
+ if (currentInFrustum !== lastFramedState) {
425
+ entry.lastFramedState = currentInFrustum;
426
+ handlers.onFramed(currentInFrustum);
427
+ }
428
+ }
429
+ let currentOcclusion = null;
430
+ if (handlers.onOccluded && internal.occlusionEnabled) {
431
+ currentOcclusion = internal.occlusionCache.get(object) ?? null;
432
+ if (currentOcclusion !== null && currentOcclusion !== lastOccludedState) {
433
+ entry.lastOccludedState = currentOcclusion;
434
+ handlers.onOccluded(currentOcclusion);
435
+ }
436
+ }
437
+ if (handlers.onVisible) {
438
+ const currentInFrustum = computeFrustum();
439
+ if (!handlers.onFramed && currentInFrustum !== lastFramedState) {
440
+ entry.lastFramedState = currentInFrustum;
441
+ }
442
+ let isOccluded = currentOcclusion;
443
+ if (isOccluded === null && internal.occlusionEnabled) {
444
+ isOccluded = internal.occlusionCache.get(object) ?? null;
445
+ }
446
+ if (isOccluded === null) isOccluded = false;
447
+ const isVisible = currentInFrustum && !isOccluded && object.visible;
448
+ if (isVisible !== lastVisibleState) {
449
+ entry.lastVisibleState = isVisible;
450
+ handlers.onVisible(isVisible);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ function hasVisibilityHandlers(handlers) {
456
+ if (!handlers) return false;
457
+ return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
458
+ }
459
+
251
460
  const RESERVED_PROPS = [
252
461
  "children",
253
462
  "key",
@@ -262,6 +471,7 @@ const RESERVED_PROPS = [
262
471
  "dispose"
263
472
  ];
264
473
  const EVENT_REGEX = /^on(Pointer|Drag|Drop|Click|DoubleClick|ContextMenu|Wheel)/;
474
+ const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
265
475
  const INDEX_REGEX = /-\d+$/;
266
476
  const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
267
477
  const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
@@ -356,6 +566,12 @@ function applyProps(object, props) {
356
566
  instance.eventCount = Object.keys(instance.handlers).length;
357
567
  continue;
358
568
  }
569
+ if (instance && VISIBILITY_EVENT_REGEX.test(prop)) {
570
+ if (typeof value === "function") instance.handlers[prop] = value;
571
+ else delete instance.handlers[prop];
572
+ instance.eventCount = Object.keys(instance.handlers).length;
573
+ continue;
574
+ }
359
575
  if (value === void 0) continue;
360
576
  let { root, key, target } = resolve(object, prop);
361
577
  if (target === void 0 && (typeof root !== "object" || root === null)) {
@@ -392,6 +608,17 @@ function applyProps(object, props) {
392
608
  if (instance.eventCount && object2.raycast !== null) {
393
609
  rootState.internal.interaction.push(object2);
394
610
  }
611
+ const root = findInitialRoot(instance);
612
+ const visibilityHandlers = {
613
+ onFramed: instance.handlers.onFramed,
614
+ onOccluded: instance.handlers.onOccluded,
615
+ onVisible: instance.handlers.onVisible
616
+ };
617
+ if (hasVisibilityHandlers(visibilityHandlers)) {
618
+ registerVisibility(root, object2, visibilityHandlers);
619
+ } else {
620
+ unregisterVisibility(root, object2);
621
+ }
395
622
  }
396
623
  if (instance && instance.props.attach === void 0) {
397
624
  if (instance.object.isBufferGeometry) instance.props.attach = "geometry";
@@ -426,6 +653,7 @@ function removeInteractivity(store, object) {
426
653
  internal.capturedMap.forEach((captures, pointerId) => {
427
654
  releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
428
655
  });
656
+ unregisterVisibility(store, object);
429
657
  }
430
658
  function createEvents(store) {
431
659
  function calculateDistance(event) {
@@ -808,8 +1036,21 @@ function formatLocation(url, line) {
808
1036
  const file = clean.split("/").pop() ?? clean;
809
1037
  return `${file}:${line}`;
810
1038
  }
1039
+ function notifyAlpha({ message, link }) {
1040
+ if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
1041
+ return;
1042
+ }
1043
+ if (shownNotices.has(message)) return;
1044
+ shownNotices.add(message);
1045
+ const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
1046
+ console.log(`%c\u{1F52C} ${message}`, boxStyle);
1047
+ {
1048
+ console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
1049
+ }
1050
+ }
811
1051
 
812
- const context = /* @__PURE__ */ React__namespace.createContext(null);
1052
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1053
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
813
1054
  const createStore = (invalidate, advance) => {
814
1055
  const rootStore = traditional.createWithEqualityFn((set, get) => {
815
1056
  const position = new webgpu.Vector3();
@@ -840,6 +1081,8 @@ const createStore = (invalidate, advance) => {
840
1081
  gl: null,
841
1082
  renderer: null,
842
1083
  camera: null,
1084
+ frustum: new webgpu.Frustum(),
1085
+ autoUpdateFrustum: true,
843
1086
  raycaster: null,
844
1087
  events: { priority: 1, enabled: true, connected: false },
845
1088
  scene: null,
@@ -921,6 +1164,13 @@ const createStore = (invalidate, advance) => {
921
1164
  initialHits: [],
922
1165
  capturedMap: /* @__PURE__ */ new Map(),
923
1166
  lastEvent: React__namespace.createRef(),
1167
+ // Visibility tracking (onFramed, onOccluded, onVisible)
1168
+ visibilityRegistry: /* @__PURE__ */ new Map(),
1169
+ // Occlusion system (WebGPU only)
1170
+ occlusionEnabled: false,
1171
+ occlusionObserver: null,
1172
+ occlusionCache: /* @__PURE__ */ new Map(),
1173
+ helperGroup: null,
924
1174
  // Updates
925
1175
  active: false,
926
1176
  frames: 0,
@@ -1006,7 +1256,15 @@ const createStore = (invalidate, advance) => {
1006
1256
  }
1007
1257
  if (camera !== oldCamera) {
1008
1258
  oldCamera = camera;
1259
+ const { rootScene } = rootStore.getState();
1260
+ if (camera && rootScene && !camera.parent) {
1261
+ rootScene.add(camera);
1262
+ }
1009
1263
  set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1264
+ const currentState = rootStore.getState();
1265
+ if (currentState.autoUpdateFrustum && camera) {
1266
+ updateFrustum(camera, currentState.frustum);
1267
+ }
1010
1268
  }
1011
1269
  });
1012
1270
  rootStore.subscribe((state2) => invalidate(state2));
@@ -1370,6 +1628,10 @@ const _Scheduler = class _Scheduler {
1370
1628
  __publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
1371
1629
  __publicField(this, "pendingFrames", 0);
1372
1630
  __publicField(this, "_frameloop", "always");
1631
+ //* Independent Mode & Error Handling State ================================
1632
+ __publicField(this, "_independent", false);
1633
+ __publicField(this, "errorHandler", null);
1634
+ __publicField(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
1373
1635
  //* Core Loop Execution Methods ================================
1374
1636
  /**
1375
1637
  * Main RAF loop callback.
@@ -1392,6 +1654,12 @@ const _Scheduler = class _Scheduler {
1392
1654
  });
1393
1655
  this.phaseGraph = new PhaseGraph();
1394
1656
  }
1657
+ static get instance() {
1658
+ return globalThis[_Scheduler.INSTANCE_KEY] ?? null;
1659
+ }
1660
+ static set instance(value) {
1661
+ globalThis[_Scheduler.INSTANCE_KEY] = value;
1662
+ }
1395
1663
  /**
1396
1664
  * Get the global scheduler instance (creates if doesn't exist).
1397
1665
  * Uses HMR data to preserve instance across hot reloads.
@@ -1440,29 +1708,43 @@ const _Scheduler = class _Scheduler {
1440
1708
  get isRunning() {
1441
1709
  return this.loopState.running;
1442
1710
  }
1711
+ get isReady() {
1712
+ return this.roots.size > 0;
1713
+ }
1714
+ get independent() {
1715
+ return this._independent;
1716
+ }
1717
+ set independent(value) {
1718
+ this._independent = value;
1719
+ if (value) this.ensureDefaultRoot();
1720
+ }
1443
1721
  //* Root Management Methods ================================
1444
1722
  /**
1445
1723
  * Register a root (Canvas) with the scheduler.
1446
1724
  * The first root to register starts the RAF loop (if frameloop='always').
1447
1725
  * @param {string} id - Unique identifier for this root
1448
- * @param {() => RootState} getState - Function to get the root's current state
1726
+ * @param {RootOptions} [options] - Optional configuration with getState and onError callbacks
1449
1727
  * @returns {() => void} Unsubscribe function to remove this root
1450
1728
  */
1451
- registerRoot(id, getState) {
1729
+ registerRoot(id, options = {}) {
1452
1730
  if (this.roots.has(id)) {
1453
1731
  console.warn(`[Scheduler] Root "${id}" already registered`);
1454
1732
  return () => this.unregisterRoot(id);
1455
1733
  }
1456
1734
  const entry = {
1457
1735
  id,
1458
- getState,
1736
+ getState: options.getState ?? (() => ({})),
1459
1737
  jobs: /* @__PURE__ */ new Map(),
1460
1738
  sortedJobs: [],
1461
1739
  needsRebuild: false
1462
1740
  };
1741
+ if (options.onError) {
1742
+ this.errorHandler = options.onError;
1743
+ }
1463
1744
  this.roots.set(id, entry);
1464
- if (this.roots.size === 1 && this._frameloop === "always") {
1465
- this.start();
1745
+ if (this.roots.size === 1) {
1746
+ this.notifyRootReady();
1747
+ if (this._frameloop === "always") this.start();
1466
1748
  }
1467
1749
  return () => this.unregisterRoot(id);
1468
1750
  }
@@ -1482,7 +1764,60 @@ const _Scheduler = class _Scheduler {
1482
1764
  this.roots.delete(id);
1483
1765
  if (this.roots.size === 0) {
1484
1766
  this.stop();
1767
+ this.errorHandler = null;
1768
+ }
1769
+ }
1770
+ /**
1771
+ * Subscribe to be notified when a root becomes available.
1772
+ * Fires immediately if a root already exists.
1773
+ * @param {() => void} callback - Function called when first root registers
1774
+ * @returns {() => void} Unsubscribe function
1775
+ */
1776
+ onRootReady(callback) {
1777
+ if (this.roots.size > 0) {
1778
+ callback();
1779
+ return () => {
1780
+ };
1485
1781
  }
1782
+ this.rootReadyCallbacks.add(callback);
1783
+ return () => this.rootReadyCallbacks.delete(callback);
1784
+ }
1785
+ /**
1786
+ * Notify all registered root-ready callbacks.
1787
+ * Called when the first root registers.
1788
+ * @returns {void}
1789
+ * @private
1790
+ */
1791
+ notifyRootReady() {
1792
+ for (const cb of this.rootReadyCallbacks) {
1793
+ try {
1794
+ cb();
1795
+ } catch (error) {
1796
+ console.error("[Scheduler] Error in root-ready callback:", error);
1797
+ }
1798
+ }
1799
+ this.rootReadyCallbacks.clear();
1800
+ }
1801
+ /**
1802
+ * Ensure a default root exists for independent mode.
1803
+ * Creates a minimal root with no state provider.
1804
+ * @returns {void}
1805
+ * @private
1806
+ */
1807
+ ensureDefaultRoot() {
1808
+ if (!this.roots.has("__default__")) {
1809
+ this.registerRoot("__default__");
1810
+ }
1811
+ }
1812
+ /**
1813
+ * Trigger error handling for job errors.
1814
+ * Uses the bound error handler if available, otherwise logs to console.
1815
+ * @param {Error} error - The error to handle
1816
+ * @returns {void}
1817
+ */
1818
+ triggerError(error) {
1819
+ if (this.errorHandler) this.errorHandler(error);
1820
+ else console.error("[Scheduler]", error);
1486
1821
  }
1487
1822
  //* Phase Management Methods ================================
1488
1823
  /**
@@ -1842,9 +2177,9 @@ const _Scheduler = class _Scheduler {
1842
2177
  const deltaMs = this.loopState.lastTime !== null ? now - this.loopState.lastTime : 0;
1843
2178
  const delta = deltaMs / 1e3;
1844
2179
  const elapsed = now - this.loopState.createdAt;
1845
- const rootState = root.getState();
2180
+ const providedState = root.getState?.() ?? {};
1846
2181
  const frameState = {
1847
- ...rootState,
2182
+ ...providedState,
1848
2183
  time: now,
1849
2184
  delta,
1850
2185
  elapsed,
@@ -1854,6 +2189,7 @@ const _Scheduler = class _Scheduler {
1854
2189
  job.callback(frameState, delta);
1855
2190
  } catch (error) {
1856
2191
  console.error(`[Scheduler] Error in job "${job.id}":`, error);
2192
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
1857
2193
  }
1858
2194
  }
1859
2195
  /**
@@ -1895,7 +2231,7 @@ const _Scheduler = class _Scheduler {
1895
2231
  /**
1896
2232
  * Execute all jobs for a single root in sorted order.
1897
2233
  * Rebuilds sorted job list if needed, then dispatches each job.
1898
- * Errors are caught and propagated to the root's error boundary.
2234
+ * Errors are caught and propagated via triggerError.
1899
2235
  * @param {RootEntry} root - The root entry to tick
1900
2236
  * @param {number} timestamp - RAF timestamp in milliseconds
1901
2237
  * @param {number} delta - Time since last frame in seconds
@@ -1907,10 +2243,9 @@ const _Scheduler = class _Scheduler {
1907
2243
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
1908
2244
  root.needsRebuild = false;
1909
2245
  }
1910
- const rootState = root.getState();
1911
- if (!rootState) return;
2246
+ const providedState = root.getState?.() ?? {};
1912
2247
  const frameState = {
1913
- ...rootState,
2248
+ ...providedState,
1914
2249
  time: timestamp,
1915
2250
  delta,
1916
2251
  elapsed: this.loopState.elapsedTime / 1e3,
@@ -1923,7 +2258,7 @@ const _Scheduler = class _Scheduler {
1923
2258
  job.callback(frameState, delta);
1924
2259
  } catch (error) {
1925
2260
  console.error(`[Scheduler] Error in job "${job.id}":`, error);
1926
- rootState.setError(error instanceof Error ? error : new Error(String(error)));
2261
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
1927
2262
  }
1928
2263
  }
1929
2264
  }
@@ -2008,8 +2343,11 @@ const _Scheduler = class _Scheduler {
2008
2343
  return /* @__PURE__ */ new Set([value]);
2009
2344
  }
2010
2345
  };
2011
- //* Static State & Methods (Singlton Usage) ================================
2012
- __publicField(_Scheduler, "instance", null);
2346
+ //* Static State & Methods (Singleton Usage) ================================
2347
+ //* Cross-Bundle Singleton Key ==============================
2348
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2349
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2350
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2013
2351
  let Scheduler = _Scheduler;
2014
2352
  const getScheduler = () => Scheduler.get();
2015
2353
  if (hmrData) {
@@ -2017,11 +2355,9 @@ if (hmrData) {
2017
2355
  }
2018
2356
 
2019
2357
  function useFrame(callback, priorityOrOptions) {
2020
- const store = useStore();
2021
- const getRootId = React__namespace.useCallback(() => {
2022
- const state = store.getState();
2023
- return state.internal.rootId;
2024
- }, [store]);
2358
+ const store = React__namespace.useContext(context);
2359
+ const isInsideCanvas = store !== null;
2360
+ const scheduler = getScheduler();
2025
2361
  const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
2026
2362
  id: priorityOrOptions.id,
2027
2363
  phase: priorityOrOptions.phase,
@@ -2041,55 +2377,71 @@ function useFrame(callback, priorityOrOptions) {
2041
2377
  const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
2042
2378
  useIsomorphicLayoutEffect(() => {
2043
2379
  if (!callback) return;
2044
- const scheduler = getScheduler();
2045
- const rootId = getRootId();
2046
- const state = store.getState();
2047
- if (isLegacyPriority) {
2048
- state.internal.priority++;
2049
- let parentRoot = state.previousRoot;
2050
- while (parentRoot) {
2051
- const parentState = parentRoot.getState();
2052
- if (parentState?.internal) parentState.internal.priority++;
2053
- parentRoot = parentState?.previousRoot;
2054
- }
2055
- notifyDepreciated({
2056
- heading: "useFrame with numeric priority is deprecated",
2057
- body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
2058
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
2059
- });
2060
- }
2061
- const wrappedCallback = (frameState, delta) => {
2062
- const localState = store.getState();
2063
- const mergedState = {
2064
- ...localState,
2065
- time: frameState.time,
2066
- delta: frameState.delta,
2067
- elapsed: frameState.elapsed,
2068
- frame: frameState.frame
2069
- };
2070
- callbackRef.current?.(mergedState, delta);
2071
- };
2072
- const unregister = scheduler.register(wrappedCallback, {
2073
- id,
2074
- rootId,
2075
- ...options
2076
- });
2077
- return () => {
2078
- unregister();
2380
+ if (isInsideCanvas) {
2381
+ const state = store.getState();
2382
+ const rootId = state.internal.rootId;
2079
2383
  if (isLegacyPriority) {
2080
- const currentState = store.getState();
2081
- if (currentState.internal) {
2082
- currentState.internal.priority--;
2083
- let parentRoot = currentState.previousRoot;
2084
- while (parentRoot) {
2085
- const parentState = parentRoot.getState();
2086
- if (parentState?.internal) parentState.internal.priority--;
2087
- parentRoot = parentState?.previousRoot;
2384
+ state.internal.priority++;
2385
+ let parentRoot = state.previousRoot;
2386
+ while (parentRoot) {
2387
+ const parentState = parentRoot.getState();
2388
+ if (parentState?.internal) parentState.internal.priority++;
2389
+ parentRoot = parentState?.previousRoot;
2390
+ }
2391
+ notifyDepreciated({
2392
+ heading: "useFrame with numeric priority is deprecated",
2393
+ body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
2394
+ link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
2395
+ });
2396
+ }
2397
+ const wrappedCallback = (frameState, delta) => {
2398
+ const localState = store.getState();
2399
+ const mergedState = {
2400
+ ...localState,
2401
+ time: frameState.time,
2402
+ delta: frameState.delta,
2403
+ elapsed: frameState.elapsed,
2404
+ frame: frameState.frame
2405
+ };
2406
+ callbackRef.current?.(mergedState, delta);
2407
+ };
2408
+ const unregister = scheduler.register(wrappedCallback, {
2409
+ id,
2410
+ rootId,
2411
+ ...options
2412
+ });
2413
+ return () => {
2414
+ unregister();
2415
+ if (isLegacyPriority) {
2416
+ const currentState = store.getState();
2417
+ if (currentState.internal) {
2418
+ currentState.internal.priority--;
2419
+ let parentRoot = currentState.previousRoot;
2420
+ while (parentRoot) {
2421
+ const parentState = parentRoot.getState();
2422
+ if (parentState?.internal) parentState.internal.priority--;
2423
+ parentRoot = parentState?.previousRoot;
2424
+ }
2088
2425
  }
2089
2426
  }
2427
+ };
2428
+ } else {
2429
+ const registerOutside = () => {
2430
+ return scheduler.register((state, delta) => callbackRef.current?.(state, delta), { id, ...options });
2431
+ };
2432
+ if (scheduler.independent || scheduler.isReady) {
2433
+ return registerOutside();
2090
2434
  }
2091
- };
2092
- }, [store, id, optionsKey, isLegacyPriority]);
2435
+ let unregisterJob = null;
2436
+ const unsubReady = scheduler.onRootReady(() => {
2437
+ unregisterJob = registerOutside();
2438
+ });
2439
+ return () => {
2440
+ unsubReady();
2441
+ unregisterJob?.();
2442
+ };
2443
+ }
2444
+ }, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
2093
2445
  const isPaused = React__namespace.useSyncExternalStore(
2094
2446
  // Subscribe function
2095
2447
  React__namespace.useCallback(
@@ -2104,7 +2456,7 @@ function useFrame(callback, priorityOrOptions) {
2104
2456
  React__namespace.useCallback(() => false, [])
2105
2457
  );
2106
2458
  const controls = React__namespace.useMemo(() => {
2107
- const scheduler = getScheduler();
2459
+ const scheduler2 = getScheduler();
2108
2460
  return {
2109
2461
  /** The job's unique ID */
2110
2462
  id,
@@ -2112,7 +2464,7 @@ function useFrame(callback, priorityOrOptions) {
2112
2464
  * Access to the global scheduler for frame loop control.
2113
2465
  * Use for controlling the entire frame loop, adding phases, etc.
2114
2466
  */
2115
- scheduler,
2467
+ scheduler: scheduler2,
2116
2468
  /**
2117
2469
  * Manually step this job only.
2118
2470
  * Bypasses FPS limiting - always runs.
@@ -2360,6 +2712,16 @@ function useTextures() {
2360
2712
  }, [store]);
2361
2713
  }
2362
2714
 
2715
+ function useRenderTarget(width, height, options) {
2716
+ const isLegacy = useThree((s) => s.isLegacy);
2717
+ const size = useThree((s) => s.size);
2718
+ return React.useMemo(() => {
2719
+ const w = width ?? size.width;
2720
+ const h = height ?? size.height;
2721
+ return new webgpu.RenderTarget(w, h, options);
2722
+ }, [width, height, size.width, size.height, options, isLegacy]);
2723
+ }
2724
+
2363
2725
  function useStore() {
2364
2726
  const store = React.useContext(context);
2365
2727
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
@@ -2411,7 +2773,7 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
2411
2773
  getScheduler().step(timestamp);
2412
2774
  }
2413
2775
 
2414
- const version = "10.0.0-alpha.0";
2776
+ const version = "10.0.0-alpha.1";
2415
2777
  const packageData = {
2416
2778
  version: version};
2417
2779
 
@@ -13712,7 +14074,8 @@ function createReconciler(config) {
13712
14074
  return reconciler2;
13713
14075
  }
13714
14076
  const NoEventPriority = 0;
13715
- const catalogue = {};
14077
+ const R3F_CATALOGUE = Symbol.for("@react-three/fiber.catalogue");
14078
+ const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
13716
14079
  const PREFIX_REGEX = /^three(?=[A-Z])/;
13717
14080
  const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
13718
14081
  let i = 0;
@@ -14210,7 +14573,9 @@ function createRoot(canvas) {
14210
14573
  camera: cameraOptions,
14211
14574
  onPointerMissed,
14212
14575
  onDragOverMissed,
14213
- onDropMissed
14576
+ onDropMissed,
14577
+ autoUpdateFrustum = true,
14578
+ occlusion = false
14214
14579
  } = props;
14215
14580
  let state = store.getState();
14216
14581
  const defaultGPUProps = {
@@ -14227,7 +14592,9 @@ function createRoot(canvas) {
14227
14592
  let renderer = state.internal.actualRenderer;
14228
14593
  if (!state.internal.actualRenderer) {
14229
14594
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14230
- await renderer.init();
14595
+ if (!renderer.hasInitialized?.()) {
14596
+ await renderer.init();
14597
+ }
14231
14598
  const backend = renderer.backend;
14232
14599
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14233
14600
  state.internal.actualRenderer = renderer;
@@ -14275,6 +14642,8 @@ function createRoot(canvas) {
14275
14642
  rootScene: scene,
14276
14643
  internal: { ...prev.internal, container: scene }
14277
14644
  }));
14645
+ const camera = state.camera;
14646
+ if (camera && !camera.parent) scene.add(camera);
14278
14647
  }
14279
14648
  if (events && !state.events.handlers) {
14280
14649
  state.set({ events: events(store) });
@@ -14302,6 +14671,10 @@ function createRoot(canvas) {
14302
14671
  if (!state.onPointerMissed) state.set({ onPointerMissed });
14303
14672
  if (!state.onDragOverMissed) state.set({ onDragOverMissed });
14304
14673
  if (!state.onDropMissed) state.set({ onDropMissed });
14674
+ if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
14675
+ if (occlusion && !state.internal.occlusionEnabled) {
14676
+ enableOcclusion(store);
14677
+ }
14305
14678
  if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
14306
14679
  state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
14307
14680
  lastConfiguredProps.performance = performance;
@@ -14374,7 +14747,37 @@ function createRoot(canvas) {
14374
14747
  const rootId = state.internal.rootId;
14375
14748
  if (!rootId) {
14376
14749
  const newRootId = scheduler.generateRootId();
14377
- const unregisterRoot = scheduler.registerRoot(newRootId, () => store.getState());
14750
+ const unregisterRoot = scheduler.registerRoot(newRootId, {
14751
+ getState: () => store.getState(),
14752
+ onError: (err) => store.getState().setError(err)
14753
+ });
14754
+ const unregisterFrustum = scheduler.register(
14755
+ () => {
14756
+ const state2 = store.getState();
14757
+ if (state2.autoUpdateFrustum && state2.camera) {
14758
+ updateFrustum(state2.camera, state2.frustum);
14759
+ }
14760
+ },
14761
+ {
14762
+ id: `${newRootId}_frustum`,
14763
+ rootId: newRootId,
14764
+ phase: "preRender",
14765
+ system: true
14766
+ }
14767
+ );
14768
+ const unregisterVisibility = scheduler.register(
14769
+ () => {
14770
+ const state2 = store.getState();
14771
+ checkVisibility(state2);
14772
+ },
14773
+ {
14774
+ id: `${newRootId}_visibility`,
14775
+ rootId: newRootId,
14776
+ phase: "preRender",
14777
+ system: true,
14778
+ after: `${newRootId}_frustum`
14779
+ }
14780
+ );
14378
14781
  const unregisterRender = scheduler.register(
14379
14782
  () => {
14380
14783
  const state2 = store.getState();
@@ -14402,6 +14805,8 @@ function createRoot(canvas) {
14402
14805
  rootId: newRootId,
14403
14806
  unregisterRoot: () => {
14404
14807
  unregisterRoot();
14808
+ unregisterFrustum();
14809
+ unregisterVisibility();
14405
14810
  unregisterRender();
14406
14811
  },
14407
14812
  scheduler
@@ -14459,6 +14864,7 @@ function unmountComponentAtNode(canvas, callback) {
14459
14864
  const unregisterRoot = state.internal.unregisterRoot;
14460
14865
  if (unregisterRoot) unregisterRoot();
14461
14866
  state.events.disconnect?.();
14867
+ cleanupHelperGroup(root.store);
14462
14868
  renderer?.renderLists?.dispose?.();
14463
14869
  renderer?.forceContextLoss?.();
14464
14870
  if (renderer?.xr) state.xr.disconnect();
@@ -14638,7 +15044,22 @@ function CanvasImpl({
14638
15044
  effectActiveRef.current = true;
14639
15045
  const canvas = canvasRef.current;
14640
15046
  if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
14641
- if (!root.current) root.current = createRoot(canvas);
15047
+ if (!root.current) {
15048
+ root.current = createRoot(canvas);
15049
+ notifyAlpha({
15050
+ message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
15051
+ link: "https://github.com/pmndrs/react-three-fiber/discussions"
15052
+ });
15053
+ const rootEntry = _roots.get(canvas);
15054
+ if (rootEntry?.store) {
15055
+ if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
15056
+ unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
15057
+ if (state.error && effectActiveRef.current) {
15058
+ setError(state.error);
15059
+ }
15060
+ });
15061
+ }
15062
+ }
14642
15063
  async function run() {
14643
15064
  if (!effectActiveRef.current || !root.current) return;
14644
15065
  await root.current.configure({
@@ -14679,15 +15100,9 @@ function CanvasImpl({
14679
15100
  }
14680
15101
  });
14681
15102
  if (!effectActiveRef.current || !root.current) return;
14682
- const store = root.current.render(
15103
+ root.current.render(
14683
15104
  /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
14684
15105
  );
14685
- if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
14686
- unsubscribeErrorRef.current = store.subscribe((state) => {
14687
- if (state.error && effectActiveRef.current) {
14688
- setError(state.error);
14689
- }
14690
- });
14691
15106
  }
14692
15107
  run();
14693
15108
  }
@@ -14808,11 +15223,46 @@ function useCompareMemoize(value, deep) {
14808
15223
  const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
14809
15224
  function useUniforms(creatorOrScope, scope) {
14810
15225
  const store = useStore();
15226
+ const removeUniforms2 = React.useCallback(
15227
+ (names, targetScope) => {
15228
+ const nameArray = Array.isArray(names) ? names : [names];
15229
+ store.setState((state) => {
15230
+ if (targetScope) {
15231
+ const currentScope = { ...state.uniforms[targetScope] };
15232
+ for (const name of nameArray) delete currentScope[name];
15233
+ return { uniforms: { ...state.uniforms, [targetScope]: currentScope } };
15234
+ }
15235
+ const uniforms2 = { ...state.uniforms };
15236
+ for (const name of nameArray) if (isUniformNode$1(uniforms2[name])) delete uniforms2[name];
15237
+ return { uniforms: uniforms2 };
15238
+ });
15239
+ },
15240
+ [store]
15241
+ );
15242
+ const clearUniforms = React.useCallback(
15243
+ (targetScope) => {
15244
+ store.setState((state) => {
15245
+ if (targetScope && targetScope !== "root") {
15246
+ const { [targetScope]: _, ...rest } = state.uniforms;
15247
+ return { uniforms: rest };
15248
+ }
15249
+ if (targetScope === "root") {
15250
+ const uniforms2 = {};
15251
+ for (const [key, value] of Object.entries(state.uniforms)) {
15252
+ if (!isUniformNode$1(value)) uniforms2[key] = value;
15253
+ }
15254
+ return { uniforms: uniforms2 };
15255
+ }
15256
+ return { uniforms: {} };
15257
+ });
15258
+ },
15259
+ [store]
15260
+ );
14811
15261
  const inputForMemoization = React.useMemo(() => {
14812
15262
  return is.fun(creatorOrScope) ? creatorOrScope(store.getState()) : creatorOrScope;
14813
15263
  }, [creatorOrScope, store]);
14814
15264
  const memoizedInput = useCompareMemoize(inputForMemoization);
14815
- return React.useMemo(() => {
15265
+ const uniforms = React.useMemo(() => {
14816
15266
  const state = store.getState();
14817
15267
  const set = store.setState;
14818
15268
  if (memoizedInput === void 0) {
@@ -14858,44 +15308,26 @@ function useUniforms(creatorOrScope, scope) {
14858
15308
  set((s) => ({
14859
15309
  uniforms: {
14860
15310
  ...s.uniforms,
14861
- [scope]: {
14862
- ...s.uniforms[scope],
14863
- ...result
14864
- }
15311
+ [scope]: { ...s.uniforms[scope], ...result }
14865
15312
  }
14866
15313
  }));
14867
15314
  } else {
14868
- set((s) => ({
14869
- uniforms: {
14870
- ...s.uniforms,
14871
- ...result
14872
- }
14873
- }));
15315
+ set((s) => ({ uniforms: { ...s.uniforms, ...result } }));
14874
15316
  }
14875
15317
  }
14876
15318
  return result;
14877
15319
  }, [store, memoizedInput, scope]);
15320
+ return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms };
14878
15321
  }
14879
15322
  function removeUniforms(set, names, scope) {
14880
15323
  set((state) => {
14881
15324
  if (scope) {
14882
15325
  const currentScope = { ...state.uniforms[scope] };
14883
- for (const name of names) {
14884
- delete currentScope[name];
14885
- }
14886
- return {
14887
- uniforms: {
14888
- ...state.uniforms,
14889
- [scope]: currentScope
14890
- }
14891
- };
15326
+ for (const name of names) delete currentScope[name];
15327
+ return { uniforms: { ...state.uniforms, [scope]: currentScope } };
14892
15328
  }
14893
15329
  const uniforms = { ...state.uniforms };
14894
- for (const name of names) {
14895
- if (isUniformNode$1(uniforms[name])) {
14896
- delete uniforms[name];
14897
- }
14898
- }
15330
+ for (const name of names) if (isUniformNode$1(uniforms[name])) delete uniforms[name];
14899
15331
  return { uniforms };
14900
15332
  });
14901
15333
  }
@@ -14909,9 +15341,7 @@ function clearRootUniforms(set) {
14909
15341
  set((state) => {
14910
15342
  const uniforms = {};
14911
15343
  for (const [key, value] of Object.entries(state.uniforms)) {
14912
- if (!isUniformNode$1(value)) {
14913
- uniforms[key] = value;
14914
- }
15344
+ if (!isUniformNode$1(value)) uniforms[key] = value;
14915
15345
  }
14916
15346
  return { uniforms };
14917
15347
  });
@@ -14986,7 +15416,42 @@ function useUniform(name, value) {
14986
15416
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
14987
15417
  function useNodes(creatorOrScope, scope) {
14988
15418
  const store = useStore();
14989
- return React.useMemo(() => {
15419
+ const removeNodes2 = React.useCallback(
15420
+ (names, targetScope) => {
15421
+ const nameArray = Array.isArray(names) ? names : [names];
15422
+ store.setState((state) => {
15423
+ if (targetScope) {
15424
+ const currentScope = { ...state.nodes[targetScope] };
15425
+ for (const name of nameArray) delete currentScope[name];
15426
+ return { nodes: { ...state.nodes, [targetScope]: currentScope } };
15427
+ }
15428
+ const nodes2 = { ...state.nodes };
15429
+ for (const name of nameArray) if (isTSLNode(nodes2[name])) delete nodes2[name];
15430
+ return { nodes: nodes2 };
15431
+ });
15432
+ },
15433
+ [store]
15434
+ );
15435
+ const clearNodes = React.useCallback(
15436
+ (targetScope) => {
15437
+ store.setState((state) => {
15438
+ if (targetScope && targetScope !== "root") {
15439
+ const { [targetScope]: _, ...rest } = state.nodes;
15440
+ return { nodes: rest };
15441
+ }
15442
+ if (targetScope === "root") {
15443
+ const nodes2 = {};
15444
+ for (const [key, value] of Object.entries(state.nodes)) {
15445
+ if (!isTSLNode(value)) nodes2[key] = value;
15446
+ }
15447
+ return { nodes: nodes2 };
15448
+ }
15449
+ return { nodes: {} };
15450
+ });
15451
+ },
15452
+ [store]
15453
+ );
15454
+ const nodes = React.useMemo(() => {
14990
15455
  const state = store.getState();
14991
15456
  const set = store.setState;
14992
15457
  if (creatorOrScope === void 0) {
@@ -14994,9 +15459,7 @@ function useNodes(creatorOrScope, scope) {
14994
15459
  }
14995
15460
  if (typeof creatorOrScope === "string") {
14996
15461
  const scopeData = state.nodes[creatorOrScope];
14997
- if (scopeData && !isTSLNode(scopeData)) {
14998
- return scopeData;
14999
- }
15462
+ if (scopeData && !isTSLNode(scopeData)) return scopeData;
15000
15463
  return {};
15001
15464
  }
15002
15465
  const creator = creatorOrScope;
@@ -15009,9 +15472,7 @@ function useNodes(creatorOrScope, scope) {
15009
15472
  if (currentScope[name]) {
15010
15473
  result[name] = currentScope[name];
15011
15474
  } else {
15012
- if (typeof node.label === "function") {
15013
- node.setName(`${scope}.${name}`);
15014
- }
15475
+ if (typeof node.label === "function") node.setName(`${scope}.${name}`);
15015
15476
  result[name] = node;
15016
15477
  hasNewNodes = true;
15017
15478
  }
@@ -15020,10 +15481,7 @@ function useNodes(creatorOrScope, scope) {
15020
15481
  set((s) => ({
15021
15482
  nodes: {
15022
15483
  ...s.nodes,
15023
- [scope]: {
15024
- ...s.nodes[scope],
15025
- ...result
15026
- }
15484
+ [scope]: { ...s.nodes[scope], ...result }
15027
15485
  }
15028
15486
  }));
15029
15487
  }
@@ -15034,44 +15492,27 @@ function useNodes(creatorOrScope, scope) {
15034
15492
  if (existing && isTSLNode(existing)) {
15035
15493
  result[name] = existing;
15036
15494
  } else {
15037
- if (typeof node.label === "function") {
15038
- node.setName(name);
15039
- }
15495
+ if (typeof node.label === "function") node.setName(name);
15040
15496
  result[name] = node;
15041
15497
  hasNewNodes = true;
15042
15498
  }
15043
15499
  }
15044
15500
  if (hasNewNodes) {
15045
- set((s) => ({
15046
- nodes: {
15047
- ...s.nodes,
15048
- ...result
15049
- }
15050
- }));
15501
+ set((s) => ({ nodes: { ...s.nodes, ...result } }));
15051
15502
  }
15052
15503
  return result;
15053
15504
  }, [store, typeof creatorOrScope === "string" ? creatorOrScope : scope]);
15505
+ return { ...nodes, removeNodes: removeNodes2, clearNodes };
15054
15506
  }
15055
15507
  function removeNodes(set, names, scope) {
15056
15508
  set((state) => {
15057
15509
  if (scope) {
15058
15510
  const currentScope = { ...state.nodes[scope] };
15059
- for (const name of names) {
15060
- delete currentScope[name];
15061
- }
15062
- return {
15063
- nodes: {
15064
- ...state.nodes,
15065
- [scope]: currentScope
15066
- }
15067
- };
15511
+ for (const name of names) delete currentScope[name];
15512
+ return { nodes: { ...state.nodes, [scope]: currentScope } };
15068
15513
  }
15069
15514
  const nodes = { ...state.nodes };
15070
- for (const name of names) {
15071
- if (isTSLNode(nodes[name])) {
15072
- delete nodes[name];
15073
- }
15074
- }
15515
+ for (const name of names) if (isTSLNode(nodes[name])) delete nodes[name];
15075
15516
  return { nodes };
15076
15517
  });
15077
15518
  }
@@ -15085,9 +15526,7 @@ function clearRootNodes(set) {
15085
15526
  set((state) => {
15086
15527
  const nodes = {};
15087
15528
  for (const [key, value] of Object.entries(state.nodes)) {
15088
- if (!isTSLNode(value)) {
15089
- nodes[key] = value;
15090
- }
15529
+ if (!isTSLNode(value)) nodes[key] = value;
15091
15530
  }
15092
15531
  return { nodes };
15093
15532
  });
@@ -15256,6 +15695,7 @@ exports.removeUniforms = removeUniforms;
15256
15695
  exports.resolve = resolve;
15257
15696
  exports.unmountComponentAtNode = unmountComponentAtNode;
15258
15697
  exports.updateCamera = updateCamera;
15698
+ exports.updateFrustum = updateFrustum;
15259
15699
  exports.useBridge = useBridge;
15260
15700
  exports.useFrame = useFrame;
15261
15701
  exports.useGraph = useGraph;
@@ -15266,6 +15706,7 @@ exports.useLocalNodes = useLocalNodes;
15266
15706
  exports.useMutableCallback = useMutableCallback;
15267
15707
  exports.useNodes = useNodes;
15268
15708
  exports.usePostProcessing = usePostProcessing;
15709
+ exports.useRenderTarget = useRenderTarget;
15269
15710
  exports.useStore = useStore;
15270
15711
  exports.useTexture = useTexture;
15271
15712
  exports.useTextures = useTextures;