@react-three/fiber 10.0.0-alpha.2 → 10.0.0-canary.b0fafc8

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,12 +1,18 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ColorManagement, LinearSRGBColorSpace, NoToneMapping, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
2
+ import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
3
3
  import { WebGLRenderTarget, WebGLRenderer } from 'three';
4
4
  import { Inspector } from 'three/addons/inspector/Inspector.js';
5
- import { jsx, Fragment } from 'react/jsx-runtime';
5
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
6
  import * as React from 'react';
7
- import React__default, { useMemo, useLayoutEffect, useEffect, useContext, useRef, useImperativeHandle, useCallback, useState } from 'react';
7
+ import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
8
8
  import useMeasure from 'react-use-measure';
9
9
  import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
10
+ import { useThree as useThree$1, useLoader as useLoader$1, useFrame as useFrame$1, createPortal as createPortal$1, applyProps as applyProps$1, extend as extend$1 } from '@react-three/fiber';
11
+ import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
12
+ import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
13
+ import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
14
+ import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
15
+ import { GainMapLoader } from '@monogrid/gainmap-js';
10
16
  import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
11
17
  import { createWithEqualityFn } from 'zustand/traditional';
12
18
  import { suspend, preload, clear } from 'suspend-react';
@@ -36,6 +42,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
36
42
  WebGLRenderer: WebGLRenderer
37
43
  }, [webgpu]);
38
44
 
