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

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.cjs CHANGED
@@ -7,6 +7,12 @@ const jsxRuntime = require('react/jsx-runtime');
7
7
  const React = require('react');
8
8
  const useMeasure = require('react-use-measure');
9
9
  const itsFine = require('its-fine');
10
+ const fiber = require('@react-three/fiber');
11
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
12
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
13
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
14
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
15
+ const gainmapJs = require('@monogrid/gainmap-js');
10
16
  const Tb = require('scheduler');
11
17
  const traditional = require('zustand/traditional');
12
18
  const suspendReact = require('suspend-react');
@@ -57,6 +63,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
57
63
  WebGLRenderer: three.WebGLRenderer
58
64
  }, [webgpu__namespace]);
59
65
 
66
+ const primaryRegistry = /* @__PURE__ */ new Map();
67
+ const pendingSubscribers = /* @__PURE__ */ new Map();
68
+ function registerPrimary(id, renderer, store) {
69
+ if (primaryRegistry.has(id)) {
70
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
71
+ }
72
+ const entry = { renderer, store };
73
+ primaryRegistry.set(id, entry);
74
+ const subscribers = pendingSubscribers.get(id);
75
+ if (subscribers) {
76
+ subscribers.forEach((callback) => callback(entry));
77
+ pendingSubscribers.delete(id);
78
+ }
79
+ return () => {
80
+ const currentEntry = primaryRegistry.get(id);
81
+ if (currentEntry?.renderer === renderer) {
82
+ primaryRegistry.delete(id);
83
+ }
84
+ };
85
+ }
86
+ function getPrimary(id) {
87
+ return primaryRegistry.get(id);
88
+ }
89
+ function waitForPrimary(id, timeout = 5e3) {
90
+ const existing = primaryRegistry.get(id);
91
+ if (existing) {
92
+ return Promise.resolve(existing);
93
+ }
94
+ return new Promise((resolve, reject) => {
95
+ const timeoutId = setTimeout(() => {
96
+ const subscribers = pendingSubscribers.get(id);
97
+ if (subscribers) {
98
+ const index = subscribers.indexOf(callback);
99
+ if (index !== -1) subscribers.splice(index, 1);
100
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
101
+ }
102
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
103
+ }, timeout);
104
+ const callback = (entry) => {
105
+ clearTimeout(timeoutId);
106
+ resolve(entry);
107
+ };
108
+ if (!pendingSubscribers.has(id)) {
109
+ pendingSubscribers.set(id, []);
110
+ }
111
+ pendingSubscribers.get(id).push(callback);
112
+ });
113
+ }
114
+ function hasPrimary(id) {
115
+ return primaryRegistry.has(id);
116
+ }
117
+ function unregisterPrimary(id) {
118
+ primaryRegistry.delete(id);
119
+ }
120
+ function getPrimaryIds() {
121
+ return Array.from(primaryRegistry.keys());
122
+ }
123
+
124
+ const presetsObj = {
125
+ apartment: "lebombo_1k.hdr",
126
+ city: "potsdamer_platz_1k.hdr",
127
+ dawn: "kiara_1_dawn_1k.hdr",
128
+ forest: "forest_slope_1k.hdr",
129
+ lobby: "st_fagans_interior_1k.hdr",
130
+ night: "dikhololo_night_1k.hdr",
131
+ park: "rooitou_park_1k.hdr",
132
+ studio: "studio_small_03_1k.hdr",
133
+ sunset: "venice_sunset_1k.hdr",
134
+ warehouse: "empty_warehouse_01_1k.hdr"
135
+ };
136
+
137
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
138
+ const isArray = (arr) => Array.isArray(arr);
139
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
140
+ function useEnvironment({
141
+ files = defaultFiles,
142
+ path = "",
143
+ preset = void 0,
144
+ colorSpace = void 0,
145
+ extensions
146
+ } = {}) {
147
+ if (preset) {
148
+ validatePreset(preset);
149
+ files = presetsObj[preset];
150
+ path = CUBEMAP_ROOT;
151
+ }
152
+ const multiFile = isArray(files);
153
+ const { extension, isCubemap } = getExtension(files);
154
+ const loader = getLoader$1(extension);
155
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
156
+ const renderer = fiber.useThree((state) => state.renderer);
157
+ React.useLayoutEffect(() => {
158
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
159
+ function clearGainmapTexture() {
160
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
161
+ }
162
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
163
+ }, [files, renderer.domElement]);
164
+ const loaderResult = fiber.useLoader(
165
+ loader,
166
+ multiFile ? [files] : files,
167
+ (loader2) => {
168
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
169
+ loader2.setRenderer?.(renderer);
170
+ }
171
+ loader2.setPath?.(path);
172
+ if (extensions) extensions(loader2);
173
+ }
174
+ );
175
+ let texture = multiFile ? (
176
+ // @ts-ignore
177
+ loaderResult[0]
178
+ ) : loaderResult;
179
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
180
+ texture = texture.renderTarget?.texture;
181
+ }
182
+ texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
183
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
184
+ return texture;
185
+ }
186
+ const preloadDefaultOptions = {
187
+ files: defaultFiles,
188
+ path: "",
189
+ preset: void 0,
190
+ extensions: void 0
191
+ };
192
+ useEnvironment.preload = (preloadOptions) => {
193
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
194
+ let { files, path = "" } = options;
195
+ const { preset, extensions } = options;
196
+ if (preset) {
197
+ validatePreset(preset);
198
+ files = presetsObj[preset];
199
+ path = CUBEMAP_ROOT;
200
+ }
201
+ const { extension } = getExtension(files);
202
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
203
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
204
+ }
205
+ const loader = getLoader$1(extension);
206
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
207
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
208
+ loader2.setPath?.(path);
209
+ if (extensions) extensions(loader2);
210
+ });
211
+ };
212
+ const clearDefaultOptins = {
213
+ files: defaultFiles,
214
+ preset: void 0
215
+ };
216
+ useEnvironment.clear = (clearOptions) => {
217
+ const options = { ...clearDefaultOptins, ...clearOptions };
218
+ let { files } = options;
219
+ const { preset } = options;
220
+ if (preset) {
221
+ validatePreset(preset);
222
+ files = presetsObj[preset];
223
+ }
224
+ const { extension } = getExtension(files);
225
+ const loader = getLoader$1(extension);
226
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
227
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
228
+ };
229
+ function validatePreset(preset) {
230
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
231
+ }
232
+ function getExtension(files) {
233
+ const isCubemap = isArray(files) && files.length === 6;
234
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
235
+ const firstEntry = isArray(files) ? files[0] : files;
236
+ 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();
237
+ return { extension, isCubemap, isGainmap };
238
+ }
239
+ function getLoader$1(extension) {
240
+ const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
241
+ return loader;
242
+ }
243
+
244
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
245
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
246
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
247
+ sceneProps = {
248
+ backgroundBlurriness: 0,
249
+ backgroundIntensity: 1,
250
+ backgroundRotation: [0, 0, 0],
251
+ environmentIntensity: 1,
252
+ environmentRotation: [0, 0, 0],
253
+ ...sceneProps
254
+ };
255
+ const target = resolveScene(scene || defaultScene);
256
+ const oldbg = target.background;
257
+ const oldenv = target.environment;
258
+ const oldSceneProps = {
259
+ // @ts-ignore
260
+ backgroundBlurriness: target.backgroundBlurriness,
261
+ // @ts-ignore
262
+ backgroundIntensity: target.backgroundIntensity,
263
+ // @ts-ignore
264
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
265
+ // @ts-ignore
266
+ environmentIntensity: target.environmentIntensity,
267
+ // @ts-ignore
268
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
269
+ };
270
+ if (background !== "only") target.environment = texture;
271
+ if (background) target.background = texture;
272
+ fiber.applyProps(target, sceneProps);
273
+ return () => {
274
+ if (background !== "only") target.environment = oldenv;
275
+ if (background) target.background = oldbg;
276
+ fiber.applyProps(target, oldSceneProps);
277
+ };
278
+ }
279
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
280
+ const defaultScene = fiber.useThree((state) => state.scene);
281
+ React__namespace.useLayoutEffect(() => {
282
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
283
+ });
284
+ return null;
285
+ }
286
+ function EnvironmentCube({
287
+ background = false,
288
+ scene,
289
+ blur,
290
+ backgroundBlurriness,
291
+ backgroundIntensity,
292
+ backgroundRotation,
293
+ environmentIntensity,
294
+ environmentRotation,
295
+ ...rest
296
+ }) {
297
+ const texture = useEnvironment(rest);
298
+ const defaultScene = fiber.useThree((state) => state.scene);
299
+ React__namespace.useLayoutEffect(() => {
300
+ return setEnvProps(background, scene, defaultScene, texture, {
301
+ backgroundBlurriness: blur ?? backgroundBlurriness,
302
+ backgroundIntensity,
303
+ backgroundRotation,
304
+ environmentIntensity,
305
+ environmentRotation
306
+ });
307
+ });
308
+ React__namespace.useEffect(() => {
309
+ return () => {
310
+ texture.dispose();
311
+ };
312
+ }, [texture]);
313
+ return null;
314
+ }
315
+ function EnvironmentPortal({
316
+ children,
317
+ near = 0.1,
318
+ far = 1e3,
319
+ resolution = 256,
320
+ frames = 1,
321
+ map,
322
+ background = false,
323
+ blur,
324
+ backgroundBlurriness,
325
+ backgroundIntensity,
326
+ backgroundRotation,
327
+ environmentIntensity,
328
+ environmentRotation,
329
+ scene,
330
+ files,
331
+ path,
332
+ preset = void 0,
333
+ extensions
334
+ }) {
335
+ const gl = fiber.useThree((state) => state.gl);
336
+ const defaultScene = fiber.useThree((state) => state.scene);
337
+ const camera = React__namespace.useRef(null);
338
+ const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
339
+ const fbo = React__namespace.useMemo(() => {
340
+ const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
341
+ fbo2.texture.type = webgpu.HalfFloatType;
342
+ return fbo2;
343
+ }, [resolution]);
344
+ React__namespace.useEffect(() => {
345
+ return () => {
346
+ fbo.dispose();
347
+ };
348
+ }, [fbo]);
349
+ React__namespace.useLayoutEffect(() => {
350
+ if (frames === 1) {
351
+ const autoClear = gl.autoClear;
352
+ gl.autoClear = true;
353
+ camera.current.update(gl, virtualScene);
354
+ gl.autoClear = autoClear;
355
+ }
356
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
357
+ backgroundBlurriness: blur ?? backgroundBlurriness,
358
+ backgroundIntensity,
359
+ backgroundRotation,
360
+ environmentIntensity,
361
+ environmentRotation
362
+ });
363
+ }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
364
+ let count = 1;
365
+ fiber.useFrame(() => {
366
+ if (frames === Infinity || count < frames) {
367
+ const autoClear = gl.autoClear;
368
+ gl.autoClear = true;
369
+ camera.current.update(gl, virtualScene);
370
+ gl.autoClear = autoClear;
371
+ count++;
372
+ }
373
+ });
374
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
375
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
376
+ children,
377
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
378
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
379
+ ] }),
380
+ virtualScene
381
+ ) });
382
+ }
383
+ function EnvironmentGround(props) {
384
+ const textureDefault = useEnvironment(props);
385
+ const texture = props.map || textureDefault;
386
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
387
+ React__namespace.useEffect(() => {
388
+ return () => {
389
+ textureDefault.dispose();
390
+ };
391
+ }, [textureDefault]);
392
+ const height = props.ground?.height ?? 15;
393
+ const radius = props.ground?.radius ?? 60;
394
+ const scale = props.ground?.scale ?? 1e3;
395
+ const args = React__namespace.useMemo(
396
+ () => [texture, height, radius],
397
+ [texture, height, radius]
398
+ );
399
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
400
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
401
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
402
+ ] });
403
+ }
404
+ function EnvironmentColor({ color, scene }) {
405
+ const defaultScene = fiber.useThree((state) => state.scene);
406
+ React__namespace.useLayoutEffect(() => {
407
+ if (color === void 0) return;
408
+ const target = resolveScene(scene || defaultScene);
409
+ const oldBg = target.background;
410
+ target.background = new webgpu.Color(color);
411
+ return () => {
412
+ target.background = oldBg;
413
+ };
414
+ });
415
+ return null;
416
+ }
417
+ function EnvironmentDualSource(props) {
418
+ const { backgroundFiles, ...envProps } = props;
419
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
420
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
421
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
422
+ ] });
423
+ }
424
+ function Environment(props) {
425
+ if (props.color && !props.files && !props.preset && !props.map) {
426
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
427
+ }
428
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
429
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
430
+ }
431
+ return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
432
+ }
433
+
60
434
  var __defProp$2 = Object.defineProperty;
