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

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
  }
@@ -528,7 +919,7 @@ function applyProps(object, props) {
528
919
  const rootState = instance && findInitialRoot(instance).getState();
529
920
  const prevHandlers = instance?.eventCount;
530
921
  for (const prop in props) {
531
- let value = props[prop];
922
+ const value = props[prop];
532
923
  if (RESERVED_PROPS.includes(prop)) continue;
533
924
  if (instance && EVENT_REGEX.test(prop)) {
534
925
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -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
  }
@@ -680,13 +1090,14 @@ function createEvents(store) {
680
1090
  for (const hit of hits) {
681
1091
  let eventObject = hit.object;
682
1092
  while (eventObject) {
683
- if (eventObject.__r3f?.eventCount)
1093
+ if (eventObject.__r3f?.eventCount) {
684
1094
  intersections.push({ ...hit, eventObject });
1095
+ }
685
1096
  eventObject = eventObject.parent;
686
1097
  }
687
1098
  }
688
1099
  if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
689
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1100
+ for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
690
1101
  if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
691
1102
  }
692
1103
  }
@@ -716,12 +1127,12 @@ function createEvents(store) {
716
1127
  releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
717
1128
  }
718
1129
  };
719
- let extractEventProps = {};
720
- for (let prop in event) {
721
- let property = event[prop];
1130
+ const extractEventProps = {};
1131
+ for (const prop in event) {
1132
+ const property = event[prop];
722
1133
  if (typeof property !== "function") extractEventProps[prop] = property;
723
1134
  }
724
- let raycastEvent = {
1135
+ const raycastEvent = {
725
1136
  ...hit,
726
1137
  ...extractEventProps,
727
1138
  pointer,
@@ -921,7 +1332,7 @@ function createPointerEvents(store) {
921
1332
  return {
922
1333
  priority: 1,
923
1334
  enabled: true,
924
- compute(event, state, previous) {
1335
+ compute(event, state) {
925
1336
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
926
1337
  state.raycaster.setFromCamera(state.pointer, state.camera);
927
1338
  },
@@ -1019,300 +1430,26 @@ function notifyAlpha({ message, link }) {
1019
1430
  }
1020
1431
  }
1021
1432
 
1022
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1023
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1024
- const createStore = (invalidate, advance) => {
1025
- const rootStore = createWithEqualityFn((set, get) => {
1026
- const position = new Vector3();
1027
- const defaultTarget = new Vector3();
1028
- const tempTarget = new Vector3();
1029
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1030
- const { width, height, top, left } = size;
1031
- const aspect = width / height;
1032
- if (target.isVector3) tempTarget.copy(target);
1033
- else tempTarget.set(...target);
1034
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1035
- if (isOrthographicCamera(camera)) {
1036
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1037
- } else {
1038
- const fov = camera.fov * Math.PI / 180;
1039
- const h = 2 * Math.tan(fov / 2) * distance;
1040
- const w = h * (width / height);
1041
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1042
- }
1043
- }
1044
- let performanceTimeout = void 0;
1045
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1046
- const pointer = new Vector2();
1047
- const rootState = {
1048
- set,
1049
- get,
1050
- // Mock objects that have to be configured
1051
- gl: null,
1052
- renderer: null,
1053
- camera: null,
1054
- frustum: new Frustum(),
1055
- autoUpdateFrustum: true,
1056
- raycaster: null,
1057
- events: { priority: 1, enabled: true, connected: false },
1058
- scene: null,
1059
- rootScene: null,
1060
- xr: null,
1061
- inspector: null,
1062
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1063
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1064
- legacy: false,
1065
- linear: false,
1066
- flat: false,
1067
- textureColorSpace: "srgb",
1068
- isLegacy: false,
1069
- webGPUSupported: false,
1070
- isNative: false,
1071
- controls: null,
1072
- pointer,
1073
- mouse: pointer,
1074
- frameloop: "always",
1075
- onPointerMissed: void 0,
1076
- onDragOverMissed: void 0,
1077
- onDropMissed: void 0,
1078
- performance: {
1079
- current: 1,
1080
- min: 0.5,
1081
- max: 1,
1082
- debounce: 200,
1083
- regress: () => {
1084
- const state2 = get();
1085
- if (performanceTimeout) clearTimeout(performanceTimeout);
1086
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1087
- performanceTimeout = setTimeout(
1088
- () => setPerformanceCurrent(get().performance.max),
1089
- state2.performance.debounce
1090
- );
1091
- }
1092
- },
1093
- size: { width: 0, height: 0, top: 0, left: 0 },
1094
- viewport: {
1095
- initialDpr: 0,
1096
- dpr: 0,
1097
- width: 0,
1098
- height: 0,
1099
- top: 0,
1100
- left: 0,
1101
- aspect: 0,
1102
- distance: 0,
1103
- factor: 0,
1104
- getCurrentViewport
1105
- },
1106
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1107
- setSize: (width, height, top = 0, left = 0) => {
1108
- const camera = get().camera;
1109
- const size = { width, height, top, left };
1110
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1111
- },
1112
- setDpr: (dpr) => set((state2) => {
1113
- const resolved = calculateDpr(dpr);
1114
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1115
- }),
1116
- setFrameloop: (frameloop = "always") => {
1117
- set(() => ({ frameloop }));
1118
- },
1119
- setError: (error) => set(() => ({ error })),
1120
- error: null,
1121
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1122
- uniforms: {},
1123
- nodes: {},
1124
- textures: /* @__PURE__ */ new Map(),
1125
- postProcessing: null,
1126
- passes: {},
1127
- previousRoot: void 0,
1128
- internal: {
1129
- // Events
1130
- interaction: [],
1131
- hovered: /* @__PURE__ */ new Map(),
1132
- subscribers: [],
1133
- initialClick: [0, 0],
1134
- initialHits: [],
1135
- capturedMap: /* @__PURE__ */ new Map(),
1136
- lastEvent: React.createRef(),
1137
- // Visibility tracking (onFramed, onOccluded, onVisible)
1138
- visibilityRegistry: /* @__PURE__ */ new Map(),
1139
- // Occlusion system (WebGPU only)
1140
- occlusionEnabled: false,
1141
- occlusionObserver: null,
1142
- occlusionCache: /* @__PURE__ */ new Map(),
1143
- helperGroup: null,
1144
- // Updates
1145
- active: false,
1146
- frames: 0,
1147
- priority: 0,
1148
- subscribe: (ref, priority, store) => {
1149
- const internal = get().internal;
1150
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1151
- internal.subscribers.push({ ref, priority, store });
1152
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1153
- return () => {
1154
- const internal2 = get().internal;
1155
- if (internal2?.subscribers) {
1156
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1157
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1158
- }
1159
- };
1160
- },
1161
- // Renderer Storage (single source of truth)
1162
- actualRenderer: null,
1163
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1164
- scheduler: null
1165
- }
1166
- };
1167
- return rootState;
1168
- });
1169
- const state = rootStore.getState();
1170
- Object.defineProperty(state, "gl", {
1171
- get() {
1172
- const currentState = rootStore.getState();
1173
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1174
- const stack = new Error().stack || "";
1175
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1176
- if (!isInternalAccess) {
1177
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1178
- notifyDepreciated({
1179
- heading: "Accessing state.gl in WebGPU mode",
1180
- 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
1181
- });
1182
- }
1183
- }
1184
- return currentState.internal.actualRenderer;
1185
- },
1186
- set(value) {
1187
- rootStore.getState().internal.actualRenderer = value;
1188
- },
1189
- enumerable: true,
1190
- configurable: true
1191
- });
1192
- Object.defineProperty(state, "renderer", {
1193
- get() {
1194
- return rootStore.getState().internal.actualRenderer;
1195
- },
1196
- set(value) {
1197
- rootStore.getState().internal.actualRenderer = value;
1198
- },
1199
- enumerable: true,
1200
- configurable: true
1201
- });
1202
- let oldScene = state.scene;
1203
- rootStore.subscribe(() => {
1204
- const currentState = rootStore.getState();
1205
- const { scene, rootScene, set } = currentState;
1206
- if (scene !== oldScene) {
1207
- oldScene = scene;
1208
- if (scene?.isScene && scene !== rootScene) {
1209
- set({ rootScene: scene });
1210
- }
1211
- }
1212
- });
1213
- let oldSize = state.size;
1214
- let oldDpr = state.viewport.dpr;
1215
- let oldCamera = state.camera;
1216
- rootStore.subscribe(() => {
1217
- const { camera, size, viewport, set, internal } = rootStore.getState();
1218
- const actualRenderer = internal.actualRenderer;
1219
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1220
- oldSize = size;
1221
- oldDpr = viewport.dpr;
1222
- updateCamera(camera, size);
1223
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1224
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1225
- actualRenderer.setSize(size.width, size.height, updateStyle);
1226
- }
1227
- if (camera !== oldCamera) {
1228
- oldCamera = camera;
1229
- const { rootScene } = rootStore.getState();
1230
- if (camera && rootScene && !camera.parent) {
1231
- rootScene.add(camera);
1232
- }
1233
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1234
- const currentState = rootStore.getState();
1235
- if (currentState.autoUpdateFrustum && camera) {
1236
- updateFrustum(camera, currentState.frustum);
1237
- }
1238
- }
1239
- });
1240
- rootStore.subscribe((state2) => invalidate(state2));
1241
- return rootStore;
1242
- };
1243
-
1244
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1245
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1246
- function getLoader(Proto) {
1247
- if (isConstructor$1(Proto)) {
1248
- let loader = memoizedLoaders.get(Proto);
1249
- if (!loader) {
1250
- loader = new Proto();
1251
- memoizedLoaders.set(Proto, loader);
1252
- }
1253
- return loader;
1254
- }
1255
- return Proto;
1256
- }
1257
- function loadingFn(extensions, onProgress) {
1258
- return function(Proto, input) {
1259
- const loader = getLoader(Proto);
1260
- if (extensions) extensions(loader);
1261
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1262
- return loader.loadAsync(input, onProgress).then((data) => {
1263
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1264
- return data;
1265
- });
1266
- }
1267
- return new Promise(
1268
- (res, reject) => loader.load(
1269
- input,
1270
- (data) => {
1271
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1272
- res(data);
1273
- },
1274
- onProgress,
1275
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1276
- )
1277
- );
1278
- };
1279
- }
1280
- function useLoader(loader, input, extensions, onProgress) {
1281
- const keys = Array.isArray(input) ? input : [input];
1282
- const fn = loadingFn(extensions, onProgress);
1283
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1284
- return Array.isArray(input) ? results : results[0];
1285
- }
1286
- useLoader.preload = function(loader, input, extensions, onProgress) {
1287
- const keys = Array.isArray(input) ? input : [input];
1288
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
1289
- };
1290
- useLoader.clear = function(loader, input) {
1291
- const keys = Array.isArray(input) ? input : [input];
1292
- keys.forEach((key) => clear([loader, key]));
1293
- };
1294
- useLoader.loader = getLoader;
1295
-
1296
- var __defProp$1 = Object.defineProperty;
1297
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1298
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1299
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1300
- class PhaseGraph {
1301
- constructor() {
1302
- /** Ordered list of phase nodes */
1303
- __publicField$1(this, "phases", []);
1304
- /** Quick lookup by name */
1305
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1306
- /** Cached ordered names (invalidated on changes) */
1307
- __publicField$1(this, "orderedNamesCache", null);
1308
- this.initializeDefaultPhases();
1309
- }
1310
- //* Initialization --------------------------------
1311
- initializeDefaultPhases() {
1312
- for (const name of DEFAULT_PHASES) {
1313
- const node = { name, isAutoGenerated: false };
1314
- this.phases.push(node);
1315
- 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);
1316
1453
  }
1317
1454
  this.invalidateCache();
1318
1455
  }
@@ -1331,8 +1468,9 @@ class PhaseGraph {
1331
1468
  const node = { name, isAutoGenerated: false };
1332
1469
  let insertIndex = this.phases.length;
1333
1470
  const targetIndex = this.getPhaseIndex(before ?? after);
1334
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1335
- else {
1471
+ if (targetIndex !== -1) {
1472
+ insertIndex = before ? targetIndex : targetIndex + 1;
1473
+ } else {
1336
1474
  const constraintType = before ? "before" : "after";
1337
1475
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1338
1476
  }
@@ -2213,116 +2351,429 @@ const _Scheduler = class _Scheduler {
2213
2351
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2214
2352
  root.needsRebuild = false;
2215
2353
  }
2216
- const providedState = root.getState?.() ?? {};
2217
- const frameState = {
2218
- ...providedState,
2219
- time: timestamp,
2220
- delta,
2221
- elapsed: this.loopState.elapsedTime / 1e3,
2222
- // Convert ms to seconds
2223
- frame: this.loopState.frameCount
2224
- };
2225
- for (const job of root.sortedJobs) {
2226
- if (!shouldRun(job, timestamp)) continue;
2227
- try {
2228
- job.callback(frameState, delta);
2229
- } catch (error) {
2230
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2231
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
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);
2232
2719
  }
2233
2720
  }
2234
- }
2235
- //* Debug & Inspection Methods ================================
2236
- /**
2237
- * Get the total number of registered jobs across all roots.
2238
- * Includes both per-root jobs and global before/after jobs.
2239
- * @returns {number} Total job count
2240
- */
2241
- getJobCount() {
2242
- let count = 0;
2243
- for (const root of this.roots.values()) {
2244
- 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);
2245
2734
  }
2246
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2735
+ return loader;
2247
2736
  }