45
+ const primaryRegistry = /* @__PURE__ */ new Map();
46
+ const pendingSubscribers = /* @__PURE__ */ new Map();
47
+ function registerPrimary(id, renderer, store) {
48
+ if (primaryRegistry.has(id)) {
49
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
50
+ }
51
+ const entry = { renderer, store };
52
+ primaryRegistry.set(id, entry);
53
+ const subscribers = pendingSubscribers.get(id);
54
+ if (subscribers) {
55
+ subscribers.forEach((callback) => callback(entry));
56
+ pendingSubscribers.delete(id);
57
+ }
58
+ return () => {
59
+ const currentEntry = primaryRegistry.get(id);
60
+ if (currentEntry?.renderer === renderer) {
61
+ primaryRegistry.delete(id);
62
+ }
63
+ };
64
+ }
65
+ function getPrimary(id) {
66
+ return primaryRegistry.get(id);
67
+ }
68
+ function waitForPrimary(id, timeout = 5e3) {
69
+ const existing = primaryRegistry.get(id);
70
+ if (existing) {
71
+ return Promise.resolve(existing);
72
+ }
73
+ return new Promise((resolve, reject) => {
74
+ const timeoutId = setTimeout(() => {
75
+ const subscribers = pendingSubscribers.get(id);
76
+ if (subscribers) {
77
+ const index = subscribers.indexOf(callback);
78
+ if (index !== -1) subscribers.splice(index, 1);
79
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
80
+ }
81
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
82
+ }, timeout);
83
+ const callback = (entry) => {
84
+ clearTimeout(timeoutId);
85
+ resolve(entry);
86
+ };
87
+ if (!pendingSubscribers.has(id)) {
88
+ pendingSubscribers.set(id, []);
89
+ }
90
+ pendingSubscribers.get(id).push(callback);
91
+ });
92
+ }
93
+ function hasPrimary(id) {
94
+ return primaryRegistry.has(id);
95
+ }
96
+ function unregisterPrimary(id) {
97
+ primaryRegistry.delete(id);
98
+ }
99
+ function getPrimaryIds() {
100
+ return Array.from(primaryRegistry.keys());
101
+ }
102
+
103
+ const presetsObj = {
104
+ apartment: "lebombo_1k.hdr",
105
+ city: "potsdamer_platz_1k.hdr",
106
+ dawn: "kiara_1_dawn_1k.hdr",
107
+ forest: "forest_slope_1k.hdr",
108
+ lobby: "st_fagans_interior_1k.hdr",
109
+ night: "dikhololo_night_1k.hdr",
110
+ park: "rooitou_park_1k.hdr",
111
+ studio: "studio_small_03_1k.hdr",
112
+ sunset: "venice_sunset_1k.hdr",
113
+ warehouse: "empty_warehouse_01_1k.hdr"
114
+ };
115
+
116
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
117
+ const isArray = (arr) => Array.isArray(arr);
118
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
119
+ function useEnvironment({
120
+ files = defaultFiles,
121
+ path = "",
122
+ preset = void 0,
123
+ colorSpace = void 0,
124
+ extensions
125
+ } = {}) {
126
+ if (preset) {
127
+ validatePreset(preset);
128
+ files = presetsObj[preset];
129
+ path = CUBEMAP_ROOT;
130
+ }
131
+ const multiFile = isArray(files);
132
+ const { extension, isCubemap } = getExtension(files);
133
+ const loader = getLoader$1(extension);
134
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
135
+ const renderer = useThree$1((state) => state.renderer);
136
+ useLayoutEffect(() => {
137
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
138
+ function clearGainmapTexture() {
139
+ useLoader$1.clear(loader, multiFile ? [files] : files);
140
+ }
141
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
142
+ }, [files, renderer.domElement]);
143
+ const loaderResult = useLoader$1(
144
+ loader,
145
+ multiFile ? [files] : files,
146
+ (loader2) => {
147
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
148
+ loader2.setRenderer?.(renderer);
149
+ }
150
+ loader2.setPath?.(path);
151
+ if (extensions) extensions(loader2);
152
+ }
153
+ );
154
+ let texture = multiFile ? (
155
+ // @ts-ignore
156
+ loaderResult[0]
157
+ ) : loaderResult;
158
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
159
+ texture = texture.renderTarget?.texture;
160
+ }
161
+ texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
162
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
163
+ return texture;
164
+ }
165
+ const preloadDefaultOptions = {
166
+ files: defaultFiles,
167
+ path: "",
168
+ preset: void 0,
169
+ extensions: void 0
170
+ };
171
+ useEnvironment.preload = (preloadOptions) => {
172
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
173
+ let { files, path = "" } = options;
174
+ const { preset, extensions } = options;
175
+ if (preset) {
176
+ validatePreset(preset);
177
+ files = presetsObj[preset];
178
+ path = CUBEMAP_ROOT;
179
+ }
180
+ const { extension } = getExtension(files);
181
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
182
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
183
+ }
184
+ const loader = getLoader$1(extension);
185
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
186
+ useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
187
+ loader2.setPath?.(path);
188
+ if (extensions) extensions(loader2);
189
+ });
190
+ };
191
+ const clearDefaultOptins = {
192
+ files: defaultFiles,
193
+ preset: void 0
194
+ };
195
+ useEnvironment.clear = (clearOptions) => {
196
+ const options = { ...clearDefaultOptins, ...clearOptions };
197
+ let { files } = options;
198
+ const { preset } = options;
199
+ if (preset) {
200
+ validatePreset(preset);
201
+ files = presetsObj[preset];
202
+ }
203
+ const { extension } = getExtension(files);
204
+ const loader = getLoader$1(extension);
205
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
206
+ useLoader$1.clear(loader, isArray(files) ? [files] : files);
207
+ };
208
+ function validatePreset(preset) {
209
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
210
+ }
211
+ function getExtension(files) {
212
+ const isCubemap = isArray(files) && files.length === 6;
213
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
214
+ const firstEntry = isArray(files) ? files[0] : files;
215
+ 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();
216
+ return { extension, isCubemap, isGainmap };
217
+ }
218
+ function getLoader$1(extension) {
219
+ const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
220
+ return loader;
221
+ }
222
+
223
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
224
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
225
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
226
+ sceneProps = {
227
+ backgroundBlurriness: 0,
228
+ backgroundIntensity: 1,
229
+ backgroundRotation: [0, 0, 0],
230
+ environmentIntensity: 1,
231
+ environmentRotation: [0, 0, 0],
232
+ ...sceneProps
233
+ };
234
+ const target = resolveScene(scene || defaultScene);
235
+ const oldbg = target.background;
236
+ const oldenv = target.environment;
237
+ const oldSceneProps = {
238
+ // @ts-ignore
239
+ backgroundBlurriness: target.backgroundBlurriness,
240
+ // @ts-ignore
241
+ backgroundIntensity: target.backgroundIntensity,
242
+ // @ts-ignore
243
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
244
+ // @ts-ignore
245
+ environmentIntensity: target.environmentIntensity,
246
+ // @ts-ignore
247
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
248
+ };
249
+ if (background !== "only") target.environment = texture;
250
+ if (background) target.background = texture;
251
+ applyProps$1(target, sceneProps);
252
+ return () => {
253
+ if (background !== "only") target.environment = oldenv;
254
+ if (background) target.background = oldbg;
255
+ applyProps$1(target, oldSceneProps);
256
+ };
257
+ }
258
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
259
+ const defaultScene = useThree$1((state) => state.scene);
260
+ React.useLayoutEffect(() => {
261
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
262
+ });
263
+ return null;
264
+ }
265
+ function EnvironmentCube({
266
+ background = false,
267
+ scene,
268
+ blur,
269
+ backgroundBlurriness,
270
+ backgroundIntensity,
271
+ backgroundRotation,
272
+ environmentIntensity,
273
+ environmentRotation,
274
+ ...rest
275
+ }) {
276
+ const texture = useEnvironment(rest);
277
+ const defaultScene = useThree$1((state) => state.scene);
278
+ React.useLayoutEffect(() => {
279
+ return setEnvProps(background, scene, defaultScene, texture, {
280
+ backgroundBlurriness: blur ?? backgroundBlurriness,
281
+ backgroundIntensity,
282
+ backgroundRotation,
283
+ environmentIntensity,
284
+ environmentRotation
285
+ });
286
+ });
287
+ React.useEffect(() => {
288
+ return () => {
289
+ texture.dispose();
290
+ };
291
+ }, [texture]);
292
+ return null;
293
+ }
294
+ function EnvironmentPortal({
295
+ children,
296
+ near = 0.1,
297
+ far = 1e3,
298
+ resolution = 256,
299
+ frames = 1,
300
+ map,
301
+ background = false,
302
+ blur,
303
+ backgroundBlurriness,
304
+ backgroundIntensity,
305
+ backgroundRotation,
306
+ environmentIntensity,
307
+ environmentRotation,
308
+ scene,
309
+ files,
310
+ path,
311
+ preset = void 0,
312
+ extensions
313
+ }) {
314
+ const gl = useThree$1((state) => state.gl);
315
+ const defaultScene = useThree$1((state) => state.scene);
316
+ const camera = React.useRef(null);
317
+ const [virtualScene] = React.useState(() => new Scene());
318
+ const fbo = React.useMemo(() => {
319
+ const fbo2 = new WebGLCubeRenderTarget(resolution);
320
+ fbo2.texture.type = HalfFloatType;
321
+ return fbo2;
322
+ }, [resolution]);
323
+ React.useEffect(() => {
324
+ return () => {
325
+ fbo.dispose();
326
+ };
327
+ }, [fbo]);
328
+ React.useLayoutEffect(() => {
329
+ if (frames === 1) {
330
+ const autoClear = gl.autoClear;
331
+ gl.autoClear = true;
332
+ camera.current.update(gl, virtualScene);
333
+ gl.autoClear = autoClear;
334
+ }
335
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
336
+ backgroundBlurriness: blur ?? backgroundBlurriness,
337
+ backgroundIntensity,
338
+ backgroundRotation,
339
+ environmentIntensity,
340
+ environmentRotation
341
+ });
342
+ }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
343
+ let count = 1;
344
+ useFrame$1(() => {
345
+ if (frames === Infinity || count < frames) {
346
+ const autoClear = gl.autoClear;
347
+ gl.autoClear = true;
348
+ camera.current.update(gl, virtualScene);
349
+ gl.autoClear = autoClear;
350
+ count++;
351
+ }
352
+ });
353
+ return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
354
+ /* @__PURE__ */ jsxs(Fragment, { children: [
355
+ children,
356
+ /* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
357
+ files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
358
+ ] }),
359
+ virtualScene
360
+ ) });
361
+ }
362
+ function EnvironmentGround(props) {
363
+ const textureDefault = useEnvironment(props);
364
+ const texture = props.map || textureDefault;
365
+ React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
366
+ React.useEffect(() => {
367
+ return () => {
368
+ textureDefault.dispose();
369
+ };
370
+ }, [textureDefault]);
371
+ const height = props.ground?.height ?? 15;
372
+ const radius = props.ground?.radius ?? 60;
373
+ const scale = props.ground?.scale ?? 1e3;
374
+ const args = React.useMemo(
375
+ () => [texture, height, radius],
376
+ [texture, height, radius]
377
+ );
378
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
379
+ /* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
380
+ /* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
381
+ ] });
382
+ }
383
+ function EnvironmentColor({ color, scene }) {
384
+ const defaultScene = useThree$1((state) => state.scene);
385
+ React.useLayoutEffect(() => {
386
+ if (color === void 0) return;
387
+ const target = resolveScene(scene || defaultScene);
388
+ const oldBg = target.background;
389
+ target.background = new Color(color);
390
+ return () => {
391
+ target.background = oldBg;
392
+ };
393
+ });
394
+ return null;
395
+ }
396
+ function EnvironmentDualSource(props) {
397
+ const { backgroundFiles, ...envProps } = props;
398
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
399
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
400
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
401
+ ] });
402
+ }
403
+ function Environment(props) {
404
+ if (props.color && !props.files && !props.preset && !props.map) {
405
+ return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
406
+ }
407
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
408
+ return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
409
+ }
410
+ return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
411
+ }
412
+
39
413
  var __defProp$2 = Object.defineProperty;
40
414
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
41
415
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -215,7 +589,8 @@ function prepare(target, root, type, props) {
215
589
  object,
216
590
  eventCount: 0,
217
591
  handlers: {},
218
- isHidden: false
592
+ isHidden: false,
593
+ deferredRefs: []
219
594
  };
220
595
  if (object) object.__r3f = instance;
221
596
  }
@@ -264,7 +639,7 @@ function createOcclusionObserverNode(store, uniform) {
264
639
  let occlusionSetupPromise = null;
265
640
  function enableOcclusion(store) {
266
641
  const state = store.getState();
267
- const { internal, renderer, rootScene } = state;
642
+ const { internal, renderer } = state;
268
643
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
269
644
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
270
645
  if (!hasOcclusionSupport) {
@@ -427,6 +802,22 @@ function hasVisibilityHandlers(handlers) {
427
802
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
428
803
  }
429
804
 
805
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
806
+ function fromRef(ref) {
807
+ return { [FROM_REF]: ref };
808
+ }
809
+ function isFromRef(value) {
810
+ return value !== null && typeof value === "object" && FROM_REF in value;
811
+ }
812
+
813
+ const ONCE = Symbol.for("@react-three/fiber.once");
814
+ function once(...args) {
815
+ return { [ONCE]: args.length ? args : true };
816
+ }
817
+ function isOnce(value) {
818
+ return value !== null && typeof value === "object" && ONCE in value;
819
+ }
820
+
430
821
  const RESERVED_PROPS = [
431
822
  "children",
432
823
  "key",
@@ -497,7 +888,7 @@ function getMemoizedPrototype(root) {
497
888
  ctor = new root.constructor();
498
889
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
499
890
  }
500
- } catch (e) {
891
+ } catch {
501
892
  }
502
893
  return ctor;
503
894
  }
@@ -543,6 +934,25 @@ function applyProps(object, props) {
543
934
  continue;
544
935
  }
545
936
  if (value === void 0) continue;
937
+ if (isFromRef(value)) {
938
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
939
+ continue;
940
+ }
941
+ if (isOnce(value)) {
942
+ if (instance?.appliedOnce?.has(prop)) continue;
943
+ if (instance) {
944
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
945
+ instance.appliedOnce.add(prop);
946
+ }
947
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
948
+ const args = value[ONCE];
949
+ if (typeof targetRoot[targetKey] === "function") {
950
+ targetRoot[targetKey](...args === true ? [] : args);
951
+ } else if (args !== true && args.length > 0) {
952
+ targetRoot[targetKey] = args[0];
953
+ }
954
+ continue;
955
+ }
546
956
  let { root, key, target } = resolve(object, prop);
547
957
  if (target === void 0 && (typeof root !== "object" || root === null)) {
548
958
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -565,7 +975,7 @@ function applyProps(object, props) {
565
975
  else target.set(value);
566
976
  } else {
567
977
  root[key] = value;
568
- if (rootState && !rootState.linear && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
978
+ if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
569
979
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
570
980
  root[key].colorSpace = rootState.textureColorSpace;
571
981
  }
@@ -922,7 +1332,7 @@ function createPointerEvents(store) {
922
1332
  return {
923
1333
  priority: 1,
924
1334
  enabled: true,
925
- compute(event, state, previous) {
1335
+ compute(event, state) {
926
1336
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
927
1337
  state.raycaster.setFromCamera(state.pointer, state.camera);
928
1338
  },
@@ -1020,331 +1430,26 @@ function notifyAlpha({ message, link }) {
1020
1430
  }
1021
1431
  }
1022
1432
 
1023
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1024
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1025
- const createStore = (invalidate, advance) => {
1026
- const rootStore = createWithEqualityFn((set, get) => {
1027
- const position = new Vector3();
1028
- const defaultTarget = new Vector3();
1029
- const tempTarget = new Vector3();
1030
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1031
- const { width, height, top, left } = size;
1032
- const aspect = width / height;
1033
- if (target.isVector3) tempTarget.copy(target);
1034
- else tempTarget.set(...target);
1035
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1036
- if (isOrthographicCamera(camera)) {
1037
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1038
- } else {
1039
- const fov = camera.fov * Math.PI / 180;
1040
- const h = 2 * Math.tan(fov / 2) * distance;
1041
- const w = h * (width / height);
1042
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1043
- }
1044
- }
1045
- let performanceTimeout = void 0;
1046
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1047
- const pointer = new Vector2();
1048
- const rootState = {
1049
- set,
1050
- get,
1051
- // Mock objects that have to be configured
1052
- gl: null,
1053
- renderer: null,
1054
- camera: null,
1055
- frustum: new Frustum(),
1056
- autoUpdateFrustum: true,
1057
- raycaster: null,
1058
- events: { priority: 1, enabled: true, connected: false },
1059
- scene: null,
1060
- rootScene: null,
1061
- xr: null,
1062
- inspector: null,
1063
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1064
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1065
- legacy: false,
1066
- linear: false,
1067
- flat: false,
1068
- textureColorSpace: "srgb",
1069
- isLegacy: false,
1070
- webGPUSupported: false,
1071
- isNative: false,
1072
- controls: null,
1073
- pointer,
1074
- mouse: pointer,
1075
- frameloop: "always",
1076
- onPointerMissed: void 0,
1077
- onDragOverMissed: void 0,
1078
- onDropMissed: void 0,
1079
- performance: {
1080
- current: 1,
1081
- min: 0.5,
1082
- max: 1,
1083
- debounce: 200,
1084
- regress: () => {
1085
- const state2 = get();
1086
- if (performanceTimeout) clearTimeout(performanceTimeout);
1087
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1088
- performanceTimeout = setTimeout(
1089
- () => setPerformanceCurrent(get().performance.max),
1090
- state2.performance.debounce
1091
- );
1092
- }
1093
- },
1094
- size: { width: 0, height: 0, top: 0, left: 0 },
1095
- viewport: {
1096
- initialDpr: 0,
1097
- dpr: 0,
1098
- width: 0,
1099
- height: 0,
1100
- top: 0,
1101
- left: 0,
1102
- aspect: 0,
1103
- distance: 0,
1104
- factor: 0,
1105
- getCurrentViewport
1106
- },
1107
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1108
- setSize: (width, height, top, left) => {
1109
- const state2 = get();
1110
- if (width === void 0) {
1111
- set({ _sizeImperative: false });
1112
- if (state2._sizeProps) {
1113
- const { width: propW, height: propH } = state2._sizeProps;
1114
- if (propW !== void 0 || propH !== void 0) {
1115
- const currentSize = state2.size;
1116
- const newSize = {
1117
- width: propW ?? currentSize.width,
1118
- height: propH ?? currentSize.height,
1119
- top: currentSize.top,
1120
- left: currentSize.left
1121
- };
1122
- set((s) => ({
1123
- size: newSize,
1124
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1125
- }));
1126
- }
1127
- }
1128
- return;
1129
- }
1130
- const w = width;
1131
- const h = height ?? width;
1132
- const t = top ?? state2.size.top;
1133
- const l = left ?? state2.size.left;
1134
- const size = { width: w, height: h, top: t, left: l };
1135
- set((s) => ({
1136
- size,
1137
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1138
- _sizeImperative: true
1139
- }));
1140
- },
1141
- setDpr: (dpr) => set((state2) => {
1142
- const resolved = calculateDpr(dpr);
1143
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1144
- }),
1145
- setFrameloop: (frameloop = "always") => {
1146
- set(() => ({ frameloop }));
1147
- },
1148
- setError: (error) => set(() => ({ error })),
1149
- error: null,
1150
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1151
- uniforms: {},
1152
- nodes: {},
1153
- textures: /* @__PURE__ */ new Map(),
1154
- postProcessing: null,
1155
- passes: {},
1156
- _hmrVersion: 0,
1157
- _sizeImperative: false,
1158
- _sizeProps: null,
1159
- previousRoot: void 0,
1160
- internal: {
1161
- // Events
1162
- interaction: [],
1163
- hovered: /* @__PURE__ */ new Map(),
1164
- subscribers: [],
1165
- initialClick: [0, 0],
1166
- initialHits: [],
1167
- capturedMap: /* @__PURE__ */ new Map(),
1168
- lastEvent: React.createRef(),
1169
- // Visibility tracking (onFramed, onOccluded, onVisible)
1170
- visibilityRegistry: /* @__PURE__ */ new Map(),
1171
- // Occlusion system (WebGPU only)
1172
- occlusionEnabled: false,
1173
- occlusionObserver: null,
1174
- occlusionCache: /* @__PURE__ */ new Map(),
1175
- helperGroup: null,
1176
- // Updates
1177
- active: false,
1178
- frames: 0,
1179
- priority: 0,
1180
- subscribe: (ref, priority, store) => {
1181
- const internal = get().internal;
1182
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1183
- internal.subscribers.push({ ref, priority, store });
1184
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1185
- return () => {
1186
- const internal2 = get().internal;
1187
- if (internal2?.subscribers) {
1188
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1189
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1190
- }
1191
- };
1192
- },
1193
- // Renderer Storage (single source of truth)
1194
- actualRenderer: null,
1195
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1196
- scheduler: null
1197
- }
1198
- };
1199
- return rootState;
1200
- });
1201
- const state = rootStore.getState();
1202
- Object.defineProperty(state, "gl", {
1203
- get() {
1204
- const currentState = rootStore.getState();
1205
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1206
- const stack = new Error().stack || "";
1207
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1208
- if (!isInternalAccess) {
1209
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1210
- notifyDepreciated({
1211
- heading: "Accessing state.gl in WebGPU mode",
1212
- body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
1213
- });
1214
- }
1215
- }
1216
- return currentState.internal.actualRenderer;
1217
- },
1218
- set(value) {
1219
- rootStore.getState().internal.actualRenderer = value;
1220
- },
1221
- enumerable: true,
1222
- configurable: true
1223
- });
1224
- Object.defineProperty(state, "renderer", {
1225
- get() {
1226
- return rootStore.getState().internal.actualRenderer;
1227
- },
1228
- set(value) {
1229
- rootStore.getState().internal.actualRenderer = value;
1230
- },
1231
- enumerable: true,
1232
- configurable: true
1233
- });
1234
- let oldScene = state.scene;
1235
- rootStore.subscribe(() => {
1236
- const currentState = rootStore.getState();
1237
- const { scene, rootScene, set } = currentState;
1238
- if (scene !== oldScene) {
1239
- oldScene = scene;
1240
- if (scene?.isScene && scene !== rootScene) {
1241
- set({ rootScene: scene });
1242
- }
1243
- }
1244
- });
1245
- let oldSize = state.size;
1246
- let oldDpr = state.viewport.dpr;
1247
- let oldCamera = state.camera;
1248
- rootStore.subscribe(() => {
1249
- const { camera, size, viewport, set, internal } = rootStore.getState();
1250
- const actualRenderer = internal.actualRenderer;
1251
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1252
- oldSize = size;
1253
- oldDpr = viewport.dpr;
1254
- updateCamera(camera, size);
1255
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1256
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1257
- actualRenderer.setSize(size.width, size.height, updateStyle);
1258
- }
1259
- if (camera !== oldCamera) {
1260
- oldCamera = camera;
1261
- const { rootScene } = rootStore.getState();
1262
- if (camera && rootScene && !camera.parent) {
1263
- rootScene.add(camera);
1264
- }
1265
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1266
- const currentState = rootStore.getState();
1267
- if (currentState.autoUpdateFrustum && camera) {
1268
- updateFrustum(camera, currentState.frustum);
1269
- }
1270
- }
1271
- });
1272
- rootStore.subscribe((state2) => invalidate(state2));
1273
- return rootStore;
1274
- };
1275
-
1276
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1277
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1278
- function getLoader(Proto) {
1279
- if (isConstructor$1(Proto)) {
1280
- let loader = memoizedLoaders.get(Proto);
1281
- if (!loader) {
1282
- loader = new Proto();
1283
- memoizedLoaders.set(Proto, loader);
1284
- }
1285
- return loader;
1286
- }
1287
- return Proto;
1288
- }
1289
- function loadingFn(extensions, onProgress) {
1290
- return function(Proto, input) {
1291
- const loader = getLoader(Proto);
1292
- if (extensions) extensions(loader);
1293
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1294
- return loader.loadAsync(input, onProgress).then((data) => {
1295
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1296
- return data;
1297
- });
1298
- }
1299
- return new Promise(
1300
- (res, reject) => loader.load(
1301
- input,
1302
- (data) => {
1303
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1304
- res(data);
1305
- },
1306
- onProgress,
1307
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1308
- )
1309
- );
1310
- };
1311
- }
1312
- function useLoader(loader, input, extensions, onProgress) {
1313
- const keys = Array.isArray(input) ? input : [input];
1314
- const fn = loadingFn(extensions, onProgress);
1315
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1316
- return Array.isArray(input) ? results : results[0];
1317
- }
1318
- useLoader.preload = function(loader, input, extensions, onProgress) {
1319
- const keys = Array.isArray(input) ? input : [input];
1320
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
1321
- };
1322
- useLoader.clear = function(loader, input) {
1323
- const keys = Array.isArray(input) ? input : [input];
1324
- keys.forEach((key) => clear([loader, key]));
1325
- };
1326
- useLoader.loader = getLoader;
1327
-
1328
- var __defProp$1 = Object.defineProperty;
1329
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1330
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1331
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1332
- class PhaseGraph {
1333
- constructor() {
1334
- /** Ordered list of phase nodes */
1335
- __publicField$1(this, "phases", []);
1336
- /** Quick lookup by name */
1337
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1338
- /** Cached ordered names (invalidated on changes) */
1339
- __publicField$1(this, "orderedNamesCache", null);
1340
- this.initializeDefaultPhases();
1341
- }
1342
- //* Initialization --------------------------------
1343
- initializeDefaultPhases() {
1344
- for (const name of DEFAULT_PHASES) {
1345
- const node = { name, isAutoGenerated: false };
1346
- this.phases.push(node);
1347
- this.phaseMap.set(name, node);
1433
+ var __defProp$1 = Object.defineProperty;
1434
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1435
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1436
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1437
+ class PhaseGraph {
1438
+ constructor() {
1439
+ /** Ordered list of phase nodes */
1440
+ __publicField$1(this, "phases", []);
1441
+ /** Quick lookup by name */
1442
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1443
+ /** Cached ordered names (invalidated on changes) */
1444
+ __publicField$1(this, "orderedNamesCache", null);
1445
+ this.initializeDefaultPhases();
1446
+ }
1447
+ //* Initialization --------------------------------
1448
+ initializeDefaultPhases() {
1449
+ for (const name of DEFAULT_PHASES) {
1450
+ const node = { name, isAutoGenerated: false };
1451
+ this.phases.push(node);
1452
+ this.phaseMap.set(name, node);
1348
1453
  }
1349
1454
  this.invalidateCache();
1350
1455
  }
@@ -2230,132 +2335,445 @@ const _Scheduler = class _Scheduler {
2230
2335
  console.error(`[Scheduler] Error in global job "${job.id}":`, error);
2231
2336
  }
2232
2337
  }
2233
- }
2234
- /**
2235
- * Execute all jobs for a single root in sorted order.
2236
- * Rebuilds sorted job list if needed, then dispatches each job.
2237
- * Errors are caught and propagated via triggerError.
2238
- * @param {RootEntry} root - The root entry to tick
2239
- * @param {number} timestamp - RAF timestamp in milliseconds
2240
- * @param {number} delta - Time since last frame in seconds
2241
- * @returns {void}
2242
- * @private
2243
- */
2244
- tickRoot(root, timestamp, delta) {
2245
- if (root.needsRebuild) {
2246
- root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2247
- root.needsRebuild = false;
2248
- }
2249
- const providedState = root.getState?.() ?? {};
2250
- const frameState = {
2251
- ...providedState,
2252
- time: timestamp,
2253
- delta,
2254
- elapsed: this.loopState.elapsedTime / 1e3,
2255
- // Convert ms to seconds
2256
- frame: this.loopState.frameCount
2257
- };
2258
- for (const job of root.sortedJobs) {
2259
- if (!shouldRun(job, timestamp)) continue;
2260
- try {
2261
- job.callback(frameState, delta);
2262
- } catch (error) {
2263
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2264
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
2338
+ }
2339
+ /**
2340
+ * Execute all jobs for a single root in sorted order.
2341
+ * Rebuilds sorted job list if needed, then dispatches each job.
2342
+ * Errors are caught and propagated via triggerError.
2343
+ * @param {RootEntry} root - The root entry to tick
2344
+ * @param {number} timestamp - RAF timestamp in milliseconds
2345
+ * @param {number} delta - Time since last frame in seconds
2346
+ * @returns {void}
2347
+ * @private
2348
+ */
2349
+ tickRoot(root, timestamp, delta) {
2350
+ if (root.needsRebuild) {
2351
+ root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2352
+ root.needsRebuild = false;
2353
+ }
2354
+ const providedState = root.getState?.() ?? {};
2355
+ const frameState = {
2356
+ ...providedState,
2357
+ time: timestamp,
2358
+ delta,
2359
+ elapsed: this.loopState.elapsedTime / 1e3,
2360
+ // Convert ms to seconds
2361
+ frame: this.loopState.frameCount
2362
+ };
2363
+ for (const job of root.sortedJobs) {
2364
+ if (!shouldRun(job, timestamp)) continue;
2365
+ try {
2366
+ job.callback(frameState, delta);
2367
+ } catch (error) {
2368
+ console.error(`[Scheduler] Error in job "${job.id}":`, error);
2369
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
2370
+ }
2371
+ }
2372
+ }
2373
+ //* Debug & Inspection Methods ================================
2374
+ /**
2375
+ * Get the total number of registered jobs across all roots.
2376
+ * Includes both per-root jobs and global before/after jobs.
2377
+ * @returns {number} Total job count
2378
+ */
2379
+ getJobCount() {
2380
+ let count = 0;
2381
+ for (const root of this.roots.values()) {
2382
+ count += root.jobs.size;
2383
+ }
2384
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2385
+ }
2386
+ /**
2387
+ * Get all registered job IDs across all roots.
2388
+ * Includes both per-root jobs and global before/after jobs.
2389
+ * @returns {string[]} Array of all job IDs
2390
+ */
2391
+ getJobIds() {
2392
+ const ids = [];
2393
+ for (const root of this.roots.values()) {
2394
+ ids.push(...root.jobs.keys());
2395
+ }
2396
+ ids.push(...this.globalBeforeJobs.keys());
2397
+ ids.push(...this.globalAfterJobs.keys());
2398
+ return ids;
2399
+ }
2400
+ /**
2401
+ * Get the number of registered roots (Canvas instances).
2402
+ * @returns {number} Number of registered roots
2403
+ */
2404
+ getRootCount() {
2405
+ return this.roots.size;
2406
+ }
2407
+ /**
2408
+ * Check if any user (non-system) jobs are registered in a specific phase.
2409
+ * Used by the default render job to know if a user has taken over rendering.
2410
+ *
2411
+ * @param phase The phase to check
2412
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2413
+ * @returns true if any user jobs exist in the phase
2414
+ */
2415
+ hasUserJobsInPhase(phase, rootId) {
2416
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2417
+ return rootsToCheck.some((root) => {
2418
+ if (!root) return false;
2419
+ for (const job of root.jobs.values()) {
2420
+ if (job.phase === phase && !job.system && job.enabled) return true;
2421
+ }
2422
+ return false;
2423
+ });
2424
+ }
2425
+ //* Utility Methods ================================
2426
+ /**
2427
+ * Generate a unique root ID for automatic root registration.
2428
+ * @returns {string} A unique root ID in the format 'root_N'
2429
+ */
2430
+ generateRootId() {
2431
+ return `root_${this.nextRootIndex++}`;
2432
+ }
2433
+ /**
2434
+ * Generate a unique job ID.
2435
+ * @returns {string} A unique job ID in the format 'job_N'
2436
+ * @private
2437
+ */
2438
+ generateJobId() {
2439
+ return `job_${this.nextJobIndex}`;
2440
+ }
2441
+ /**
2442
+ * Normalize before/after constraints to a Set.
2443
+ * Handles undefined, single string, or array inputs.
2444
+ * @param {string | string[] | undefined} value - The constraint value(s)
2445
+ * @returns {Set<string>} Normalized Set of constraint strings
2446
+ * @private
2447
+ */
2448
+ normalizeConstraints(value) {
2449
+ if (!value) return /* @__PURE__ */ new Set();
2450
+ if (Array.isArray(value)) return new Set(value);
2451
+ return /* @__PURE__ */ new Set([value]);
2452
+ }
2453
+ };
2454
+ //* Static State & Methods (Singleton Usage) ================================
2455
+ //* Cross-Bundle Singleton Key ==============================
2456
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2457
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2458
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2459
+ let Scheduler = _Scheduler;
2460
+ const getScheduler = () => Scheduler.get();
2461
+ if (hmrData) {
2462
+ hmrData.accept?.();
2463
+ }
2464
+
2465
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2466
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
2467
+ const createStore = (invalidate, advance) => {
2468
+ const rootStore = createWithEqualityFn((set, get) => {
2469
+ const position = new Vector3();
2470
+ const defaultTarget = new Vector3();
2471
+ const tempTarget = new Vector3();
2472
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2473
+ const { width, height, top, left } = size;
2474
+ const aspect = width / height;
2475
+ if (target.isVector3) tempTarget.copy(target);
2476
+ else tempTarget.set(...target);
2477
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2478
+ if (isOrthographicCamera(camera)) {
2479
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2480
+ } else {
2481
+ const fov = camera.fov * Math.PI / 180;
2482
+ const h = 2 * Math.tan(fov / 2) * distance;
2483
+ const w = h * (width / height);
2484
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2485
+ }
2486
+ }
2487
+ let performanceTimeout = void 0;
2488
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2489
+ const pointer = new Vector2();
2490
+ const rootState = {
2491
+ set,
2492
+ get,
2493
+ // Mock objects that have to be configured
2494
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2495
+ primaryStore: null,
2496
+ gl: null,
2497
+ renderer: null,
2498
+ camera: null,
2499
+ frustum: new Frustum(),
2500
+ autoUpdateFrustum: true,
2501
+ raycaster: null,
2502
+ events: { priority: 1, enabled: true, connected: false },
2503
+ scene: null,
2504
+ rootScene: null,
2505
+ xr: null,
2506
+ inspector: null,
2507
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2508
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2509
+ textureColorSpace: SRGBColorSpace,
2510
+ isLegacy: false,
2511
+ webGPUSupported: false,
2512
+ isNative: false,
2513
+ controls: null,
2514
+ pointer,
2515
+ mouse: pointer,
2516
+ frameloop: "always",
2517
+ onPointerMissed: void 0,
2518
+ onDragOverMissed: void 0,
2519
+ onDropMissed: void 0,
2520
+ performance: {
2521
+ current: 1,
2522
+ min: 0.5,
2523
+ max: 1,
2524
+ debounce: 200,
2525
+ regress: () => {
2526
+ const state2 = get();
2527
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2528
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2529
+ performanceTimeout = setTimeout(
2530
+ () => setPerformanceCurrent(get().performance.max),
2531
+ state2.performance.debounce
2532
+ );
2533
+ }
2534
+ },
2535
+ size: { width: 0, height: 0, top: 0, left: 0 },
2536
+ viewport: {
2537
+ initialDpr: 0,
2538
+ dpr: 0,
2539
+ width: 0,
2540
+ height: 0,
2541
+ top: 0,
2542
+ left: 0,
2543
+ aspect: 0,
2544
+ distance: 0,
2545
+ factor: 0,
2546
+ getCurrentViewport
2547
+ },
2548
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2549
+ setSize: (width, height, top, left) => {
2550
+ const state2 = get();
2551
+ if (width === void 0) {
2552
+ set({ _sizeImperative: false });
2553
+ if (state2._sizeProps) {
2554
+ const { width: propW, height: propH } = state2._sizeProps;
2555
+ if (propW !== void 0 || propH !== void 0) {
2556
+ const currentSize = state2.size;
2557
+ const newSize = {
2558
+ width: propW ?? currentSize.width,
2559
+ height: propH ?? currentSize.height,
2560
+ top: currentSize.top,
2561
+ left: currentSize.left
2562
+ };
2563
+ set((s) => ({
2564
+ size: newSize,
2565
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2566
+ }));
2567
+ getScheduler().invalidate();
2568
+ }
2569
+ }
2570
+ return;
2571
+ }
2572
+ const w = width;
2573
+ const h = height ?? width;
2574
+ const t = top ?? state2.size.top;
2575
+ const l = left ?? state2.size.left;
2576
+ const size = { width: w, height: h, top: t, left: l };
2577
+ set((s) => ({
2578
+ size,
2579
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2580
+ _sizeImperative: true
2581
+ }));
2582
+ getScheduler().invalidate();
2583
+ },
2584
+ setDpr: (dpr) => set((state2) => {
2585
+ const resolved = calculateDpr(dpr);
2586
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2587
+ }),
2588
+ setFrameloop: (frameloop = "always") => {
2589
+ set(() => ({ frameloop }));
2590
+ },
2591
+ setError: (error) => set(() => ({ error })),
2592
+ error: null,
2593
+ //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2594
+ uniforms: {},
2595
+ nodes: {},
2596
+ textures: /* @__PURE__ */ new Map(),
2597
+ postProcessing: null,
2598
+ passes: {},
2599
+ _hmrVersion: 0,
2600
+ _sizeImperative: false,
2601
+ _sizeProps: null,
2602
+ previousRoot: void 0,
2603
+ internal: {
2604
+ // Events
2605
+ interaction: [],
2606
+ hovered: /* @__PURE__ */ new Map(),
2607
+ subscribers: [],
2608
+ initialClick: [0, 0],
2609
+ initialHits: [],
2610
+ capturedMap: /* @__PURE__ */ new Map(),
2611
+ lastEvent: React.createRef(),
2612
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2613
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2614
+ // Occlusion system (WebGPU only)
2615
+ occlusionEnabled: false,
2616
+ occlusionObserver: null,
2617
+ occlusionCache: /* @__PURE__ */ new Map(),
2618
+ helperGroup: null,
2619
+ // Updates
2620
+ active: false,
2621
+ frames: 0,
2622
+ priority: 0,
2623
+ subscribe: (ref, priority, store) => {
2624
+ const internal = get().internal;
2625
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2626
+ internal.subscribers.push({ ref, priority, store });
2627
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2628
+ return () => {
2629
+ const internal2 = get().internal;
2630
+ if (internal2?.subscribers) {
2631
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2632
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2633
+ }
2634
+ };
2635
+ },
2636
+ // Renderer Storage (single source of truth)
2637
+ actualRenderer: null,
2638
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2639
+ scheduler: null
2640
+ }
2641
+ };
2642
+ return rootState;
2643
+ });
2644
+ const state = rootStore.getState();
2645
+ Object.defineProperty(state, "gl", {
2646
+ get() {
2647
+ const currentState = rootStore.getState();
2648
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2649
+ const stack = new Error().stack || "";
2650
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2651
+ if (!isInternalAccess) {
2652
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2653
+ notifyDepreciated({
2654
+ heading: "Accessing state.gl in WebGPU mode",
2655
+ body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
2656
+ });
2657
+ }
2658
+ }
2659
+ return currentState.internal.actualRenderer;
2660
+ },
2661
+ set(value) {
2662
+ rootStore.getState().internal.actualRenderer = value;
2663
+ },
2664
+ enumerable: true,
2665
+ configurable: true
2666
+ });
2667
+ Object.defineProperty(state, "renderer", {
2668
+ get() {
2669
+ return rootStore.getState().internal.actualRenderer;
2670
+ },
2671
+ set(value) {
2672
+ rootStore.getState().internal.actualRenderer = value;
2673
+ },
2674
+ enumerable: true,
2675
+ configurable: true
2676
+ });
2677
+ let oldScene = state.scene;
2678
+ rootStore.subscribe(() => {
2679
+ const currentState = rootStore.getState();
2680
+ const { scene, rootScene, set } = currentState;
2681
+ if (scene !== oldScene) {
2682
+ oldScene = scene;
2683
+ if (scene?.isScene && scene !== rootScene) {
2684
+ set({ rootScene: scene });
2685
+ }
2686
+ }
2687
+ });
2688
+ let oldSize = state.size;
2689
+ let oldDpr = state.viewport.dpr;
2690
+ let oldCamera = state.camera;
2691
+ rootStore.subscribe(() => {
2692
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2693
+ const actualRenderer = internal.actualRenderer;
2694
+ const canvasTarget = internal.canvasTarget;
2695
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2696
+ oldSize = size;
2697
+ oldDpr = viewport.dpr;
2698
+ updateCamera(camera, size);
2699
+ if (canvasTarget) {
2700
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2701
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2702
+ canvasTarget.setSize(size.width, size.height, updateStyle);
2703
+ } else {
2704
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2705
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2706
+ actualRenderer.setSize(size.width, size.height, updateStyle);
2707
+ }
2708
+ }
2709
+ if (camera !== oldCamera) {
2710
+ oldCamera = camera;
2711
+ const { rootScene } = rootStore.getState();
2712
+ if (camera && rootScene && !camera.parent) {
2713
+ rootScene.add(camera);
2714
+ }
2715
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2716
+ const currentState = rootStore.getState();
2717
+ if (currentState.autoUpdateFrustum && camera) {
2718
+ updateFrustum(camera, currentState.frustum);
2265
2719
  }
2266
2720
  }
2267
- }
2268
- //* Debug & Inspection Methods ================================
2269
- /**
2270
- * Get the total number of registered jobs across all roots.
2271
- * Includes both per-root jobs and global before/after jobs.
2272
- * @returns {number} Total job count
2273
- */
2274
- getJobCount() {
2275
- let count = 0;
2276
- for (const root of this.roots.values()) {
2277
- count += root.jobs.size;
2721
+ });
2722
+ rootStore.subscribe((state2) => invalidate(state2));
2723
+ return rootStore;
2724
+ };
2725
+
2726
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2727
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2728
+ function getLoader(Proto) {
2729
+ if (isConstructor$1(Proto)) {
2730
+ let loader = memoizedLoaders.get(Proto);
2731
+ if (!loader) {
2732
+ loader = new Proto();
2733
+ memoizedLoaders.set(Proto, loader);
2278
2734
  }
2279
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2735
+ return loader;
2280
2736
  }
2281
- /**
2282
- * Get all registered job IDs across all roots.
2283
- * Includes both per-root jobs and global before/after jobs.
2284
- * @returns {string[]} Array of all job IDs
2285
- */
2286
- getJobIds() {
2287
- const ids = [];
2288
- for (const root of this.roots.values()) {
2289
- ids.push(...root.jobs.keys());
2737
+ return Proto;
2738
+ }
2739
+ function loadingFn(extensions, onProgress) {
2740
+ return function(Proto, input) {
2741
+ const loader = getLoader(Proto);
2742
+ if (extensions) extensions(loader);
2743
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2744
+ return loader.loadAsync(input, onProgress).then((data) => {
2745
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2746
+ return data;
2747
+ });
2290
2748
  }
2291
- ids.push(...this.globalBeforeJobs.keys());
2292
- ids.push(...this.globalAfterJobs.keys());
2293
- return ids;
2294
- }
2295
- /**
2296
- * Get the number of registered roots (Canvas instances).
2297
- * @returns {number} Number of registered roots
2298
- */
2299
- getRootCount() {
2300
- return this.roots.size;
2301
- }
2302
- /**
2303
- * Check if any user (non-system) jobs are registered in a specific phase.
2304
- * Used by the default render job to know if a user has taken over rendering.
2305
- *
2306
- * @param phase The phase to check
2307
- * @param rootId Optional root ID to check (checks all roots if not provided)
2308
- * @returns true if any user jobs exist in the phase
2309
- */
2310
- hasUserJobsInPhase(phase, rootId) {
2311
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2312
- return rootsToCheck.some((root) => {
2313
- if (!root) return false;
2314
- for (const job of root.jobs.values()) {
2315
- if (job.phase === phase && !job.system && job.enabled) return true;
2316
- }
2317
- return false;
2318
- });
2319
- }
2320
- //* Utility Methods ================================
2321
- /**
2322
- * Generate a unique root ID for automatic root registration.
2323
- * @returns {string} A unique root ID in the format 'root_N'
2324
- */
2325
- generateRootId() {
2326
- return `root_${this.nextRootIndex++}`;
2327
- }
2328
- /**
2329
- * Generate a unique job ID.
2330
- * @returns {string} A unique job ID in the format 'job_N'
2331
- * @private
2332
- */
2333
- generateJobId() {
2334
- return `job_${this.nextJobIndex}`;
2335
- }
2336
- /**
2337
- * Normalize before/after constraints to a Set.
2338
- * Handles undefined, single string, or array inputs.
2339
- * @param {string | string[] | undefined} value - The constraint value(s)
2340
- * @returns {Set<string>} Normalized Set of constraint strings
2341
- * @private
2342
- */
2343
- normalizeConstraints(value) {
2344
- if (!value) return /* @__PURE__ */ new Set();
2345
- if (Array.isArray(value)) return new Set(value);
2346
- return /* @__PURE__ */ new Set([value]);
2347
- }
2348
- };
2349
- //* Static State & Methods (Singleton Usage) ================================
2350
- //* Cross-Bundle Singleton Key ==============================
2351
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2352
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2353
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2354
- let Scheduler = _Scheduler;
2355
- const getScheduler = () => Scheduler.get();
2356
- if (hmrData) {
2357
- hmrData.accept?.();
2749
+ return new Promise(
2750
+ (res, reject) => loader.load(
2751
+ input,
2752
+ (data) => {
2753
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2754
+ res(data);
2755
+ },
2756
+ onProgress,
2757
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2758
+ )
2759
+ );
2760
+ };
2761
+ }
2762
+ function useLoader(loader, input, extensions, onProgress) {
2763
+ const keys = Array.isArray(input) ? input : [input];
2764
+ const fn = loadingFn(extensions, onProgress);
2765
+ const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
2766
+ return Array.isArray(input) ? results : results[0];
2358
2767
  }
2768
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2769
+ const keys = Array.isArray(input) ? input : [input];
2770
+ keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
2771
+ };
2772
+ useLoader.clear = function(loader, input) {
2773
+ const keys = Array.isArray(input) ? input : [input];
2774
+ keys.forEach((key) => clear([loader, key]));
2775
+ };
2776
+ useLoader.loader = getLoader;
2359
2777
 
