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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,10 +7,17 @@ const jsxRuntime = require('react/jsx-runtime');
7
7
  const React = require('react');
8
8
  const useMeasure = require('react-use-measure');
9
9
  const itsFine = require('its-fine');
10
+ const fiber = require('@react-three/fiber');
11
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
12
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
13
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
14
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
15
+ const gainmapJs = require('@monogrid/gainmap-js');
10
16
  const Tb = require('scheduler');
11
17
  const traditional = require('zustand/traditional');
12
18
  const suspendReact = require('suspend-react');
13
19
 
20
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
21
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
15
22
 
16
23
  function _interopNamespaceCompat(e) {
@@ -56,6 +63,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
56
63
  WebGLRenderer: three.WebGLRenderer
57
64
  }, [webgpu__namespace]);
58
65
 
66
+ const primaryRegistry = /* @__PURE__ */ new Map();
67
+ const pendingSubscribers = /* @__PURE__ */ new Map();
68
+ function registerPrimary(id, renderer, store) {
69
+ if (primaryRegistry.has(id)) {
70
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
71
+ }
72
+ const entry = { renderer, store };
73
+ primaryRegistry.set(id, entry);
74
+ const subscribers = pendingSubscribers.get(id);
75
+ if (subscribers) {
76
+ subscribers.forEach((callback) => callback(entry));
77
+ pendingSubscribers.delete(id);
78
+ }
79
+ return () => {
80
+ const currentEntry = primaryRegistry.get(id);
81
+ if (currentEntry?.renderer === renderer) {
82
+ primaryRegistry.delete(id);
83
+ }
84
+ };
85
+ }
86
+ function getPrimary(id) {
87
+ return primaryRegistry.get(id);
88
+ }
89
+ function waitForPrimary(id, timeout = 5e3) {
90
+ const existing = primaryRegistry.get(id);
91
+ if (existing) {
92
+ return Promise.resolve(existing);
93
+ }
94
+ return new Promise((resolve, reject) => {
95
+ const timeoutId = setTimeout(() => {
96
+ const subscribers = pendingSubscribers.get(id);
97
+ if (subscribers) {
98
+ const index = subscribers.indexOf(callback);
99
+ if (index !== -1) subscribers.splice(index, 1);
100
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
101
+ }
102
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
103
+ }, timeout);
104
+ const callback = (entry) => {
105
+ clearTimeout(timeoutId);
106
+ resolve(entry);
107
+ };
108
+ if (!pendingSubscribers.has(id)) {
109
+ pendingSubscribers.set(id, []);
110
+ }
111
+ pendingSubscribers.get(id).push(callback);
112
+ });
113
+ }
114
+ function hasPrimary(id) {
115
+ return primaryRegistry.has(id);
116
+ }
117
+ function unregisterPrimary(id) {
118
+ primaryRegistry.delete(id);
119
+ }
120
+ function getPrimaryIds() {
121
+ return Array.from(primaryRegistry.keys());
122
+ }
123
+
124
+ const presetsObj = {
125
+ apartment: "lebombo_1k.hdr",
126
+ city: "potsdamer_platz_1k.hdr",
127
+ dawn: "kiara_1_dawn_1k.hdr",
128
+ forest: "forest_slope_1k.hdr",
129
+ lobby: "st_fagans_interior_1k.hdr",
130
+ night: "dikhololo_night_1k.hdr",
131
+ park: "rooitou_park_1k.hdr",
132
+ studio: "studio_small_03_1k.hdr",
133
+ sunset: "venice_sunset_1k.hdr",
134
+ warehouse: "empty_warehouse_01_1k.hdr"
135
+ };
136
+
137
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
138
+ const isArray = (arr) => Array.isArray(arr);
139
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
140
+ function useEnvironment({
141
+ files = defaultFiles,
142
+ path = "",
143
+ preset = void 0,
144
+ colorSpace = void 0,
145
+ extensions
146
+ } = {}) {
147
+ if (preset) {
148
+ validatePreset(preset);
149
+ files = presetsObj[preset];
150
+ path = CUBEMAP_ROOT;
151
+ }
152
+ const multiFile = isArray(files);
153
+ const { extension, isCubemap } = getExtension(files);
154
+ const loader = getLoader$1(extension);
155
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
156
+ const renderer = fiber.useThree((state) => state.renderer);
157
+ React.useLayoutEffect(() => {
158
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
159
+ function clearGainmapTexture() {
160
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
161
+ }
162
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
163
+ }, [extension, files, loader, multiFile, renderer.domElement]);
164
+ const loaderResult = fiber.useLoader(
165
+ loader,
166
+ multiFile ? [files] : files,
167
+ (loader2) => {
168
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
169
+ loader2.setRenderer?.(renderer);
170
+ }
171
+ loader2.setPath?.(path);
172
+ if (extensions) extensions(loader2);
173
+ }
174
+ );
175
+ let texture = multiFile ? (
176
+ // @ts-ignore
177
+ loaderResult[0]
178
+ ) : loaderResult;
179
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
180
+ texture = texture.renderTarget?.texture;
181
+ }
182
+ texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
183
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
184
+ return texture;
185
+ }
186
+ const preloadDefaultOptions = {
187
+ files: defaultFiles,
188
+ path: "",
189
+ preset: void 0,
190
+ extensions: void 0
191
+ };
192
+ useEnvironment.preload = (preloadOptions) => {
193
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
194
+ let { files, path = "" } = options;
195
+ const { preset, extensions } = options;
196
+ if (preset) {
197
+ validatePreset(preset);
198
+ files = presetsObj[preset];
199
+ path = CUBEMAP_ROOT;
200
+ }
201
+ const { extension } = getExtension(files);
202
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
203
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
204
+ }
205
+ const loader = getLoader$1(extension);
206
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
207
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
208
+ loader2.setPath?.(path);
209
+ if (extensions) extensions(loader2);
210
+ });
211
+ };
212
+ const clearDefaultOptins = {
213
+ files: defaultFiles,
214
+ preset: void 0
215
+ };
216
+ useEnvironment.clear = (clearOptions) => {
217
+ const options = { ...clearDefaultOptins, ...clearOptions };
218
+ let { files } = options;
219
+ const { preset } = options;
220
+ if (preset) {
221
+ validatePreset(preset);
222
+ files = presetsObj[preset];
223
+ }
224
+ const { extension } = getExtension(files);
225
+ const loader = getLoader$1(extension);
226
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
227
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
228
+ };
229
+ function validatePreset(preset) {
230
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
231
+ }
232
+ function getExtension(files) {
233
+ const isCubemap = isArray(files) && files.length === 6;
234
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
235
+ const firstEntry = isArray(files) ? files[0] : files;
236
+ const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
237
+ return { extension, isCubemap, isGainmap };
238
+ }
239
+ function getLoader$1(extension) {
240
+ const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
241
+ return loader;
242
+ }
243
+
244
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
245
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
246
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
247
+ sceneProps = {
248
+ backgroundBlurriness: 0,
249
+ backgroundIntensity: 1,
250
+ backgroundRotation: [0, 0, 0],
251
+ environmentIntensity: 1,
252
+ environmentRotation: [0, 0, 0],
253
+ ...sceneProps
254
+ };
255
+ const target = resolveScene(scene || defaultScene);
256
+ const oldbg = target.background;
257
+ const oldenv = target.environment;
258
+ const oldSceneProps = {
259
+ // @ts-ignore
260
+ backgroundBlurriness: target.backgroundBlurriness,
261
+ // @ts-ignore
262
+ backgroundIntensity: target.backgroundIntensity,
263
+ // @ts-ignore
264
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
265
+ // @ts-ignore
266
+ environmentIntensity: target.environmentIntensity,
267
+ // @ts-ignore
268
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
269
+ };
270
+ if (background !== "only") target.environment = texture;
271
+ if (background) target.background = texture;
272
+ fiber.applyProps(target, sceneProps);
273
+ return () => {
274
+ if (background !== "only") target.environment = oldenv;
275
+ if (background) target.background = oldbg;
276
+ fiber.applyProps(target, oldSceneProps);
277
+ };
278
+ }
279
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
280
+ const defaultScene = fiber.useThree((state) => state.scene);
281
+ React__namespace.useLayoutEffect(() => {
282
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
283
+ });
284
+ return null;
285
+ }
286
+ function EnvironmentCube({
287
+ background = false,
288
+ scene,
289
+ blur,
290
+ backgroundBlurriness,
291
+ backgroundIntensity,
292
+ backgroundRotation,
293
+ environmentIntensity,
294
+ environmentRotation,
295
+ ...rest
296
+ }) {
297
+ const texture = useEnvironment(rest);
298
+ const defaultScene = fiber.useThree((state) => state.scene);
299
+ React__namespace.useLayoutEffect(() => {
300
+ return setEnvProps(background, scene, defaultScene, texture, {
301
+ backgroundBlurriness: blur ?? backgroundBlurriness,
302
+ backgroundIntensity,
303
+ backgroundRotation,
304
+ environmentIntensity,
305
+ environmentRotation
306
+ });
307
+ });
308
+ React__namespace.useEffect(() => {
309
+ return () => {
310
+ texture.dispose();
311
+ };
312
+ }, [texture]);
313
+ return null;
314
+ }
315
+ function EnvironmentPortal({
316
+ children,
317
+ near = 0.1,
318
+ far = 1e3,
319
+ resolution = 256,
320
+ frames = 1,
321
+ map,
322
+ background = false,
323
+ blur,
324
+ backgroundBlurriness,
325
+ backgroundIntensity,
326
+ backgroundRotation,
327
+ environmentIntensity,
328
+ environmentRotation,
329
+ scene,
330
+ files,
331
+ path,
332
+ preset = void 0,
333
+ extensions
334
+ }) {
335
+ const gl = fiber.useThree((state) => state.gl);
336
+ const defaultScene = fiber.useThree((state) => state.scene);
337
+ const camera = React__namespace.useRef(null);
338
+ const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
339
+ const fbo = React__namespace.useMemo(() => {
340
+ const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
341
+ fbo2.texture.type = webgpu.HalfFloatType;
342
+ return fbo2;
343
+ }, [resolution]);
344
+ React__namespace.useEffect(() => {
345
+ return () => {
346
+ fbo.dispose();
347
+ };
348
+ }, [fbo]);
349
+ React__namespace.useLayoutEffect(() => {
350
+ if (frames === 1) {
351
+ const autoClear = gl.autoClear;
352
+ gl.autoClear = true;
353
+ camera.current.update(gl, virtualScene);
354
+ gl.autoClear = autoClear;
355
+ }
356
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
357
+ backgroundBlurriness: blur ?? backgroundBlurriness,
358
+ backgroundIntensity,
359
+ backgroundRotation,
360
+ environmentIntensity,
361
+ environmentRotation
362
+ });
363
+ }, [
364
+ children,
365
+ virtualScene,
366
+ fbo.texture,
367
+ scene,
368
+ defaultScene,
369
+ background,
370
+ frames,
371
+ gl,
372
+ blur,
373
+ backgroundBlurriness,
374
+ backgroundIntensity,
375
+ backgroundRotation,
376
+ environmentIntensity,
377
+ environmentRotation
378
+ ]);
379
+ let count = 1;
380
+ fiber.useFrame(() => {
381
+ if (frames === Infinity || count < frames) {
382
+ const autoClear = gl.autoClear;
383
+ gl.autoClear = true;
384
+ camera.current.update(gl, virtualScene);
385
+ gl.autoClear = autoClear;
386
+ count++;
387
+ }
388
+ });
389
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
390
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
391
+ children,
392
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
393
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
394
+ ] }),
395
+ virtualScene
396
+ ) });
397
+ }
398
+ function EnvironmentGround(props) {
399
+ const textureDefault = useEnvironment(props);
400
+ const texture = props.map || textureDefault;
401
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
402
+ React__namespace.useEffect(() => {
403
+ return () => {
404
+ textureDefault.dispose();
405
+ };
406
+ }, [textureDefault]);
407
+ const height = props.ground?.height ?? 15;
408
+ const radius = props.ground?.radius ?? 60;
409
+ const scale = props.ground?.scale ?? 1e3;
410
+ const args = React__namespace.useMemo(
411
+ () => [texture, height, radius],
412
+ [texture, height, radius]
413
+ );
414
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
415
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
416
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
417
+ ] });
418
+ }
419
+ function EnvironmentColor({ color, scene }) {
420
+ const defaultScene = fiber.useThree((state) => state.scene);
421
+ React__namespace.useLayoutEffect(() => {
422
+ if (color === void 0) return;
423
+ const target = resolveScene(scene || defaultScene);
424
+ const oldBg = target.background;
425
+ target.background = new webgpu.Color(color);
426
+ return () => {
427
+ target.background = oldBg;
428
+ };
429
+ });
430
+ return null;
431
+ }
432
+ function EnvironmentDualSource(props) {
433
+ const { backgroundFiles, ...envProps } = props;
434
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
435
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
436
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
437
+ ] });
438
+ }
439
+ function Environment(props) {
440
+ if (props.color && !props.files && !props.preset && !props.map) {
441
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
442
+ }
443
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
444
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
445
+ }
446
+ return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
447
+ }
448
+
59
449
  var __defProp$2 = Object.defineProperty;