61
435
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
62
436
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -236,7 +610,8 @@ function prepare(target, root, type, props) {
236
610
  object,
237
611
  eventCount: 0,
238
612
  handlers: {},
239
- isHidden: false
613
+ isHidden: false,
614
+ deferredRefs: []
240
615
  };
241
616
  if (object) object.__r3f = instance;
242
617
  }
@@ -285,7 +660,7 @@ function createOcclusionObserverNode(store, uniform) {
285
660
  let occlusionSetupPromise = null;
286
661
  function enableOcclusion(store) {
287
662
  const state = store.getState();
288
- const { internal, renderer, rootScene } = state;
663
+ const { internal, renderer } = state;
289
664
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
290
665
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
291
666
  if (!hasOcclusionSupport) {
@@ -448,6 +823,22 @@ function hasVisibilityHandlers(handlers) {
448
823
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
449
824
  }
450
825
 
826
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
827
+ function fromRef(ref) {
828
+ return { [FROM_REF]: ref };
829
+ }
830
+ function isFromRef(value) {
831
+ return value !== null && typeof value === "object" && FROM_REF in value;
832
+ }
833
+
834
+ const ONCE = Symbol.for("@react-three/fiber.once");
835
+ function once(...args) {
836
+ return { [ONCE]: args.length ? args : true };
837
+ }
838
+ function isOnce(value) {
839
+ return value !== null && typeof value === "object" && ONCE in value;
840
+ }
841
+
451
842
  const RESERVED_PROPS = [
452
843
  "children",
453
844
  "key",
@@ -518,7 +909,7 @@ function getMemoizedPrototype(root) {
518
909
  ctor = new root.constructor();
519
910
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
520
911
  }
521
- } catch (e) {
912
+ } catch {
522
913
  }
523
914
  return ctor;
524
915
  }
@@ -564,6 +955,25 @@ function applyProps(object, props) {
564
955
  continue;
565
956
  }
566
957
  if (value === void 0) continue;
958
+ if (isFromRef(value)) {
959
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
960
+ continue;
961
+ }
962
+ if (isOnce(value)) {
963
+ if (instance?.appliedOnce?.has(prop)) continue;
964
+ if (instance) {
965
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
966
+ instance.appliedOnce.add(prop);
967
+ }
968
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
969
+ const args = value[ONCE];
970
+ if (typeof targetRoot[targetKey] === "function") {
971
+ targetRoot[targetKey](...args === true ? [] : args);
972
+ } else if (args !== true && args.length > 0) {
973
+ targetRoot[targetKey] = args[0];
974
+ }
975
+ continue;
976
+ }
567
977
  let { root, key, target } = resolve(object, prop);
568
978
  if (target === void 0 && (typeof root !== "object" || root === null)) {
569
979
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -586,7 +996,7 @@ function applyProps(object, props) {
586
996
  else target.set(value);
587
997
  } else {
588
998
  root[key] = value;
589
- 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
999
+ if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
590
1000
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
591
1001
  root[key].colorSpace = rootState.textureColorSpace;
592
1002
  }
@@ -943,7 +1353,7 @@ function createPointerEvents(store) {
943
1353
  return {
944
1354
  priority: 1,
945
1355
  enabled: true,
946
- compute(event, state, previous) {
1356
+ compute(event, state) {
947
1357
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
948
1358
  state.raycaster.setFromCamera(state.pointer, state.camera);
949
1359
  },
@@ -1041,331 +1451,26 @@ function notifyAlpha({ message, link }) {
1041
1451
  }
1042
1452
  }
1043
1453
 
1044
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1045
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1046
- const createStore = (invalidate, advance) => {
1047
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1048
- const position = new webgpu.Vector3();
1049
- const defaultTarget = new webgpu.Vector3();
1050
- const tempTarget = new webgpu.Vector3();
1051
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1052
- const { width, height, top, left } = size;
1053
- const aspect = width / height;
1054
- if (target.isVector3) tempTarget.copy(target);
1055
- else tempTarget.set(...target);
1056
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1057
- if (isOrthographicCamera(camera)) {
1058
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1059
- } else {
1060
- const fov = camera.fov * Math.PI / 180;
1061
- const h = 2 * Math.tan(fov / 2) * distance;
1062
- const w = h * (width / height);
1063
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1064
- }
1065
- }
1066
- let performanceTimeout = void 0;
1067
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1068
- const pointer = new webgpu.Vector2();
1069
- const rootState = {
1070
- set,
1071
- get,
1072
- // Mock objects that have to be configured
1073
- gl: null,
1074
- renderer: null,
1075
- camera: null,
1076
- frustum: new webgpu.Frustum(),
1077
- autoUpdateFrustum: true,
1078
- raycaster: null,
1079
- events: { priority: 1, enabled: true, connected: false },
1080
- scene: null,
1081
- rootScene: null,
1082
- xr: null,
1083
- inspector: null,
1084
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1085
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1086
- legacy: false,
1087
- linear: false,
1088
- flat: false,
1089
- textureColorSpace: "srgb",
1090
- isLegacy: false,
1091
- webGPUSupported: false,
1092
- isNative: false,
1093
- controls: null,
1094
- pointer,
1095
- mouse: pointer,
1096
- frameloop: "always",
1097
- onPointerMissed: void 0,
1098
- onDragOverMissed: void 0,
1099
- onDropMissed: void 0,
1100
- performance: {
1101
- current: 1,
1102
- min: 0.5,
1103
- max: 1,
1104
- debounce: 200,
1105
- regress: () => {
1106
- const state2 = get();
1107
- if (performanceTimeout) clearTimeout(performanceTimeout);
1108
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1109
- performanceTimeout = setTimeout(
1110
- () => setPerformanceCurrent(get().performance.max),
1111
- state2.performance.debounce
1112
- );
1113
- }
1114
- },
1115
- size: { width: 0, height: 0, top: 0, left: 0 },
1116
- viewport: {
1117
- initialDpr: 0,
1118
- dpr: 0,
1119
- width: 0,
1120
- height: 0,
1121
- top: 0,
1122
- left: 0,
1123
- aspect: 0,
1124
- distance: 0,
1125
- factor: 0,
1126
- getCurrentViewport
1127
- },
1128
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1129
- setSize: (width, height, top, left) => {
1130
- const state2 = get();
1131
- if (width === void 0) {
1132
- set({ _sizeImperative: false });
1133
- if (state2._sizeProps) {
1134
- const { width: propW, height: propH } = state2._sizeProps;
1135
- if (propW !== void 0 || propH !== void 0) {
1136
- const currentSize = state2.size;
1137
- const newSize = {
1138
- width: propW ?? currentSize.width,
1139
- height: propH ?? currentSize.height,
1140
- top: currentSize.top,
1141
- left: currentSize.left
1142
- };
1143
- set((s) => ({
1144
- size: newSize,
1145
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1146
- }));
1147
- }
1148
- }
1149
- return;
1150
- }
1151
- const w = width;
1152
- const h = height ?? width;
1153
- const t = top ?? state2.size.top;
1154
- const l = left ?? state2.size.left;
1155
- const size = { width: w, height: h, top: t, left: l };
1156
- set((s) => ({
1157
- size,
1158
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1159
- _sizeImperative: true
1160
- }));
1161
- },
1162
- setDpr: (dpr) => set((state2) => {
1163
- const resolved = calculateDpr(dpr);
1164
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1165
- }),
1166
- setFrameloop: (frameloop = "always") => {
1167
- set(() => ({ frameloop }));
1168
- },
1169
- setError: (error) => set(() => ({ error })),
1170
- error: null,
1171
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1172
- uniforms: {},
1173
- nodes: {},
1174
- textures: /* @__PURE__ */ new Map(),
1175
- postProcessing: null,
1176
- passes: {},
1177
- _hmrVersion: 0,
1178
- _sizeImperative: false,
1179
- _sizeProps: null,
1180
- previousRoot: void 0,
1181
- internal: {
1182
- // Events
1183
- interaction: [],
1184
- hovered: /* @__PURE__ */ new Map(),
1185
- subscribers: [],
1186
- initialClick: [0, 0],
1187
- initialHits: [],
1188
- capturedMap: /* @__PURE__ */ new Map(),
1189
- lastEvent: React__namespace.createRef(),
1190
- // Visibility tracking (onFramed, onOccluded, onVisible)
1191
- visibilityRegistry: /* @__PURE__ */ new Map(),
1192
- // Occlusion system (WebGPU only)
1193
- occlusionEnabled: false,
1194
- occlusionObserver: null,
1195
- occlusionCache: /* @__PURE__ */ new Map(),
1196
- helperGroup: null,
1197
- // Updates
1198
- active: false,
1199
- frames: 0,
1200
- priority: 0,
1201
- subscribe: (ref, priority, store) => {
1202
- const internal = get().internal;
1203
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1204
- internal.subscribers.push({ ref, priority, store });
1205
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1206
- return () => {
1207
- const internal2 = get().internal;
1208
- if (internal2?.subscribers) {
1209
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1210
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1211
- }
1212
- };
1213
- },
1214
- // Renderer Storage (single source of truth)
1215
- actualRenderer: null,
1216
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1217
- scheduler: null
1218
- }
1219
- };
1220
- return rootState;
1221
- });
1222
- const state = rootStore.getState();
1223
- Object.defineProperty(state, "gl", {
1224
- get() {
1225
- const currentState = rootStore.getState();
1226
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1227
- const stack = new Error().stack || "";
1228
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1229
- if (!isInternalAccess) {
1230
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1231
- notifyDepreciated({
1232
- heading: "Accessing state.gl in WebGPU mode",
1233
- 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
1234
- });
1235
- }
1236
- }
1237
- return currentState.internal.actualRenderer;
1238
- },
1239
- set(value) {
1240
- rootStore.getState().internal.actualRenderer = value;
1241
- },
1242
- enumerable: true,
1243
- configurable: true
1244
- });
1245
- Object.defineProperty(state, "renderer", {
1246
- get() {
1247
- return rootStore.getState().internal.actualRenderer;
1248
- },
1249
- set(value) {
1250
- rootStore.getState().internal.actualRenderer = value;
1251
- },
1252
- enumerable: true,
1253
- configurable: true
1254
- });
1255
- let oldScene = state.scene;
1256
- rootStore.subscribe(() => {
1257
- const currentState = rootStore.getState();
1258
- const { scene, rootScene, set } = currentState;
1259
- if (scene !== oldScene) {
1260
- oldScene = scene;
1261
- if (scene?.isScene && scene !== rootScene) {
1262
- set({ rootScene: scene });
1263
- }
1264
- }
1265
- });
1266
- let oldSize = state.size;
1267
- let oldDpr = state.viewport.dpr;
1268
- let oldCamera = state.camera;
1269
- rootStore.subscribe(() => {
1270
- const { camera, size, viewport, set, internal } = rootStore.getState();
1271
- const actualRenderer = internal.actualRenderer;
1272
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1273
- oldSize = size;
1274
- oldDpr = viewport.dpr;
1275
- updateCamera(camera, size);
1276
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1277
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1278
- actualRenderer.setSize(size.width, size.height, updateStyle);
1279
- }
1280
- if (camera !== oldCamera) {
1281
- oldCamera = camera;
1282
- const { rootScene } = rootStore.getState();
1283
- if (camera && rootScene && !camera.parent) {
1284
- rootScene.add(camera);
1285
- }
1286
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1287
- const currentState = rootStore.getState();
1288
- if (currentState.autoUpdateFrustum && camera) {
1289
- updateFrustum(camera, currentState.frustum);
1290
- }
1291
- }
1292
- });
1293
- rootStore.subscribe((state2) => invalidate(state2));
1294
- return rootStore;
1295
- };
1296
-
1297
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1298
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1299
- function getLoader(Proto) {
1300
- if (isConstructor$1(Proto)) {
1301
- let loader = memoizedLoaders.get(Proto);
1302
- if (!loader) {
1303
- loader = new Proto();
1304
- memoizedLoaders.set(Proto, loader);
1305
- }
1306
- return loader;
1307
- }
1308
- return Proto;
1309
- }
1310
- function loadingFn(extensions, onProgress) {
1311
- return function(Proto, input) {
1312
- const loader = getLoader(Proto);
1313
- if (extensions) extensions(loader);
1314
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1315
- return loader.loadAsync(input, onProgress).then((data) => {
1316
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1317
- return data;
1318
- });
1319
- }
1320
- return new Promise(
1321
- (res, reject) => loader.load(
1322
- input,
1323
- (data) => {
1324
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1325
- res(data);
1326
- },
1327
- onProgress,
1328
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1329
- )
1330
- );
1331
- };
1332
- }
1333
- function useLoader(loader, input, extensions, onProgress) {
1334
- const keys = Array.isArray(input) ? input : [input];
1335
- const fn = loadingFn(extensions, onProgress);
1336
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1337
- return Array.isArray(input) ? results : results[0];
1338
- }
1339
- useLoader.preload = function(loader, input, extensions, onProgress) {
1340
- const keys = Array.isArray(input) ? input : [input];
1341
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1342
- };
1343
- useLoader.clear = function(loader, input) {
1344
- const keys = Array.isArray(input) ? input : [input];
1345
- keys.forEach((key) => suspendReact.clear([loader, key]));
1346
- };
1347
- useLoader.loader = getLoader;
1348
-
1349
- var __defProp$1 = Object.defineProperty;
1350
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1351
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1352
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1353
- class PhaseGraph {
1354
- constructor() {
1355
- /** Ordered list of phase nodes */
1356
- __publicField$1(this, "phases", []);
1357
- /** Quick lookup by name */
1358
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1359
- /** Cached ordered names (invalidated on changes) */
1360
- __publicField$1(this, "orderedNamesCache", null);
1361
- this.initializeDefaultPhases();
1362
- }
1363
- //* Initialization --------------------------------
1364
- initializeDefaultPhases() {
1365
- for (const name of DEFAULT_PHASES) {
1366
- const node = { name, isAutoGenerated: false };
1367
- this.phases.push(node);
1368
- this.phaseMap.set(name, node);
1454
+ var __defProp$1 = Object.defineProperty;
1455
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1456
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1457
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1458
+ class PhaseGraph {
1459
+ constructor() {
1460
+ /** Ordered list of phase nodes */
1461
+ __publicField$1(this, "phases", []);
1462
+ /** Quick lookup by name */
1463
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1464
+ /** Cached ordered names (invalidated on changes) */
1465
+ __publicField$1(this, "orderedNamesCache", null);
1466
+ this.initializeDefaultPhases();
1467
+ }
1468
+ //* Initialization --------------------------------
1469
+ initializeDefaultPhases() {
1470
+ for (const name of DEFAULT_PHASES) {
1471
+ const node = { name, isAutoGenerated: false };
1472
+ this.phases.push(node);
1473
+ this.phaseMap.set(name, node);
1369
1474
  }
1370
1475
  this.invalidateCache();
1371
1476
  }
@@ -2251,132 +2356,445 @@ const _Scheduler = class _Scheduler {
2251
2356
  console.error(`[Scheduler] Error in global job "${job.id}":`, error);
2252
2357
  }
2253
2358
  }
2254
- }
2255
- /**
2256
- * Execute all jobs for a single root in sorted order.
2257
- * Rebuilds sorted job list if needed, then dispatches each job.
2258
- * Errors are caught and propagated via triggerError.
2259
- * @param {RootEntry} root - The root entry to tick
2260
- * @param {number} timestamp - RAF timestamp in milliseconds
2261
- * @param {number} delta - Time since last frame in seconds
2262
- * @returns {void}
2263
- * @private
2264
- */
2265
- tickRoot(root, timestamp, delta) {
2266
- if (root.needsRebuild) {
2267
- root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2268
- root.needsRebuild = false;
2269
- }
2270
- const providedState = root.getState?.() ?? {};
2271
- const frameState = {
2272
- ...providedState,
2273
- time: timestamp,
2274
- delta,
2275
- elapsed: this.loopState.elapsedTime / 1e3,
2276
- // Convert ms to seconds
2277
- frame: this.loopState.frameCount
2278
- };
2279
- for (const job of root.sortedJobs) {
2280
- if (!shouldRun(job, timestamp)) continue;
2281
- try {
2282
- job.callback(frameState, delta);
2283
- } catch (error) {
2284
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2285
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
2359
+ }
2360
+ /**
2361
+ * Execute all jobs for a single root in sorted order.
2362
+ * Rebuilds sorted job list if needed, then dispatches each job.
2363
+ * Errors are caught and propagated via triggerError.
2364
+ * @param {RootEntry} root - The root entry to tick
2365
+ * @param {number} timestamp - RAF timestamp in milliseconds
2366
+ * @param {number} delta - Time since last frame in seconds
2367
+ * @returns {void}
2368
+ * @private
2369
+ */
2370
+ tickRoot(root, timestamp, delta) {
2371
+ if (root.needsRebuild) {
2372
+ root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2373
+ root.needsRebuild = false;
2374
+ }
2375
+ const providedState = root.getState?.() ?? {};
2376
+ const frameState = {
2377
+ ...providedState,
2378
+ time: timestamp,
2379
+ delta,
2380
+ elapsed: this.loopState.elapsedTime / 1e3,
2381
+ // Convert ms to seconds
2382
+ frame: this.loopState.frameCount
2383
+ };
2384
+ for (const job of root.sortedJobs) {
2385
+ if (!shouldRun(job, timestamp)) continue;
2386
+ try {
2387
+ job.callback(frameState, delta);
2388
+ } catch (error) {
2389
+ console.error(`[Scheduler] Error in job "${job.id}":`, error);
2390
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
2391
+ }
2392
+ }
2393
+ }
2394
+ //* Debug & Inspection Methods ================================
2395
+ /**
2396
+ * Get the total number of registered jobs across all roots.
2397
+ * Includes both per-root jobs and global before/after jobs.
2398
+ * @returns {number} Total job count
2399
+ */
2400
+ getJobCount() {
2401
+ let count = 0;
2402
+ for (const root of this.roots.values()) {
2403
+ count += root.jobs.size;
2404
+ }
2405
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2406
+ }
2407
+ /**
2408
+ * Get all registered job IDs across all roots.
2409
+ * Includes both per-root jobs and global before/after jobs.
2410
+ * @returns {string[]} Array of all job IDs
2411
+ */
2412
+ getJobIds() {
2413
+ const ids = [];
2414
+ for (const root of this.roots.values()) {
2415
+ ids.push(...root.jobs.keys());
2416
+ }
2417
+ ids.push(...this.globalBeforeJobs.keys());
2418
+ ids.push(...this.globalAfterJobs.keys());
2419
+ return ids;
2420
+ }
2421
+ /**
2422
+ * Get the number of registered roots (Canvas instances).
2423
+ * @returns {number} Number of registered roots
2424
+ */
2425
+ getRootCount() {
2426
+ return this.roots.size;
2427
+ }
2428
+ /**
2429
+ * Check if any user (non-system) jobs are registered in a specific phase.
2430
+ * Used by the default render job to know if a user has taken over rendering.
2431
+ *
2432
+ * @param phase The phase to check
2433
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2434
+ * @returns true if any user jobs exist in the phase
2435
+ */
2436
+ hasUserJobsInPhase(phase, rootId) {
2437
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2438
+ return rootsToCheck.some((root) => {
2439
+ if (!root) return false;
2440
+ for (const job of root.jobs.values()) {
2441
+ if (job.phase === phase && !job.system && job.enabled) return true;
2442
+ }
2443
+ return false;
2444
+ });
2445
+ }
2446
+ //* Utility Methods ================================
2447
+ /**
2448
+ * Generate a unique root ID for automatic root registration.
2449
+ * @returns {string} A unique root ID in the format 'root_N'
2450
+ */
2451
+ generateRootId() {
2452
+ return `root_${this.nextRootIndex++}`;
2453
+ }
2454
+ /**
2455
+ * Generate a unique job ID.
2456
+ * @returns {string} A unique job ID in the format 'job_N'
2457
+ * @private
2458
+ */
2459
+ generateJobId() {
2460
+ return `job_${this.nextJobIndex}`;
2461
+ }
2462
+ /**
2463
+ * Normalize before/after constraints to a Set.
2464
+ * Handles undefined, single string, or array inputs.
2465
+ * @param {string | string[] | undefined} value - The constraint value(s)
2466
+ * @returns {Set<string>} Normalized Set of constraint strings
2467
+ * @private
2468
+ */
2469
+ normalizeConstraints(value) {
2470
+ if (!value) return /* @__PURE__ */ new Set();
2471
+ if (Array.isArray(value)) return new Set(value);
2472
+ return /* @__PURE__ */ new Set([value]);
2473
+ }
2474
+ };
2475
+ //* Static State & Methods (Singleton Usage) ================================
2476
+ //* Cross-Bundle Singleton Key ==============================
2477
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2478
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2479
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2480
+ let Scheduler = _Scheduler;
2481
+ const getScheduler = () => Scheduler.get();
2482
+ if (hmrData) {
2483
+ hmrData.accept?.();
2484
+ }
2485
+
2486
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2487
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2488
+ const createStore = (invalidate, advance) => {
2489
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2490
+ const position = new webgpu.Vector3();
2491
+ const defaultTarget = new webgpu.Vector3();
2492
+ const tempTarget = new webgpu.Vector3();
2493
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2494
+ const { width, height, top, left } = size;
2495
+ const aspect = width / height;
2496
+ if (target.isVector3) tempTarget.copy(target);
2497
+ else tempTarget.set(...target);
2498
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2499
+ if (isOrthographicCamera(camera)) {
2500
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2501
+ } else {
2502
+ const fov = camera.fov * Math.PI / 180;
2503
+ const h = 2 * Math.tan(fov / 2) * distance;
2504
+ const w = h * (width / height);
2505
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2506
+ }
2507
+ }
2508
+ let performanceTimeout = void 0;
2509
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2510
+ const pointer = new webgpu.Vector2();
2511
+ const rootState = {
2512
+ set,
2513
+ get,
2514
+ // Mock objects that have to be configured
2515
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2516
+ primaryStore: null,
2517
+ gl: null,
2518
+ renderer: null,
2519
+ camera: null,
2520
+ frustum: new webgpu.Frustum(),
2521
+ autoUpdateFrustum: true,
2522
+ raycaster: null,
2523
+ events: { priority: 1, enabled: true, connected: false },
2524
+ scene: null,
2525
+ rootScene: null,
2526
+ xr: null,
2527
+ inspector: null,
2528
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2529
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2530
+ textureColorSpace: webgpu.SRGBColorSpace,
2531
+ isLegacy: false,
2532
+ webGPUSupported: false,
2533
+ isNative: false,
2534
+ controls: null,
2535
+ pointer,
2536
+ mouse: pointer,
2537
+ frameloop: "always",
2538
+ onPointerMissed: void 0,
2539
+ onDragOverMissed: void 0,
2540
+ onDropMissed: void 0,
2541
+ performance: {
2542
+ current: 1,
2543
+ min: 0.5,
2544
+ max: 1,
2545
+ debounce: 200,
2546
+ regress: () => {
2547
+ const state2 = get();
2548
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2549
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2550
+ performanceTimeout = setTimeout(
2551
+ () => setPerformanceCurrent(get().performance.max),
2552
+ state2.performance.debounce
2553
+ );
2554
+ }
2555
+ },
2556
+ size: { width: 0, height: 0, top: 0, left: 0 },
2557
+ viewport: {
2558
+ initialDpr: 0,
2559
+ dpr: 0,
2560
+ width: 0,
2561
+ height: 0,
2562
+ top: 0,
2563
+ left: 0,
2564
+ aspect: 0,
2565
+ distance: 0,
2566
+ factor: 0,
2567
+ getCurrentViewport
2568
+ },
2569
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2570
+ setSize: (width, height, top, left) => {
2571
+ const state2 = get();
2572
+ if (width === void 0) {
2573
+ set({ _sizeImperative: false });
2574
+ if (state2._sizeProps) {
2575
+ const { width: propW, height: propH } = state2._sizeProps;
2576
+ if (propW !== void 0 || propH !== void 0) {
2577
+ const currentSize = state2.size;
2578
+ const newSize = {
2579
+ width: propW ?? currentSize.width,
2580
+ height: propH ?? currentSize.height,
2581
+ top: currentSize.top,
2582
+ left: currentSize.left
2583
+ };
2584
+ set((s) => ({
2585
+ size: newSize,
2586
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2587
+ }));
2588
+ getScheduler().invalidate();
2589
+ }
2590
+ }
2591
+ return;
2592
+ }
2593
+ const w = width;
2594
+ const h = height ?? width;
2595
+ const t = top ?? state2.size.top;
2596
+ const l = left ?? state2.size.left;
2597
+ const size = { width: w, height: h, top: t, left: l };
2598
+ set((s) => ({
2599
+ size,
2600
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2601
+ _sizeImperative: true
2602
+ }));
2603
+ getScheduler().invalidate();
2604
+ },
2605
+ setDpr: (dpr) => set((state2) => {
2606
+ const resolved = calculateDpr(dpr);
2607
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2608
+ }),
2609
+ setFrameloop: (frameloop = "always") => {
2610
+ set(() => ({ frameloop }));
2611
+ },
2612
+ setError: (error) => set(() => ({ error })),
2613
+ error: null,
2614
+ //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2615
+ uniforms: {},
2616
+ nodes: {},
2617
+ textures: /* @__PURE__ */ new Map(),
2618
+ postProcessing: null,
2619
+ passes: {},
2620
+ _hmrVersion: 0,
2621
+ _sizeImperative: false,
2622
+ _sizeProps: null,
2623
+ previousRoot: void 0,
2624
+ internal: {
2625
+ // Events
2626
+ interaction: [],
2627
+ hovered: /* @__PURE__ */ new Map(),
2628
+ subscribers: [],
2629
+ initialClick: [0, 0],
2630
+ initialHits: [],
2631
+ capturedMap: /* @__PURE__ */ new Map(),
2632
+ lastEvent: React__namespace.createRef(),
2633
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2634
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2635
+ // Occlusion system (WebGPU only)
2636
+ occlusionEnabled: false,
2637
+ occlusionObserver: null,
2638
+ occlusionCache: /* @__PURE__ */ new Map(),
2639
+ helperGroup: null,
2640
+ // Updates
2641
+ active: false,
2642
+ frames: 0,
2643
+ priority: 0,
2644
+ subscribe: (ref, priority, store) => {
2645
+ const internal = get().internal;
2646
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2647
+ internal.subscribers.push({ ref, priority, store });
2648
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2649
+ return () => {
2650
+ const internal2 = get().internal;
2651
+ if (internal2?.subscribers) {
2652
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2653
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2654
+ }
2655
+ };
2656
+ },
2657
+ // Renderer Storage (single source of truth)
2658
+ actualRenderer: null,
2659
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2660
+ scheduler: null
2661
+ }
2662
+ };
2663
+ return rootState;
2664
+ });
2665
+ const state = rootStore.getState();
2666
+ Object.defineProperty(state, "gl", {
2667
+ get() {
2668
+ const currentState = rootStore.getState();
2669
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2670
+ const stack = new Error().stack || "";
2671
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2672
+ if (!isInternalAccess) {
2673
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2674
+ notifyDepreciated({
2675
+ heading: "Accessing state.gl in WebGPU mode",
2676
+ 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
2677
+ });
2678
+ }
2679
+ }
2680
+ return currentState.internal.actualRenderer;
2681
+ },
2682
+ set(value) {
2683
+ rootStore.getState().internal.actualRenderer = value;
2684
+ },
2685
+ enumerable: true,
2686
+ configurable: true
2687
+ });
2688
+ Object.defineProperty(state, "renderer", {
2689
+ get() {
2690
+ return rootStore.getState().internal.actualRenderer;
2691
+ },
2692
+ set(value) {
2693
+ rootStore.getState().internal.actualRenderer = value;
2694
+ },
2695
+ enumerable: true,
2696
+ configurable: true
2697
+ });
2698
+ let oldScene = state.scene;
2699
+ rootStore.subscribe(() => {
2700
+ const currentState = rootStore.getState();
2701
+ const { scene, rootScene, set } = currentState;
2702
+ if (scene !== oldScene) {
2703
+ oldScene = scene;
2704
+ if (scene?.isScene && scene !== rootScene) {
2705
+ set({ rootScene: scene });
2706
+ }
2707
+ }
2708
+ });
2709
+ let oldSize = state.size;
2710
+ let oldDpr = state.viewport.dpr;
2711
+ let oldCamera = state.camera;
2712
+ rootStore.subscribe(() => {
2713
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2714
+ const actualRenderer = internal.actualRenderer;
2715
+ const canvasTarget = internal.canvasTarget;
2716
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2717
+ oldSize = size;
2718
+ oldDpr = viewport.dpr;
2719
+ updateCamera(camera, size);
2720
+ if (canvasTarget) {
2721
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2722
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2723
+ canvasTarget.setSize(size.width, size.height, updateStyle);
2724
+ } else {
2725
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2726
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2727
+ actualRenderer.setSize(size.width, size.height, updateStyle);
2728
+ }
2729
+ }
2730
+ if (camera !== oldCamera) {
2731
+ oldCamera = camera;
2732
+ const { rootScene } = rootStore.getState();
2733
+ if (camera && rootScene && !camera.parent) {
2734
+ rootScene.add(camera);
2735
+ }
2736
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2737
+ const currentState = rootStore.getState();
2738
+ if (currentState.autoUpdateFrustum && camera) {
2739
+ updateFrustum(camera, currentState.frustum);
2286
2740
  }
2287
2741
  }
2288
- }
2289
- //* Debug & Inspection Methods ================================
2290
- /**
2291
- * Get the total number of registered jobs across all roots.
2292
- * Includes both per-root jobs and global before/after jobs.
2293
- * @returns {number} Total job count
2294
- */
2295
- getJobCount() {
2296
- let count = 0;
2297
- for (const root of this.roots.values()) {
2298
- count += root.jobs.size;
2742
+ });
2743
+ rootStore.subscribe((state2) => invalidate(state2));
2744
+ return rootStore;
2745
+ };
2746
+
2747
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2748
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2749
+ function getLoader(Proto) {
2750
+ if (isConstructor$1(Proto)) {
2751
+ let loader = memoizedLoaders.get(Proto);
2752
+ if (!loader) {
2753
+ loader = new Proto();
2754
+ memoizedLoaders.set(Proto, loader);
2299
2755
  }
2300
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2756
+ return loader;
2301
2757
  }
2302
- /**
2303
- * Get all registered job IDs across all roots.
2304
- * Includes both per-root jobs and global before/after jobs.
2305
- * @returns {string[]} Array of all job IDs
2306
- */
2307
- getJobIds() {
2308
- const ids = [];
2309
- for (const root of this.roots.values()) {
2310
- ids.push(...root.jobs.keys());
2758
+ return Proto;
2759
+ }
2760
+ function loadingFn(extensions, onProgress) {
2761
+ return function(Proto, input) {
2762
+ const loader = getLoader(Proto);
2763
+ if (extensions) extensions(loader);
2764
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2765
+ return loader.loadAsync(input, onProgress).then((data) => {
2766
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2767
+ return data;
2768
+ });
2311
2769
  }
2312
- ids.push(...this.globalBeforeJobs.keys());
2313
- ids.push(...this.globalAfterJobs.keys());
2314
- return ids;
2315
- }
2316
- /**
2317
- * Get the number of registered roots (Canvas instances).
2318
- * @returns {number} Number of registered roots
2319
- */
2320
- getRootCount() {
2321
- return this.roots.size;
2322
- }
2323
- /**
2324
- * Check if any user (non-system) jobs are registered in a specific phase.
2325
- * Used by the default render job to know if a user has taken over rendering.
2326
- *
2327
- * @param phase The phase to check
2328
- * @param rootId Optional root ID to check (checks all roots if not provided)
2329
- * @returns true if any user jobs exist in the phase
2330
- */
2331
- hasUserJobsInPhase(phase, rootId) {
2332
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2333
- return rootsToCheck.some((root) => {
2334
- if (!root) return false;
2335
- for (const job of root.jobs.values()) {
2336
- if (job.phase === phase && !job.system && job.enabled) return true;
2337
- }
2338
- return false;
2339
- });
2340
- }
2341
- //* Utility Methods ================================
2342
- /**
2343
- * Generate a unique root ID for automatic root registration.
2344
- * @returns {string} A unique root ID in the format 'root_N'
2345
- */
2346
- generateRootId() {
2347
- return `root_${this.nextRootIndex++}`;
2348
- }
2349
- /**
2350
- * Generate a unique job ID.
2351
- * @returns {string} A unique job ID in the format 'job_N'
2352
- * @private
2353
- */
2354
- generateJobId() {
2355
- return `job_${this.nextJobIndex}`;
2356
- }
2357
- /**
2358
- * Normalize before/after constraints to a Set.
2359
- * Handles undefined, single string, or array inputs.
2360
- * @param {string | string[] | undefined} value - The constraint value(s)
2361
- * @returns {Set<string>} Normalized Set of constraint strings
2362
- * @private
2363
- */
2364
- normalizeConstraints(value) {
2365
- if (!value) return /* @__PURE__ */ new Set();
2366
- if (Array.isArray(value)) return new Set(value);
2367
- return /* @__PURE__ */ new Set([value]);
2368
- }
2369
- };
2370
- //* Static State & Methods (Singleton Usage) ================================
2371
- //* Cross-Bundle Singleton Key ==============================
2372
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2373
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2374
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2375
- let Scheduler = _Scheduler;
2376
- const getScheduler = () => Scheduler.get();
2377
- if (hmrData) {
2378
- hmrData.accept?.();
2770
+ return new Promise(
2771
+ (res, reject) => loader.load(
2772
+ input,
2773
+ (data) => {
2774
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2775
+ res(data);
2776
+ },
2777
+ onProgress,
2778
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2779
+ )
2780
+ );
2781
+ };
2782
+ }
2783
+ function useLoader(loader, input, extensions, onProgress) {
2784
+ const keys = Array.isArray(input) ? input : [input];
2785
+ const fn = loadingFn(extensions, onProgress);
2786
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2787
+ return Array.isArray(input) ? results : results[0];
2379
2788
  }