2360
2778
  function useFrame(callback, priorityOrOptions) {
2361
2779
  const store = React.useContext(context);
@@ -2536,6 +2954,9 @@ function useTexture(input, optionsOrOnLoad) {
2536
2954
  const textureCache = useThree((state) => state.textures);
2537
2955
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2538
2956
  const { onLoad, cache = false } = options;
2957
+ const onLoadRef = useRef(onLoad);
2958
+ onLoadRef.current = onLoad;
2959
+ const onLoadCalledForRef = useRef(null);
2539
2960
  const urls = useMemo(() => getUrls(input), [input]);
2540
2961
  const cachedResult = useMemo(() => {
2541
2962
  if (!cache) return null;
@@ -2546,9 +2967,13 @@ function useTexture(input, optionsOrOnLoad) {
2546
2967
  TextureLoader,
2547
2968
  IsObject(input) ? Object.values(input) : input
2548
2969
  );
2970
+ const inputKey = urls.join("\0");
2549
2971
  useLayoutEffect(() => {
2550
- if (!cachedResult) onLoad?.(loadedTextures);
2551
- }, [onLoad, cachedResult, loadedTextures]);
2972
+ if (cachedResult) return;
2973
+ if (onLoadCalledForRef.current === inputKey) return;
2974
+ onLoadCalledForRef.current = inputKey;
2975
+ onLoadRef.current?.(loadedTextures);
2976
+ }, [cachedResult, loadedTextures, inputKey]);
2552
2977
  useEffect(() => {
2553
2978
  if (cachedResult) return;
2554
2979
  if ("initTexture" in renderer) {
@@ -2715,16 +3140,33 @@ function useTextures() {
2715
3140
  }, [store]);
2716
3141
  }
2717
3142
 
2718
- function useRenderTarget(width, height, options) {
3143
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2719
3144
  const isLegacy = useThree((s) => s.isLegacy);
2720
3145
  const size = useThree((s) => s.size);
3146
+ let width;
3147
+ let height;
3148
+ let opts;
3149
+ if (typeof widthOrOptions === "object") {
3150
+ opts = widthOrOptions;
3151
+ } else if (typeof widthOrOptions === "number") {
3152
+ width = widthOrOptions;
3153
+ if (typeof heightOrOptions === "object") {
3154
+ height = widthOrOptions;
3155
+ opts = heightOrOptions;
3156
+ } else if (typeof heightOrOptions === "number") {
3157
+ height = heightOrOptions;
3158
+ opts = options;
3159
+ } else {
3160
+ height = widthOrOptions;
3161
+ }
3162
+ }
2721
3163
  return useMemo(() => {
2722
3164
  const w = width ?? size.width;
2723
3165
  const h = height ?? size.height;
2724
3166
  {
2725
- return isLegacy ? new WebGLRenderTarget(w, h, options) : new RenderTarget(w, h, options);
3167
+ return isLegacy ? new WebGLRenderTarget(w, h, opts) : new RenderTarget(w, h, opts);
2726
3168
  }
2727
- }, [width, height, size.width, size.height, options, isLegacy]);
3169
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2728
3170
  }
2729
3171
 
2730
3172
  function useStore() {
@@ -2774,7 +3216,7 @@ function addTail(callback) {
2774
3216
  function invalidate(state, frames = 1, stackFrames = false) {
2775
3217
  getScheduler().invalidate(frames, stackFrames);
2776
3218
  }
2777
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3219
+ function advance(timestamp) {
2778
3220
  getScheduler().step(timestamp);
2779
3221
  }
2780
3222
 
@@ -14228,6 +14670,7 @@ function swapInstances() {
14228
14670
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14229
14671
  instance.object.__r3f = instance;
14230
14672
  setFiberRef(fiber, instance.object);
14673
+ delete instance.appliedOnce;
14231
14674
  applyProps(instance.object, instance.props);
14232
14675
  if (instance.props.attach) {
14233
14676
  attach(parent, instance);
@@ -14301,8 +14744,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14301
14744
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14302
14745
  if (isTailSibling) swapInstances();
14303
14746
  },
14304
- finalizeInitialChildren: () => false,
14305
- commitMount() {
14747
+ finalizeInitialChildren: (instance) => {
14748
+ for (const prop in instance.props) {
14749
+ if (isFromRef(instance.props[prop])) return true;
14750
+ }
14751
+ return false;
14752
+ },
14753
+ commitMount(instance) {
14754
+ const resolved = {};
14755
+ for (const prop in instance.props) {
14756
+ const value = instance.props[prop];
14757
+ if (isFromRef(value)) {
14758
+ const ref = value[FROM_REF];
14759
+ if (ref.current != null) resolved[prop] = ref.current;
14760
+ }
14761
+ }
14762
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14306
14763
  },
14307
14764
  getPublicInstance: (instance) => instance?.object,
14308
14765
  prepareForCommit: () => null,
@@ -14523,6 +14980,9 @@ function createRoot(canvas) {
14523
14980
  let resolve;
14524
14981
  pending = new Promise((_resolve) => resolve = _resolve);
14525
14982
  const {
14983
+ id: canvasId,
14984
+ primaryCanvas,
14985
+ scheduler: schedulerConfig,
14526
14986
  gl: glConfig,
14527
14987
  renderer: rendererConfig,
14528
14988
  size: propsSize,
@@ -14530,10 +14990,7 @@ function createRoot(canvas) {
14530
14990
  events,
14531
14991
  onCreated: onCreatedCallback,
14532
14992
  shadows = false,
14533
- linear = false,
14534
- flat = false,
14535
14993
  textureColorSpace = SRGBColorSpace,
14536
- legacy = false,
14537
14994
  orthographic = false,
14538
14995
  frameloop = "always",
14539
14996
  dpr = [1, 2],
@@ -14545,7 +15002,8 @@ function createRoot(canvas) {
14545
15002
  onDropMissed,
14546
15003
  autoUpdateFrustum = true,
14547
15004
  occlusion = false,
14548
- _sizeProps
15005
+ _sizeProps,
15006
+ forceEven
14549
15007
  } = props;
14550
15008
  const state = store.getState();
14551
15009
  const defaultGLProps = {
@@ -14555,7 +15013,8 @@ function createRoot(canvas) {
14555
15013
  alpha: true
14556
15014
  };
14557
15015
  const defaultGPUProps = {
14558
- canvas
15016
+ canvas,
15017
+ antialias: true
14559
15018
  };
14560
15019
  const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
14561
15020
  if (glConfig && rendererConfig) {
@@ -14569,10 +15028,35 @@ function createRoot(canvas) {
14569
15028
  });
14570
15029
  }
14571
15030
  let renderer = state.internal.actualRenderer;
15031
+ if (primaryCanvas && wantsGL) {
15032
+ throw new Error(
15033
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15034
+ );
15035
+ }
14572
15036
  if (wantsGL && !state.internal.actualRenderer) {
14573
15037
  renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
14574
15038
  state.internal.actualRenderer = renderer;
14575
- state.set({ isLegacy: true, gl: renderer, renderer });
15039
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
15040
+ } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
15041
+ const primary = await waitForPrimary(primaryCanvas);
15042
+ renderer = primary.renderer;
15043
+ state.internal.actualRenderer = renderer;
15044
+ const canvasTarget = new CanvasTarget(canvas);
15045
+ primary.store.setState((prev) => ({
15046
+ internal: { ...prev.internal, isMultiCanvas: true }
15047
+ }));
15048
+ state.set((prev) => ({
15049
+ webGPUSupported: primary.store.getState().webGPUSupported,
15050
+ renderer,
15051
+ primaryStore: primary.store,
15052
+ internal: {
15053
+ ...prev.internal,
15054
+ canvasTarget,
15055
+ isMultiCanvas: true,
15056
+ isSecondary: true,
15057
+ targetId: primaryCanvas
15058
+ }
15059
+ }));
14576
15060
  } else if (!wantsGL && !state.internal.actualRenderer) {
14577
15061
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14578
15062
  if (!renderer.hasInitialized?.()) {
@@ -14581,7 +15065,18 @@ function createRoot(canvas) {
14581
15065
  const backend = renderer.backend;
14582
15066
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14583
15067
  state.internal.actualRenderer = renderer;
14584
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15068
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15069
+ if (canvasId && !state.internal.isSecondary) {
15070
+ const canvasTarget = new CanvasTarget(canvas);
15071
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15072
+ state.set((prev) => ({
15073
+ internal: {
15074
+ ...prev.internal,
15075
+ canvasTarget,
15076
+ unregisterPrimary
15077
+ }
15078
+ }));
15079
+ }
14585
15080
  }
14586
15081
  let raycaster = state.raycaster;
14587
15082
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
@@ -14590,6 +15085,7 @@ function createRoot(canvas) {
14590
15085
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14591
15086
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14592
15087
  }
15088
+ let tempCamera = state.camera;
14593
15089
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14594
15090
  lastCamera = cameraOptions;
14595
15091
  const isCamera = cameraOptions?.isCamera;
@@ -14609,6 +15105,7 @@ function createRoot(canvas) {
14609
15105
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14610
15106
  }
14611
15107
  state.set({ camera });
15108
+ tempCamera = camera;
14612
15109
  raycaster.camera = camera;
14613
15110
  }
14614
15111
  if (!state.scene) {
@@ -14626,7 +15123,7 @@ function createRoot(canvas) {
14626
15123
  rootScene: scene,
14627
15124
  internal: { ...prev.internal, container: scene }
14628
15125
  }));
14629
- const camera = state.camera;
15126
+ const camera = tempCamera;
14630
15127
  if (camera && !camera.parent) scene.add(camera);
14631
15128
  }
14632
15129
  if (events && !state.events.handlers) {
@@ -14643,6 +15140,9 @@ function createRoot(canvas) {
14643
15140
  if (_sizeProps !== void 0) {
14644
15141
  state.set({ _sizeProps });
14645
15142
  }
15143
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15144
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15145
+ }
14646
15146
  const size = computeInitialSize(canvas, propsSize);
14647
15147
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14648
15148
  const wasImperative = state._sizeImperative;
@@ -14672,7 +15172,7 @@ function createRoot(canvas) {
14672
15172
  const handleXRFrame = (timestamp, frame) => {
14673
15173
  const state2 = store.getState();
14674
15174
  if (state2.frameloop === "never") return;
14675
- advance(timestamp, true);
15175
+ advance(timestamp);
14676
15176
  };
14677
15177
  const actualRenderer = state.internal.actualRenderer;
14678
15178
  const handleSessionChange = () => {
@@ -14684,16 +15184,16 @@ function createRoot(canvas) {
14684
15184
  };
14685
15185
  const xr = {
14686
15186
  connect() {
14687
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14688
- const actualRenderer2 = renderer2 || gl;
14689
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14690
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15187
+ const { gl, renderer: renderer2 } = store.getState();
15188
+ const xrManager = (renderer2 || gl).xr;
15189
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15190
+ xrManager.addEventListener("sessionend", handleSessionChange);
14691
15191
  },
14692
15192
  disconnect() {
14693
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14694
- const actualRenderer2 = renderer2 || gl;
14695
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14696
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15193
+ const { gl, renderer: renderer2 } = store.getState();
15194
+ const xrManager = (renderer2 || gl).xr;
15195
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15196
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14697
15197
  }
14698
15198
  };
14699
15199
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14721,34 +15221,9 @@ function createRoot(canvas) {
14721
15221
  renderer.shadowMap.needsUpdate = true;
14722
15222
  }
14723
15223
  }
14724
- {
14725
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14726
- const linearChanged = linear !== lastConfiguredProps.linear;
14727
- const flatChanged = flat !== lastConfiguredProps.flat;
14728
- if (legacyChanged) {
14729
- if (legacy) {
14730
- notifyDepreciated({
14731
- heading: "Legacy Color Management",
14732
- body: "Legacy color management is deprecated and will be removed in a future version.",
14733
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
14734
- });
14735
- }
14736
- }
14737
- if (legacyChanged) {
14738
- ColorManagement.enabled = !legacy;
14739
- lastConfiguredProps.legacy = legacy;
14740
- }
14741
- if (!configured || linearChanged) {
14742
- renderer.outputColorSpace = linear ? LinearSRGBColorSpace : SRGBColorSpace;
14743
- lastConfiguredProps.linear = linear;
14744
- }
14745
- if (!configured || flatChanged) {
14746
- renderer.toneMapping = flat ? NoToneMapping : ACESFilmicToneMapping;
14747
- lastConfiguredProps.flat = flat;
14748
- }
14749
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14750
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14751
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15224
+ if (!configured) {
15225
+ renderer.outputColorSpace = SRGBColorSpace;
15226
+ renderer.toneMapping = ACESFilmicToneMapping;
14752
15227
  }
14753
15228
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14754
15229
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
@@ -14766,11 +15241,26 @@ function createRoot(canvas) {
14766
15241
  const scheduler = getScheduler();
14767
15242
  const rootId = state.internal.rootId;
14768
15243
  if (!rootId) {
14769
- const newRootId = scheduler.generateRootId();
15244
+ const newRootId = canvasId || scheduler.generateRootId();
14770
15245
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14771
15246
  getState: () => store.getState(),
14772
15247
  onError: (err) => store.getState().setError(err)
14773
15248
  });
15249
+ const unregisterCanvasTarget = scheduler.register(
15250
+ () => {
15251
+ const state2 = store.getState();
15252
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15253
+ const renderer2 = state2.internal.actualRenderer;
15254
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15255
+ }
15256
+ },
15257
+ {
15258
+ id: `${newRootId}_canvasTarget`,
15259
+ rootId: newRootId,
15260
+ phase: "start",
15261
+ system: true
15262
+ }
15263
+ );
14774
15264
  const unregisterFrustum = scheduler.register(
14775
15265
  () => {
14776
15266
  const state2 = store.getState();
@@ -14812,11 +15302,15 @@ function createRoot(canvas) {
14812
15302
  }
14813
15303
  },
14814
15304
  {
14815
- id: `${newRootId}_render`,
15305
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15306
+ id: canvasId || `${newRootId}_render`,
14816
15307
  rootId: newRootId,
14817
15308
  phase: "render",
14818
- system: true
15309
+ system: true,
14819
15310
  // Internal flag: this is a system job, not user-controlled
15311
+ // Apply scheduler config for render ordering and rate limiting
15312
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15313
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14820
15314
  }
14821
15315
  );
14822
15316
  state.set((state2) => ({
@@ -14825,6 +15319,7 @@ function createRoot(canvas) {
14825
15319
  rootId: newRootId,
14826
15320
  unregisterRoot: () => {
14827
15321
  unregisterRoot();
15322
+ unregisterCanvasTarget();
14828
15323
  unregisterFrustum();
14829
15324
  unregisterVisibility();
14830
15325
  unregisterRender();
@@ -14883,15 +15378,24 @@ function unmountComponentAtNode(canvas, callback) {
14883
15378
  const renderer = state.internal.actualRenderer;
14884
15379
  const unregisterRoot = state.internal.unregisterRoot;
14885
15380
  if (unregisterRoot) unregisterRoot();
15381
+ const unregisterPrimary = state.internal.unregisterPrimary;
15382
+ if (unregisterPrimary) unregisterPrimary();
15383
+ const canvasTarget = state.internal.canvasTarget;
15384
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14886
15385
  state.events.disconnect?.();
14887
15386
  cleanupHelperGroup(root.store);
14888
- renderer?.renderLists?.dispose?.();
14889
- renderer?.forceContextLoss?.();
14890
- if (renderer?.xr) state.xr.disconnect();
15387
+ if (state.isLegacy && renderer) {
15388
+ ;
15389
+ renderer.renderLists?.dispose?.();
15390
+ renderer.forceContextLoss?.();
15391
+ }
15392
+ if (!state.internal.isSecondary) {
15393
+ if (renderer?.xr) state.xr.disconnect();
15394
+ }
14891
15395
  dispose(state.scene);
14892
15396
  _roots.delete(canvas);
14893
15397
  if (callback) callback(canvas);
14894
- } catch (e) {
15398
+ } catch {
14895
15399
  }
14896
15400
  }, 500);
14897
15401
  }
@@ -14899,36 +15403,34 @@ function unmountComponentAtNode(canvas, callback) {
14899
15403
  }
14900
15404
  }
14901
15405
  function createPortal(children, container, state) {
14902
- return /* @__PURE__ */ jsx(PortalWrapper, { children, container, state });
15406
+ return /* @__PURE__ */ jsx(Portal, { children, container, state });
14903
15407
  }
14904
- function PortalWrapper({ children, container, state }) {
15408
+ function Portal({ children, container, state }) {
14905
15409
  const isRef = useCallback((obj) => obj && "current" in obj, []);
14906
- const [resolvedContainer, setResolvedContainer] = useState(() => {
15410
+ const [resolvedContainer, _setResolvedContainer] = useState(() => {
14907
15411
  if (isRef(container)) return container.current ?? null;
14908
15412
  return container;
14909
15413
  });
15414
+ const setResolvedContainer = useCallback(
15415
+ (newContainer) => {
15416
+ if (!newContainer || newContainer === resolvedContainer) return;
15417
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15418
+ },
15419
+ [resolvedContainer, _setResolvedContainer, isRef]
15420
+ );
14910
15421
  useMemo(() => {
14911
- if (isRef(container)) {
14912
- const current = container.current;
14913
- if (!current) {
14914
- queueMicrotask(() => {
14915
- const updated = container.current;
14916
- if (updated && updated !== resolvedContainer) {
14917
- setResolvedContainer(updated);
14918
- }
14919
- });
14920
- } else if (current !== resolvedContainer) {
14921
- setResolvedContainer(current);
14922
- }
14923
- } else if (container !== resolvedContainer) {
14924
- setResolvedContainer(container);
15422
+ if (isRef(container) && !container.current) {
15423
+ return queueMicrotask(() => {
15424
+ setResolvedContainer(container.current);
15425
+ });
14925
15426
  }
14926
- }, [container, resolvedContainer, isRef]);
15427
+ setResolvedContainer(container);
15428
+ }, [container, isRef, setResolvedContainer]);
14927
15429
  if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
14928
15430
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14929
- return /* @__PURE__ */ jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15431
+ return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14930
15432
  }
14931
- function Portal({ state = {}, children, container }) {
15433
+ function PortalInner({ state = {}, children, container }) {
14932
15434
  const { events, size, injectScene = true, ...rest } = state;
14933
15435
  const previousRoot = useStore();
14934
15436
  const [raycaster] = useState(() => new Raycaster());
@@ -14949,11 +15451,12 @@ function Portal({ state = {}, children, container }) {
14949
15451
  };
14950
15452
  }, [portalScene, container, injectScene]);
14951
15453
  const inject = useMutableCallback((rootState, injectState) => {
15454
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14952
15455
  let viewport = void 0;
14953
- if (injectState.camera && size) {
15456
+ if (injectState.camera && (size || injectState.size)) {
14954
15457
  const camera = injectState.camera;
14955
- viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), size);
14956
- if (camera !== rootState.camera) updateCamera(camera, size);
15458
+ viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
15459
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14957
15460
  }
14958
15461
  return {
14959
15462
  // The intersect consists of the previous root state
@@ -14970,7 +15473,7 @@ function Portal({ state = {}, children, container }) {
14970
15473
  previousRoot,
14971
15474
  // Events, size and viewport can be overridden by the inject layer
14972
15475
  events: { ...rootState.events, ...injectState.events, ...events },
14973
- size: { ...rootState.size, ...size },
15476
+ size: resolvedSize,
14974
15477
  viewport: { ...rootState.viewport, ...viewport },
14975
15478
  // Layers are allowed to override events
14976
15479
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -15004,15 +15507,13 @@ function CanvasImpl({
15004
15507
  fallback,
15005
15508
  resize,
15006
15509
  style,
15510
+ id,
15007
15511
  gl,
15008
- renderer,
15512
+ renderer: rendererProp,
15009
15513
  events = createPointerEvents,
15010
15514
  eventSource,
15011
15515
  eventPrefix,
15012
15516
  shadows,
15013
- linear,
15014
- flat,
15015
- legacy,
15016
15517
  orthographic,
15017
15518
  frameloop,
15018
15519
  dpr,
@@ -15027,10 +15528,43 @@ function CanvasImpl({
15027
15528
  hmr,
15028
15529
  width,
15029
15530
  height,
15531
+ background,
15532
+ forceEven,
15030
15533
  ...props
15031
15534
  }) {
15535
+ const { primaryCanvas, scheduler, ...rendererConfig } = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp) ? rendererProp : { primaryCanvas: void 0, scheduler: void 0 };
15536
+ const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15032
15537
  React.useMemo(() => extend(THREE), []);
15033
15538
  const Bridge = useBridge();
15539
+ const backgroundProps = React.useMemo(() => {
15540
+ if (!background) return null;
15541
+ if (typeof background === "object" && !background.isColor) {
15542
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15543
+ return {
15544
+ ...rest,
15545
+ preset,
15546
+ files: envMap || files,
15547
+ backgroundFiles: backgroundMap,
15548
+ background: true
15549
+ };
15550
+ }
15551
+ if (typeof background === "number") {
15552
+ return { color: background, background: true };
15553
+ }
15554
+ if (typeof background === "string") {
15555
+ if (background in presetsObj) {
15556
+ return { preset: background, background: true };
15557
+ }
15558
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15559
+ return { files: background, background: true };
15560
+ }
15561
+ return { color: background, background: true };
15562
+ }
15563
+ if (background.isColor) {
15564
+ return { color: background, background: true };
15565
+ }
15566
+ return null;
15567
+ }, [background]);
15034
15568
  const hasInitialSizeRef = React.useRef(false);
15035
15569
  const measureConfig = React.useMemo(() => {
15036
15570
  if (!hasInitialSizeRef.current) {
@@ -15047,15 +15581,20 @@ function CanvasImpl({
15047
15581
  };
15048
15582
  }, [resize, hasInitialSizeRef.current]);
15049
15583
  const [containerRef, containerRect] = useMeasure(measureConfig);
15050
- const effectiveSize = React.useMemo(
15051
- () => ({
15052
- width: width ?? containerRect.width,
15053
- height: height ?? containerRect.height,
15584
+ const effectiveSize = React.useMemo(() => {
15585
+ let w = width ?? containerRect.width;
15586
+ let h = height ?? containerRect.height;
15587
+ if (forceEven) {
15588
+ w = Math.ceil(w / 2) * 2;
15589
+ h = Math.ceil(h / 2) * 2;
15590
+ }
15591
+ return {
15592
+ width: w,
15593
+ height: h,
15054
15594
  top: containerRect.top,
15055
15595
  left: containerRect.left
15056
- }),
15057
- [width, height, containerRect]
15058
- );
15596
+ };
15597
+ }, [width, height, containerRect, forceEven]);
15059
15598
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15060
15599
  hasInitialSizeRef.current = true;
15061
15600
  }
@@ -15095,14 +15634,14 @@ function CanvasImpl({
15095
15634
  async function run() {
15096
15635
  if (!effectActiveRef.current || !root.current) return;
15097
15636
  await root.current.configure({
15637
+ id,
15638
+ primaryCanvas,
15639
+ scheduler,
15098
15640
  gl,
15099
15641
  renderer,
15100
15642
  scene,
15101
15643
  events,
15102
15644
  shadows,
15103
- linear,
15104
- flat,
15105
- legacy,
15106
15645
  orthographic,
15107
15646
  frameloop,
15108
15647
  dpr,
@@ -15112,6 +15651,7 @@ function CanvasImpl({
15112
15651
  size: effectiveSize,
15113
15652
  // Store size props for reset functionality
15114
15653
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15654
+ forceEven,
15115
15655
  // Pass mutable reference to onPointerMissed so it's free to update
15116
15656
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15117
15657
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15135,7 +15675,10 @@ function CanvasImpl({
15135
15675
  });
15136
15676
  if (!effectActiveRef.current || !root.current) return;
15137
15677
  root.current.render(
15138
- /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsx(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15678
+ /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
15679
+ backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
15680
+ children ?? null
15681
+ ] }) }) })
15139
15682
  );
15140
15683
  }
15141
15684
  run();
@@ -15162,14 +15705,16 @@ function CanvasImpl({
15162
15705
  const canvas = canvasRef.current;
15163
15706
  if (!canvas) return;
15164
15707
  const handleHMR = () => {
15165
- const rootEntry = _roots.get(canvas);
15166
- if (rootEntry?.store) {
15167
- rootEntry.store.setState((state) => ({
15168
- nodes: {},
15169
- uniforms: {},
15170
- _hmrVersion: state._hmrVersion + 1
15171
- }));
15172
- }
15708
+ queueMicrotask(() => {
15709
+ const rootEntry = _roots.get(canvas);
15710
+ if (rootEntry?.store) {
15711
+ rootEntry.store.setState((state) => ({
15712
+ nodes: {},
15713
+ uniforms: {},
15714
+ _hmrVersion: state._hmrVersion + 1
15715
+ }));
15716
+ }
15717
+ });
15173
15718
  };
15174
15719
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15175
15720
  const hot = import.meta.hot;
@@ -15198,7 +15743,7 @@ function CanvasImpl({
15198
15743
  ...style
15199
15744
  },
15200
15745
  ...props,
15201
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15746
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15202
15747
  }
15203
15748
  );
15204
15749
  }
@@ -15208,4 +15753,4 @@ function Canvas(props) {
15208
15753
 
15209
15754
  extend(THREE);
15210
15755
 
15211
- export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree };
15756
+ export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, reconciler, registerPrimary, removeInteractivity, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useEnvironment, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree, waitForPrimary };