60
450
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
61
451
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -235,7 +625,8 @@ function prepare(target, root, type, props) {
235
625
  object,
236
626
  eventCount: 0,
237
627
  handlers: {},
238
- isHidden: false
628
+ isHidden: false,
629
+ deferredRefs: []
239
630
  };
240
631
  if (object) object.__r3f = instance;
241
632
  }
@@ -284,7 +675,7 @@ function createOcclusionObserverNode(store, uniform) {
284
675
  let occlusionSetupPromise = null;
285
676
  function enableOcclusion(store) {
286
677
  const state = store.getState();
287
- const { internal, renderer, rootScene } = state;
678
+ const { internal, renderer } = state;
288
679
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
289
680
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
290
681
  if (!hasOcclusionSupport) {
@@ -447,6 +838,22 @@ function hasVisibilityHandlers(handlers) {
447
838
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
448
839
  }
449
840
 
841
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
842
+ function fromRef(ref) {
843
+ return { [FROM_REF]: ref };
844
+ }
845
+ function isFromRef(value) {
846
+ return value !== null && typeof value === "object" && FROM_REF in value;
847
+ }
848
+
849
+ const ONCE = Symbol.for("@react-three/fiber.once");
850
+ function once(...args) {
851
+ return { [ONCE]: args.length ? args : true };
852
+ }
853
+ function isOnce(value) {
854
+ return value !== null && typeof value === "object" && ONCE in value;
855
+ }
856
+
450
857
  const RESERVED_PROPS = [
451
858
  "children",
452
859
  "key",
@@ -517,7 +924,7 @@ function getMemoizedPrototype(root) {
517
924
  ctor = new root.constructor();
518
925
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
519
926
  }
520
- } catch (e) {
927
+ } catch {
521
928
  }
522
929
  return ctor;
523
930
  }
@@ -548,7 +955,7 @@ function applyProps(object, props) {
548
955
  const rootState = instance && findInitialRoot(instance).getState();
549
956
  const prevHandlers = instance?.eventCount;
550
957
  for (const prop in props) {
551
- let value = props[prop];
958
+ const value = props[prop];
552
959
  if (RESERVED_PROPS.includes(prop)) continue;
553
960
  if (instance && EVENT_REGEX.test(prop)) {
554
961
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -563,6 +970,25 @@ function applyProps(object, props) {
563
970
  continue;
564
971
  }
565
972
  if (value === void 0) continue;
973
+ if (isFromRef(value)) {
974
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
975
+ continue;
976
+ }
977
+ if (isOnce(value)) {
978
+ if (instance?.appliedOnce?.has(prop)) continue;
979
+ if (instance) {
980
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
981
+ instance.appliedOnce.add(prop);
982
+ }
983
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
984
+ const args = value[ONCE];
985
+ if (typeof targetRoot[targetKey] === "function") {
986
+ targetRoot[targetKey](...args === true ? [] : args);
987
+ } else if (args !== true && args.length > 0) {
988
+ targetRoot[targetKey] = args[0];
989
+ }
990
+ continue;
991
+ }
566
992
  let { root, key, target } = resolve(object, prop);
567
993
  if (target === void 0 && (typeof root !== "object" || root === null)) {
568
994
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -585,7 +1011,10 @@ function applyProps(object, props) {
585
1011
  else target.set(value);
586
1012
  } else {
587
1013
  root[key] = value;
588
- 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
1014
+ if (key.endsWith("Node") && root.isMaterial) {
1015
+ root.needsUpdate = true;
1016
+ }
1017
+ if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
589
1018
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
590
1019
  root[key].colorSpace = rootState.textureColorSpace;
591
1020
  }
@@ -618,38 +1047,60 @@ function applyProps(object, props) {
618
1047
  return object;
619
1048
  }
620
1049
 
1050
+ const DEFAULT_POINTER_ID = 0;
1051
+ const XR_POINTER_ID_START = 1e3;
1052
+ function getPointerState(internal, pointerId) {
1053
+ let state = internal.pointerMap.get(pointerId);
1054
+ if (!state) {
1055
+ state = {
1056
+ hovered: /* @__PURE__ */ new Map(),
1057
+ captured: /* @__PURE__ */ new Map(),
1058
+ initialClick: [0, 0],
1059
+ initialHits: []
1060
+ };
1061
+ internal.pointerMap.set(pointerId, state);
1062
+ }
1063
+ return state;
1064
+ }
1065
+ function getPointerId(event) {
1066
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1067
+ }
621
1068
  function makeId(event) {
622
1069
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
623
1070
  }
624
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
625
- const captureData = captures.get(obj);
1071
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1072
+ const pointerState = internal.pointerMap.get(pointerId);
1073
+ if (!pointerState) return;
1074
+ const captureData = pointerState.captured.get(obj);
626
1075
  if (captureData) {
627
- captures.delete(obj);
628
- if (captures.size === 0) {
629
- capturedMap.delete(pointerId);
630
- captureData.target.releasePointerCapture(pointerId);
631
- }
1076
+ pointerState.captured.delete(obj);
1077
+ captureData.target.releasePointerCapture(pointerId);
632
1078
  }
633
1079
  }
634
1080
  function removeInteractivity(store, object) {
635
1081
  const { internal } = store.getState();
636
1082
  internal.interaction = internal.interaction.filter((o) => o !== object);
637
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
638
- internal.hovered.forEach((value, key) => {
639
- if (value.eventObject === object || value.object === object) {
640
- internal.hovered.delete(key);
1083
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1084
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1085
+ pointerState.hovered.forEach((value, key) => {
1086
+ if (value.eventObject === object || value.object === object) {
1087
+ pointerState.hovered.delete(key);
1088
+ }
1089
+ });
1090
+ if (pointerState.captured.has(object)) {
1091
+ releaseInternalPointerCapture(internal, object, pointerId);
641
1092
  }
642
- });
643
- internal.capturedMap.forEach((captures, pointerId) => {
644
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
645
- });
1093
+ }
646
1094
  unregisterVisibility(store, object);
647
1095
  }
648
1096
  function createEvents(store) {
649
- function calculateDistance(event) {
1097
+ function calculateDistance(event, pointerId) {
650
1098
  const { internal } = store.getState();
651
- const dx = event.offsetX - internal.initialClick[0];
652
- const dy = event.offsetY - internal.initialClick[1];
1099
+ const pointerState = internal.pointerMap.get(pointerId);
1100
+ if (!pointerState) return 0;
1101
+ const [initialX, initialY] = pointerState.initialClick;
1102
+ const dx = event.offsetX - initialX;
1103
+ const dy = event.offsetY - initialY;
653
1104
  return Math.round(Math.sqrt(dx * dx + dy * dy));
654
1105
  }
655
1106
  function filterPointerEvents(objects) {
@@ -685,6 +1136,15 @@ function createEvents(store) {
685
1136
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
686
1137
  }
687
1138
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1139
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1140
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1141
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1142
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1143
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1144
+ if (aInteractivePriority !== bInteractivePriority) {
1145
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1146
+ }
1147
+ }
688
1148
  const aState = getRootState(a.object);
689
1149
  const bState = getRootState(b.object);
690
1150
  const aPriority = aState?.events?.priority ?? 1;
@@ -700,14 +1160,19 @@ function createEvents(store) {
700
1160
  for (const hit of hits) {
701
1161
  let eventObject = hit.object;
702
1162
  while (eventObject) {
703
- if (eventObject.__r3f?.eventCount)
1163
+ if (eventObject.__r3f?.eventCount) {
704
1164
  intersections.push({ ...hit, eventObject });
1165
+ }
705
1166
  eventObject = eventObject.parent;
706
1167
  }
707
1168
  }
708
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
709
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
710
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1169
+ if ("pointerId" in event) {
1170
+ const pointerId = event.pointerId;
1171
+ const pointerState = state.internal.pointerMap.get(pointerId);
1172
+ if (pointerState?.captured.size) {
1173
+ for (const captureData of pointerState.captured.values()) {
1174
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1175
+ }
711
1176
  }
712
1177
  }
713
1178
  return intersections;
@@ -720,28 +1185,26 @@ function createEvents(store) {
720
1185
  if (state) {
721
1186
  const { raycaster, pointer, camera, internal } = state;
722
1187
  const unprojectedPoint = new webgpu.Vector3(pointer.x, pointer.y, 0).unproject(camera);
723
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1188
+ const hasPointerCapture = (id) => {
1189
+ const pointerState = internal.pointerMap.get(id);
1190
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1191
+ };
724
1192
  const setPointerCapture = (id) => {
725
1193
  const captureData = { intersection: hit, target: event.target };
726
- if (internal.capturedMap.has(id)) {
727
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
728
- } else {
729
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
730
- }
1194
+ const pointerState = getPointerState(internal, id);
1195
+ pointerState.captured.set(hit.eventObject, captureData);
731
1196
  event.target.setPointerCapture(id);
732
1197
  };
733
1198
  const releasePointerCapture = (id) => {
734
- const captures = internal.capturedMap.get(id);
735
- if (captures) {
736
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
737
- }
1199
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
738
1200
  };
739
- let extractEventProps = {};
740
- for (let prop in event) {
741
- let property = event[prop];
1201
+ const extractEventProps = {};
1202
+ for (const prop in event) {
1203
+ const property = event[prop];
742
1204
  if (typeof property !== "function") extractEventProps[prop] = property;
743
1205
  }
744
- let raycastEvent = {
1206
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
1207
+ const raycastEvent = {
745
1208
  ...hit,
746
1209
  ...extractEventProps,
747
1210
  pointer,
@@ -751,18 +1214,19 @@ function createEvents(store) {
751
1214
  unprojectedPoint,
752
1215
  ray: raycaster.ray,
753
1216
  camera,
1217
+ pointerId: eventPointerId,
754
1218
  // Hijack stopPropagation, which just sets a flag
755
1219
  stopPropagation() {
756
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1220
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
757
1221
  if (
758
1222
  // ...if this pointer hasn't been captured
759
- !capturesForPointer || // ... or if the hit object is capturing the pointer
760
- capturesForPointer.has(hit.eventObject)
1223
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1224
+ pointerState.captured.has(hit.eventObject)
761
1225
  ) {
762
1226
  raycastEvent.stopped = localState.stopped = true;
763
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1227
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
764
1228
  const higher = intersections.slice(0, intersections.indexOf(hit));
765
- cancelPointer([...higher, hit]);
1229
+ cancelPointer([...higher, hit], eventPointerId);
766
1230
  }
767
1231
  }
768
1232
  },
@@ -778,15 +1242,18 @@ function createEvents(store) {
778
1242
  }
779
1243
  return intersections;
780
1244
  }
781
- function cancelPointer(intersections) {
1245
+ function cancelPointer(intersections, pointerId) {
782
1246
  const { internal } = store.getState();
783
- for (const hoveredObj of internal.hovered.values()) {
1247
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1248
+ const pointerState = internal.pointerMap.get(pid);
1249
+ if (!pointerState) return;
1250
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
784
1251
  if (!intersections.length || !intersections.find(
785
1252
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
786
1253
  )) {
787
1254
  const eventObject = hoveredObj.eventObject;
788
1255
  const instance = eventObject.__r3f;
789
- internal.hovered.delete(makeId(hoveredObj));
1256
+ pointerState.hovered.delete(hoveredId);
790
1257
  if (instance?.eventCount) {
791
1258
  const handlers = instance.handlers;
792
1259
  const data = { ...hoveredObj, intersections };
@@ -815,41 +1282,118 @@ function createEvents(store) {
815
1282
  instance?.handlers.onDropMissed?.(event);
816
1283
  }
817
1284
  }
1285
+ function cleanupPointer(pointerId) {
1286
+ const { internal } = store.getState();
1287
+ const pointerState = internal.pointerMap.get(pointerId);
1288
+ if (pointerState) {
1289
+ for (const [, hoveredObj] of pointerState.hovered) {
1290
+ const eventObject = hoveredObj.eventObject;
1291
+ const instance = eventObject.__r3f;
1292
+ if (instance?.eventCount) {
1293
+ const handlers = instance.handlers;
1294
+ const data = { ...hoveredObj, intersections: [] };
1295
+ handlers.onPointerOut?.(data);
1296
+ handlers.onPointerLeave?.(data);
1297
+ }
1298
+ }
1299
+ internal.pointerMap.delete(pointerId);
1300
+ }
1301
+ internal.pointerDirty.delete(pointerId);
1302
+ }
1303
+ function processDeferredPointer(event, pointerId) {
1304
+ const state = store.getState();
1305
+ const { internal } = state;
1306
+ if (!state.events.enabled) return;
1307
+ const filter = filterPointerEvents;
1308
+ const hits = intersect(event, filter);
1309
+ cancelPointer(hits, pointerId);
1310
+ function onIntersect(data) {
1311
+ const eventObject = data.eventObject;
1312
+ const instance = eventObject.__r3f;
1313
+ if (!instance?.eventCount) return;
1314
+ const handlers = instance.handlers;
1315
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1316
+ const id = makeId(data);
1317
+ const pointerState = getPointerState(internal, pointerId);
1318
+ const hoveredItem = pointerState.hovered.get(id);
1319
+ if (!hoveredItem) {
1320
+ pointerState.hovered.set(id, data);
1321
+ handlers.onPointerOver?.(data);
1322
+ handlers.onPointerEnter?.(data);
1323
+ } else if (hoveredItem.stopped) {
1324
+ data.stopPropagation();
1325
+ }
1326
+ }
1327
+ handlers.onPointerMove?.(data);
1328
+ }
1329
+ handleIntersects(hits, event, 0, onIntersect);
1330
+ }
818
1331
  function handlePointer(name) {
819
1332
  switch (name) {
820
1333
  case "onPointerLeave":
821
- case "onPointerCancel":
822
1334
  case "onDragLeave":
823
1335
  return () => cancelPointer([]);
1336
+ // Global cancel of these events
1337
+ case "onPointerCancel":
1338
+ return (event) => {
1339
+ const pointerId = getPointerId(event);
1340
+ cleanupPointer(pointerId);
1341
+ };
824
1342
  case "onLostPointerCapture":
825
1343
  return (event) => {
826
1344
  const { internal } = store.getState();
827
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1345
+ const pointerId = getPointerId(event);
1346
+ const pointerState = internal.pointerMap.get(pointerId);
1347
+ if (pointerState?.captured.size) {
828
1348
  requestAnimationFrame(() => {
829
- if (internal.capturedMap.has(event.pointerId)) {
830
- internal.capturedMap.delete(event.pointerId);
831
- cancelPointer([]);
1349
+ const pointerState2 = internal.pointerMap.get(pointerId);
1350
+ if (pointerState2?.captured.size) {
1351
+ pointerState2.captured.clear();
832
1352
  }
1353
+ cancelPointer([], pointerId);
833
1354
  });
834
1355
  }
835
1356
  };
836
1357
  }
837
1358
  return function handleEvent(event) {
838
1359
  const state = store.getState();
839
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1360
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1361
+ const pointerId = getPointerId(event);
840
1362
  internal.lastEvent.current = event;
841
- if (!state.events.enabled) return;
1363
+ if (!events.enabled) return;
842
1364
  const isPointerMove = name === "onPointerMove";
843
1365
  const isDragOver = name === "onDragOver";
844
1366
  const isDrop = name === "onDrop";
845
1367
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1368
+ const isPointerDown = name === "onPointerDown";
1369
+ const isPointerUp = name === "onPointerUp";
1370
+ const isWheel = name === "onWheel";
1371
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1372
+ if (isPointerMove && canDeferRaycasts) {
1373
+ events.compute?.(event, state);
1374
+ internal.pointerDirty.set(pointerId, event);
1375
+ return;
1376
+ }
1377
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1378
+ events.compute?.(event, state);
1379
+ internal.pointerDirty.set(pointerId, event);
1380
+ return;
1381
+ }
1382
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1383
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1384
+ internal.pointerDirty.delete(pointerId);
1385
+ processDeferredPointer(deferredEvent, pointerId);
1386
+ }
846
1387
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
847
1388
  const hits = intersect(event, filter);
848
- const delta = isClickEvent ? calculateDistance(event) : 0;
849
- if (name === "onPointerDown") {
850
- internal.initialClick = [event.offsetX, event.offsetY];
851
- internal.initialHits = hits.map((hit) => hit.eventObject);
852
- }
1389
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1390
+ if (isPointerDown) {
1391
+ const pointerState2 = getPointerState(internal, pointerId);
1392
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1393
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1394
+ }
1395
+ const pointerState = internal.pointerMap.get(pointerId);
1396
+ const initialHits = pointerState?.initialHits ?? [];
853
1397
  if (isClickEvent && !hits.length) {
854
1398
  if (delta <= 2) {
855
1399
  pointerMissed(event, internal.interaction);
@@ -864,7 +1408,9 @@ function createEvents(store) {
864
1408
  dropMissed(event, internal.interaction);
865
1409
  if (onDropMissed) onDropMissed(event);
866
1410
  }
867
- if (isPointerMove || isDragOver) cancelPointer(hits);
1411
+ if (isPointerMove || isDragOver) {
1412
+ cancelPointer(hits, pointerId);
1413
+ }
868
1414
  function onIntersect(data) {
869
1415
  const eventObject = data.eventObject;
870
1416
  const instance = eventObject.__r3f;
@@ -873,9 +1419,10 @@ function createEvents(store) {
873
1419
  if (isPointerMove) {
874
1420
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
875
1421
  const id = makeId(data);
876
- const hoveredItem = internal.hovered.get(id);
1422
+ const pointerState2 = getPointerState(internal, pointerId);
1423
+ const hoveredItem = pointerState2.hovered.get(id);
877
1424
  if (!hoveredItem) {
878
- internal.hovered.set(id, data);
1425
+ pointerState2.hovered.set(id, data);
879
1426
  handlers.onPointerOver?.(data);
880
1427
  handlers.onPointerEnter?.(data);
881
1428
  } else if (hoveredItem.stopped) {
@@ -885,9 +1432,10 @@ function createEvents(store) {
885
1432
  handlers.onPointerMove?.(data);
886
1433
  } else if (isDragOver) {
887
1434
  const id = makeId(data);
888
- const hoveredItem = internal.hovered.get(id);
1435
+ const pointerState2 = getPointerState(internal, pointerId);
1436
+ const hoveredItem = pointerState2.hovered.get(id);
889
1437
  if (!hoveredItem) {
890
- internal.hovered.set(id, data);
1438
+ pointerState2.hovered.set(id, data);
891
1439
  handlers.onDragOverEnter?.(data);
892
1440
  } else if (hoveredItem.stopped) {
893
1441
  data.stopPropagation();
@@ -898,18 +1446,18 @@ function createEvents(store) {
898
1446
  } else {
899
1447
  const handler = handlers[name];
900
1448
  if (handler) {
901
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1449
+ if (!isClickEvent || initialHits.includes(eventObject)) {
902
1450
  pointerMissed(
903
1451
  event,
904
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1452
+ internal.interaction.filter((object) => !initialHits.includes(object))
905
1453
  );
906
1454
  handler(data);
907
1455
  }
908
1456
  } else {
909
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1457
+ if (isClickEvent && initialHits.includes(eventObject)) {
910
1458
  pointerMissed(
911
1459
  event,
912
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1460
+ internal.interaction.filter((object) => !initialHits.includes(object))
913
1461
  );
914
1462
  }
915
1463
  }
@@ -918,7 +1466,15 @@ function createEvents(store) {
918
1466
  handleIntersects(hits, event, delta, onIntersect);
919
1467
  };
920
1468
  }
921
- return { handlePointer };
1469
+ function flushDeferredPointers() {
1470
+ const { internal, events } = store.getState();
1471
+ if (!events.frameTimedRaycasts) return;
1472
+ for (const [pointerId, event] of internal.pointerDirty) {
1473
+ processDeferredPointer(event, pointerId);
1474
+ }
1475
+ internal.pointerDirty.clear();
1476
+ }
1477
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
922
1478
  }
923
1479
  const DOM_EVENTS = {
924
1480
  onClick: ["click", false],
@@ -937,11 +1493,16 @@ const DOM_EVENTS = {
937
1493
  onLostPointerCapture: ["lostpointercapture", true]
938
1494
  };
939
1495
  function createPointerEvents(store) {
940
- const { handlePointer } = createEvents(store);
1496
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1497
+ let nextXRPointerId = XR_POINTER_ID_START;
1498
+ const xrPointers = /* @__PURE__ */ new Map();
941
1499
  return {
942
1500
  priority: 1,
943
1501
  enabled: true,
944
- compute(event, state, previous) {
1502
+ frameTimedRaycasts: true,
1503
+ alwaysFireOnScroll: true,
1504
+ updateOnFrame: false,
1505
+ compute(event, state) {
945
1506
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
946
1507
  state.raycaster.setFromCamera(state.pointer, state.camera);
947
1508
  },
@@ -950,11 +1511,33 @@ function createPointerEvents(store) {
950
1511
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
951
1512
  {}
952
1513
  ),
953
- update: () => {
1514
+ update: (pointerId) => {
1515
+ const { events, internal } = store.getState();
1516
+ if (!events.handlers) return;
1517
+ if (pointerId !== void 0) {
1518
+ const event = internal.pointerDirty.get(pointerId);
1519
+ if (event) {
1520
+ internal.pointerDirty.delete(pointerId);
1521
+ processDeferredPointer(event, pointerId);
1522
+ } else if (internal.lastEvent?.current) {
1523
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1524
+ }
1525
+ } else {
1526
+ flushDeferredPointers();
1527
+ if (internal.lastEvent?.current) {
1528
+ events.handlers.onPointerMove(internal.lastEvent.current);
1529
+ }
1530
+ }
1531
+ },
1532
+ flush: () => {
954
1533
  const { events, internal } = store.getState();
955
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1534
+ flushDeferredPointers();
1535
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1536
+ events.handlers.onPointerMove(internal.lastEvent.current);
1537
+ }
956
1538
  },
957
1539
  connect: (target) => {
1540
+ if (!target) return;
958
1541
  const { set, events } = store.getState();
959
1542
  events.disconnect?.();
960
1543
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -978,6 +1561,32 @@ function createPointerEvents(store) {
978
1561
  }
979
1562
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
980
1563
  }
1564
+ },
1565
+ registerPointer: (config) => {
1566
+ const pointerId = nextXRPointerId++;
1567
+ xrPointers.set(pointerId, config);
1568
+ const { internal } = store.getState();
1569
+ getPointerState(internal, pointerId);
1570
+ return pointerId;
1571
+ },
1572
+ unregisterPointer: (pointerId) => {
1573
+ xrPointers.delete(pointerId);
1574
+ const { internal } = store.getState();
1575
+ const pointerState = internal.pointerMap.get(pointerId);
1576
+ if (pointerState) {
1577
+ for (const [, hoveredObj] of pointerState.hovered) {
1578
+ const eventObject = hoveredObj.eventObject;
1579
+ const instance = eventObject.__r3f;
1580
+ if (instance?.eventCount) {
1581
+ const handlers = instance.handlers;
1582
+ const data = { ...hoveredObj, intersections: [] };
1583
+ handlers.onPointerOut?.(data);
1584
+ handlers.onPointerLeave?.(data);
1585
+ }
1586
+ }
1587
+ internal.pointerMap.delete(pointerId);
1588
+ }
1589
+ internal.pointerDirty.delete(pointerId);
981
1590
  }
982
1591
  };
983
1592
  }
@@ -1039,300 +1648,26 @@ function notifyAlpha({ message, link }) {
1039
1648
  }
1040
1649
  }
1041
1650
 
1042
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1043
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1044
- const createStore = (invalidate, advance) => {
1045
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1046
- const position = new webgpu.Vector3();
1047
- const defaultTarget = new webgpu.Vector3();
1048
- const tempTarget = new webgpu.Vector3();
1049
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1050
- const { width, height, top, left } = size;
1051
- const aspect = width / height;
1052
- if (target.isVector3) tempTarget.copy(target);
1053
- else tempTarget.set(...target);
1054
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1055
- if (isOrthographicCamera(camera)) {
1056
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1057
- } else {
1058
- const fov = camera.fov * Math.PI / 180;
1059
- const h = 2 * Math.tan(fov / 2) * distance;
1060
- const w = h * (width / height);
1061
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1062
- }
1063
- }
1064
- let performanceTimeout = void 0;
1065
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1066
- const pointer = new webgpu.Vector2();
1067
- const rootState = {
1068
- set,
1069
- get,
1070
- // Mock objects that have to be configured
1071
- gl: null,
1072
- renderer: null,
1073
- camera: null,
1074
- frustum: new webgpu.Frustum(),
1075
- autoUpdateFrustum: true,
1076
- raycaster: null,
1077
- events: { priority: 1, enabled: true, connected: false },
1078
- scene: null,
1079
- rootScene: null,
1080
- xr: null,
1081
- inspector: null,
1082
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1083
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1084
- legacy: false,
1085
- linear: false,
1086
- flat: false,
1087
- textureColorSpace: "srgb",
1088
- isLegacy: false,
1089
- webGPUSupported: false,
1090
- isNative: false,
1091
- controls: null,
1092
- pointer,
1093
- mouse: pointer,
1094
- frameloop: "always",
1095
- onPointerMissed: void 0,
1096
- onDragOverMissed: void 0,
1097
- onDropMissed: void 0,
1098
- performance: {
1099
- current: 1,
1100
- min: 0.5,
1101
- max: 1,
1102
- debounce: 200,
1103
- regress: () => {
1104
- const state2 = get();
1105
- if (performanceTimeout) clearTimeout(performanceTimeout);
1106
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1107
- performanceTimeout = setTimeout(
1108
- () => setPerformanceCurrent(get().performance.max),
1109
- state2.performance.debounce
1110
- );
1111
- }
1112
- },
1113
- size: { width: 0, height: 0, top: 0, left: 0 },
1114
- viewport: {
1115
- initialDpr: 0,
1116
- dpr: 0,
1117
- width: 0,
1118
- height: 0,
1119
- top: 0,
1120
- left: 0,
1121
- aspect: 0,
1122
- distance: 0,
1123
- factor: 0,
1124
- getCurrentViewport
1125
- },
1126
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1127
- setSize: (width, height, top = 0, left = 0) => {
1128
- const camera = get().camera;
1129
- const size = { width, height, top, left };
1130
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1131
- },
1132
- setDpr: (dpr) => set((state2) => {
1133
- const resolved = calculateDpr(dpr);
1134
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1135
- }),
1136
- setFrameloop: (frameloop = "always") => {
1137
- set(() => ({ frameloop }));
1138
- },
1139
- setError: (error) => set(() => ({ error })),
1140
- error: null,
1141
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1142
- uniforms: {},
1143
- nodes: {},
1144
- textures: /* @__PURE__ */ new Map(),
1145
- postProcessing: null,
1146
- passes: {},
1147
- previousRoot: void 0,
1148
- internal: {
1149
- // Events
1150
- interaction: [],
1151
- hovered: /* @__PURE__ */ new Map(),
1152
- subscribers: [],
1153
- initialClick: [0, 0],
1154
- initialHits: [],
1155
- capturedMap: /* @__PURE__ */ new Map(),
1156
- lastEvent: React__namespace.createRef(),
1157
- // Visibility tracking (onFramed, onOccluded, onVisible)
1158
- visibilityRegistry: /* @__PURE__ */ new Map(),
1159
- // Occlusion system (WebGPU only)
1160
- occlusionEnabled: false,
1161
- occlusionObserver: null,
1162
- occlusionCache: /* @__PURE__ */ new Map(),
1163
- helperGroup: null,
1164
- // Updates
1165
- active: false,
1166
- frames: 0,
1167
- priority: 0,
1168
- subscribe: (ref, priority, store) => {
1169
- const internal = get().internal;
1170
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1171
- internal.subscribers.push({ ref, priority, store });
1172
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1173
- return () => {
1174
- const internal2 = get().internal;
1175
- if (internal2?.subscribers) {
1176
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1177
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1178
- }
1179
- };
1180
- },
1181
- // Renderer Storage (single source of truth)
1182
- actualRenderer: null,
1183
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1184
- scheduler: null
1185
- }
1186
- };
1187
- return rootState;
1188
- });
1189
- const state = rootStore.getState();
1190
- Object.defineProperty(state, "gl", {
1191
- get() {
1192
- const currentState = rootStore.getState();
1193
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1194
- const stack = new Error().stack || "";
1195
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1196
- if (!isInternalAccess) {
1197
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1198
- notifyDepreciated({
1199
- heading: "Accessing state.gl in WebGPU mode",
1200
- 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
1201
- });
1202
- }
1203
- }
1204
- return currentState.internal.actualRenderer;
1205
- },
1206
- set(value) {
1207
- rootStore.getState().internal.actualRenderer = value;
1208
- },
1209
- enumerable: true,
1210
- configurable: true
1211
- });
1212
- Object.defineProperty(state, "renderer", {
1213
- get() {
1214
- return rootStore.getState().internal.actualRenderer;
1215
- },
1216
- set(value) {
1217
- rootStore.getState().internal.actualRenderer = value;
1218
- },
1219
- enumerable: true,
1220
- configurable: true
1221
- });
1222
- let oldScene = state.scene;
1223
- rootStore.subscribe(() => {
1224
- const currentState = rootStore.getState();
1225
- const { scene, rootScene, set } = currentState;
1226
- if (scene !== oldScene) {
1227
- oldScene = scene;
1228
- if (scene?.isScene && scene !== rootScene) {
1229
- set({ rootScene: scene });
1230
- }
1231
- }
1232
- });
1233
- let oldSize = state.size;
1234
- let oldDpr = state.viewport.dpr;
1235
- let oldCamera = state.camera;
1236
- rootStore.subscribe(() => {
1237
- const { camera, size, viewport, set, internal } = rootStore.getState();
1238
- const actualRenderer = internal.actualRenderer;
1239
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1240
- oldSize = size;
1241
- oldDpr = viewport.dpr;
1242
- updateCamera(camera, size);
1243
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1244
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1245
- actualRenderer.setSize(size.width, size.height, updateStyle);
1246
- }
1247
- if (camera !== oldCamera) {
1248
- oldCamera = camera;
1249
- const { rootScene } = rootStore.getState();
1250
- if (camera && rootScene && !camera.parent) {
1251
- rootScene.add(camera);
1252
- }
1253
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1254
- const currentState = rootStore.getState();
1255
- if (currentState.autoUpdateFrustum && camera) {
1256
- updateFrustum(camera, currentState.frustum);
1257
- }
1258
- }
1259
- });
1260
- rootStore.subscribe((state2) => invalidate(state2));
1261
- return rootStore;
1262
- };
1263
-
1264
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1265
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1266
- function getLoader(Proto) {
1267
- if (isConstructor$1(Proto)) {
1268
- let loader = memoizedLoaders.get(Proto);
1269
- if (!loader) {
1270
- loader = new Proto();
1271
- memoizedLoaders.set(Proto, loader);
1272
- }
1273
- return loader;
1274
- }
1275
- return Proto;
1276
- }
1277
- function loadingFn(extensions, onProgress) {
1278
- return function(Proto, input) {
1279
- const loader = getLoader(Proto);
1280
- if (extensions) extensions(loader);
1281
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1282
- return loader.loadAsync(input, onProgress).then((data) => {
1283
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1284
- return data;
1285
- });
1286
- }
1287
- return new Promise(
1288
- (res, reject) => loader.load(
1289
- input,
1290
- (data) => {
1291
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1292
- res(data);
1293
- },
1294
- onProgress,
1295
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1296
- )
1297
- );
1298
- };
1299
- }
1300
- function useLoader(loader, input, extensions, onProgress) {
1301
- const keys = Array.isArray(input) ? input : [input];
1302
- const fn = loadingFn(extensions, onProgress);
1303
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1304
- return Array.isArray(input) ? results : results[0];
1305
- }
1306
- useLoader.preload = function(loader, input, extensions, onProgress) {
1307
- const keys = Array.isArray(input) ? input : [input];
1308
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1309
- };
1310
- useLoader.clear = function(loader, input) {
1311
- const keys = Array.isArray(input) ? input : [input];
1312
- keys.forEach((key) => suspendReact.clear([loader, key]));
1313
- };
1314
- useLoader.loader = getLoader;
1315
-
1316
- var __defProp$1 = Object.defineProperty;
1317
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1318
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1319
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1320
- class PhaseGraph {
1321
- constructor() {
1322
- /** Ordered list of phase nodes */
1323
- __publicField$1(this, "phases", []);
1324
- /** Quick lookup by name */
1325
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1326
- /** Cached ordered names (invalidated on changes) */
1327
- __publicField$1(this, "orderedNamesCache", null);
1328
- this.initializeDefaultPhases();
1329
- }
1330
- //* Initialization --------------------------------
1331
- initializeDefaultPhases() {
1332
- for (const name of DEFAULT_PHASES) {
1333
- const node = { name, isAutoGenerated: false };
1334
- this.phases.push(node);
1335
- this.phaseMap.set(name, node);
1651
+ var __defProp$1 = Object.defineProperty;
1652
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1653
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1654
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1655
+ class PhaseGraph {
1656
+ constructor() {
1657
+ /** Ordered list of phase nodes */
1658
+ __publicField$1(this, "phases", []);
1659
+ /** Quick lookup by name */
1660
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1661
+ /** Cached ordered names (invalidated on changes) */
1662
+ __publicField$1(this, "orderedNamesCache", null);
1663
+ this.initializeDefaultPhases();
1664
+ }
1665
+ //* Initialization --------------------------------
1666
+ initializeDefaultPhases() {
1667
+ for (const name of DEFAULT_PHASES) {
1668
+ const node = { name, isAutoGenerated: false };
1669
+ this.phases.push(node);
1670
+ this.phaseMap.set(name, node);
1336
1671
  }
1337
1672
  this.invalidateCache();
1338
1673
  }
@@ -1351,8 +1686,9 @@ class PhaseGraph {
1351
1686
  const node = { name, isAutoGenerated: false };
1352
1687
  let insertIndex = this.phases.length;
1353
1688
  const targetIndex = this.getPhaseIndex(before ?? after);
1354
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1355
- else {
1689
+ if (targetIndex !== -1) {
1690
+ insertIndex = before ? targetIndex : targetIndex + 1;
1691
+ } else {
1356
1692
  const constraintType = before ? "before" : "after";
1357
1693
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1358
1694
  }
@@ -1564,7 +1900,7 @@ function shouldRun(job, now) {
1564
1900
  const minInterval = 1e3 / job.fps;
1565
1901
  const lastRun = job.lastRun ?? 0;
1566
1902
  const elapsed = now - lastRun;
1567
- if (elapsed < minInterval) return false;
1903
+ if (elapsed < minInterval - 1) return false;
1568
1904
  if (job.drop) {
1569
1905
  job.lastRun = now;
1570
1906
  } else {
@@ -2251,98 +2587,426 @@ const _Scheduler = class _Scheduler {
2251
2587
  this.triggerError(error instanceof Error ? error : new Error(String(error)));
2252
2588
  }
2253
2589
  }
2254
- }
2255
- //* Debug & Inspection Methods ================================
2256
- /**
2257
- * Get the total number of registered jobs across all roots.
2258
- * Includes both per-root jobs and global before/after jobs.
2259
- * @returns {number} Total job count
2260
- */
2261
- getJobCount() {
2262
- let count = 0;
2263
- for (const root of this.roots.values()) {
2264
- count += root.jobs.size;
2590
+ }
2591
+ //* Debug & Inspection Methods ================================
2592
+ /**
2593
+ * Get the total number of registered jobs across all roots.
2594
+ * Includes both per-root jobs and global before/after jobs.
2595
+ * @returns {number} Total job count
2596
+ */
2597
+ getJobCount() {
2598
+ let count = 0;
2599
+ for (const root of this.roots.values()) {
2600
+ count += root.jobs.size;
2601
+ }
2602
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2603
+ }
2604
+ /**
2605
+ * Get all registered job IDs across all roots.
2606
+ * Includes both per-root jobs and global before/after jobs.
2607
+ * @returns {string[]} Array of all job IDs
2608
+ */
2609
+ getJobIds() {
2610
+ const ids = [];
2611
+ for (const root of this.roots.values()) {
2612
+ ids.push(...root.jobs.keys());
2613
+ }
2614
+ ids.push(...this.globalBeforeJobs.keys());
2615
+ ids.push(...this.globalAfterJobs.keys());
2616
+ return ids;
2617
+ }
2618
+ /**
2619
+ * Get the number of registered roots (Canvas instances).
2620
+ * @returns {number} Number of registered roots
2621
+ */
2622
+ getRootCount() {
2623
+ return this.roots.size;
2624
+ }
2625
+ /**
2626
+ * Check if any user (non-system) jobs are registered in a specific phase.
2627
+ * Used by the default render job to know if a user has taken over rendering.
2628
+ *
2629
+ * @param phase The phase to check
2630
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2631
+ * @returns true if any user jobs exist in the phase
2632
+ */
2633
+ hasUserJobsInPhase(phase, rootId) {
2634
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2635
+ return rootsToCheck.some((root) => {
2636
+ if (!root) return false;
2637
+ for (const job of root.jobs.values()) {
2638
+ if (job.phase === phase && !job.system && job.enabled) return true;
2639
+ }
2640
+ return false;
2641
+ });
2642
+ }
2643
+ //* Utility Methods ================================
2644
+ /**
2645
+ * Generate a unique root ID for automatic root registration.
2646
+ * @returns {string} A unique root ID in the format 'root_N'
2647
+ */
2648
+ generateRootId() {
2649
+ return `root_${this.nextRootIndex++}`;
2650
+ }
2651
+ /**
2652
+ * Generate a unique job ID.
2653
+ * @returns {string} A unique job ID in the format 'job_N'
2654
+ * @private
2655
+ */
2656
+ generateJobId() {
2657
+ return `job_${this.nextJobIndex}`;
2658
+ }
2659
+ /**
2660
+ * Normalize before/after constraints to a Set.
2661
+ * Handles undefined, single string, or array inputs.
2662
+ * @param {string | string[] | undefined} value - The constraint value(s)
2663
+ * @returns {Set<string>} Normalized Set of constraint strings
2664
+ * @private
2665
+ */
2666
+ normalizeConstraints(value) {
2667
+ if (!value) return /* @__PURE__ */ new Set();
2668
+ if (Array.isArray(value)) return new Set(value);
2669
+ return /* @__PURE__ */ new Set([value]);
2670
+ }
2671
+ };
2672
+ //* Static State & Methods (Singleton Usage) ================================
2673
+ //* Cross-Bundle Singleton Key ==============================
2674
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2675
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2676
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2677
+ let Scheduler = _Scheduler;
2678
+ const getScheduler = () => Scheduler.get();
2679
+ if (hmrData) {
2680
+ hmrData.accept?.();
2681
+ }
2682
+
2683
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2684
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2685
+ const createStore = (invalidate, advance) => {
2686
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2687
+ const position = new webgpu.Vector3();
2688
+ const defaultTarget = new webgpu.Vector3();
2689
+ const tempTarget = new webgpu.Vector3();
2690
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2691
+ const { width, height, top, left } = size;
2692
+ const aspect = width / height;
2693
+ if (target.isVector3) tempTarget.copy(target);
2694
+ else tempTarget.set(...target);
2695
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2696
+ if (isOrthographicCamera(camera)) {
2697
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2698
+ } else {
2699
+ const fov = camera.fov * Math.PI / 180;
2700
+ const h = 2 * Math.tan(fov / 2) * distance;
2701
+ const w = h * (width / height);
2702
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2703
+ }
2704
+ }
2705
+ let performanceTimeout = void 0;
2706
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2707
+ const pointer = new webgpu.Vector2();
2708
+ const rootState = {
2709
+ set,
2710
+ get,
2711
+ // Mock objects that have to be configured
2712
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2713
+ primaryStore: null,
2714
+ gl: null,
2715
+ renderer: null,
2716
+ camera: null,
2717
+ frustum: new webgpu.Frustum(),
2718
+ autoUpdateFrustum: true,
2719
+ raycaster: null,
2720
+ events: {
2721
+ priority: 1,
2722
+ enabled: true,
2723
+ connected: false,
2724
+ frameTimedRaycasts: true,
2725
+ alwaysFireOnScroll: true,
2726
+ updateOnFrame: false
2727
+ },
2728
+ scene: null,
2729
+ rootScene: null,
2730
+ xr: null,
2731
+ inspector: null,
2732
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2733
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2734
+ textureColorSpace: webgpu.SRGBColorSpace,
2735
+ isLegacy: false,
2736
+ webGPUSupported: false,
2737
+ isNative: false,
2738
+ controls: null,
2739
+ pointer,
2740
+ mouse: pointer,
2741
+ frameloop: "always",
2742
+ onPointerMissed: void 0,
2743
+ onDragOverMissed: void 0,
2744
+ onDropMissed: void 0,
2745
+ performance: {
2746
+ current: 1,
2747
+ min: 0.5,
2748
+ max: 1,
2749
+ debounce: 200,
2750
+ regress: () => {
2751
+ const state2 = get();
2752
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2753
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2754
+ performanceTimeout = setTimeout(
2755
+ () => setPerformanceCurrent(get().performance.max),
2756
+ state2.performance.debounce
2757
+ );
2758
+ }
2759
+ },
2760
+ size: { width: 0, height: 0, top: 0, left: 0 },
2761
+ viewport: {
2762
+ initialDpr: 0,
2763
+ dpr: 0,
2764
+ width: 0,
2765
+ height: 0,
2766
+ top: 0,
2767
+ left: 0,
2768
+ aspect: 0,
2769
+ distance: 0,
2770
+ factor: 0,
2771
+ getCurrentViewport
2772
+ },
2773
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2774
+ setSize: (width, height, top, left) => {
2775
+ const state2 = get();
2776
+ if (width === void 0) {
2777
+ set({ _sizeImperative: false });
2778
+ if (state2._sizeProps) {
2779
+ const { width: propW, height: propH } = state2._sizeProps;
2780
+ if (propW !== void 0 || propH !== void 0) {
2781
+ const currentSize = state2.size;
2782
+ const newSize = {
2783
+ width: propW ?? currentSize.width,
2784
+ height: propH ?? currentSize.height,
2785
+ top: currentSize.top,
2786
+ left: currentSize.left
2787
+ };
2788
+ set((s) => ({
2789
+ size: newSize,
2790
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2791
+ }));
2792
+ getScheduler().invalidate();
2793
+ }
2794
+ }
2795
+ return;
2796
+ }
2797
+ const w = width;
2798
+ const h = height ?? width;
2799
+ const t = top ?? state2.size.top;
2800
+ const l = left ?? state2.size.left;
2801
+ const size = { width: w, height: h, top: t, left: l };
2802
+ set((s) => ({
2803
+ size,
2804
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2805
+ _sizeImperative: true
2806
+ }));
2807
+ getScheduler().invalidate();
2808
+ },
2809
+ setDpr: (dpr) => set((state2) => {
2810
+ const resolved = calculateDpr(dpr);
2811
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2812
+ }),
2813
+ setFrameloop: (frameloop = "always") => {
2814
+ set(() => ({ frameloop }));
2815
+ },
2816
+ setError: (error) => set(() => ({ error })),
2817
+ error: null,
2818
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2819
+ uniforms: {},
2820
+ nodes: {},
2821
+ buffers: {},
2822
+ gpuStorage: {},
2823
+ textures: /* @__PURE__ */ new Map(),
2824
+ renderPipeline: null,
2825
+ passes: {},
2826
+ _hmrVersion: 0,
2827
+ _sizeImperative: false,
2828
+ _sizeProps: null,
2829
+ previousRoot: void 0,
2830
+ internal: {
2831
+ // Events
2832
+ interaction: [],
2833
+ subscribers: [],
2834
+ // Per-pointer state (new unified structure)
2835
+ pointerMap: /* @__PURE__ */ new Map(),
2836
+ pointerDirty: /* @__PURE__ */ new Map(),
2837
+ lastEvent: React__namespace.createRef(),
2838
+ // Deprecated but kept for backwards compatibility
2839
+ hovered: /* @__PURE__ */ new Map(),
2840
+ initialClick: [0, 0],
2841
+ initialHits: [],
2842
+ capturedMap: /* @__PURE__ */ new Map(),
2843
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2844
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2845
+ // Occlusion system (WebGPU only)
2846
+ occlusionEnabled: false,
2847
+ occlusionObserver: null,
2848
+ occlusionCache: /* @__PURE__ */ new Map(),
2849
+ helperGroup: null,
2850
+ // Updates
2851
+ active: false,
2852
+ frames: 0,
2853
+ priority: 0,
2854
+ subscribe: (ref, priority, store) => {
2855
+ const internal = get().internal;
2856
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2857
+ internal.subscribers.push({ ref, priority, store });
2858
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2859
+ return () => {
2860
+ const internal2 = get().internal;
2861
+ if (internal2?.subscribers) {
2862
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2863
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2864
+ }
2865
+ };
2866
+ },
2867
+ // Renderer Storage (single source of truth)
2868
+ actualRenderer: null,
2869
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2870
+ scheduler: null
2871
+ }
2872
+ };
2873
+ return rootState;
2874
+ });
2875
+ const state = rootStore.getState();
2876
+ Object.defineProperty(state, "gl", {
2877
+ get() {
2878
+ const currentState = rootStore.getState();
2879
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2880
+ const stack = new Error().stack || "";
2881
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2882
+ if (!isInternalAccess) {
2883
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2884
+ notifyDepreciated({
2885
+ heading: "Accessing state.gl in WebGPU mode",
2886
+ 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
2887
+ });
2888
+ }
2889
+ }
2890
+ return currentState.internal.actualRenderer;
2891
+ },
2892
+ set(value) {
2893
+ rootStore.getState().internal.actualRenderer = value;
2894
+ },
2895
+ enumerable: true,
2896
+ configurable: true
2897
+ });
2898
+ Object.defineProperty(state, "renderer", {
2899
+ get() {
2900
+ return rootStore.getState().internal.actualRenderer;
2901
+ },
2902
+ set(value) {
2903
+ rootStore.getState().internal.actualRenderer = value;
2904
+ },
2905
+ enumerable: true,
2906
+ configurable: true
2907
+ });
2908
+ let oldScene = state.scene;
2909
+ rootStore.subscribe(() => {
2910
+ const currentState = rootStore.getState();
2911
+ const { scene, rootScene, set } = currentState;
2912
+ if (scene !== oldScene) {
2913
+ oldScene = scene;
2914
+ if (scene?.isScene && scene !== rootScene) {
2915
+ set({ rootScene: scene });
2916
+ }
2917
+ }
2918
+ });
2919
+ let oldSize = state.size;
2920
+ let oldDpr = state.viewport.dpr;
2921
+ let oldCamera = state.camera;
2922
+ rootStore.subscribe(() => {
2923
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2924
+ const actualRenderer = internal.actualRenderer;
2925
+ const canvasTarget = internal.canvasTarget;
2926
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2927
+ oldSize = size;
2928
+ oldDpr = viewport.dpr;
2929
+ updateCamera(camera, size);
2930
+ if (internal.isSecondary && canvasTarget) {
2931
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2932
+ canvasTarget.setSize(size.width, size.height, false);
2933
+ } else {
2934
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2935
+ actualRenderer.setSize(size.width, size.height, false);
2936
+ if (canvasTarget) {
2937
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2938
+ canvasTarget.setSize(size.width, size.height, false);
2939
+ }
2940
+ }
2941
+ }
2942
+ if (camera !== oldCamera) {
2943
+ oldCamera = camera;
2944
+ const { rootScene } = rootStore.getState();
2945
+ if (camera && rootScene && !camera.parent) {
2946
+ rootScene.add(camera);
2947
+ }
2948
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2949
+ const currentState = rootStore.getState();
2950
+ if (currentState.autoUpdateFrustum && camera) {
2951
+ updateFrustum(camera, currentState.frustum);
2952
+ }
2265
2953
  }
2266
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2267
- }
2268
- /**
2269
- * Get all registered job IDs across all roots.
2270
- * Includes both per-root jobs and global before/after jobs.
2271
- * @returns {string[]} Array of all job IDs
2272
- */
2273
- getJobIds() {
2274
- const ids = [];
2275
- for (const root of this.roots.values()) {
2276
- ids.push(...root.jobs.keys());
2954
+ });
2955
+ rootStore.subscribe((state2) => invalidate(state2));
2956
+ return rootStore;
2957
+ };
2958
+
2959
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2960
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2961
+ function getLoader(Proto) {
2962
+ if (isConstructor$1(Proto)) {
2963
+ let loader = memoizedLoaders.get(Proto);
2964
+ if (!loader) {
2965
+ loader = new Proto();
2966
+ memoizedLoaders.set(Proto, loader);
2277
2967
  }
2278
- ids.push(...this.globalBeforeJobs.keys());
2279
- ids.push(...this.globalAfterJobs.keys());
2280
- return ids;
2281
- }
2282
- /**
2283
- * Get the number of registered roots (Canvas instances).
2284
- * @returns {number} Number of registered roots
2285
- */
2286
- getRootCount() {
2287
- return this.roots.size;
2288
- }
2289
- /**
2290
- * Check if any user (non-system) jobs are registered in a specific phase.
2291
- * Used by the default render job to know if a user has taken over rendering.
2292
- *
2293
- * @param phase The phase to check
2294
- * @param rootId Optional root ID to check (checks all roots if not provided)
2295
- * @returns true if any user jobs exist in the phase
2296
- */
2297
- hasUserJobsInPhase(phase, rootId) {
2298
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2299
- return rootsToCheck.some((root) => {
2300
- if (!root) return false;
2301
- for (const job of root.jobs.values()) {
2302
- if (job.phase === phase && !job.system && job.enabled) return true;
2303
- }
2304
- return false;
2305
- });
2306
- }
2307
- //* Utility Methods ================================
2308
- /**
2309
- * Generate a unique root ID for automatic root registration.
2310
- * @returns {string} A unique root ID in the format 'root_N'
2311
- */
2312
- generateRootId() {
2313
- return `root_${this.nextRootIndex++}`;
2314
- }
2315
- /**
2316
- * Generate a unique job ID.
2317
- * @returns {string} A unique job ID in the format 'job_N'
2318
- * @private
2319
- */
2320
- generateJobId() {
2321
- return `job_${this.nextJobIndex}`;
2322
- }
2323
- /**
2324
- * Normalize before/after constraints to a Set.
2325
- * Handles undefined, single string, or array inputs.
2326
- * @param {string | string[] | undefined} value - The constraint value(s)
2327
- * @returns {Set<string>} Normalized Set of constraint strings
2328
- * @private
2329
- */
2330
- normalizeConstraints(value) {
2331
- if (!value) return /* @__PURE__ */ new Set();
2332
- if (Array.isArray(value)) return new Set(value);
2333
- return /* @__PURE__ */ new Set([value]);
2968
+ return loader;
2334
2969
  }
2335
- };
2336
- //* Static State & Methods (Singleton Usage) ================================
2337
- //* Cross-Bundle Singleton Key ==============================
2338
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2339
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2340
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2341
- let Scheduler = _Scheduler;
2342
- const getScheduler = () => Scheduler.get();
2343
- if (hmrData) {
2344
- hmrData.accept?.();
2970
+ return Proto;
2971
+ }
2972
+ function loadingFn(extensions, onProgress) {
2973
+ return function(Proto, input) {
2974
+ const loader = getLoader(Proto);
2975
+ if (extensions) extensions(loader);
2976
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2977
+ return loader.loadAsync(input, onProgress).then((data) => {
2978
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2979
+ return data;
2980
+ });
2981
+ }
2982
+ return new Promise(
2983
+ (res, reject) => loader.load(
2984
+ input,
2985
+ (data) => {
2986
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2987
+ res(data);
2988
+ },
2989
+ onProgress,
2990
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2991
+ )
2992
+ );
2993
+ };
2345
2994
  }
2995
+ function useLoader(loader, input, extensions, onProgress) {
2996
+ const keys = Array.isArray(input) ? input : [input];
2997
+ const fn = loadingFn(extensions, onProgress);
2998
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2999
+ return Array.isArray(input) ? results : results[0];
3000
+ }
3001
+ useLoader.preload = function(loader, input, extensions, onProgress) {
3002
+ const keys = Array.isArray(input) ? input : [input];
3003
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
3004
+ };
3005
+ useLoader.clear = function(loader, input) {
3006
+ const keys = Array.isArray(input) ? input : [input];
3007
+ keys.forEach((key) => suspendReact.clear([loader, key]));
3008
+ };
3009
+ useLoader.loader = getLoader;
2346
3010
 
2347
3011
  function useFrame(callback, priorityOrOptions) {
2348
3012
  const store = React__namespace.useContext(context);
@@ -2523,6 +3187,9 @@ function useTexture(input, optionsOrOnLoad) {
2523
3187
  const textureCache = useThree((state) => state.textures);
2524
3188
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2525
3189
  const { onLoad, cache = false } = options;
3190
+ const onLoadRef = React.useRef(onLoad);
3191
+ onLoadRef.current = onLoad;
3192
+ const onLoadCalledForRef = React.useRef(null);
2526
3193
  const urls = React.useMemo(() => getUrls(input), [input]);
2527
3194
  const cachedResult = React.useMemo(() => {
2528
3195
  if (!cache) return null;
@@ -2533,9 +3200,13 @@ function useTexture(input, optionsOrOnLoad) {
2533
3200
  webgpu.TextureLoader,
2534
3201
  IsObject(input) ? Object.values(input) : input
2535
3202
  );
3203
+ const inputKey = urls.join("\0");
2536
3204
  React.useLayoutEffect(() => {
2537
- if (!cachedResult) onLoad?.(loadedTextures);
2538
- }, [onLoad, cachedResult, loadedTextures]);
3205
+ if (cachedResult) return;
3206
+ if (onLoadCalledForRef.current === inputKey) return;
3207
+ onLoadCalledForRef.current = inputKey;
3208
+ onLoadRef.current?.(loadedTextures);
3209
+ }, [cachedResult, loadedTextures, inputKey]);
2539
3210
  React.useEffect(() => {
2540
3211
  if (cachedResult) return;
2541
3212
  if ("initTexture" in renderer) {
@@ -2702,16 +3373,33 @@ function useTextures() {
2702
3373
  }, [store]);
2703
3374
  }
2704
3375
 
2705
- function useRenderTarget(width, height, options) {
3376
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2706
3377
  const isLegacy = useThree((s) => s.isLegacy);
2707
3378
  const size = useThree((s) => s.size);
3379
+ let width;
3380
+ let height;
3381
+ let opts;
3382
+ if (typeof widthOrOptions === "object") {
3383
+ opts = widthOrOptions;
3384
+ } else if (typeof widthOrOptions === "number") {
3385
+ width = widthOrOptions;
3386
+ if (typeof heightOrOptions === "object") {
3387
+ height = widthOrOptions;
3388
+ opts = heightOrOptions;
3389
+ } else if (typeof heightOrOptions === "number") {
3390
+ height = heightOrOptions;
3391
+ opts = options;
3392
+ } else {
3393
+ height = widthOrOptions;
3394
+ }
3395
+ }
2708
3396
  return React.useMemo(() => {
2709
3397
  const w = width ?? size.width;
2710
3398
  const h = height ?? size.height;
2711
3399
  {
2712
- return isLegacy ? new three.WebGLRenderTarget(w, h, options) : new webgpu.RenderTarget(w, h, options);
3400
+ return isLegacy ? new three.WebGLRenderTarget(w, h, opts) : new webgpu.RenderTarget(w, h, opts);
2713
3401
  }
2714
- }, [width, height, size.width, size.height, options, isLegacy]);
3402
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2715
3403
  }
2716
3404
 
2717
3405
  function useStore() {
@@ -2761,28 +3449,18 @@ function addTail(callback) {
2761
3449
  function invalidate(state, frames = 1, stackFrames = false) {
2762
3450
  getScheduler().invalidate(frames, stackFrames);
2763
3451
  }
2764
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3452
+ function advance(timestamp) {
2765
3453
  getScheduler().step(timestamp);
2766
3454
  }
2767
3455
 
2768
- const version = "10.0.0-alpha.1";
3456
+ const version = "10.0.0-alpha.2";
2769
3457
  const packageData = {
2770
3458
  version: version};
2771
3459
 
2772
3460
  function Xb(Tt) {
2773
3461
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2774
3462
  }
2775
- var Rm = { exports: {} }, Og = { exports: {} };
2776
- /**
2777
- * @license React
2778
- * react-reconciler.production.js
2779
- *
2780
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2781
- *
2782
- * This source code is licensed under the MIT license found in the
2783
- * LICENSE file in the root directory of this source tree.
2784
- */
2785
- var _b;
3463
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2786
3464
  function Kb() {
2787
3465
  return _b || (_b = 1, (function(Tt) {
2788
3466
  Tt.exports = function(m) {
@@ -3854,7 +4532,6 @@ Error generating stack: ` + l.message + `
3854
4532
  if (J === cl || J === jc) throw J;
3855
4533
  var Ge = Yn(29, J, null, P.mode);
3856
4534
  return Ge.lanes = H, Ge.return = P, Ge;
3857
- } finally {
3858
4535
  }
3859
4536
  };
3860
4537
  }
@@ -4508,7 +5185,6 @@ Error generating stack: ` + l.message + `
4508
5185
  var h = r.lastRenderedState, y = d(h, a);
4509
5186
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4510
5187
  } catch {
4511
- } finally {
4512
5188
  }
4513
5189
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4514
5190
  }
@@ -6929,10 +7605,7 @@ Error generating stack: ` + l.message + `
6929
7605
  function vr(t, r) {
6930
7606
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6931
7607
  }
6932
- var ie = {}, Fm = React__default, tt = Tb__default, 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");
6933
- var gc = Symbol.for("react.activity");
6934
- var $r = Symbol.for("react.memo_cache_sentinel");
6935
- 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;
7608
+ var ie = {}, Fm = React__default, tt = Tb__default, 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;
6936
7609
  m.cloneMutableInstance;
6937
7610
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6938
7611
  m.cloneMutableTextInstance;
@@ -7301,17 +7974,7 @@ No matching component was found for:
7301
7974
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7302
7975
  })(Og)), Og.exports;
7303
7976
  }
7304
- var Mg = { exports: {} };
7305
- /**
7306
- * @license React
7307
- * react-reconciler.development.js
7308
- *
7309
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7310
- *
7311
- * This source code is licensed under the MIT license found in the
7312
- * LICENSE file in the root directory of this source tree.
7313
- */
7314
- var Rb;
7977
+ var Mg = { exports: {} }, Rb;
7315
7978
  function e0() {
7316
7979
  return Rb || (Rb = 1, (function(Tt) {
7317
7980
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13078,10 +13741,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13078
13741
  function Ic() {
13079
13742
  return di;
13080
13743
  }
13081
- var le = {}, qm = React__default, St = Tb__default, 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");
13082
- var Ds = Symbol.for("react.activity");
13083
- var Bh = Symbol.for("react.memo_cache_sentinel");
13084
- 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;
13744
+ var le = {}, qm = React__default, St = Tb__default, 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;
13085
13745
  m.cloneMutableInstance;
13086
13746
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13087
13747
  m.cloneMutableTextInstance;
@@ -14049,15 +14709,6 @@ function n0() {
14049
14709
  var t0 = n0();
14050
14710
  const r0 = Xb(t0);
14051
14711
 
14052
- /**
14053
- * @license React
14054
- * react-reconciler-constants.production.js
14055
- *
14056
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14057
- *
14058
- * This source code is licensed under the MIT license found in the
14059
- * LICENSE file in the root directory of this source tree.
14060
- */
14061
14712
  const t = 1, o = 8, r = 32, e = 2;
14062
14713
 
14063
14714
  function createReconciler(config) {
@@ -14084,10 +14735,11 @@ function extend(objects) {
14084
14735
  function validateInstance(type, props) {
14085
14736
  const name = toPascalCase(type);
14086
14737
  const target = catalogue[name];
14087
- if (type !== "primitive" && !target)
14738
+ if (type !== "primitive" && !target) {
14088
14739
  throw new Error(
14089
14740
  `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`
14090
14741
  );
14742
+ }
14091
14743
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14092
14744
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14093
14745
  }
@@ -14251,6 +14903,7 @@ function swapInstances() {
14251
14903
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14252
14904
  instance.object.__r3f = instance;
14253
14905
  setFiberRef(fiber, instance.object);
14906
+ delete instance.appliedOnce;
14254
14907
  applyProps(instance.object, instance.props);
14255
14908
  if (instance.props.attach) {
14256
14909
  attach(parent, instance);
@@ -14324,8 +14977,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14324
14977
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14325
14978
  if (isTailSibling) swapInstances();
14326
14979
  },
14327
- finalizeInitialChildren: () => false,
14328
- commitMount() {
14980
+ finalizeInitialChildren: (instance) => {
14981
+ for (const prop in instance.props) {
14982
+ if (isFromRef(instance.props[prop])) return true;
14983
+ }
14984
+ return false;
14985
+ },
14986
+ commitMount(instance) {
14987
+ const resolved = {};
14988
+ for (const prop in instance.props) {
14989
+ const value = instance.props[prop];
14990
+ if (isFromRef(value)) {
14991
+ const ref = value[FROM_REF];
14992
+ if (ref.current != null) resolved[prop] = ref.current;
14993
+ }
14994
+ }
14995
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14329
14996
  },
14330
14997
  getPublicInstance: (instance) => instance?.object,
14331
14998
  prepareForCommit: () => null,
@@ -14538,14 +15205,17 @@ function createRoot(canvas) {
14538
15205
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14539
15206
  let onCreated;
14540
15207
  let lastCamera;
14541
- let lastConfiguredProps = {};
15208
+ const lastConfiguredProps = {};
14542
15209
  let configured = false;
14543
15210
  let pending = null;
14544
15211
  return {
14545
15212
  async configure(props = {}) {
14546
15213
  let resolve;
14547
15214
  pending = new Promise((_resolve) => resolve = _resolve);
14548
- let {
15215
+ const {
15216
+ id: canvasId,
15217
+ primaryCanvas,
15218
+ scheduler: schedulerConfig,
14549
15219
  gl: glConfig,
14550
15220
  renderer: rendererConfig,
14551
15221
  size: propsSize,
@@ -14553,10 +15223,6 @@ function createRoot(canvas) {
14553
15223
  events,
14554
15224
  onCreated: onCreatedCallback,
14555
15225
  shadows = false,
14556
- linear = false,
14557
- flat = false,
14558
- textureColorSpace = webgpu.SRGBColorSpace,
14559
- legacy = false,
14560
15226
  orthographic = false,
14561
15227
  frameloop = "always",
14562
15228
  dpr = [1, 2],
@@ -14567,9 +15233,12 @@ function createRoot(canvas) {
14567
15233
  onDragOverMissed,
14568
15234
  onDropMissed,
14569
15235
  autoUpdateFrustum = true,
14570
- occlusion = false
15236
+ occlusion = false,
15237
+ _sizeProps,
15238
+ forceEven
14571
15239
  } = props;
14572
- let state = store.getState();
15240
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
15241
+ const state = store.getState();
14573
15242
  const defaultGLProps = {
14574
15243
  canvas,
14575
15244
  powerPreference: "high-performance",
@@ -14577,7 +15246,8 @@ function createRoot(canvas) {
14577
15246
  alpha: true
14578
15247
  };
14579
15248
  const defaultGPUProps = {
14580
- canvas
15249
+ canvas,
15250
+ antialias: true
14581
15251
  };
14582
15252
  const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
14583
15253
  if (glConfig && rendererConfig) {
@@ -14591,26 +15261,70 @@ function createRoot(canvas) {
14591
15261
  });
14592
15262
  }
14593
15263
  let renderer = state.internal.actualRenderer;
15264
+ if (primaryCanvas && wantsGL) {
15265
+ throw new Error(
15266
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15267
+ );
15268
+ }
14594
15269
  if (wantsGL && !state.internal.actualRenderer) {
14595
15270
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14596
15271
  state.internal.actualRenderer = renderer;
14597
- state.set({ isLegacy: true, gl: renderer, renderer });
15272
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
15273
+ } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
15274
+ const primary = await waitForPrimary(primaryCanvas);
15275
+ renderer = primary.renderer;
15276
+ state.internal.actualRenderer = renderer;
15277
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15278
+ primary.store.setState((prev) => ({
15279
+ internal: { ...prev.internal, isMultiCanvas: true }
15280
+ }));
15281
+ state.set((prev) => ({
15282
+ webGPUSupported: primary.store.getState().webGPUSupported,
15283
+ renderer,
15284
+ primaryStore: primary.store,
15285
+ internal: {
15286
+ ...prev.internal,
15287
+ canvasTarget,
15288
+ isMultiCanvas: true,
15289
+ isSecondary: true,
15290
+ targetId: primaryCanvas
15291
+ }
15292
+ }));
14598
15293
  } else if (!wantsGL && !state.internal.actualRenderer) {
14599
15294
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14600
15295
  if (!renderer.hasInitialized?.()) {
15296
+ const size2 = computeInitialSize(canvas, propsSize);
15297
+ if (size2.width > 0 && size2.height > 0) {
15298
+ const pixelRatio = calculateDpr(dpr);
15299
+ canvas.width = size2.width * pixelRatio;
15300
+ canvas.height = size2.height * pixelRatio;
15301
+ }
14601
15302
  await renderer.init();
14602
15303
  }
14603
15304
  const backend = renderer.backend;
14604
15305
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14605
15306
  state.internal.actualRenderer = renderer;
14606
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15307
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15308
+ if (canvasId && !state.internal.isSecondary) {
15309
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15310
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15311
+ state.set((prev) => ({
15312
+ internal: {
15313
+ ...prev.internal,
15314
+ canvasTarget,
15315
+ unregisterPrimary
15316
+ }
15317
+ }));
15318
+ }
14607
15319
  }
14608
15320
  let raycaster = state.raycaster;
14609
15321
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
14610
15322
  const { params, ...options } = raycastOptions || {};
14611
15323
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14612
- if (!is.equ(params, raycaster.params, shallowLoose))
15324
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14613
15325
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15326
+ }
15327
+ let tempCamera = state.camera;
14614
15328
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14615
15329
  lastCamera = cameraOptions;
14616
15330
  const isCamera = cameraOptions?.isCamera;
@@ -14630,6 +15344,7 @@ function createRoot(canvas) {
14630
15344
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14631
15345
  }
14632
15346
  state.set({ camera });
15347
+ tempCamera = camera;
14633
15348
  raycaster.camera = camera;
14634
15349
  }
14635
15350
  if (!state.scene) {
@@ -14647,7 +15362,7 @@ function createRoot(canvas) {
14647
15362
  rootScene: scene,
14648
15363
  internal: { ...prev.internal, container: scene }
14649
15364
  }));
14650
- const camera = state.camera;
15365
+ const camera = tempCamera;
14651
15366
  if (camera && !camera.parent) scene.add(camera);
14652
15367
  }
14653
15368
  if (events && !state.events.handlers) {
@@ -14661,9 +15376,17 @@ function createRoot(canvas) {
14661
15376
  wasEnabled = enabled;
14662
15377
  });
14663
15378
  }
15379
+ if (_sizeProps !== void 0) {
15380
+ state.set({ _sizeProps });
15381
+ }
15382
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15383
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15384
+ }
14664
15385
  const size = computeInitialSize(canvas, propsSize);
14665
- if (!is.equ(size, state.size, shallowLoose)) {
15386
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15387
+ const wasImperative = state._sizeImperative;
14666
15388
  state.setSize(size.width, size.height, size.top, size.left);
15389
+ if (!wasImperative) state.set({ _sizeImperative: false });
14667
15390
  }
14668
15391
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14669
15392
  state.setDpr(dpr);
@@ -14685,10 +15408,10 @@ function createRoot(canvas) {
14685
15408
  lastConfiguredProps.performance = performance;
14686
15409
  }
14687
15410
  if (!state.xr) {
14688
- const handleXRFrame = (timestamp, frame) => {
15411
+ const handleXRFrame = (timestamp, _frame) => {
14689
15412
  const state2 = store.getState();
14690
15413
  if (state2.frameloop === "never") return;
14691
- advance(timestamp, true);
15414
+ advance(timestamp);
14692
15415
  };
14693
15416
  const actualRenderer = state.internal.actualRenderer;
14694
15417
  const handleSessionChange = () => {
@@ -14700,16 +15423,16 @@ function createRoot(canvas) {
14700
15423
  };
14701
15424
  const xr = {
14702
15425
  connect() {
14703
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14704
- const actualRenderer2 = renderer2 || gl;
14705
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14706
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15426
+ const { gl, renderer: renderer2 } = store.getState();
15427
+ const xrManager = (renderer2 || gl).xr;
15428
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15429
+ xrManager.addEventListener("sessionend", handleSessionChange);
14707
15430
  },
14708
15431
  disconnect() {
14709
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14710
- const actualRenderer2 = renderer2 || gl;
14711
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14712
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15432
+ const { gl, renderer: renderer2 } = store.getState();
15433
+ const xrManager = (renderer2 || gl).xr;
15434
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15435
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14713
15436
  }
14714
15437
  };
14715
15438
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14721,69 +15444,92 @@ function createRoot(canvas) {
14721
15444
  const oldType = renderer.shadowMap.type;
14722
15445
  renderer.shadowMap.enabled = !!shadows;
14723
15446
  if (is.boo(shadows)) {
14724
- renderer.shadowMap.type = webgpu.PCFSoftShadowMap;
15447
+ renderer.shadowMap.type = webgpu.PCFShadowMap;
14725
15448
  } else if (is.str(shadows)) {
15449
+ if (shadows === "soft") {
15450
+ notifyDepreciated({
15451
+ heading: 'shadows="soft" is deprecated',
15452
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15453
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15454
+ });
15455
+ }
14726
15456
  const types = {
14727
15457
  basic: webgpu.BasicShadowMap,
14728
15458
  percentage: webgpu.PCFShadowMap,
14729
- soft: webgpu.PCFSoftShadowMap,
15459
+ soft: webgpu.PCFShadowMap,
14730
15460
  variance: webgpu.VSMShadowMap
14731
15461
  };
14732
- renderer.shadowMap.type = types[shadows] ?? webgpu.PCFSoftShadowMap;
15462
+ renderer.shadowMap.type = types[shadows] ?? webgpu.PCFShadowMap;
14733
15463
  } else if (is.obj(shadows)) {
14734
15464
  Object.assign(renderer.shadowMap, shadows);
14735
15465
  }
14736
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15466
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14737
15467
  renderer.shadowMap.needsUpdate = true;
14738
- }
14739
- {
14740
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14741
- const linearChanged = linear !== lastConfiguredProps.linear;
14742
- const flatChanged = flat !== lastConfiguredProps.flat;
14743
- if (legacyChanged) {
14744
- if (legacy)
14745
- notifyDepreciated({
14746
- heading: "Legacy Color Management",
14747
- body: "Legacy color management is deprecated and will be removed in a future version.",
14748
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
14749
- });
14750
- }
14751
- if (legacyChanged) {
14752
- webgpu.ColorManagement.enabled = !legacy;
14753
- lastConfiguredProps.legacy = legacy;
14754
15468
  }
14755
- if (!configured || linearChanged) {
14756
- renderer.outputColorSpace = linear ? webgpu.LinearSRGBColorSpace : webgpu.SRGBColorSpace;
14757
- lastConfiguredProps.linear = linear;
14758
- }
14759
- if (!configured || flatChanged) {
14760
- renderer.toneMapping = flat ? webgpu.NoToneMapping : webgpu.ACESFilmicToneMapping;
14761
- lastConfiguredProps.flat = flat;
14762
- }
14763
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14764
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14765
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15469
+ }
15470
+ if (!configured) {
15471
+ renderer.outputColorSpace = webgpu.SRGBColorSpace;
15472
+ renderer.toneMapping = webgpu.ACESFilmicToneMapping;
14766
15473
  }
14767
15474
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14768
15475
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14769
15476
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14770
15477
  }
14771
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
14772
- applyProps(renderer, glConfig);
15478
+ const r3fProps = ["textureColorSpace"];
15479
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15480
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15481
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
15482
+ const glProps = {};
15483
+ for (const key in glConfig) {
15484
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15485
+ }
15486
+ applyProps(renderer, glProps);
15487
+ }
14773
15488
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14774
15489
  const currentRenderer = state.renderer;
14775
15490
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14776
- applyProps(currentRenderer, rendererConfig);
15491
+ const rendererProps = {};
15492
+ for (const key in rendererConfig) {
15493
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15494
+ }
15495
+ applyProps(currentRenderer, rendererProps);
14777
15496
  }
14778
15497
  }
14779
15498
  const scheduler = getScheduler();
14780
15499
  const rootId = state.internal.rootId;
14781
15500
  if (!rootId) {
14782
- const newRootId = scheduler.generateRootId();
15501
+ const newRootId = canvasId || scheduler.generateRootId();
14783
15502
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14784
15503
  getState: () => store.getState(),
14785
15504
  onError: (err) => store.getState().setError(err)
14786
15505
  });
15506
+ const unregisterCanvasTarget = scheduler.register(
15507
+ () => {
15508
+ const state2 = store.getState();
15509
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15510
+ const renderer2 = state2.internal.actualRenderer;
15511
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15512
+ }
15513
+ },
15514
+ {
15515
+ id: `${newRootId}_canvasTarget`,
15516
+ rootId: newRootId,
15517
+ phase: "start",
15518
+ system: true
15519
+ }
15520
+ );
15521
+ const unregisterEventsFlush = scheduler.register(
15522
+ () => {
15523
+ const state2 = store.getState();
15524
+ state2.events.flush?.();
15525
+ },
15526
+ {
15527
+ id: `${newRootId}_events`,
15528
+ rootId: newRootId,
15529
+ phase: "input",
15530
+ system: true
15531
+ }
15532
+ );
14787
15533
  const unregisterFrustum = scheduler.register(
14788
15534
  () => {
14789
15535
  const state2 = store.getState();
@@ -14818,18 +15564,22 @@ function createRoot(canvas) {
14818
15564
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14819
15565
  if (userHandlesRender || state2.internal.priority) return;
14820
15566
  try {
14821
- if (state2.postProcessing?.render) state2.postProcessing.render();
15567
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14822
15568
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14823
15569
  } catch (error) {
14824
15570
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14825
15571
  }
14826
15572
  },
14827
15573
  {
14828
- id: `${newRootId}_render`,
15574
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15575
+ id: canvasId || `${newRootId}_render`,
14829
15576
  rootId: newRootId,
14830
15577
  phase: "render",
14831
- system: true
15578
+ system: true,
14832
15579
  // Internal flag: this is a system job, not user-controlled
15580
+ // Apply scheduler config for render ordering and rate limiting
15581
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15582
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14833
15583
  }
14834
15584
  );
14835
15585
  state.set((state2) => ({
@@ -14838,6 +15588,8 @@ function createRoot(canvas) {
14838
15588
  rootId: newRootId,
14839
15589
  unregisterRoot: () => {
14840
15590
  unregisterRoot();
15591
+ unregisterCanvasTarget();
15592
+ unregisterEventsFlush();
14841
15593
  unregisterFrustum();
14842
15594
  unregisterVisibility();
14843
15595
  unregisterRender();
@@ -14896,15 +15648,24 @@ function unmountComponentAtNode(canvas, callback) {
14896
15648
  const renderer = state.internal.actualRenderer;
14897
15649
  const unregisterRoot = state.internal.unregisterRoot;
14898
15650
  if (unregisterRoot) unregisterRoot();
15651
+ const unregisterPrimary = state.internal.unregisterPrimary;
15652
+ if (unregisterPrimary) unregisterPrimary();
15653
+ const canvasTarget = state.internal.canvasTarget;
15654
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14899
15655
  state.events.disconnect?.();
14900
15656
  cleanupHelperGroup(root.store);
14901
- renderer?.renderLists?.dispose?.();
14902
- renderer?.forceContextLoss?.();
14903
- if (renderer?.xr) state.xr.disconnect();
15657
+ if (state.isLegacy && renderer) {
15658
+ ;
15659
+ renderer.renderLists?.dispose?.();
15660
+ renderer.forceContextLoss?.();
15661
+ }
15662
+ if (!state.internal.isSecondary) {
15663
+ if (renderer?.xr) state.xr.disconnect();
15664
+ }
14904
15665
  dispose(state.scene);
14905
15666
  _roots.delete(canvas);
14906
15667
  if (callback) callback(canvas);
14907
- } catch (e) {
15668
+ } catch {
14908
15669
  }
14909
15670
  }, 500);
14910
15671
  }
@@ -14912,36 +15673,34 @@ function unmountComponentAtNode(canvas, callback) {
14912
15673
  }
14913
15674
  }
14914
15675
  function createPortal(children, container, state) {
14915
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15676
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14916
15677
  }
14917
- function PortalWrapper({ children, container, state }) {
15678
+ function Portal({ children, container, state }) {
14918
15679
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14919
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15680
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14920
15681
  if (isRef(container)) return container.current ?? null;
14921
15682
  return container;
14922
15683
  });
15684
+ const setResolvedContainer = React.useCallback(
15685
+ (newContainer) => {
15686
+ if (!newContainer || newContainer === resolvedContainer) return;
15687
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15688
+ },
15689
+ [resolvedContainer, _setResolvedContainer, isRef]
15690
+ );
14923
15691
  React.useMemo(() => {
14924
- if (isRef(container)) {
14925
- const current = container.current;
14926
- if (!current) {
14927
- queueMicrotask(() => {
14928
- const updated = container.current;
14929
- if (updated && updated !== resolvedContainer) {
14930
- setResolvedContainer(updated);
14931
- }
14932
- });
14933
- } else if (current !== resolvedContainer) {
14934
- setResolvedContainer(current);
14935
- }
14936
- } else if (container !== resolvedContainer) {
14937
- setResolvedContainer(container);
15692
+ if (isRef(container) && !container.current) {
15693
+ return queueMicrotask(() => {
15694
+ setResolvedContainer(container.current);
15695
+ });
14938
15696
  }
14939
- }, [container, resolvedContainer, isRef]);
15697
+ setResolvedContainer(container);
15698
+ }, [container, isRef, setResolvedContainer]);
14940
15699
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14941
15700
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14942
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15701
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14943
15702
  }
14944
- function Portal({ state = {}, children, container }) {
15703
+ function PortalInner({ state = {}, children, container }) {
14945
15704
  const { events, size, injectScene = true, ...rest } = state;
14946
15705
  const previousRoot = useStore();
14947
15706
  const [raycaster] = React.useState(() => new webgpu.Raycaster());
@@ -14962,11 +15721,12 @@ function Portal({ state = {}, children, container }) {
14962
15721
  };
14963
15722
  }, [portalScene, container, injectScene]);
14964
15723
  const inject = useMutableCallback((rootState, injectState) => {
15724
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14965
15725
  let viewport = void 0;
14966
- if (injectState.camera && size) {
15726
+ if (injectState.camera && (size || injectState.size)) {
14967
15727
  const camera = injectState.camera;
14968
- viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), size);
14969
- if (camera !== rootState.camera) updateCamera(camera, size);
15728
+ viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
15729
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14970
15730
  }
14971
15731
  return {
14972
15732
  // The intersect consists of the previous root state
@@ -14983,7 +15743,7 @@ function Portal({ state = {}, children, container }) {
14983
15743
  previousRoot,
14984
15744
  // Events, size and viewport can be overridden by the inject layer
14985
15745
  events: { ...rootState.events, ...injectState.events, ...events },
14986
- size: { ...rootState.size, ...size },
15746
+ size: resolvedSize,
14987
15747
  viewport: { ...rootState.viewport, ...viewport },
14988
15748
  // Layers are allowed to override events
14989
15749
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14995,9 +15755,13 @@ function Portal({ state = {}, children, container }) {
14995
15755
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14996
15756
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14997
15757
  onMutate(previousRoot.getState());
14998
- previousRoot.subscribe(onMutate);
14999
15758
  return store;
15000
15759
  }, [previousRoot, container]);
15760
+ useIsomorphicLayoutEffect(() => {
15761
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15762
+ const unsubscribe = previousRoot.subscribe(onMutate);
15763
+ return unsubscribe;
15764
+ }, [previousRoot, usePortalStore]);
15001
15765
  return (
15002
15766
  // @ts-ignore, reconciler types are not maintained
15003
15767
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -15017,15 +15781,13 @@ function CanvasImpl({
15017
15781
  fallback,
15018
15782
  resize,
15019
15783
  style,
15784
+ id,
15020
15785
  gl,
15021
- renderer,
15786
+ renderer: rendererProp,
15022
15787
  events = createPointerEvents,
15023
15788
  eventSource,
15024
15789
  eventPrefix,
15025
15790
  shadows,
15026
- linear,
15027
- flat,
15028
- legacy,
15029
15791
  orthographic,
15030
15792
  frameloop,
15031
15793
  dpr,
@@ -15037,10 +15799,56 @@ function CanvasImpl({
15037
15799
  onDragOverMissed,
15038
15800
  onDropMissed,
15039
15801
  onCreated,
15802
+ hmr,
15803
+ width,
15804
+ height,
15805
+ background,
15806
+ forceEven,
15040
15807
  ...props
15041
15808
  }) {
15809
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15810
+ let primaryCanvas;
15811
+ let scheduler;
15812
+ let renderer;
15813
+ if (isRendererConfig) {
15814
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15815
+ primaryCanvas = pc;
15816
+ scheduler = sc;
15817
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15818
+ } else {
15819
+ renderer = rendererProp;
15820
+ }
15042
15821
  React__namespace.useMemo(() => extend(THREE), []);
15043
15822
  const Bridge = useBridge();
15823
+ const backgroundProps = React__namespace.useMemo(() => {
15824
+ if (!background) return null;
15825
+ if (typeof background === "object" && !background.isColor) {
15826
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15827
+ return {
15828
+ ...rest,
15829
+ preset,
15830
+ files: envMap || files,
15831
+ backgroundFiles: backgroundMap,
15832
+ background: true
15833
+ };
15834
+ }
15835
+ if (typeof background === "number") {
15836
+ return { color: background, background: true };
15837
+ }
15838
+ if (typeof background === "string") {
15839
+ if (background in presetsObj) {
15840
+ return { preset: background, background: true };
15841
+ }
15842
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15843
+ return { files: background, background: true };
15844
+ }
15845
+ return { color: background, background: true };
15846
+ }
15847
+ if (background.isColor) {
15848
+ return { color: background, background: true };
15849
+ }
15850
+ return null;
15851
+ }, [background]);
15044
15852
  const hasInitialSizeRef = React__namespace.useRef(false);
15045
15853
  const measureConfig = React__namespace.useMemo(() => {
15046
15854
  if (!hasInitialSizeRef.current) {
@@ -15057,7 +15865,21 @@ function CanvasImpl({
15057
15865
  };
15058
15866
  }, [resize, hasInitialSizeRef.current]);
15059
15867
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15060
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
15868
+ const effectiveSize = React__namespace.useMemo(() => {
15869
+ let w = width ?? containerRect.width;
15870
+ let h = height ?? containerRect.height;
15871
+ if (forceEven) {
15872
+ w = Math.ceil(w / 2) * 2;
15873
+ h = Math.ceil(h / 2) * 2;
15874
+ }
15875
+ return {
15876
+ width: w,
15877
+ height: h,
15878
+ top: containerRect.top,
15879
+ left: containerRect.left
15880
+ };
15881
+ }, [width, height, containerRect, forceEven]);
15882
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15061
15883
  hasInitialSizeRef.current = true;
15062
15884
  }
15063
15885
  const canvasRef = React__namespace.useRef(null);
@@ -15076,7 +15898,7 @@ function CanvasImpl({
15076
15898
  useIsomorphicLayoutEffect(() => {
15077
15899
  effectActiveRef.current = true;
15078
15900
  const canvas = canvasRef.current;
15079
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15901
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15080
15902
  if (!root.current) {
15081
15903
  root.current = createRoot(canvas);
15082
15904
  notifyAlpha({
@@ -15096,21 +15918,24 @@ function CanvasImpl({
15096
15918
  async function run() {
15097
15919
  if (!effectActiveRef.current || !root.current) return;
15098
15920
  await root.current.configure({
15921
+ id,
15922
+ primaryCanvas,
15923
+ scheduler,
15099
15924
  gl,
15100
15925
  renderer,
15101
15926
  scene,
15102
15927
  events,
15103
15928
  shadows,
15104
- linear,
15105
- flat,
15106
- legacy,
15107
15929
  orthographic,
15108
15930
  frameloop,
15109
15931
  dpr,
15110
15932
  performance,
15111
15933
  raycaster,
15112
15934
  camera,
15113
- size: containerRect,
15935
+ size: effectiveSize,
15936
+ // Store size props for reset functionality
15937
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15938
+ forceEven,
15114
15939
  // Pass mutable reference to onPointerMissed so it's free to update
15115
15940
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15116
15941
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15134,7 +15959,10 @@ function CanvasImpl({
15134
15959
  });
15135
15960
  if (!effectActiveRef.current || !root.current) return;
15136
15961
  root.current.render(
15137
- /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15962
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
15963
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15964
+ children ?? null
15965
+ ] }) }) })
15138
15966
  );
15139
15967
  }
15140
15968
  run();
@@ -15156,6 +15984,35 @@ function CanvasImpl({
15156
15984
  };
15157
15985
  }
15158
15986
  }, []);
15987
+ React__namespace.useEffect(() => {
15988
+ if (hmr === false) return;
15989
+ const canvas = canvasRef.current;
15990
+ if (!canvas) return;
15991
+ const handleHMR = () => {
15992
+ queueMicrotask(() => {
15993
+ const rootEntry = _roots.get(canvas);
15994
+ if (rootEntry?.store) {
15995
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15996
+ rootEntry.store.setState((state) => ({
15997
+ nodes: {},
15998
+ uniforms: {},
15999
+ _hmrVersion: state._hmrVersion + 1
16000
+ }));
16001
+ }
16002
+ });
16003
+ };
16004
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
16005
+ const hot = undefined;
16006
+ hot.on("vite:afterUpdate", handleHMR);
16007
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
16008
+ }
16009
+ if (typeof module !== "undefined" && module.hot) {
16010
+ const hot = module.hot;
16011
+ hot.addStatusHandler((status) => {
16012
+ if (status === "idle") handleHMR();
16013
+ });
16014
+ }
16015
+ }, [hmr]);
15159
16016
  const pointerEvents = eventSource ? "none" : "auto";
15160
16017
  return /* @__PURE__ */ jsxRuntime.jsx(
15161
16018
  "div",
@@ -15170,7 +16027,16 @@ function CanvasImpl({
15170
16027
  ...style
15171
16028
  },
15172
16029
  ...props,
15173
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
16030
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
16031
+ "canvas",
16032
+ {
16033
+ ref: canvasRef,
16034
+ id,
16035
+ className: "r3f-canvas",
16036
+ style: { display: "block", width: "100%", height: "100%" },
16037
+ children: fallback
16038
+ }
16039
+ ) })
15174
16040
  }
15175
16041
  );
15176
16042
  }
@@ -15182,8 +16048,15 @@ extend(THREE);
15182
16048
 
15183
16049
  exports.Block = Block;
15184
16050
  exports.Canvas = Canvas;
16051
+ exports.Environment = Environment;
16052
+ exports.EnvironmentCube = EnvironmentCube;
16053
+ exports.EnvironmentMap = EnvironmentMap;
16054
+ exports.EnvironmentPortal = EnvironmentPortal;
15185
16055
  exports.ErrorBoundary = ErrorBoundary;
16056
+ exports.FROM_REF = FROM_REF;
15186
16057
  exports.IsObject = IsObject;
16058
+ exports.ONCE = ONCE;
16059
+ exports.Portal = Portal;
15187
16060
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15188
16061
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15189
16062
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15213,30 +16086,41 @@ exports.events = createPointerEvents;
15213
16086
  exports.extend = extend;
15214
16087
  exports.findInitialRoot = findInitialRoot;
15215
16088
  exports.flushSync = flushSync;
16089
+ exports.fromRef = fromRef;
15216
16090
  exports.getInstanceProps = getInstanceProps;
16091
+ exports.getPrimary = getPrimary;
16092
+ exports.getPrimaryIds = getPrimaryIds;
15217
16093
  exports.getRootState = getRootState;
15218
16094
  exports.getScheduler = getScheduler;
15219
16095
  exports.getUuidPrefix = getUuidPrefix;
15220
16096
  exports.hasConstructor = hasConstructor;
16097
+ exports.hasPrimary = hasPrimary;
15221
16098
  exports.invalidate = invalidate;
15222
16099
  exports.invalidateInstance = invalidateInstance;
15223
16100
  exports.is = is;
15224
16101
  exports.isColorRepresentation = isColorRepresentation;
15225
16102
  exports.isCopyable = isCopyable;
16103
+ exports.isFromRef = isFromRef;
15226
16104
  exports.isObject3D = isObject3D;
16105
+ exports.isOnce = isOnce;
15227
16106
  exports.isOrthographicCamera = isOrthographicCamera;
15228
16107
  exports.isRef = isRef;
15229
16108
  exports.isRenderer = isRenderer;
15230
16109
  exports.isTexture = isTexture;
15231
16110
  exports.isVectorLike = isVectorLike;
16111
+ exports.once = once;
15232
16112
  exports.prepare = prepare;
16113
+ exports.presetsObj = presetsObj;
15233
16114
  exports.reconciler = reconciler;
16115
+ exports.registerPrimary = registerPrimary;
15234
16116
  exports.removeInteractivity = removeInteractivity;
15235
16117
  exports.resolve = resolve;
15236
16118
  exports.unmountComponentAtNode = unmountComponentAtNode;
16119
+ exports.unregisterPrimary = unregisterPrimary;
15237
16120
  exports.updateCamera = updateCamera;
15238
16121
  exports.updateFrustum = updateFrustum;
15239
16122
  exports.useBridge = useBridge;
16123
+ exports.useEnvironment = useEnvironment;
15240
16124
  exports.useFrame = useFrame;
15241
16125
  exports.useGraph = useGraph;
15242
16126
  exports.useInstanceHandle = useInstanceHandle;
@@ -15248,3 +16132,4 @@ exports.useStore = useStore;
15248
16132
  exports.useTexture = useTexture;
15249
16133
  exports.useTextures = useTextures;
15250
16134
  exports.useThree = useThree;
16135
+ exports.waitForPrimary = waitForPrimary;