2248
- /**
2249
- * Get all registered job IDs across all roots.
2250
- * Includes both per-root jobs and global before/after jobs.
2251
- * @returns {string[]} Array of all job IDs
2252
- */
2253
- getJobIds() {
2254
- const ids = [];
2255
- for (const root of this.roots.values()) {
2256
- 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
+ });
2257
2748
  }
2258
- ids.push(...this.globalBeforeJobs.keys());
2259
- ids.push(...this.globalAfterJobs.keys());
2260
- return ids;
2261
- }
2262
- /**
2263
- * Get the number of registered roots (Canvas instances).
2264
- * @returns {number} Number of registered roots
2265
- */
2266
- getRootCount() {
2267
- return this.roots.size;
2268
- }
2269
- /**
2270
- * Check if any user (non-system) jobs are registered in a specific phase.
2271
- * Used by the default render job to know if a user has taken over rendering.
2272
- *
2273
- * @param phase The phase to check
2274
- * @param rootId Optional root ID to check (checks all roots if not provided)
2275
- * @returns true if any user jobs exist in the phase
2276
- */
2277
- hasUserJobsInPhase(phase, rootId) {
2278
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2279
- return rootsToCheck.some((root) => {
2280
- if (!root) return false;
2281
- for (const job of root.jobs.values()) {
2282
- if (job.phase === phase && !job.system && job.enabled) return true;
2283
- }
2284
- return false;
2285
- });
2286
- }
2287
- //* Utility Methods ================================
2288
- /**
2289
- * Generate a unique root ID for automatic root registration.
2290
- * @returns {string} A unique root ID in the format 'root_N'
2291
- */
2292
- generateRootId() {
2293
- return `root_${this.nextRootIndex++}`;
2294
- }
2295
- /**
2296
- * Generate a unique job ID.
2297
- * @returns {string} A unique job ID in the format 'job_N'
2298
- * @private
2299
- */
2300
- generateJobId() {
2301
- return `job_${this.nextJobIndex}`;
2302
- }
2303
- /**
2304
- * Normalize before/after constraints to a Set.
2305
- * Handles undefined, single string, or array inputs.
2306
- * @param {string | string[] | undefined} value - The constraint value(s)
2307
- * @returns {Set<string>} Normalized Set of constraint strings
2308
- * @private
2309
- */
2310
- normalizeConstraints(value) {
2311
- if (!value) return /* @__PURE__ */ new Set();
2312
- if (Array.isArray(value)) return new Set(value);
2313
- return /* @__PURE__ */ new Set([value]);
2314
- }
2315
- };
2316
- //* Static State & Methods (Singleton Usage) ================================
2317
- //* Cross-Bundle Singleton Key ==============================
2318
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2319
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2320
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2321
- let Scheduler = _Scheduler;
2322
- const getScheduler = () => Scheduler.get();
2323
- if (hmrData) {
2324
- 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];
2325
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;
2326
2777
 