2789
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2790
+ const keys = Array.isArray(input) ? input : [input];
2791
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2792
+ };
2793
+ useLoader.clear = function(loader, input) {
2794
+ const keys = Array.isArray(input) ? input : [input];
2795
+ keys.forEach((key) => suspendReact.clear([loader, key]));
2796
+ };
2797
+ useLoader.loader = getLoader;
2380
2798
 
2381
2799
  function useFrame(callback, priorityOrOptions) {
2382
2800
  const store = React__namespace.useContext(context);
@@ -2557,6 +2975,9 @@ function useTexture(input, optionsOrOnLoad) {
2557
2975
  const textureCache = useThree((state) => state.textures);
2558
2976
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2559
2977
  const { onLoad, cache = false } = options;
2978
+ const onLoadRef = React.useRef(onLoad);
2979
+ onLoadRef.current = onLoad;
2980
+ const onLoadCalledForRef = React.useRef(null);
2560
2981
  const urls = React.useMemo(() => getUrls(input), [input]);
2561
2982
  const cachedResult = React.useMemo(() => {
2562
2983
  if (!cache) return null;
@@ -2567,9 +2988,13 @@ function useTexture(input, optionsOrOnLoad) {
2567
2988
  webgpu.TextureLoader,
2568
2989
  IsObject(input) ? Object.values(input) : input
2569
2990
  );
2991
+ const inputKey = urls.join("\0");
2570
2992
  React.useLayoutEffect(() => {
2571
- if (!cachedResult) onLoad?.(loadedTextures);
2572
- }, [onLoad, cachedResult, loadedTextures]);
2993
+ if (cachedResult) return;
2994
+ if (onLoadCalledForRef.current === inputKey) return;
2995
+ onLoadCalledForRef.current = inputKey;
2996
+ onLoadRef.current?.(loadedTextures);
2997
+ }, [cachedResult, loadedTextures, inputKey]);
2573
2998
  React.useEffect(() => {
2574
2999
  if (cachedResult) return;
2575
3000
  if ("initTexture" in renderer) {
@@ -2736,16 +3161,33 @@ function useTextures() {
2736
3161
  }, [store]);
2737
3162
  }
2738
3163
 
2739
- function useRenderTarget(width, height, options) {
3164
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2740
3165
  const isLegacy = useThree((s) => s.isLegacy);
2741
3166
  const size = useThree((s) => s.size);
3167
+ let width;
3168
+ let height;
3169
+ let opts;
3170
+ if (typeof widthOrOptions === "object") {
3171
+ opts = widthOrOptions;
3172
+ } else if (typeof widthOrOptions === "number") {
3173
+ width = widthOrOptions;
3174
+ if (typeof heightOrOptions === "object") {
3175
+ height = widthOrOptions;
3176
+ opts = heightOrOptions;
3177
+ } else if (typeof heightOrOptions === "number") {
3178
+ height = heightOrOptions;
3179
+ opts = options;
3180
+ } else {
3181
+ height = widthOrOptions;
3182
+ }
3183
+ }
2742
3184
  return React.useMemo(() => {
2743
3185
  const w = width ?? size.width;
2744
3186
  const h = height ?? size.height;
2745
3187
  {
2746
- return isLegacy ? new three.WebGLRenderTarget(w, h, options) : new webgpu.RenderTarget(w, h, options);
3188
+ return isLegacy ? new three.WebGLRenderTarget(w, h, opts) : new webgpu.RenderTarget(w, h, opts);
2747
3189
  }
2748
- }, [width, height, size.width, size.height, options, isLegacy]);
3190
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2749
3191
  }
2750
3192
 
2751
3193
  function useStore() {
@@ -2795,7 +3237,7 @@ function addTail(callback) {
2795
3237
  function invalidate(state, frames = 1, stackFrames = false) {
2796
3238
  getScheduler().invalidate(frames, stackFrames);
2797
3239
  }
2798
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3240
+ function advance(timestamp) {
2799
3241
  getScheduler().step(timestamp);
2800
3242
  }
2801
3243
 
@@ -14249,6 +14691,7 @@ function swapInstances() {
14249
14691
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14250
14692
  instance.object.__r3f = instance;
14251
14693
  setFiberRef(fiber, instance.object);
14694
+ delete instance.appliedOnce;
14252
14695
  applyProps(instance.object, instance.props);
14253
14696
  if (instance.props.attach) {
14254
14697
  attach(parent, instance);
@@ -14322,8 +14765,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14322
14765
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14323
14766
  if (isTailSibling) swapInstances();
14324
14767
  },
14325
- finalizeInitialChildren: () => false,
14326
- commitMount() {
14768
+ finalizeInitialChildren: (instance) => {
14769
+ for (const prop in instance.props) {
14770
+ if (isFromRef(instance.props[prop])) return true;
14771
+ }
14772
+ return false;
14773
+ },
14774
+ commitMount(instance) {
14775
+ const resolved = {};
14776
+ for (const prop in instance.props) {
14777
+ const value = instance.props[prop];
14778
+ if (isFromRef(value)) {
14779
+ const ref = value[FROM_REF];
14780
+ if (ref.current != null) resolved[prop] = ref.current;
14781
+ }
14782
+ }
14783
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14327
14784
  },
14328
14785
  getPublicInstance: (instance) => instance?.object,
14329
14786
  prepareForCommit: () => null,
@@ -14544,6 +15001,9 @@ function createRoot(canvas) {
14544
15001
  let resolve;
14545
15002
  pending = new Promise((_resolve) => resolve = _resolve);
14546
15003
  const {
15004
+ id: canvasId,
15005
+ primaryCanvas,
15006
+ scheduler: schedulerConfig,
14547
15007
  gl: glConfig,
14548
15008
  renderer: rendererConfig,
14549
15009
  size: propsSize,
@@ -14551,10 +15011,7 @@ function createRoot(canvas) {
14551
15011
  events,
14552
15012
  onCreated: onCreatedCallback,
14553
15013
  shadows = false,
14554
- linear = false,
14555
- flat = false,
14556
15014
  textureColorSpace = webgpu.SRGBColorSpace,
14557
- legacy = false,
14558
15015
  orthographic = false,
14559
15016
  frameloop = "always",
14560
15017
  dpr = [1, 2],
@@ -14566,7 +15023,8 @@ function createRoot(canvas) {
14566
15023
  onDropMissed,
14567
15024
  autoUpdateFrustum = true,
14568
15025
  occlusion = false,
14569
- _sizeProps
15026
+ _sizeProps,
15027
+ forceEven
14570
15028
  } = props;
14571
15029
  const state = store.getState();
14572
15030
  const defaultGLProps = {
@@ -14576,7 +15034,8 @@ function createRoot(canvas) {
14576
15034
  alpha: true
14577
15035
  };
14578
15036
  const defaultGPUProps = {
14579
- canvas
15037
+ canvas,
15038
+ antialias: true
14580
15039
  };
14581
15040
  const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
14582
15041
  if (glConfig && rendererConfig) {
@@ -14590,10 +15049,35 @@ function createRoot(canvas) {
14590
15049
  });
14591
15050
  }
14592
15051
  let renderer = state.internal.actualRenderer;
15052
+ if (primaryCanvas && wantsGL) {
15053
+ throw new Error(
15054
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15055
+ );
15056
+ }
14593
15057
  if (wantsGL && !state.internal.actualRenderer) {
14594
15058
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14595
15059
  state.internal.actualRenderer = renderer;
14596
- state.set({ isLegacy: true, gl: renderer, renderer });
15060
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
15061
+ } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
15062
+ const primary = await waitForPrimary(primaryCanvas);
15063
+ renderer = primary.renderer;
15064
+ state.internal.actualRenderer = renderer;
15065
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15066
+ primary.store.setState((prev) => ({
15067
+ internal: { ...prev.internal, isMultiCanvas: true }
15068
+ }));
15069
+ state.set((prev) => ({
15070
+ webGPUSupported: primary.store.getState().webGPUSupported,
15071
+ renderer,
15072
+ primaryStore: primary.store,
15073
+ internal: {
15074
+ ...prev.internal,
15075
+ canvasTarget,
15076
+ isMultiCanvas: true,
15077
+ isSecondary: true,
15078
+ targetId: primaryCanvas
15079
+ }
15080
+ }));
14597
15081
  } else if (!wantsGL && !state.internal.actualRenderer) {
14598
15082
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14599
15083
  if (!renderer.hasInitialized?.()) {
@@ -14602,7 +15086,18 @@ function createRoot(canvas) {
14602
15086
  const backend = renderer.backend;
14603
15087
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14604
15088
  state.internal.actualRenderer = renderer;
14605
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15089
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15090
+ if (canvasId && !state.internal.isSecondary) {
15091
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15092
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15093
+ state.set((prev) => ({
15094
+ internal: {
15095
+ ...prev.internal,
15096
+ canvasTarget,
15097
+ unregisterPrimary
15098
+ }
15099
+ }));
15100
+ }
14606
15101
  }
14607
15102
  let raycaster = state.raycaster;
14608
15103
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
@@ -14611,6 +15106,7 @@ function createRoot(canvas) {
14611
15106
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14612
15107
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14613
15108
  }
15109
+ let tempCamera = state.camera;
14614
15110
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14615
15111
  lastCamera = cameraOptions;
14616
15112
  const isCamera = cameraOptions?.isCamera;
@@ -14630,6 +15126,7 @@ function createRoot(canvas) {
14630
15126
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14631
15127
  }
14632
15128
  state.set({ camera });
15129
+ tempCamera = camera;
14633
15130
  raycaster.camera = camera;
14634
15131
  }
14635
15132
  if (!state.scene) {
@@ -14647,7 +15144,7 @@ function createRoot(canvas) {
14647
15144
  rootScene: scene,
14648
15145
  internal: { ...prev.internal, container: scene }
14649
15146
  }));
14650
- const camera = state.camera;
15147
+ const camera = tempCamera;
14651
15148
  if (camera && !camera.parent) scene.add(camera);
14652
15149
  }
14653
15150
  if (events && !state.events.handlers) {
@@ -14664,6 +15161,9 @@ function createRoot(canvas) {
14664
15161
  if (_sizeProps !== void 0) {
14665
15162
  state.set({ _sizeProps });
14666
15163
  }
15164
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15165
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15166
+ }
14667
15167
  const size = computeInitialSize(canvas, propsSize);
14668
15168
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14669
15169
  const wasImperative = state._sizeImperative;
@@ -14693,7 +15193,7 @@ function createRoot(canvas) {
14693
15193
  const handleXRFrame = (timestamp, frame) => {
14694
15194
  const state2 = store.getState();
14695
15195
  if (state2.frameloop === "never") return;
14696
- advance(timestamp, true);
15196
+ advance(timestamp);
14697
15197
  };
14698
15198
  const actualRenderer = state.internal.actualRenderer;
14699
15199
  const handleSessionChange = () => {
@@ -14705,16 +15205,16 @@ function createRoot(canvas) {
14705
15205
  };
14706
15206
  const xr = {
14707
15207
  connect() {
14708
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14709
- const actualRenderer2 = renderer2 || gl;
14710
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14711
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15208
+ const { gl, renderer: renderer2 } = store.getState();
15209
+ const xrManager = (renderer2 || gl).xr;
15210
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15211
+ xrManager.addEventListener("sessionend", handleSessionChange);
14712
15212
  },
14713
15213
  disconnect() {
14714
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14715
- const actualRenderer2 = renderer2 || gl;
14716
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14717
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15214
+ const { gl, renderer: renderer2 } = store.getState();
15215
+ const xrManager = (renderer2 || gl).xr;
15216
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15217
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14718
15218
  }
14719
15219
  };
14720
15220
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14742,34 +15242,9 @@ function createRoot(canvas) {
14742
15242
  renderer.shadowMap.needsUpdate = true;
14743
15243
  }
14744
15244
  }
14745
- {
14746
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14747
- const linearChanged = linear !== lastConfiguredProps.linear;
14748
- const flatChanged = flat !== lastConfiguredProps.flat;
14749
- if (legacyChanged) {
14750
- if (legacy) {
14751
- notifyDepreciated({
14752
- heading: "Legacy Color Management",
14753
- body: "Legacy color management is deprecated and will be removed in a future version.",
14754
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
14755
- });
14756
- }
14757
- }
14758
- if (legacyChanged) {
14759
- webgpu.ColorManagement.enabled = !legacy;
14760
- lastConfiguredProps.legacy = legacy;
14761
- }
14762
- if (!configured || linearChanged) {
14763
- renderer.outputColorSpace = linear ? webgpu.LinearSRGBColorSpace : webgpu.SRGBColorSpace;
14764
- lastConfiguredProps.linear = linear;
14765
- }
14766
- if (!configured || flatChanged) {
14767
- renderer.toneMapping = flat ? webgpu.NoToneMapping : webgpu.ACESFilmicToneMapping;
14768
- lastConfiguredProps.flat = flat;
14769
- }
14770
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14771
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14772
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15245
+ if (!configured) {
15246
+ renderer.outputColorSpace = webgpu.SRGBColorSpace;
15247
+ renderer.toneMapping = webgpu.ACESFilmicToneMapping;
14773
15248
  }
14774
15249
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14775
15250
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
@@ -14787,11 +15262,26 @@ function createRoot(canvas) {
14787
15262
  const scheduler = getScheduler();
14788
15263
  const rootId = state.internal.rootId;
14789
15264
  if (!rootId) {
14790
- const newRootId = scheduler.generateRootId();
15265
+ const newRootId = canvasId || scheduler.generateRootId();
14791
15266
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14792
15267
  getState: () => store.getState(),
14793
15268
  onError: (err) => store.getState().setError(err)
14794
15269
  });
15270
+ const unregisterCanvasTarget = scheduler.register(
15271
+ () => {
15272
+ const state2 = store.getState();
15273
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15274
+ const renderer2 = state2.internal.actualRenderer;
15275
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15276
+ }
15277
+ },
15278
+ {
15279
+ id: `${newRootId}_canvasTarget`,
15280
+ rootId: newRootId,
15281
+ phase: "start",
15282
+ system: true
15283
+ }
15284
+ );
14795
15285
  const unregisterFrustum = scheduler.register(
14796
15286
  () => {
14797
15287
  const state2 = store.getState();
@@ -14833,11 +15323,15 @@ function createRoot(canvas) {
14833
15323
  }
14834
15324
  },
14835
15325
  {
14836
- id: `${newRootId}_render`,
15326
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15327
+ id: canvasId || `${newRootId}_render`,
14837
15328
  rootId: newRootId,
14838
15329
  phase: "render",
14839
- system: true
15330
+ system: true,
14840
15331
  // Internal flag: this is a system job, not user-controlled
15332
+ // Apply scheduler config for render ordering and rate limiting
15333
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15334
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14841
15335
  }
14842
15336
  );
14843
15337
  state.set((state2) => ({
@@ -14846,6 +15340,7 @@ function createRoot(canvas) {
14846
15340
  rootId: newRootId,
14847
15341
  unregisterRoot: () => {
14848
15342
  unregisterRoot();
15343
+ unregisterCanvasTarget();
14849
15344
  unregisterFrustum();
14850
15345
  unregisterVisibility();
14851
15346
  unregisterRender();
@@ -14904,15 +15399,24 @@ function unmountComponentAtNode(canvas, callback) {
14904
15399
  const renderer = state.internal.actualRenderer;
14905
15400
  const unregisterRoot = state.internal.unregisterRoot;
14906
15401
  if (unregisterRoot) unregisterRoot();
15402
+ const unregisterPrimary = state.internal.unregisterPrimary;
15403
+ if (unregisterPrimary) unregisterPrimary();
15404
+ const canvasTarget = state.internal.canvasTarget;
15405
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14907
15406
  state.events.disconnect?.();
14908
15407
  cleanupHelperGroup(root.store);
14909
- renderer?.renderLists?.dispose?.();
14910
- renderer?.forceContextLoss?.();
14911
- if (renderer?.xr) state.xr.disconnect();
15408
+ if (state.isLegacy && renderer) {
15409
+ ;
15410
+ renderer.renderLists?.dispose?.();
15411
+ renderer.forceContextLoss?.();
15412
+ }
15413
+ if (!state.internal.isSecondary) {
15414
+ if (renderer?.xr) state.xr.disconnect();
15415
+ }
14912
15416
  dispose(state.scene);
14913
15417
  _roots.delete(canvas);
14914
15418
  if (callback) callback(canvas);
14915
- } catch (e) {
15419
+ } catch {
14916
15420
  }
14917
15421
  }, 500);
14918
15422
  }
@@ -14920,36 +15424,34 @@ function unmountComponentAtNode(canvas, callback) {
14920
15424
  }
14921
15425
  }
14922
15426
  function createPortal(children, container, state) {
14923
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15427
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14924
15428
  }
14925
- function PortalWrapper({ children, container, state }) {
15429
+ function Portal({ children, container, state }) {
14926
15430
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14927
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15431
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14928
15432
  if (isRef(container)) return container.current ?? null;
14929
15433
  return container;
14930
15434
  });
15435
+ const setResolvedContainer = React.useCallback(
15436
+ (newContainer) => {
15437
+ if (!newContainer || newContainer === resolvedContainer) return;
15438
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15439
+ },
15440
+ [resolvedContainer, _setResolvedContainer, isRef]
15441
+ );
14931
15442
  React.useMemo(() => {
14932
- if (isRef(container)) {
14933
- const current = container.current;
14934
- if (!current) {
14935
- queueMicrotask(() => {
14936
- const updated = container.current;
14937
- if (updated && updated !== resolvedContainer) {
14938
- setResolvedContainer(updated);
14939
- }
14940
- });
14941
- } else if (current !== resolvedContainer) {
14942
- setResolvedContainer(current);
14943
- }
14944
- } else if (container !== resolvedContainer) {
14945
- setResolvedContainer(container);
15443
+ if (isRef(container) && !container.current) {
15444
+ return queueMicrotask(() => {
15445
+ setResolvedContainer(container.current);
15446
+ });
14946
15447
  }
14947
- }, [container, resolvedContainer, isRef]);
15448
+ setResolvedContainer(container);
15449
+ }, [container, isRef, setResolvedContainer]);
14948
15450
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14949
15451
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14950
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15452
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14951
15453
  }
14952
- function Portal({ state = {}, children, container }) {
15454
+ function PortalInner({ state = {}, children, container }) {
14953
15455
  const { events, size, injectScene = true, ...rest } = state;
14954
15456
  const previousRoot = useStore();
14955
15457
  const [raycaster] = React.useState(() => new webgpu.Raycaster());
@@ -14970,11 +15472,12 @@ function Portal({ state = {}, children, container }) {
14970
15472
  };
14971
15473
  }, [portalScene, container, injectScene]);
14972
15474
  const inject = useMutableCallback((rootState, injectState) => {
15475
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14973
15476
  let viewport = void 0;
14974
- if (injectState.camera && size) {
15477
+ if (injectState.camera && (size || injectState.size)) {
14975
15478
  const camera = injectState.camera;
14976
- viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), size);
14977
- if (camera !== rootState.camera) updateCamera(camera, size);
15479
+ viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
15480
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14978
15481
  }
14979
15482
  return {
14980
15483
  // The intersect consists of the previous root state
@@ -14991,7 +15494,7 @@ function Portal({ state = {}, children, container }) {
14991
15494
  previousRoot,
14992
15495
  // Events, size and viewport can be overridden by the inject layer
14993
15496
  events: { ...rootState.events, ...injectState.events, ...events },
14994
- size: { ...rootState.size, ...size },
15497
+ size: resolvedSize,
14995
15498
  viewport: { ...rootState.viewport, ...viewport },
14996
15499
  // Layers are allowed to override events
14997
15500
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -15025,15 +15528,13 @@ function CanvasImpl({
15025
15528
  fallback,
15026
15529
  resize,
15027
15530
  style,
15531
+ id,
15028
15532
  gl,
15029
- renderer,
15533
+ renderer: rendererProp,
15030
15534
  events = createPointerEvents,
15031
15535
  eventSource,
15032
15536
  eventPrefix,
15033
15537
  shadows,
15034
- linear,
15035
- flat,
15036
- legacy,
15037
15538
  orthographic,
15038
15539
  frameloop,
15039
15540
  dpr,
@@ -15048,10 +15549,43 @@ function CanvasImpl({
15048
15549
  hmr,
15049
15550
  width,
15050
15551
  height,
15552
+ background,
15553
+ forceEven,
15051
15554
  ...props
15052
15555
  }) {
15556
+ 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 };
15557
+ const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15053
15558
  React__namespace.useMemo(() => extend(THREE), []);
15054
15559
  const Bridge = useBridge();
15560
+ const backgroundProps = React__namespace.useMemo(() => {
15561
+ if (!background) return null;
15562
+ if (typeof background === "object" && !background.isColor) {
15563
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15564
+ return {
15565
+ ...rest,
15566
+ preset,
15567
+ files: envMap || files,
15568
+ backgroundFiles: backgroundMap,
15569
+ background: true
15570
+ };
15571
+ }
15572
+ if (typeof background === "number") {
15573
+ return { color: background, background: true };
15574
+ }
15575
+ if (typeof background === "string") {
15576
+ if (background in presetsObj) {
15577
+ return { preset: background, background: true };
15578
+ }
15579
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15580
+ return { files: background, background: true };
15581
+ }
15582
+ return { color: background, background: true };
15583
+ }
15584
+ if (background.isColor) {
15585
+ return { color: background, background: true };
15586
+ }
15587
+ return null;
15588
+ }, [background]);
15055
15589
  const hasInitialSizeRef = React__namespace.useRef(false);
15056
15590
  const measureConfig = React__namespace.useMemo(() => {
15057
15591
  if (!hasInitialSizeRef.current) {
@@ -15068,15 +15602,20 @@ function CanvasImpl({
15068
15602
  };
15069
15603
  }, [resize, hasInitialSizeRef.current]);
15070
15604
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15071
- const effectiveSize = React__namespace.useMemo(
15072
- () => ({
15073
- width: width ?? containerRect.width,
15074
- height: height ?? containerRect.height,
15605
+ const effectiveSize = React__namespace.useMemo(() => {
15606
+ let w = width ?? containerRect.width;
15607
+ let h = height ?? containerRect.height;
15608
+ if (forceEven) {
15609
+ w = Math.ceil(w / 2) * 2;
15610
+ h = Math.ceil(h / 2) * 2;
15611
+ }
15612
+ return {
15613
+ width: w,
15614
+ height: h,
15075
15615
  top: containerRect.top,
15076
15616
  left: containerRect.left
15077
- }),
15078
- [width, height, containerRect]
15079
- );
15617
+ };
15618
+ }, [width, height, containerRect, forceEven]);
15080
15619
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15081
15620
  hasInitialSizeRef.current = true;
15082
15621
  }
@@ -15116,14 +15655,14 @@ function CanvasImpl({
15116
15655
  async function run() {
15117
15656
  if (!effectActiveRef.current || !root.current) return;
15118
15657
  await root.current.configure({
15658
+ id,
15659
+ primaryCanvas,
15660
+ scheduler,
15119
15661
  gl,
15120
15662
  renderer,
15121
15663
  scene,
15122
15664
  events,
15123
15665
  shadows,
15124
- linear,
15125
- flat,
15126
- legacy,
15127
15666
  orthographic,
15128
15667
  frameloop,
15129
15668
  dpr,
@@ -15133,6 +15672,7 @@ function CanvasImpl({
15133
15672
  size: effectiveSize,
15134
15673
  // Store size props for reset functionality
15135
15674
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15675
+ forceEven,
15136
15676
  // Pass mutable reference to onPointerMissed so it's free to update
15137
15677
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15138
15678
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15156,7 +15696,10 @@ function CanvasImpl({
15156
15696
  });
15157
15697
  if (!effectActiveRef.current || !root.current) return;
15158
15698
  root.current.render(
15159
- /* @__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 }) }) })
15699
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
15700
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15701
+ children ?? null
15702
+ ] }) }) })
15160
15703
  );
15161
15704
  }
15162
15705
  run();
@@ -15183,14 +15726,16 @@ function CanvasImpl({
15183
15726
  const canvas = canvasRef.current;
15184
15727
  if (!canvas) return;
15185
15728
  const handleHMR = () => {
15186
- const rootEntry = _roots.get(canvas);
15187
- if (rootEntry?.store) {
15188
- rootEntry.store.setState((state) => ({
15189
- nodes: {},
15190
- uniforms: {},
15191
- _hmrVersion: state._hmrVersion + 1
15192
- }));
15193
- }
15729
+ queueMicrotask(() => {
15730
+ const rootEntry = _roots.get(canvas);
15731
+ if (rootEntry?.store) {
15732
+ rootEntry.store.setState((state) => ({
15733
+ nodes: {},
15734
+ uniforms: {},
15735
+ _hmrVersion: state._hmrVersion + 1
15736
+ }));
15737
+ }
15738
+ });
15194
15739
  };
15195
15740
  if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15196
15741
  const hot = undefined;
@@ -15219,7 +15764,7 @@ function CanvasImpl({
15219
15764
  ...style
15220
15765
  },
15221
15766
  ...props,
15222
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15767
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15223
15768
  }
15224
15769
  );
15225
15770
  }
@@ -15231,8 +15776,15 @@ extend(THREE);
15231
15776
 
15232
15777
  exports.Block = Block;
15233
15778
  exports.Canvas = Canvas;
15779
+ exports.Environment = Environment;
15780
+ exports.EnvironmentCube = EnvironmentCube;
15781
+ exports.EnvironmentMap = EnvironmentMap;
15782
+ exports.EnvironmentPortal = EnvironmentPortal;
15234
15783
  exports.ErrorBoundary = ErrorBoundary;
15784
+ exports.FROM_REF = FROM_REF;
15235
15785
  exports.IsObject = IsObject;
15786
+ exports.ONCE = ONCE;
15787
+ exports.Portal = Portal;
15236
15788
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15237
15789
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15238
15790
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15262,30 +15814,41 @@ exports.events = createPointerEvents;
15262
15814
  exports.extend = extend;
15263
15815
  exports.findInitialRoot = findInitialRoot;
15264
15816
  exports.flushSync = flushSync;
15817
+ exports.fromRef = fromRef;
15265
15818
  exports.getInstanceProps = getInstanceProps;
15819
+ exports.getPrimary = getPrimary;
15820
+ exports.getPrimaryIds = getPrimaryIds;
15266
15821
  exports.getRootState = getRootState;
15267
15822
  exports.getScheduler = getScheduler;
15268
15823
  exports.getUuidPrefix = getUuidPrefix;
15269
15824
  exports.hasConstructor = hasConstructor;
15825
+ exports.hasPrimary = hasPrimary;
15270
15826
  exports.invalidate = invalidate;
15271
15827
  exports.invalidateInstance = invalidateInstance;
15272
15828
  exports.is = is;
15273
15829
  exports.isColorRepresentation = isColorRepresentation;
15274
15830
  exports.isCopyable = isCopyable;
15831
+ exports.isFromRef = isFromRef;
15275
15832
  exports.isObject3D = isObject3D;
15833
+ exports.isOnce = isOnce;
15276
15834
  exports.isOrthographicCamera = isOrthographicCamera;
15277
15835
  exports.isRef = isRef;
15278
15836
  exports.isRenderer = isRenderer;
15279
15837
  exports.isTexture = isTexture;
15280
15838
  exports.isVectorLike = isVectorLike;
15839
+ exports.once = once;
15281
15840
  exports.prepare = prepare;
15841
+ exports.presetsObj = presetsObj;
15282
15842
  exports.reconciler = reconciler;
15843
+ exports.registerPrimary = registerPrimary;
15283
15844
  exports.removeInteractivity = removeInteractivity;
15284
15845
  exports.resolve = resolve;
15285
15846
  exports.unmountComponentAtNode = unmountComponentAtNode;
15847
+ exports.unregisterPrimary = unregisterPrimary;
15286
15848
  exports.updateCamera = updateCamera;
15287
15849
  exports.updateFrustum = updateFrustum;
15288
15850
  exports.useBridge = useBridge;
15851
+ exports.useEnvironment = useEnvironment;
15289
15852
  exports.useFrame = useFrame;
15290
15853
  exports.useGraph = useGraph;
15291
15854
  exports.useInstanceHandle = useInstanceHandle;
@@ -15297,3 +15860,4 @@ exports.useStore = useStore;
15297
15860
  exports.useTexture = useTexture;
15298
15861
  exports.useTextures = useTextures;
15299
15862
  exports.useThree = useThree;
15863
+ exports.waitForPrimary = waitForPrimary;