2327
2778
  function useFrame(callback, priorityOrOptions) {
2328
2779
  const store = React.useContext(context);
@@ -2503,6 +2954,9 @@ function useTexture(input, optionsOrOnLoad) {
2503
2954
  const textureCache = useThree((state) => state.textures);
2504
2955
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2505
2956
  const { onLoad, cache = false } = options;
2957
+ const onLoadRef = useRef(onLoad);
2958
+ onLoadRef.current = onLoad;
2959
+ const onLoadCalledForRef = useRef(null);
2506
2960
  const urls = useMemo(() => getUrls(input), [input]);
2507
2961
  const cachedResult = useMemo(() => {
2508
2962
  if (!cache) return null;
@@ -2513,9 +2967,13 @@ function useTexture(input, optionsOrOnLoad) {
2513
2967
  TextureLoader,
2514
2968
  IsObject(input) ? Object.values(input) : input
2515
2969
  );
2970
+ const inputKey = urls.join("\0");
2516
2971
  useLayoutEffect(() => {
2517
- if (!cachedResult) onLoad?.(loadedTextures);
2518
- }, [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]);
2519
2977
  useEffect(() => {
2520
2978
  if (cachedResult) return;
2521
2979
  if ("initTexture" in renderer) {
@@ -2682,16 +3140,33 @@ function useTextures() {
2682
3140
  }, [store]);
2683
3141
  }
2684
3142
 
2685
- function useRenderTarget(width, height, options) {
3143
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2686
3144
  const isLegacy = useThree((s) => s.isLegacy);
2687
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
+ }
2688
3163
  return useMemo(() => {
2689
3164
  const w = width ?? size.width;
2690
3165
  const h = height ?? size.height;
2691
3166
  {
2692
- 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);
2693
3168
  }
2694
- }, [width, height, size.width, size.height, options, isLegacy]);
3169
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2695
3170
  }
2696
3171
 
2697
3172
  function useStore() {
@@ -2741,28 +3216,18 @@ function addTail(callback) {
2741
3216
  function invalidate(state, frames = 1, stackFrames = false) {
2742
3217
  getScheduler().invalidate(frames, stackFrames);
2743
3218
  }
2744
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3219
+ function advance(timestamp) {
2745
3220
  getScheduler().step(timestamp);
2746
3221
  }
2747
3222
 
2748
- const version = "10.0.0-alpha.1";
3223
+ const version = "10.0.0-alpha.2";
2749
3224
  const packageData = {
2750
3225
  version: version};
2751
3226
 
2752
3227
  function Xb(Tt) {
2753
3228
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2754
3229
  }
2755
- var Rm = { exports: {} }, Og = { exports: {} };
2756
- /**
2757
- * @license React
2758
- * react-reconciler.production.js
2759
- *
2760
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2761
- *
2762
- * This source code is licensed under the MIT license found in the
2763
- * LICENSE file in the root directory of this source tree.
2764
- */
2765
- var _b;
3230
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2766
3231
  function Kb() {
2767
3232
  return _b || (_b = 1, (function(Tt) {
2768
3233
  Tt.exports = function(m) {
@@ -3834,7 +4299,6 @@ Error generating stack: ` + l.message + `
3834
4299
  if (J === cl || J === jc) throw J;
3835
4300
  var Ge = Yn(29, J, null, P.mode);
3836
4301
  return Ge.lanes = H, Ge.return = P, Ge;
3837
- } finally {
3838
4302
  }
3839
4303
  };
3840
4304
  }
@@ -4488,7 +4952,6 @@ Error generating stack: ` + l.message + `
4488
4952
  var h = r.lastRenderedState, y = d(h, a);
4489
4953
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4490
4954
  } catch {
4491
- } finally {
4492
4955
  }
4493
4956
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4494
4957
  }
@@ -6909,10 +7372,7 @@ Error generating stack: ` + l.message + `
6909
7372
  function vr(t, r) {
6910
7373
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6911
7374
  }
6912
- var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy");
6913
- var gc = Symbol.for("react.activity");
6914
- var $r = Symbol.for("react.memo_cache_sentinel");
6915
- var Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
7375
+ var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy"), gc = Symbol.for("react.activity"), $r = Symbol.for("react.memo_cache_sentinel"), Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
6916
7376
  m.cloneMutableInstance;
6917
7377
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6918
7378
  m.cloneMutableTextInstance;
@@ -7281,17 +7741,7 @@ No matching component was found for:
7281
7741
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7282
7742
  })(Og)), Og.exports;
7283
7743
  }
7284
- var Mg = { exports: {} };
7285
- /**
7286
- * @license React
7287
- * react-reconciler.development.js
7288
- *
7289
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7290
- *
7291
- * This source code is licensed under the MIT license found in the
7292
- * LICENSE file in the root directory of this source tree.
7293
- */
7294
- var Rb;
7744
+ var Mg = { exports: {} }, Rb;
7295
7745
  function e0() {
7296
7746
  return Rb || (Rb = 1, (function(Tt) {
7297
7747
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13058,10 +13508,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13058
13508
  function Ic() {
13059
13509
  return di;
13060
13510
  }
13061
- var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy");
13062
- var Ds = Symbol.for("react.activity");
13063
- var Bh = Symbol.for("react.memo_cache_sentinel");
13064
- var ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13511
+ var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy"), Ds = Symbol.for("react.activity"), Bh = Symbol.for("react.memo_cache_sentinel"), ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13065
13512
  m.cloneMutableInstance;
13066
13513
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13067
13514
  m.cloneMutableTextInstance;
@@ -14029,15 +14476,6 @@ function n0() {
14029
14476
  var t0 = n0();
14030
14477
  const r0 = Xb(t0);
14031
14478
 
14032
- /**
14033
- * @license React
14034
- * react-reconciler-constants.production.js
14035
- *
14036
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14037
- *
14038
- * This source code is licensed under the MIT license found in the
14039
- * LICENSE file in the root directory of this source tree.
14040
- */
14041
14479
  const t = 1, o = 8, r = 32, e = 2;
14042
14480
 
14043
14481
  function createReconciler(config) {
@@ -14064,10 +14502,11 @@ function extend(objects) {
14064
14502
  function validateInstance(type, props) {
14065
14503
  const name = toPascalCase(type);
14066
14504
  const target = catalogue[name];
14067
- if (type !== "primitive" && !target)
14505
+ if (type !== "primitive" && !target) {
14068
14506
  throw new Error(
14069
14507
  `R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`
14070
14508
  );
14509
+ }
14071
14510
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14072
14511
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14073
14512
  }
@@ -14231,6 +14670,7 @@ function swapInstances() {
14231
14670
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14232
14671
  instance.object.__r3f = instance;
14233
14672
  setFiberRef(fiber, instance.object);
14673
+ delete instance.appliedOnce;
14234
14674
  applyProps(instance.object, instance.props);
14235
14675
  if (instance.props.attach) {
14236
14676
  attach(parent, instance);
@@ -14304,8 +14744,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14304
14744
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14305
14745
  if (isTailSibling) swapInstances();
14306
14746
  },
14307
- finalizeInitialChildren: () => false,
14308
- 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);
14309
14763
  },
14310
14764
  getPublicInstance: (instance) => instance?.object,
14311
14765
  prepareForCommit: () => null,
@@ -14518,14 +14972,17 @@ function createRoot(canvas) {
14518
14972
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14519
14973
  let onCreated;
14520
14974
  let lastCamera;
14521
- let lastConfiguredProps = {};
14975
+ const lastConfiguredProps = {};
14522
14976
  let configured = false;
14523
14977
  let pending = null;
14524
14978
  return {
14525
14979
  async configure(props = {}) {
14526
14980
  let resolve;
14527
14981
  pending = new Promise((_resolve) => resolve = _resolve);
14528
- let {
14982
+ const {
14983
+ id: canvasId,
14984
+ primaryCanvas,
14985
+ scheduler: schedulerConfig,
14529
14986
  gl: glConfig,
14530
14987
  renderer: rendererConfig,
14531
14988
  size: propsSize,
@@ -14533,10 +14990,7 @@ function createRoot(canvas) {
14533
14990
  events,
14534
14991
  onCreated: onCreatedCallback,
14535
14992
  shadows = false,
14536
- linear = false,
14537
- flat = false,
14538
14993
  textureColorSpace = SRGBColorSpace,
14539
- legacy = false,
14540
14994
  orthographic = false,
14541
14995
  frameloop = "always",
14542
14996
  dpr = [1, 2],
@@ -14547,9 +15001,11 @@ function createRoot(canvas) {
14547
15001
  onDragOverMissed,
14548
15002
  onDropMissed,
14549
15003
  autoUpdateFrustum = true,
14550
- occlusion = false
15004
+ occlusion = false,
15005
+ _sizeProps,
15006
+ forceEven
14551
15007
  } = props;
14552
- let state = store.getState();
15008
+ const state = store.getState();
14553
15009
  const defaultGLProps = {
14554
15010
  canvas,
14555
15011
  powerPreference: "high-performance",
@@ -14557,7 +15013,8 @@ function createRoot(canvas) {
14557
15013
  alpha: true
14558
15014
  };
14559
15015
  const defaultGPUProps = {
14560
- canvas
15016
+ canvas,
15017
+ antialias: true
14561
15018
  };
14562
15019
  const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
14563
15020
  if (glConfig && rendererConfig) {
@@ -14571,10 +15028,35 @@ function createRoot(canvas) {
14571
15028
  });
14572
15029
  }
14573
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
+ }
14574
15036
  if (wantsGL && !state.internal.actualRenderer) {
14575
15037
  renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
14576
15038
  state.internal.actualRenderer = renderer;
14577
- 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
+ }));
14578
15060
  } else if (!wantsGL && !state.internal.actualRenderer) {
14579
15061
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14580
15062
  if (!renderer.hasInitialized?.()) {
@@ -14583,14 +15065,27 @@ function createRoot(canvas) {
14583
15065
  const backend = renderer.backend;
14584
15066
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14585
15067
  state.internal.actualRenderer = renderer;
14586
- 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
+ }
14587
15080
  }
14588
15081
  let raycaster = state.raycaster;
14589
15082
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
14590
15083
  const { params, ...options } = raycastOptions || {};
14591
15084
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14592
- if (!is.equ(params, raycaster.params, shallowLoose))
15085
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14593
15086
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15087
+ }
15088
+ let tempCamera = state.camera;
14594
15089
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14595
15090
  lastCamera = cameraOptions;
14596
15091
  const isCamera = cameraOptions?.isCamera;
@@ -14610,6 +15105,7 @@ function createRoot(canvas) {
14610
15105
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14611
15106
  }
14612
15107
  state.set({ camera });
15108
+ tempCamera = camera;
14613
15109
  raycaster.camera = camera;
14614
15110
  }
14615
15111
  if (!state.scene) {
@@ -14627,7 +15123,7 @@ function createRoot(canvas) {
14627
15123
  rootScene: scene,
14628
15124
  internal: { ...prev.internal, container: scene }
14629
15125
  }));
14630
- const camera = state.camera;
15126
+ const camera = tempCamera;
14631
15127
  if (camera && !camera.parent) scene.add(camera);
14632
15128
  }
14633
15129
  if (events && !state.events.handlers) {
@@ -14641,9 +15137,17 @@ function createRoot(canvas) {
14641
15137
  wasEnabled = enabled;
14642
15138
  });
14643
15139
  }
15140
+ if (_sizeProps !== void 0) {
15141
+ state.set({ _sizeProps });
15142
+ }
15143
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15144
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15145
+ }
14644
15146
  const size = computeInitialSize(canvas, propsSize);
14645
- if (!is.equ(size, state.size, shallowLoose)) {
15147
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15148
+ const wasImperative = state._sizeImperative;
14646
15149
  state.setSize(size.width, size.height, size.top, size.left);
15150
+ if (!wasImperative) state.set({ _sizeImperative: false });
14647
15151
  }
14648
15152
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14649
15153
  state.setDpr(dpr);
@@ -14668,7 +15172,7 @@ function createRoot(canvas) {
14668
15172
  const handleXRFrame = (timestamp, frame) => {
14669
15173
  const state2 = store.getState();
14670
15174
  if (state2.frameloop === "never") return;
14671
- advance(timestamp, true);
15175
+ advance(timestamp);
14672
15176
  };
14673
15177
  const actualRenderer = state.internal.actualRenderer;
14674
15178
  const handleSessionChange = () => {
@@ -14680,16 +15184,16 @@ function createRoot(canvas) {
14680
15184
  };
14681
15185
  const xr = {
14682
15186
  connect() {
14683
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14684
- const actualRenderer2 = renderer2 || gl;
14685
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14686
- 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);
14687
15191
  },
14688
15192
  disconnect() {
14689
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14690
- const actualRenderer2 = renderer2 || gl;
14691
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14692
- 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);
14693
15197
  }
14694
15198
  };
14695
15199
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14713,43 +15217,21 @@ function createRoot(canvas) {
14713
15217
  } else if (is.obj(shadows)) {
14714
15218
  Object.assign(renderer.shadowMap, shadows);
14715
15219
  }
14716
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15220
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14717
15221
  renderer.shadowMap.needsUpdate = true;
14718
- }
14719
- {
14720
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14721
- const linearChanged = linear !== lastConfiguredProps.linear;
14722
- const flatChanged = flat !== lastConfiguredProps.flat;
14723
- if (legacyChanged) {
14724
- if (legacy)
14725
- notifyDepreciated({
14726
- heading: "Legacy Color Management",
14727
- body: "Legacy color management is deprecated and will be removed in a future version.",
14728
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
14729
- });
14730
- }
14731
- if (legacyChanged) {
14732
- ColorManagement.enabled = !legacy;
14733
- lastConfiguredProps.legacy = legacy;
14734
15222
  }
14735
- if (!configured || linearChanged) {
14736
- renderer.outputColorSpace = linear ? LinearSRGBColorSpace : SRGBColorSpace;
14737
- lastConfiguredProps.linear = linear;
14738
- }
14739
- if (!configured || flatChanged) {
14740
- renderer.toneMapping = flat ? NoToneMapping : ACESFilmicToneMapping;
14741
- lastConfiguredProps.flat = flat;
14742
- }
14743
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14744
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14745
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15223
+ }
15224
+ if (!configured) {
15225
+ renderer.outputColorSpace = SRGBColorSpace;
15226
+ renderer.toneMapping = ACESFilmicToneMapping;
14746
15227
  }
14747
15228
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14748
15229
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14749
15230
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14750
15231
  }
14751
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
15232
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14752
15233
  applyProps(renderer, glConfig);
15234
+ }
14753
15235
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14754
15236
  const currentRenderer = state.renderer;
14755
15237
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
@@ -14759,11 +15241,26 @@ function createRoot(canvas) {
14759
15241
  const scheduler = getScheduler();
14760
15242
  const rootId = state.internal.rootId;
14761
15243
  if (!rootId) {
14762
- const newRootId = scheduler.generateRootId();
15244
+ const newRootId = canvasId || scheduler.generateRootId();
14763
15245
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14764
15246
  getState: () => store.getState(),
14765
15247
  onError: (err) => store.getState().setError(err)
14766
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
+ );
14767
15264
  const unregisterFrustum = scheduler.register(
14768
15265
  () => {
14769
15266
  const state2 = store.getState();
@@ -14805,11 +15302,15 @@ function createRoot(canvas) {
14805
15302
  }
14806
15303
  },
14807
15304
  {
14808
- id: `${newRootId}_render`,
15305
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15306
+ id: canvasId || `${newRootId}_render`,
14809
15307
  rootId: newRootId,
14810
15308
  phase: "render",
14811
- system: true
15309
+ system: true,
14812
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 }
14813
15314
  }
14814
15315
  );
14815
15316
  state.set((state2) => ({
@@ -14818,6 +15319,7 @@ function createRoot(canvas) {
14818
15319
  rootId: newRootId,
14819
15320
  unregisterRoot: () => {
14820
15321
  unregisterRoot();
15322
+ unregisterCanvasTarget();
14821
15323
  unregisterFrustum();
14822
15324
  unregisterVisibility();
14823
15325
  unregisterRender();
@@ -14876,15 +15378,24 @@ function unmountComponentAtNode(canvas, callback) {
14876
15378
  const renderer = state.internal.actualRenderer;
14877
15379
  const unregisterRoot = state.internal.unregisterRoot;
14878
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();
14879
15385
  state.events.disconnect?.();
14880
15386
  cleanupHelperGroup(root.store);
14881
- renderer?.renderLists?.dispose?.();
14882
- renderer?.forceContextLoss?.();
14883
- 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
+ }
14884
15395
  dispose(state.scene);
14885
15396
  _roots.delete(canvas);
14886
15397
  if (callback) callback(canvas);
14887
- } catch (e) {
15398
+ } catch {
14888
15399
  }
14889
15400
  }, 500);
14890
15401
  }
@@ -14892,36 +15403,34 @@ function unmountComponentAtNode(canvas, callback) {
14892
15403
  }
14893
15404
  }
14894
15405
  function createPortal(children, container, state) {
14895
- return /* @__PURE__ */ jsx(PortalWrapper, { children, container, state });
15406
+ return /* @__PURE__ */ jsx(Portal, { children, container, state });
14896
15407
  }
14897
- function PortalWrapper({ children, container, state }) {
15408
+ function Portal({ children, container, state }) {
14898
15409
  const isRef = useCallback((obj) => obj && "current" in obj, []);
14899
- const [resolvedContainer, setResolvedContainer] = useState(() => {
15410
+ const [resolvedContainer, _setResolvedContainer] = useState(() => {
14900
15411
  if (isRef(container)) return container.current ?? null;
14901
15412
  return container;
14902
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
+ );
14903
15421
  useMemo(() => {
14904
- if (isRef(container)) {
14905
- const current = container.current;
14906
- if (!current) {
14907
- queueMicrotask(() => {
14908
- const updated = container.current;
14909
- if (updated && updated !== resolvedContainer) {
14910
- setResolvedContainer(updated);
14911
- }
14912
- });
14913
- } else if (current !== resolvedContainer) {
14914
- setResolvedContainer(current);
14915
- }
14916
- } else if (container !== resolvedContainer) {
14917
- setResolvedContainer(container);
15422
+ if (isRef(container) && !container.current) {
15423
+ return queueMicrotask(() => {
15424
+ setResolvedContainer(container.current);
15425
+ });
14918
15426
  }
14919
- }, [container, resolvedContainer, isRef]);
15427
+ setResolvedContainer(container);
15428
+ }, [container, isRef, setResolvedContainer]);
14920
15429
  if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
14921
15430
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14922
- return /* @__PURE__ */ jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15431
+ return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14923
15432
  }
14924
- function Portal({ state = {}, children, container }) {
15433
+ function PortalInner({ state = {}, children, container }) {
14925
15434
  const { events, size, injectScene = true, ...rest } = state;
14926
15435
  const previousRoot = useStore();
14927
15436
  const [raycaster] = useState(() => new Raycaster());
@@ -14942,11 +15451,12 @@ function Portal({ state = {}, children, container }) {
14942
15451
  };
14943
15452
  }, [portalScene, container, injectScene]);
14944
15453
  const inject = useMutableCallback((rootState, injectState) => {
15454
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14945
15455
  let viewport = void 0;
14946
- if (injectState.camera && size) {
15456
+ if (injectState.camera && (size || injectState.size)) {
14947
15457
  const camera = injectState.camera;
14948
- viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), size);
14949
- if (camera !== rootState.camera) updateCamera(camera, size);
15458
+ viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
15459
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14950
15460
  }
14951
15461
  return {
14952
15462
  // The intersect consists of the previous root state
@@ -14963,7 +15473,7 @@ function Portal({ state = {}, children, container }) {
14963
15473
  previousRoot,
14964
15474
  // Events, size and viewport can be overridden by the inject layer
14965
15475
  events: { ...rootState.events, ...injectState.events, ...events },
14966
- size: { ...rootState.size, ...size },
15476
+ size: resolvedSize,
14967
15477
  viewport: { ...rootState.viewport, ...viewport },
14968
15478
  // Layers are allowed to override events
14969
15479
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14997,15 +15507,13 @@ function CanvasImpl({
14997
15507
  fallback,
14998
15508
  resize,
14999
15509
  style,
15510
+ id,
15000
15511
  gl,
15001
- renderer,
15512
+ renderer: rendererProp,
15002
15513
  events = createPointerEvents,
15003
15514
  eventSource,
15004
15515
  eventPrefix,
15005
15516
  shadows,
15006
- linear,
15007
- flat,
15008
- legacy,
15009
15517
  orthographic,
15010
15518
  frameloop,
15011
15519
  dpr,
@@ -15017,10 +15525,46 @@ function CanvasImpl({
15017
15525
  onDragOverMissed,
15018
15526
  onDropMissed,
15019
15527
  onCreated,
15528
+ hmr,
15529
+ width,
15530
+ height,
15531
+ background,
15532
+ forceEven,
15020
15533
  ...props
15021
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;
15022
15537
  React.useMemo(() => extend(THREE), []);
15023
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]);
15024
15568
  const hasInitialSizeRef = React.useRef(false);
15025
15569
  const measureConfig = React.useMemo(() => {
15026
15570
  if (!hasInitialSizeRef.current) {
@@ -15037,7 +15581,21 @@ function CanvasImpl({
15037
15581
  };
15038
15582
  }, [resize, hasInitialSizeRef.current]);
15039
15583
  const [containerRef, containerRect] = useMeasure(measureConfig);
15040
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
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,
15594
+ top: containerRect.top,
15595
+ left: containerRect.left
15596
+ };
15597
+ }, [width, height, containerRect, forceEven]);
15598
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15041
15599
  hasInitialSizeRef.current = true;
15042
15600
  }
15043
15601
  const canvasRef = React.useRef(null);
@@ -15056,7 +15614,7 @@ function CanvasImpl({
15056
15614
  useIsomorphicLayoutEffect(() => {
15057
15615
  effectActiveRef.current = true;
15058
15616
  const canvas = canvasRef.current;
15059
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15617
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15060
15618
  if (!root.current) {
15061
15619
  root.current = createRoot(canvas);
15062
15620
  notifyAlpha({
@@ -15076,21 +15634,24 @@ function CanvasImpl({
15076
15634
  async function run() {
15077
15635
  if (!effectActiveRef.current || !root.current) return;
15078
15636
  await root.current.configure({
15637
+ id,
15638
+ primaryCanvas,
15639
+ scheduler,
15079
15640
  gl,
15080
15641
  renderer,
15081
15642
  scene,
15082
15643
  events,
15083
15644
  shadows,
15084
- linear,
15085
- flat,
15086
- legacy,
15087
15645
  orthographic,
15088
15646
  frameloop,
15089
15647
  dpr,
15090
15648
  performance,
15091
15649
  raycaster,
15092
15650
  camera,
15093
- size: containerRect,
15651
+ size: effectiveSize,
15652
+ // Store size props for reset functionality
15653
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15654
+ forceEven,
15094
15655
  // Pass mutable reference to onPointerMissed so it's free to update
15095
15656
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15096
15657
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15114,7 +15675,10 @@ function CanvasImpl({
15114
15675
  });
15115
15676
  if (!effectActiveRef.current || !root.current) return;
15116
15677
  root.current.render(
15117
- /* @__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
+ ] }) }) })
15118
15682
  );
15119
15683
  }
15120
15684
  run();
@@ -15136,6 +15700,35 @@ function CanvasImpl({
15136
15700
  };
15137
15701
  }
15138
15702
  }, []);
15703
+ React.useEffect(() => {
15704
+ if (hmr === false) return;
15705
+ const canvas = canvasRef.current;
15706
+ if (!canvas) return;
15707
+ const handleHMR = () => {
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
+ });
15718
+ };
15719
+ if (typeof import.meta !== "undefined" && import.meta.hot) {
15720
+ const hot = import.meta.hot;
15721
+ hot.on("vite:afterUpdate", handleHMR);
15722
+ return () => hot.dispose?.(() => {
15723
+ });
15724
+ }
15725
+ if (typeof module !== "undefined" && module.hot) {
15726
+ const hot = module.hot;
15727
+ hot.addStatusHandler((status) => {
15728
+ if (status === "idle") handleHMR();
15729
+ });
15730
+ }
15731
+ }, [hmr]);
15139
15732
  const pointerEvents = eventSource ? "none" : "auto";
15140
15733
  return /* @__PURE__ */ jsx(
15141
15734
  "div",
@@ -15150,7 +15743,7 @@ function CanvasImpl({
15150
15743
  ...style
15151
15744
  },
15152
15745
  ...props,
15153
- 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 }) })
15154
15747
  }
15155
15748
  );
15156
15749
  }
@@ -15160,4 +15753,4 @@ function Canvas(props) {
15160
15753
 
15161
15754
  extend(THREE);
15162
15755
 
15163
- 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 };