@react-three/fiber 10.0.0-alpha.2 → 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.
@@ -6,6 +6,12 @@ const jsxRuntime = require('react/jsx-runtime');
6
6
  const React = require('react');
7
7
  const useMeasure = require('react-use-measure');
8
8
  const itsFine = require('its-fine');
9
+ const fiber = require('@react-three/fiber');
10
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
11
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
12
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
13
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
14
+ const gainmapJs = require('@monogrid/gainmap-js');
9
15
  const Tb = require('scheduler');
10
16
  const traditional = require('zustand/traditional');
11
17
  const suspendReact = require('suspend-react');
@@ -67,6 +73,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
67
73
  WebGLRenderer: WebGLRenderer
68
74
  }, [webgpu__namespace]);
69
75
 
76
+ const primaryRegistry = /* @__PURE__ */ new Map();
77
+ const pendingSubscribers = /* @__PURE__ */ new Map();
78
+ function registerPrimary(id, renderer, store) {
79
+ if (primaryRegistry.has(id)) {
80
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
81
+ }
82
+ const entry = { renderer, store };
83
+ primaryRegistry.set(id, entry);
84
+ const subscribers = pendingSubscribers.get(id);
85
+ if (subscribers) {
86
+ subscribers.forEach((callback) => callback(entry));
87
+ pendingSubscribers.delete(id);
88
+ }
89
+ return () => {
90
+ const currentEntry = primaryRegistry.get(id);
91
+ if (currentEntry?.renderer === renderer) {
92
+ primaryRegistry.delete(id);
93
+ }
94
+ };
95
+ }
96
+ function getPrimary(id) {
97
+ return primaryRegistry.get(id);
98
+ }
99
+ function waitForPrimary(id, timeout = 5e3) {
100
+ const existing = primaryRegistry.get(id);
101
+ if (existing) {
102
+ return Promise.resolve(existing);
103
+ }
104
+ return new Promise((resolve, reject) => {
105
+ const timeoutId = setTimeout(() => {
106
+ const subscribers = pendingSubscribers.get(id);
107
+ if (subscribers) {
108
+ const index = subscribers.indexOf(callback);
109
+ if (index !== -1) subscribers.splice(index, 1);
110
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
111
+ }
112
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
113
+ }, timeout);
114
+ const callback = (entry) => {
115
+ clearTimeout(timeoutId);
116
+ resolve(entry);
117
+ };
118
+ if (!pendingSubscribers.has(id)) {
119
+ pendingSubscribers.set(id, []);
120
+ }
121
+ pendingSubscribers.get(id).push(callback);
122
+ });
123
+ }
124
+ function hasPrimary(id) {
125
+ return primaryRegistry.has(id);
126
+ }
127
+ function unregisterPrimary(id) {
128
+ primaryRegistry.delete(id);
129
+ }
130
+ function getPrimaryIds() {
131
+ return Array.from(primaryRegistry.keys());
132
+ }
133
+
134
+ const presetsObj = {
135
+ apartment: "lebombo_1k.hdr",
136
+ city: "potsdamer_platz_1k.hdr",
137
+ dawn: "kiara_1_dawn_1k.hdr",
138
+ forest: "forest_slope_1k.hdr",
139
+ lobby: "st_fagans_interior_1k.hdr",
140
+ night: "dikhololo_night_1k.hdr",
141
+ park: "rooitou_park_1k.hdr",
142
+ studio: "studio_small_03_1k.hdr",
143
+ sunset: "venice_sunset_1k.hdr",
144
+ warehouse: "empty_warehouse_01_1k.hdr"
145
+ };
146
+
147
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
148
+ const isArray = (arr) => Array.isArray(arr);
149
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
150
+ function useEnvironment({
151
+ files = defaultFiles,
152
+ path = "",
153
+ preset = void 0,
154
+ colorSpace = void 0,
155
+ extensions
156
+ } = {}) {
157
+ if (preset) {
158
+ validatePreset(preset);
159
+ files = presetsObj[preset];
160
+ path = CUBEMAP_ROOT;
161
+ }
162
+ const multiFile = isArray(files);
163
+ const { extension, isCubemap } = getExtension(files);
164
+ const loader = getLoader$1(extension);
165
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
166
+ const renderer = fiber.useThree((state) => state.renderer);
167
+ React.useLayoutEffect(() => {
168
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
169
+ function clearGainmapTexture() {
170
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
171
+ }
172
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
173
+ }, [extension, files, loader, multiFile, renderer.domElement]);
174
+ const loaderResult = fiber.useLoader(
175
+ loader,
176
+ multiFile ? [files] : files,
177
+ (loader2) => {
178
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
179
+ loader2.setRenderer?.(renderer);
180
+ }
181
+ loader2.setPath?.(path);
182
+ if (extensions) extensions(loader2);
183
+ }
184
+ );
185
+ let texture = multiFile ? (
186
+ // @ts-ignore
187
+ loaderResult[0]
188
+ ) : loaderResult;
189
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
190
+ texture = texture.renderTarget?.texture;
191
+ }
192
+ texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
193
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
194
+ return texture;
195
+ }
196
+ const preloadDefaultOptions = {
197
+ files: defaultFiles,
198
+ path: "",
199
+ preset: void 0,
200
+ extensions: void 0
201
+ };
202
+ useEnvironment.preload = (preloadOptions) => {
203
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
204
+ let { files, path = "" } = options;
205
+ const { preset, extensions } = options;
206
+ if (preset) {
207
+ validatePreset(preset);
208
+ files = presetsObj[preset];
209
+ path = CUBEMAP_ROOT;
210
+ }
211
+ const { extension } = getExtension(files);
212
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
213
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
214
+ }
215
+ const loader = getLoader$1(extension);
216
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
217
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
218
+ loader2.setPath?.(path);
219
+ if (extensions) extensions(loader2);
220
+ });
221
+ };
222
+ const clearDefaultOptins = {
223
+ files: defaultFiles,
224
+ preset: void 0
225
+ };
226
+ useEnvironment.clear = (clearOptions) => {
227
+ const options = { ...clearDefaultOptins, ...clearOptions };
228
+ let { files } = options;
229
+ const { preset } = options;
230
+ if (preset) {
231
+ validatePreset(preset);
232
+ files = presetsObj[preset];
233
+ }
234
+ const { extension } = getExtension(files);
235
+ const loader = getLoader$1(extension);
236
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
237
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
238
+ };
239
+ function validatePreset(preset) {
240
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
241
+ }
242
+ function getExtension(files) {
243
+ const isCubemap = isArray(files) && files.length === 6;
244
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
245
+ const firstEntry = isArray(files) ? files[0] : files;
246
+ 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();
247
+ return { extension, isCubemap, isGainmap };
248
+ }
249
+ function getLoader$1(extension) {
250
+ 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;
251
+ return loader;
252
+ }
253
+
254
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
255
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
256
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
257
+ sceneProps = {
258
+ backgroundBlurriness: 0,
259
+ backgroundIntensity: 1,
260
+ backgroundRotation: [0, 0, 0],
261
+ environmentIntensity: 1,
262
+ environmentRotation: [0, 0, 0],
263
+ ...sceneProps
264
+ };
265
+ const target = resolveScene(scene || defaultScene);
266
+ const oldbg = target.background;
267
+ const oldenv = target.environment;
268
+ const oldSceneProps = {
269
+ // @ts-ignore
270
+ backgroundBlurriness: target.backgroundBlurriness,
271
+ // @ts-ignore
272
+ backgroundIntensity: target.backgroundIntensity,
273
+ // @ts-ignore
274
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
275
+ // @ts-ignore
276
+ environmentIntensity: target.environmentIntensity,
277
+ // @ts-ignore
278
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
279
+ };
280
+ if (background !== "only") target.environment = texture;
281
+ if (background) target.background = texture;
282
+ fiber.applyProps(target, sceneProps);
283
+ return () => {
284
+ if (background !== "only") target.environment = oldenv;
285
+ if (background) target.background = oldbg;
286
+ fiber.applyProps(target, oldSceneProps);
287
+ };
288
+ }
289
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
290
+ const defaultScene = fiber.useThree((state) => state.scene);
291
+ React__namespace.useLayoutEffect(() => {
292
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
293
+ });
294
+ return null;
295
+ }
296
+ function EnvironmentCube({
297
+ background = false,
298
+ scene,
299
+ blur,
300
+ backgroundBlurriness,
301
+ backgroundIntensity,
302
+ backgroundRotation,
303
+ environmentIntensity,
304
+ environmentRotation,
305
+ ...rest
306
+ }) {
307
+ const texture = useEnvironment(rest);
308
+ const defaultScene = fiber.useThree((state) => state.scene);
309
+ React__namespace.useLayoutEffect(() => {
310
+ return setEnvProps(background, scene, defaultScene, texture, {
311
+ backgroundBlurriness: blur ?? backgroundBlurriness,
312
+ backgroundIntensity,
313
+ backgroundRotation,
314
+ environmentIntensity,
315
+ environmentRotation
316
+ });
317
+ });
318
+ React__namespace.useEffect(() => {
319
+ return () => {
320
+ texture.dispose();
321
+ };
322
+ }, [texture]);
323
+ return null;
324
+ }
325
+ function EnvironmentPortal({
326
+ children,
327
+ near = 0.1,
328
+ far = 1e3,
329
+ resolution = 256,
330
+ frames = 1,
331
+ map,
332
+ background = false,
333
+ blur,
334
+ backgroundBlurriness,
335
+ backgroundIntensity,
336
+ backgroundRotation,
337
+ environmentIntensity,
338
+ environmentRotation,
339
+ scene,
340
+ files,
341
+ path,
342
+ preset = void 0,
343
+ extensions
344
+ }) {
345
+ const gl = fiber.useThree((state) => state.gl);
346
+ const defaultScene = fiber.useThree((state) => state.scene);
347
+ const camera = React__namespace.useRef(null);
348
+ const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
349
+ const fbo = React__namespace.useMemo(() => {
350
+ const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
351
+ fbo2.texture.type = webgpu.HalfFloatType;
352
+ return fbo2;
353
+ }, [resolution]);
354
+ React__namespace.useEffect(() => {
355
+ return () => {
356
+ fbo.dispose();
357
+ };
358
+ }, [fbo]);
359
+ React__namespace.useLayoutEffect(() => {
360
+ if (frames === 1) {
361
+ const autoClear = gl.autoClear;
362
+ gl.autoClear = true;
363
+ camera.current.update(gl, virtualScene);
364
+ gl.autoClear = autoClear;
365
+ }
366
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
367
+ backgroundBlurriness: blur ?? backgroundBlurriness,
368
+ backgroundIntensity,
369
+ backgroundRotation,
370
+ environmentIntensity,
371
+ environmentRotation
372
+ });
373
+ }, [
374
+ children,
375
+ virtualScene,
376
+ fbo.texture,
377
+ scene,
378
+ defaultScene,
379
+ background,
380
+ frames,
381
+ gl,
382
+ blur,
383
+ backgroundBlurriness,
384
+ backgroundIntensity,
385
+ backgroundRotation,
386
+ environmentIntensity,
387
+ environmentRotation
388
+ ]);
389
+ let count = 1;
390
+ fiber.useFrame(() => {
391
+ if (frames === Infinity || count < frames) {
392
+ const autoClear = gl.autoClear;
393
+ gl.autoClear = true;
394
+ camera.current.update(gl, virtualScene);
395
+ gl.autoClear = autoClear;
396
+ count++;
397
+ }
398
+ });
399
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
400
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
401
+ children,
402
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
403
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
404
+ ] }),
405
+ virtualScene
406
+ ) });
407
+ }
408
+ function EnvironmentGround(props) {
409
+ const textureDefault = useEnvironment(props);
410
+ const texture = props.map || textureDefault;
411
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
412
+ React__namespace.useEffect(() => {
413
+ return () => {
414
+ textureDefault.dispose();
415
+ };
416
+ }, [textureDefault]);
417
+ const height = props.ground?.height ?? 15;
418
+ const radius = props.ground?.radius ?? 60;
419
+ const scale = props.ground?.scale ?? 1e3;
420
+ const args = React__namespace.useMemo(
421
+ () => [texture, height, radius],
422
+ [texture, height, radius]
423
+ );
424
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
425
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
426
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
427
+ ] });
428
+ }
429
+ function EnvironmentColor({ color, scene }) {
430
+ const defaultScene = fiber.useThree((state) => state.scene);
431
+ React__namespace.useLayoutEffect(() => {
432
+ if (color === void 0) return;
433
+ const target = resolveScene(scene || defaultScene);
434
+ const oldBg = target.background;
435
+ target.background = new webgpu.Color(color);
436
+ return () => {
437
+ target.background = oldBg;
438
+ };
439
+ });
440
+ return null;
441
+ }
442
+ function EnvironmentDualSource(props) {
443
+ const { backgroundFiles, ...envProps } = props;
444
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
445
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
446
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
447
+ ] });
448
+ }
449
+ function Environment(props) {
450
+ if (props.color && !props.files && !props.preset && !props.map) {
451
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
452
+ }
453
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
454
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
455
+ }
456
+ 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 });
457
+ }
458
+
70
459
  var __defProp$3 = Object.defineProperty;
71
460
  var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
72
461
  var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -246,7 +635,8 @@ function prepare(target, root, type, props) {
246
635
  object,
247
636
  eventCount: 0,
248
637
  handlers: {},
249
- isHidden: false
638
+ isHidden: false,
639
+ deferredRefs: []
250
640
  };
251
641
  if (object) object.__r3f = instance;
252
642
  }
@@ -295,7 +685,7 @@ function createOcclusionObserverNode(store, uniform) {
295
685
  let occlusionSetupPromise = null;
296
686
  function enableOcclusion(store) {
297
687
  const state = store.getState();
298
- const { internal, renderer, rootScene } = state;
688
+ const { internal, renderer } = state;
299
689
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
300
690
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
301
691
  if (!hasOcclusionSupport) {
@@ -458,6 +848,22 @@ function hasVisibilityHandlers(handlers) {
458
848
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
459
849
  }
460
850
 
851
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
852
+ function fromRef(ref) {
853
+ return { [FROM_REF]: ref };
854
+ }
855
+ function isFromRef(value) {
856
+ return value !== null && typeof value === "object" && FROM_REF in value;
857
+ }
858
+
859
+ const ONCE = Symbol.for("@react-three/fiber.once");
860
+ function once(...args) {
861
+ return { [ONCE]: args.length ? args : true };
862
+ }
863
+ function isOnce(value) {
864
+ return value !== null && typeof value === "object" && ONCE in value;
865
+ }
866
+
461
867
  const RESERVED_PROPS = [
462
868
  "children",
463
869
  "key",
@@ -528,7 +934,7 @@ function getMemoizedPrototype(root) {
528
934
  ctor = new root.constructor();
529
935
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
530
936
  }
531
- } catch (e) {
937
+ } catch {
532
938
  }
533
939
  return ctor;
534
940
  }
@@ -574,6 +980,25 @@ function applyProps(object, props) {
574
980
  continue;
575
981
  }
576
982
  if (value === void 0) continue;
983
+ if (isFromRef(value)) {
984
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
985
+ continue;
986
+ }
987
+ if (isOnce(value)) {
988
+ if (instance?.appliedOnce?.has(prop)) continue;
989
+ if (instance) {
990
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
991
+ instance.appliedOnce.add(prop);
992
+ }
993
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
994
+ const args = value[ONCE];
995
+ if (typeof targetRoot[targetKey] === "function") {
996
+ targetRoot[targetKey](...args === true ? [] : args);
997
+ } else if (args !== true && args.length > 0) {
998
+ targetRoot[targetKey] = args[0];
999
+ }
1000
+ continue;
1001
+ }
577
1002
  let { root, key, target } = resolve(object, prop);
578
1003
  if (target === void 0 && (typeof root !== "object" || root === null)) {
579
1004
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -596,7 +1021,10 @@ function applyProps(object, props) {
596
1021
  else target.set(value);
597
1022
  } else {
598
1023
  root[key] = value;
599
- 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
1024
+ if (key.endsWith("Node") && root.isMaterial) {
1025
+ root.needsUpdate = true;
1026
+ }
1027
+ 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
600
1028
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
601
1029
  root[key].colorSpace = rootState.textureColorSpace;
602
1030
  }
@@ -629,38 +1057,60 @@ function applyProps(object, props) {
629
1057
  return object;
630
1058
  }
631
1059
 
1060
+ const DEFAULT_POINTER_ID = 0;
1061
+ const XR_POINTER_ID_START = 1e3;
1062
+ function getPointerState(internal, pointerId) {
1063
+ let state = internal.pointerMap.get(pointerId);
1064
+ if (!state) {
1065
+ state = {
1066
+ hovered: /* @__PURE__ */ new Map(),
1067
+ captured: /* @__PURE__ */ new Map(),
1068
+ initialClick: [0, 0],
1069
+ initialHits: []
1070
+ };
1071
+ internal.pointerMap.set(pointerId, state);
1072
+ }
1073
+ return state;
1074
+ }
1075
+ function getPointerId(event) {
1076
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1077
+ }
632
1078
  function makeId(event) {
633
1079
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
634
1080
  }
635
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
636
- const captureData = captures.get(obj);
1081
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1082
+ const pointerState = internal.pointerMap.get(pointerId);
1083
+ if (!pointerState) return;
1084
+ const captureData = pointerState.captured.get(obj);
637
1085
  if (captureData) {
638
- captures.delete(obj);
639
- if (captures.size === 0) {
640
- capturedMap.delete(pointerId);
641
- captureData.target.releasePointerCapture(pointerId);
642
- }
1086
+ pointerState.captured.delete(obj);
1087
+ captureData.target.releasePointerCapture(pointerId);
643
1088
  }
644
1089
  }
645
1090
  function removeInteractivity(store, object) {
646
1091
  const { internal } = store.getState();
647
1092
  internal.interaction = internal.interaction.filter((o) => o !== object);
648
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
649
- internal.hovered.forEach((value, key) => {
650
- if (value.eventObject === object || value.object === object) {
651
- internal.hovered.delete(key);
1093
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1094
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1095
+ pointerState.hovered.forEach((value, key) => {
1096
+ if (value.eventObject === object || value.object === object) {
1097
+ pointerState.hovered.delete(key);
1098
+ }
1099
+ });
1100
+ if (pointerState.captured.has(object)) {
1101
+ releaseInternalPointerCapture(internal, object, pointerId);
652
1102
  }
653
- });
654
- internal.capturedMap.forEach((captures, pointerId) => {
655
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
656
- });
1103
+ }
657
1104
  unregisterVisibility(store, object);
658
1105
  }
659
1106
  function createEvents(store) {
660
- function calculateDistance(event) {
1107
+ function calculateDistance(event, pointerId) {
661
1108
  const { internal } = store.getState();
662
- const dx = event.offsetX - internal.initialClick[0];
663
- const dy = event.offsetY - internal.initialClick[1];
1109
+ const pointerState = internal.pointerMap.get(pointerId);
1110
+ if (!pointerState) return 0;
1111
+ const [initialX, initialY] = pointerState.initialClick;
1112
+ const dx = event.offsetX - initialX;
1113
+ const dy = event.offsetY - initialY;
664
1114
  return Math.round(Math.sqrt(dx * dx + dy * dy));
665
1115
  }
666
1116
  function filterPointerEvents(objects) {
@@ -696,6 +1146,15 @@ function createEvents(store) {
696
1146
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
697
1147
  }
698
1148
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1149
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1150
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1151
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1152
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1153
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1154
+ if (aInteractivePriority !== bInteractivePriority) {
1155
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1156
+ }
1157
+ }
699
1158
  const aState = getRootState(a.object);
700
1159
  const bState = getRootState(b.object);
701
1160
  const aPriority = aState?.events?.priority ?? 1;
@@ -717,9 +1176,13 @@ function createEvents(store) {
717
1176
  eventObject = eventObject.parent;
718
1177
  }
719
1178
  }
720
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
721
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
722
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1179
+ if ("pointerId" in event) {
1180
+ const pointerId = event.pointerId;
1181
+ const pointerState = state.internal.pointerMap.get(pointerId);
1182
+ if (pointerState?.captured.size) {
1183
+ for (const captureData of pointerState.captured.values()) {
1184
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1185
+ }
723
1186
  }
724
1187
  }
725
1188
  return intersections;
@@ -732,27 +1195,25 @@ function createEvents(store) {
732
1195
  if (state) {
733
1196
  const { raycaster, pointer, camera, internal } = state;
734
1197
  const unprojectedPoint = new webgpu.Vector3(pointer.x, pointer.y, 0).unproject(camera);
735
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1198
+ const hasPointerCapture = (id) => {
1199
+ const pointerState = internal.pointerMap.get(id);
1200
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1201
+ };
736
1202
  const setPointerCapture = (id) => {
737
1203
  const captureData = { intersection: hit, target: event.target };
738
- if (internal.capturedMap.has(id)) {
739
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
740
- } else {
741
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
742
- }
1204
+ const pointerState = getPointerState(internal, id);
1205
+ pointerState.captured.set(hit.eventObject, captureData);
743
1206
  event.target.setPointerCapture(id);
744
1207
  };
745
1208
  const releasePointerCapture = (id) => {
746
- const captures = internal.capturedMap.get(id);
747
- if (captures) {
748
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
749
- }
1209
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
750
1210
  };
751
1211
  const extractEventProps = {};
752
1212
  for (const prop in event) {
753
1213
  const property = event[prop];
754
1214
  if (typeof property !== "function") extractEventProps[prop] = property;
755
1215
  }
1216
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
756
1217
  const raycastEvent = {
757
1218
  ...hit,
758
1219
  ...extractEventProps,
@@ -763,18 +1224,19 @@ function createEvents(store) {
763
1224
  unprojectedPoint,
764
1225
  ray: raycaster.ray,
765
1226
  camera,
1227
+ pointerId: eventPointerId,
766
1228
  // Hijack stopPropagation, which just sets a flag
767
1229
  stopPropagation() {
768
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1230
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
769
1231
  if (
770
1232
  // ...if this pointer hasn't been captured
771
- !capturesForPointer || // ... or if the hit object is capturing the pointer
772
- capturesForPointer.has(hit.eventObject)
1233
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1234
+ pointerState.captured.has(hit.eventObject)
773
1235
  ) {
774
1236
  raycastEvent.stopped = localState.stopped = true;
775
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1237
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
776
1238
  const higher = intersections.slice(0, intersections.indexOf(hit));
777
- cancelPointer([...higher, hit]);
1239
+ cancelPointer([...higher, hit], eventPointerId);
778
1240
  }
779
1241
  }
780
1242
  },
@@ -790,15 +1252,18 @@ function createEvents(store) {
790
1252
  }
791
1253
  return intersections;
792
1254
  }
793
- function cancelPointer(intersections) {
1255
+ function cancelPointer(intersections, pointerId) {
794
1256
  const { internal } = store.getState();
795
- for (const hoveredObj of internal.hovered.values()) {
1257
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1258
+ const pointerState = internal.pointerMap.get(pid);
1259
+ if (!pointerState) return;
1260
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
796
1261
  if (!intersections.length || !intersections.find(
797
1262
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
798
1263
  )) {
799
1264
  const eventObject = hoveredObj.eventObject;
800
1265
  const instance = eventObject.__r3f;
801
- internal.hovered.delete(makeId(hoveredObj));
1266
+ pointerState.hovered.delete(hoveredId);
802
1267
  if (instance?.eventCount) {
803
1268
  const handlers = instance.handlers;
804
1269
  const data = { ...hoveredObj, intersections };
@@ -827,41 +1292,118 @@ function createEvents(store) {
827
1292
  instance?.handlers.onDropMissed?.(event);
828
1293
  }
829
1294
  }
1295
+ function cleanupPointer(pointerId) {
1296
+ const { internal } = store.getState();
1297
+ const pointerState = internal.pointerMap.get(pointerId);
1298
+ if (pointerState) {
1299
+ for (const [, hoveredObj] of pointerState.hovered) {
1300
+ const eventObject = hoveredObj.eventObject;
1301
+ const instance = eventObject.__r3f;
1302
+ if (instance?.eventCount) {
1303
+ const handlers = instance.handlers;
1304
+ const data = { ...hoveredObj, intersections: [] };
1305
+ handlers.onPointerOut?.(data);
1306
+ handlers.onPointerLeave?.(data);
1307
+ }
1308
+ }
1309
+ internal.pointerMap.delete(pointerId);
1310
+ }
1311
+ internal.pointerDirty.delete(pointerId);
1312
+ }
1313
+ function processDeferredPointer(event, pointerId) {
1314
+ const state = store.getState();
1315
+ const { internal } = state;
1316
+ if (!state.events.enabled) return;
1317
+ const filter = filterPointerEvents;
1318
+ const hits = intersect(event, filter);
1319
+ cancelPointer(hits, pointerId);
1320
+ function onIntersect(data) {
1321
+ const eventObject = data.eventObject;
1322
+ const instance = eventObject.__r3f;
1323
+ if (!instance?.eventCount) return;
1324
+ const handlers = instance.handlers;
1325
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1326
+ const id = makeId(data);
1327
+ const pointerState = getPointerState(internal, pointerId);
1328
+ const hoveredItem = pointerState.hovered.get(id);
1329
+ if (!hoveredItem) {
1330
+ pointerState.hovered.set(id, data);
1331
+ handlers.onPointerOver?.(data);
1332
+ handlers.onPointerEnter?.(data);
1333
+ } else if (hoveredItem.stopped) {
1334
+ data.stopPropagation();
1335
+ }
1336
+ }
1337
+ handlers.onPointerMove?.(data);
1338
+ }
1339
+ handleIntersects(hits, event, 0, onIntersect);
1340
+ }
830
1341
  function handlePointer(name) {
831
1342
  switch (name) {
832
1343
  case "onPointerLeave":
833
- case "onPointerCancel":
834
1344
  case "onDragLeave":
835
1345
  return () => cancelPointer([]);
1346
+ // Global cancel of these events
1347
+ case "onPointerCancel":
1348
+ return (event) => {
1349
+ const pointerId = getPointerId(event);
1350
+ cleanupPointer(pointerId);
1351
+ };
836
1352
  case "onLostPointerCapture":
837
1353
  return (event) => {
838
1354
  const { internal } = store.getState();
839
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1355
+ const pointerId = getPointerId(event);
1356
+ const pointerState = internal.pointerMap.get(pointerId);
1357
+ if (pointerState?.captured.size) {
840
1358
  requestAnimationFrame(() => {
841
- if (internal.capturedMap.has(event.pointerId)) {
842
- internal.capturedMap.delete(event.pointerId);
843
- cancelPointer([]);
1359
+ const pointerState2 = internal.pointerMap.get(pointerId);
1360
+ if (pointerState2?.captured.size) {
1361
+ pointerState2.captured.clear();
844
1362
  }
1363
+ cancelPointer([], pointerId);
845
1364
  });
846
1365
  }
847
1366
  };
848
1367
  }
849
1368
  return function handleEvent(event) {
850
1369
  const state = store.getState();
851
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1370
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1371
+ const pointerId = getPointerId(event);
852
1372
  internal.lastEvent.current = event;
853
- if (!state.events.enabled) return;
1373
+ if (!events.enabled) return;
854
1374
  const isPointerMove = name === "onPointerMove";
855
1375
  const isDragOver = name === "onDragOver";
856
1376
  const isDrop = name === "onDrop";
857
1377
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1378
+ const isPointerDown = name === "onPointerDown";
1379
+ const isPointerUp = name === "onPointerUp";
1380
+ const isWheel = name === "onWheel";
1381
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1382
+ if (isPointerMove && canDeferRaycasts) {
1383
+ events.compute?.(event, state);
1384
+ internal.pointerDirty.set(pointerId, event);
1385
+ return;
1386
+ }
1387
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1388
+ events.compute?.(event, state);
1389
+ internal.pointerDirty.set(pointerId, event);
1390
+ return;
1391
+ }
1392
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1393
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1394
+ internal.pointerDirty.delete(pointerId);
1395
+ processDeferredPointer(deferredEvent, pointerId);
1396
+ }
858
1397
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
859
1398
  const hits = intersect(event, filter);
860
- const delta = isClickEvent ? calculateDistance(event) : 0;
861
- if (name === "onPointerDown") {
862
- internal.initialClick = [event.offsetX, event.offsetY];
863
- internal.initialHits = hits.map((hit) => hit.eventObject);
864
- }
1399
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1400
+ if (isPointerDown) {
1401
+ const pointerState2 = getPointerState(internal, pointerId);
1402
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1403
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1404
+ }
1405
+ const pointerState = internal.pointerMap.get(pointerId);
1406
+ const initialHits = pointerState?.initialHits ?? [];
865
1407
  if (isClickEvent && !hits.length) {
866
1408
  if (delta <= 2) {
867
1409
  pointerMissed(event, internal.interaction);
@@ -876,7 +1418,9 @@ function createEvents(store) {
876
1418
  dropMissed(event, internal.interaction);
877
1419
  if (onDropMissed) onDropMissed(event);
878
1420
  }
879
- if (isPointerMove || isDragOver) cancelPointer(hits);
1421
+ if (isPointerMove || isDragOver) {
1422
+ cancelPointer(hits, pointerId);
1423
+ }
880
1424
  function onIntersect(data) {
881
1425
  const eventObject = data.eventObject;
882
1426
  const instance = eventObject.__r3f;
@@ -885,9 +1429,10 @@ function createEvents(store) {
885
1429
  if (isPointerMove) {
886
1430
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
887
1431
  const id = makeId(data);
888
- const hoveredItem = internal.hovered.get(id);
1432
+ const pointerState2 = getPointerState(internal, pointerId);
1433
+ const hoveredItem = pointerState2.hovered.get(id);
889
1434
  if (!hoveredItem) {
890
- internal.hovered.set(id, data);
1435
+ pointerState2.hovered.set(id, data);
891
1436
  handlers.onPointerOver?.(data);
892
1437
  handlers.onPointerEnter?.(data);
893
1438
  } else if (hoveredItem.stopped) {
@@ -897,9 +1442,10 @@ function createEvents(store) {
897
1442
  handlers.onPointerMove?.(data);
898
1443
  } else if (isDragOver) {
899
1444
  const id = makeId(data);
900
- const hoveredItem = internal.hovered.get(id);
1445
+ const pointerState2 = getPointerState(internal, pointerId);
1446
+ const hoveredItem = pointerState2.hovered.get(id);
901
1447
  if (!hoveredItem) {
902
- internal.hovered.set(id, data);
1448
+ pointerState2.hovered.set(id, data);
903
1449
  handlers.onDragOverEnter?.(data);
904
1450
  } else if (hoveredItem.stopped) {
905
1451
  data.stopPropagation();
@@ -910,18 +1456,18 @@ function createEvents(store) {
910
1456
  } else {
911
1457
  const handler = handlers[name];
912
1458
  if (handler) {
913
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1459
+ if (!isClickEvent || initialHits.includes(eventObject)) {
914
1460
  pointerMissed(
915
1461
  event,
916
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1462
+ internal.interaction.filter((object) => !initialHits.includes(object))
917
1463
  );
918
1464
  handler(data);
919
1465
  }
920
1466
  } else {
921
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1467
+ if (isClickEvent && initialHits.includes(eventObject)) {
922
1468
  pointerMissed(
923
1469
  event,
924
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1470
+ internal.interaction.filter((object) => !initialHits.includes(object))
925
1471
  );
926
1472
  }
927
1473
  }
@@ -930,7 +1476,15 @@ function createEvents(store) {
930
1476
  handleIntersects(hits, event, delta, onIntersect);
931
1477
  };
932
1478
  }
933
- return { handlePointer };
1479
+ function flushDeferredPointers() {
1480
+ const { internal, events } = store.getState();
1481
+ if (!events.frameTimedRaycasts) return;
1482
+ for (const [pointerId, event] of internal.pointerDirty) {
1483
+ processDeferredPointer(event, pointerId);
1484
+ }
1485
+ internal.pointerDirty.clear();
1486
+ }
1487
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
934
1488
  }
935
1489
  const DOM_EVENTS = {
936
1490
  onClick: ["click", false],
@@ -949,11 +1503,16 @@ const DOM_EVENTS = {
949
1503
  onLostPointerCapture: ["lostpointercapture", true]
950
1504
  };
951
1505
  function createPointerEvents(store) {
952
- const { handlePointer } = createEvents(store);
1506
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1507
+ let nextXRPointerId = XR_POINTER_ID_START;
1508
+ const xrPointers = /* @__PURE__ */ new Map();
953
1509
  return {
954
1510
  priority: 1,
955
1511
  enabled: true,
956
- compute(event, state, previous) {
1512
+ frameTimedRaycasts: true,
1513
+ alwaysFireOnScroll: true,
1514
+ updateOnFrame: false,
1515
+ compute(event, state) {
957
1516
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
958
1517
  state.raycaster.setFromCamera(state.pointer, state.camera);
959
1518
  },
@@ -962,11 +1521,33 @@ function createPointerEvents(store) {
962
1521
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
963
1522
  {}
964
1523
  ),
965
- update: () => {
1524
+ update: (pointerId) => {
1525
+ const { events, internal } = store.getState();
1526
+ if (!events.handlers) return;
1527
+ if (pointerId !== void 0) {
1528
+ const event = internal.pointerDirty.get(pointerId);
1529
+ if (event) {
1530
+ internal.pointerDirty.delete(pointerId);
1531
+ processDeferredPointer(event, pointerId);
1532
+ } else if (internal.lastEvent?.current) {
1533
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1534
+ }
1535
+ } else {
1536
+ flushDeferredPointers();
1537
+ if (internal.lastEvent?.current) {
1538
+ events.handlers.onPointerMove(internal.lastEvent.current);
1539
+ }
1540
+ }
1541
+ },
1542
+ flush: () => {
966
1543
  const { events, internal } = store.getState();
967
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1544
+ flushDeferredPointers();
1545
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1546
+ events.handlers.onPointerMove(internal.lastEvent.current);
1547
+ }
968
1548
  },
969
1549
  connect: (target) => {
1550
+ if (!target) return;
970
1551
  const { set, events } = store.getState();
971
1552
  events.disconnect?.();
972
1553
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -990,6 +1571,32 @@ function createPointerEvents(store) {
990
1571
  }
991
1572
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
992
1573
  }
1574
+ },
1575
+ registerPointer: (config) => {
1576
+ const pointerId = nextXRPointerId++;
1577
+ xrPointers.set(pointerId, config);
1578
+ const { internal } = store.getState();
1579
+ getPointerState(internal, pointerId);
1580
+ return pointerId;
1581
+ },
1582
+ unregisterPointer: (pointerId) => {
1583
+ xrPointers.delete(pointerId);
1584
+ const { internal } = store.getState();
1585
+ const pointerState = internal.pointerMap.get(pointerId);
1586
+ if (pointerState) {
1587
+ for (const [, hoveredObj] of pointerState.hovered) {
1588
+ const eventObject = hoveredObj.eventObject;
1589
+ const instance = eventObject.__r3f;
1590
+ if (instance?.eventCount) {
1591
+ const handlers = instance.handlers;
1592
+ const data = { ...hoveredObj, intersections: [] };
1593
+ handlers.onPointerOut?.(data);
1594
+ handlers.onPointerLeave?.(data);
1595
+ }
1596
+ }
1597
+ internal.pointerMap.delete(pointerId);
1598
+ }
1599
+ internal.pointerDirty.delete(pointerId);
993
1600
  }
994
1601
  };
995
1602
  }
@@ -1051,331 +1658,26 @@ function notifyAlpha({ message, link }) {
1051
1658
  }
1052
1659
  }
1053
1660
 
1054
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1055
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1056
- const createStore = (invalidate, advance) => {
1057
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1058
- const position = new webgpu.Vector3();
1059
- const defaultTarget = new webgpu.Vector3();
1060
- const tempTarget = new webgpu.Vector3();
1061
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1062
- const { width, height, top, left } = size;
1063
- const aspect = width / height;
1064
- if (target.isVector3) tempTarget.copy(target);
1065
- else tempTarget.set(...target);
1066
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1067
- if (isOrthographicCamera(camera)) {
1068
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1069
- } else {
1070
- const fov = camera.fov * Math.PI / 180;
1071
- const h = 2 * Math.tan(fov / 2) * distance;
1072
- const w = h * (width / height);
1073
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1074
- }
1075
- }
1076
- let performanceTimeout = void 0;
1077
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1078
- const pointer = new webgpu.Vector2();
1079
- const rootState = {
1080
- set,
1081
- get,
1082
- // Mock objects that have to be configured
1083
- gl: null,
1084
- renderer: null,
1085
- camera: null,
1086
- frustum: new webgpu.Frustum(),
1087
- autoUpdateFrustum: true,
1088
- raycaster: null,
1089
- events: { priority: 1, enabled: true, connected: false },
1090
- scene: null,
1091
- rootScene: null,
1092
- xr: null,
1093
- inspector: null,
1094
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1095
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1096
- legacy: false,
1097
- linear: false,
1098
- flat: false,
1099
- textureColorSpace: "srgb",
1100
- isLegacy: false,
1101
- webGPUSupported: false,
1102
- isNative: false,
1103
- controls: null,
1104
- pointer,
1105
- mouse: pointer,
1106
- frameloop: "always",
1107
- onPointerMissed: void 0,
1108
- onDragOverMissed: void 0,
1109
- onDropMissed: void 0,
1110
- performance: {
1111
- current: 1,
1112
- min: 0.5,
1113
- max: 1,
1114
- debounce: 200,
1115
- regress: () => {
1116
- const state2 = get();
1117
- if (performanceTimeout) clearTimeout(performanceTimeout);
1118
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1119
- performanceTimeout = setTimeout(
1120
- () => setPerformanceCurrent(get().performance.max),
1121
- state2.performance.debounce
1122
- );
1123
- }
1124
- },
1125
- size: { width: 0, height: 0, top: 0, left: 0 },
1126
- viewport: {
1127
- initialDpr: 0,
1128
- dpr: 0,
1129
- width: 0,
1130
- height: 0,
1131
- top: 0,
1132
- left: 0,
1133
- aspect: 0,
1134
- distance: 0,
1135
- factor: 0,
1136
- getCurrentViewport
1137
- },
1138
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1139
- setSize: (width, height, top, left) => {
1140
- const state2 = get();
1141
- if (width === void 0) {
1142
- set({ _sizeImperative: false });
1143
- if (state2._sizeProps) {
1144
- const { width: propW, height: propH } = state2._sizeProps;
1145
- if (propW !== void 0 || propH !== void 0) {
1146
- const currentSize = state2.size;
1147
- const newSize = {
1148
- width: propW ?? currentSize.width,
1149
- height: propH ?? currentSize.height,
1150
- top: currentSize.top,
1151
- left: currentSize.left
1152
- };
1153
- set((s) => ({
1154
- size: newSize,
1155
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1156
- }));
1157
- }
1158
- }
1159
- return;
1160
- }
1161
- const w = width;
1162
- const h = height ?? width;
1163
- const t = top ?? state2.size.top;
1164
- const l = left ?? state2.size.left;
1165
- const size = { width: w, height: h, top: t, left: l };
1166
- set((s) => ({
1167
- size,
1168
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1169
- _sizeImperative: true
1170
- }));
1171
- },
1172
- setDpr: (dpr) => set((state2) => {
1173
- const resolved = calculateDpr(dpr);
1174
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1175
- }),
1176
- setFrameloop: (frameloop = "always") => {
1177
- set(() => ({ frameloop }));
1178
- },
1179
- setError: (error) => set(() => ({ error })),
1180
- error: null,
1181
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1182
- uniforms: {},
1183
- nodes: {},
1184
- textures: /* @__PURE__ */ new Map(),
1185
- postProcessing: null,
1186
- passes: {},
1187
- _hmrVersion: 0,
1188
- _sizeImperative: false,
1189
- _sizeProps: null,
1190
- previousRoot: void 0,
1191
- internal: {
1192
- // Events
1193
- interaction: [],
1194
- hovered: /* @__PURE__ */ new Map(),
1195
- subscribers: [],
1196
- initialClick: [0, 0],
1197
- initialHits: [],
1198
- capturedMap: /* @__PURE__ */ new Map(),
1199
- lastEvent: React__namespace.createRef(),
1200
- // Visibility tracking (onFramed, onOccluded, onVisible)
1201
- visibilityRegistry: /* @__PURE__ */ new Map(),
1202
- // Occlusion system (WebGPU only)
1203
- occlusionEnabled: false,
1204
- occlusionObserver: null,
1205
- occlusionCache: /* @__PURE__ */ new Map(),
1206
- helperGroup: null,
1207
- // Updates
1208
- active: false,
1209
- frames: 0,
1210
- priority: 0,
1211
- subscribe: (ref, priority, store) => {
1212
- const internal = get().internal;
1213
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1214
- internal.subscribers.push({ ref, priority, store });
1215
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1216
- return () => {
1217
- const internal2 = get().internal;
1218
- if (internal2?.subscribers) {
1219
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1220
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1221
- }
1222
- };
1223
- },
1224
- // Renderer Storage (single source of truth)
1225
- actualRenderer: null,
1226
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1227
- scheduler: null
1228
- }
1229
- };
1230
- return rootState;
1231
- });
1232
- const state = rootStore.getState();
1233
- Object.defineProperty(state, "gl", {
1234
- get() {
1235
- const currentState = rootStore.getState();
1236
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1237
- const stack = new Error().stack || "";
1238
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1239
- if (!isInternalAccess) {
1240
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1241
- notifyDepreciated({
1242
- heading: "Accessing state.gl in WebGPU mode",
1243
- 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
1244
- });
1245
- }
1246
- }
1247
- return currentState.internal.actualRenderer;
1248
- },
1249
- set(value) {
1250
- rootStore.getState().internal.actualRenderer = value;
1251
- },
1252
- enumerable: true,
1253
- configurable: true
1254
- });
1255
- Object.defineProperty(state, "renderer", {
1256
- get() {
1257
- return rootStore.getState().internal.actualRenderer;
1258
- },
1259
- set(value) {
1260
- rootStore.getState().internal.actualRenderer = value;
1261
- },
1262
- enumerable: true,
1263
- configurable: true
1264
- });
1265
- let oldScene = state.scene;
1266
- rootStore.subscribe(() => {
1267
- const currentState = rootStore.getState();
1268
- const { scene, rootScene, set } = currentState;
1269
- if (scene !== oldScene) {
1270
- oldScene = scene;
1271
- if (scene?.isScene && scene !== rootScene) {
1272
- set({ rootScene: scene });
1273
- }
1274
- }
1275
- });
1276
- let oldSize = state.size;
1277
- let oldDpr = state.viewport.dpr;
1278
- let oldCamera = state.camera;
1279
- rootStore.subscribe(() => {
1280
- const { camera, size, viewport, set, internal } = rootStore.getState();
1281
- const actualRenderer = internal.actualRenderer;
1282
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1283
- oldSize = size;
1284
- oldDpr = viewport.dpr;
1285
- updateCamera(camera, size);
1286
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1287
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1288
- actualRenderer.setSize(size.width, size.height, updateStyle);
1289
- }
1290
- if (camera !== oldCamera) {
1291
- oldCamera = camera;
1292
- const { rootScene } = rootStore.getState();
1293
- if (camera && rootScene && !camera.parent) {
1294
- rootScene.add(camera);
1295
- }
1296
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1297
- const currentState = rootStore.getState();
1298
- if (currentState.autoUpdateFrustum && camera) {
1299
- updateFrustum(camera, currentState.frustum);
1300
- }
1301
- }
1302
- });
1303
- rootStore.subscribe((state2) => invalidate(state2));
1304
- return rootStore;
1305
- };
1306
-
1307
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1308
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1309
- function getLoader(Proto) {
1310
- if (isConstructor$1(Proto)) {
1311
- let loader = memoizedLoaders.get(Proto);
1312
- if (!loader) {
1313
- loader = new Proto();
1314
- memoizedLoaders.set(Proto, loader);
1315
- }
1316
- return loader;
1317
- }
1318
- return Proto;
1319
- }
1320
- function loadingFn(extensions, onProgress) {
1321
- return function(Proto, input) {
1322
- const loader = getLoader(Proto);
1323
- if (extensions) extensions(loader);
1324
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1325
- return loader.loadAsync(input, onProgress).then((data) => {
1326
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1327
- return data;
1328
- });
1329
- }
1330
- return new Promise(
1331
- (res, reject) => loader.load(
1332
- input,
1333
- (data) => {
1334
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1335
- res(data);
1336
- },
1337
- onProgress,
1338
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1339
- )
1340
- );
1341
- };
1342
- }
1343
- function useLoader(loader, input, extensions, onProgress) {
1344
- const keys = Array.isArray(input) ? input : [input];
1345
- const fn = loadingFn(extensions, onProgress);
1346
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1347
- return Array.isArray(input) ? results : results[0];
1348
- }
1349
- useLoader.preload = function(loader, input, extensions, onProgress) {
1350
- const keys = Array.isArray(input) ? input : [input];
1351
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1352
- };
1353
- useLoader.clear = function(loader, input) {
1354
- const keys = Array.isArray(input) ? input : [input];
1355
- keys.forEach((key) => suspendReact.clear([loader, key]));
1356
- };
1357
- useLoader.loader = getLoader;
1358
-
1359
- var __defProp$2 = Object.defineProperty;
1360
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1361
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1362
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1363
- class PhaseGraph {
1364
- constructor() {
1365
- /** Ordered list of phase nodes */
1366
- __publicField$2(this, "phases", []);
1367
- /** Quick lookup by name */
1368
- __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1369
- /** Cached ordered names (invalidated on changes) */
1370
- __publicField$2(this, "orderedNamesCache", null);
1371
- this.initializeDefaultPhases();
1372
- }
1373
- //* Initialization --------------------------------
1374
- initializeDefaultPhases() {
1375
- for (const name of DEFAULT_PHASES) {
1376
- const node = { name, isAutoGenerated: false };
1377
- this.phases.push(node);
1378
- this.phaseMap.set(name, node);
1661
+ var __defProp$2 = Object.defineProperty;
1662
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1663
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1664
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1665
+ class PhaseGraph {
1666
+ constructor() {
1667
+ /** Ordered list of phase nodes */
1668
+ __publicField$2(this, "phases", []);
1669
+ /** Quick lookup by name */
1670
+ __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1671
+ /** Cached ordered names (invalidated on changes) */
1672
+ __publicField$2(this, "orderedNamesCache", null);
1673
+ this.initializeDefaultPhases();
1674
+ }
1675
+ //* Initialization --------------------------------
1676
+ initializeDefaultPhases() {
1677
+ for (const name of DEFAULT_PHASES) {
1678
+ const node = { name, isAutoGenerated: false };
1679
+ this.phases.push(node);
1680
+ this.phaseMap.set(name, node);
1379
1681
  }
1380
1682
  this.invalidateCache();
1381
1683
  }
@@ -1608,7 +1910,7 @@ function shouldRun(job, now) {
1608
1910
  const minInterval = 1e3 / job.fps;
1609
1911
  const lastRun = job.lastRun ?? 0;
1610
1912
  const elapsed = now - lastRun;
1611
- if (elapsed < minInterval) return false;
1913
+ if (elapsed < minInterval - 1) return false;
1612
1914
  if (job.drop) {
1613
1915
  job.lastRun = now;
1614
1916
  } else {
@@ -2388,36 +2690,364 @@ if (hmrData) {
2388
2690
  hmrData.accept?.();
2389
2691
  }
2390
2692
 
2391
- function useFrame(callback, priorityOrOptions) {
2392
- const store = React__namespace.useContext(context);
2393
- const isInsideCanvas = store !== null;
2394
- const scheduler = getScheduler();
2395
- const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
2396
- id: priorityOrOptions.id,
2397
- phase: priorityOrOptions.phase,
2398
- priority: priorityOrOptions.priority,
2399
- fps: priorityOrOptions.fps,
2400
- drop: priorityOrOptions.drop,
2401
- enabled: priorityOrOptions.enabled,
2402
- before: priorityOrOptions.before,
2403
- after: priorityOrOptions.after
2404
- }) : "";
2405
- const options = React__namespace.useMemo(() => {
2406
- return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
2407
- }, [optionsKey]);
2408
- const reactId = React__namespace.useId();
2409
- const id = options.id ?? reactId;
2410
- const callbackRef = useMutableCallback(callback);
2411
- const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
2412
- useIsomorphicLayoutEffect(() => {
2413
- if (!callback) return;
2414
- if (isInsideCanvas) {
2415
- const state = store.getState();
2416
- const rootId = state.internal.rootId;
2417
- if (isLegacyPriority) {
2418
- state.internal.priority++;
2419
- let parentRoot = state.previousRoot;
2420
- while (parentRoot) {
2693
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2694
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2695
+ const createStore = (invalidate, advance) => {
2696
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2697
+ const position = new webgpu.Vector3();
2698
+ const defaultTarget = new webgpu.Vector3();
2699
+ const tempTarget = new webgpu.Vector3();
2700
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2701
+ const { width, height, top, left } = size;
2702
+ const aspect = width / height;
2703
+ if (target.isVector3) tempTarget.copy(target);
2704
+ else tempTarget.set(...target);
2705
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2706
+ if (isOrthographicCamera(camera)) {
2707
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2708
+ } else {
2709
+ const fov = camera.fov * Math.PI / 180;
2710
+ const h = 2 * Math.tan(fov / 2) * distance;
2711
+ const w = h * (width / height);
2712
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2713
+ }
2714
+ }
2715
+ let performanceTimeout = void 0;
2716
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2717
+ const pointer = new webgpu.Vector2();
2718
+ const rootState = {
2719
+ set,
2720
+ get,
2721
+ // Mock objects that have to be configured
2722
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2723
+ primaryStore: null,
2724
+ gl: null,
2725
+ renderer: null,
2726
+ camera: null,
2727
+ frustum: new webgpu.Frustum(),
2728
+ autoUpdateFrustum: true,
2729
+ raycaster: null,
2730
+ events: {
2731
+ priority: 1,
2732
+ enabled: true,
2733
+ connected: false,
2734
+ frameTimedRaycasts: true,
2735
+ alwaysFireOnScroll: true,
2736
+ updateOnFrame: false
2737
+ },
2738
+ scene: null,
2739
+ rootScene: null,
2740
+ xr: null,
2741
+ inspector: null,
2742
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2743
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2744
+ textureColorSpace: webgpu.SRGBColorSpace,
2745
+ isLegacy: false,
2746
+ webGPUSupported: false,
2747
+ isNative: false,
2748
+ controls: null,
2749
+ pointer,
2750
+ mouse: pointer,
2751
+ frameloop: "always",
2752
+ onPointerMissed: void 0,
2753
+ onDragOverMissed: void 0,
2754
+ onDropMissed: void 0,
2755
+ performance: {
2756
+ current: 1,
2757
+ min: 0.5,
2758
+ max: 1,
2759
+ debounce: 200,
2760
+ regress: () => {
2761
+ const state2 = get();
2762
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2763
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2764
+ performanceTimeout = setTimeout(
2765
+ () => setPerformanceCurrent(get().performance.max),
2766
+ state2.performance.debounce
2767
+ );
2768
+ }
2769
+ },
2770
+ size: { width: 0, height: 0, top: 0, left: 0 },
2771
+ viewport: {
2772
+ initialDpr: 0,
2773
+ dpr: 0,
2774
+ width: 0,
2775
+ height: 0,
2776
+ top: 0,
2777
+ left: 0,
2778
+ aspect: 0,
2779
+ distance: 0,
2780
+ factor: 0,
2781
+ getCurrentViewport
2782
+ },
2783
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2784
+ setSize: (width, height, top, left) => {
2785
+ const state2 = get();
2786
+ if (width === void 0) {
2787
+ set({ _sizeImperative: false });
2788
+ if (state2._sizeProps) {
2789
+ const { width: propW, height: propH } = state2._sizeProps;
2790
+ if (propW !== void 0 || propH !== void 0) {
2791
+ const currentSize = state2.size;
2792
+ const newSize = {
2793
+ width: propW ?? currentSize.width,
2794
+ height: propH ?? currentSize.height,
2795
+ top: currentSize.top,
2796
+ left: currentSize.left
2797
+ };
2798
+ set((s) => ({
2799
+ size: newSize,
2800
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2801
+ }));
2802
+ getScheduler().invalidate();
2803
+ }
2804
+ }
2805
+ return;
2806
+ }
2807
+ const w = width;
2808
+ const h = height ?? width;
2809
+ const t = top ?? state2.size.top;
2810
+ const l = left ?? state2.size.left;
2811
+ const size = { width: w, height: h, top: t, left: l };
2812
+ set((s) => ({
2813
+ size,
2814
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2815
+ _sizeImperative: true
2816
+ }));
2817
+ getScheduler().invalidate();
2818
+ },
2819
+ setDpr: (dpr) => set((state2) => {
2820
+ const resolved = calculateDpr(dpr);
2821
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2822
+ }),
2823
+ setFrameloop: (frameloop = "always") => {
2824
+ set(() => ({ frameloop }));
2825
+ },
2826
+ setError: (error) => set(() => ({ error })),
2827
+ error: null,
2828
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2829
+ uniforms: {},
2830
+ nodes: {},
2831
+ buffers: {},
2832
+ gpuStorage: {},
2833
+ textures: /* @__PURE__ */ new Map(),
2834
+ renderPipeline: null,
2835
+ passes: {},
2836
+ _hmrVersion: 0,
2837
+ _sizeImperative: false,
2838
+ _sizeProps: null,
2839
+ previousRoot: void 0,
2840
+ internal: {
2841
+ // Events
2842
+ interaction: [],
2843
+ subscribers: [],
2844
+ // Per-pointer state (new unified structure)
2845
+ pointerMap: /* @__PURE__ */ new Map(),
2846
+ pointerDirty: /* @__PURE__ */ new Map(),
2847
+ lastEvent: React__namespace.createRef(),
2848
+ // Deprecated but kept for backwards compatibility
2849
+ hovered: /* @__PURE__ */ new Map(),
2850
+ initialClick: [0, 0],
2851
+ initialHits: [],
2852
+ capturedMap: /* @__PURE__ */ new Map(),
2853
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2854
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2855
+ // Occlusion system (WebGPU only)
2856
+ occlusionEnabled: false,
2857
+ occlusionObserver: null,
2858
+ occlusionCache: /* @__PURE__ */ new Map(),
2859
+ helperGroup: null,
2860
+ // Updates
2861
+ active: false,
2862
+ frames: 0,
2863
+ priority: 0,
2864
+ subscribe: (ref, priority, store) => {
2865
+ const internal = get().internal;
2866
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2867
+ internal.subscribers.push({ ref, priority, store });
2868
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2869
+ return () => {
2870
+ const internal2 = get().internal;
2871
+ if (internal2?.subscribers) {
2872
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2873
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2874
+ }
2875
+ };
2876
+ },
2877
+ // Renderer Storage (single source of truth)
2878
+ actualRenderer: null,
2879
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2880
+ scheduler: null
2881
+ }
2882
+ };
2883
+ return rootState;
2884
+ });
2885
+ const state = rootStore.getState();
2886
+ Object.defineProperty(state, "gl", {
2887
+ get() {
2888
+ const currentState = rootStore.getState();
2889
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2890
+ const stack = new Error().stack || "";
2891
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2892
+ if (!isInternalAccess) {
2893
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2894
+ notifyDepreciated({
2895
+ heading: "Accessing state.gl in WebGPU mode",
2896
+ 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
2897
+ });
2898
+ }
2899
+ }
2900
+ return currentState.internal.actualRenderer;
2901
+ },
2902
+ set(value) {
2903
+ rootStore.getState().internal.actualRenderer = value;
2904
+ },
2905
+ enumerable: true,
2906
+ configurable: true
2907
+ });
2908
+ Object.defineProperty(state, "renderer", {
2909
+ get() {
2910
+ return rootStore.getState().internal.actualRenderer;
2911
+ },
2912
+ set(value) {
2913
+ rootStore.getState().internal.actualRenderer = value;
2914
+ },
2915
+ enumerable: true,
2916
+ configurable: true
2917
+ });
2918
+ let oldScene = state.scene;
2919
+ rootStore.subscribe(() => {
2920
+ const currentState = rootStore.getState();
2921
+ const { scene, rootScene, set } = currentState;
2922
+ if (scene !== oldScene) {
2923
+ oldScene = scene;
2924
+ if (scene?.isScene && scene !== rootScene) {
2925
+ set({ rootScene: scene });
2926
+ }
2927
+ }
2928
+ });
2929
+ let oldSize = state.size;
2930
+ let oldDpr = state.viewport.dpr;
2931
+ let oldCamera = state.camera;
2932
+ rootStore.subscribe(() => {
2933
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2934
+ const actualRenderer = internal.actualRenderer;
2935
+ const canvasTarget = internal.canvasTarget;
2936
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2937
+ oldSize = size;
2938
+ oldDpr = viewport.dpr;
2939
+ updateCamera(camera, size);
2940
+ if (internal.isSecondary && canvasTarget) {
2941
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2942
+ canvasTarget.setSize(size.width, size.height, false);
2943
+ } else {
2944
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2945
+ actualRenderer.setSize(size.width, size.height, false);
2946
+ if (canvasTarget) {
2947
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2948
+ canvasTarget.setSize(size.width, size.height, false);
2949
+ }
2950
+ }
2951
+ }
2952
+ if (camera !== oldCamera) {
2953
+ oldCamera = camera;
2954
+ const { rootScene } = rootStore.getState();
2955
+ if (camera && rootScene && !camera.parent) {
2956
+ rootScene.add(camera);
2957
+ }
2958
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2959
+ const currentState = rootStore.getState();
2960
+ if (currentState.autoUpdateFrustum && camera) {
2961
+ updateFrustum(camera, currentState.frustum);
2962
+ }
2963
+ }
2964
+ });
2965
+ rootStore.subscribe((state2) => invalidate(state2));
2966
+ return rootStore;
2967
+ };
2968
+
2969
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2970
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2971
+ function getLoader(Proto) {
2972
+ if (isConstructor$1(Proto)) {
2973
+ let loader = memoizedLoaders.get(Proto);
2974
+ if (!loader) {
2975
+ loader = new Proto();
2976
+ memoizedLoaders.set(Proto, loader);
2977
+ }
2978
+ return loader;
2979
+ }
2980
+ return Proto;
2981
+ }
2982
+ function loadingFn(extensions, onProgress) {
2983
+ return function(Proto, input) {
2984
+ const loader = getLoader(Proto);
2985
+ if (extensions) extensions(loader);
2986
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2987
+ return loader.loadAsync(input, onProgress).then((data) => {
2988
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2989
+ return data;
2990
+ });
2991
+ }
2992
+ return new Promise(
2993
+ (res, reject) => loader.load(
2994
+ input,
2995
+ (data) => {
2996
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2997
+ res(data);
2998
+ },
2999
+ onProgress,
3000
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
3001
+ )
3002
+ );
3003
+ };
3004
+ }
3005
+ function useLoader(loader, input, extensions, onProgress) {
3006
+ const keys = Array.isArray(input) ? input : [input];
3007
+ const fn = loadingFn(extensions, onProgress);
3008
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
3009
+ return Array.isArray(input) ? results : results[0];
3010
+ }
3011
+ useLoader.preload = function(loader, input, extensions, onProgress) {
3012
+ const keys = Array.isArray(input) ? input : [input];
3013
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
3014
+ };
3015
+ useLoader.clear = function(loader, input) {
3016
+ const keys = Array.isArray(input) ? input : [input];
3017
+ keys.forEach((key) => suspendReact.clear([loader, key]));
3018
+ };
3019
+ useLoader.loader = getLoader;
3020
+
3021
+ function useFrame(callback, priorityOrOptions) {
3022
+ const store = React__namespace.useContext(context);
3023
+ const isInsideCanvas = store !== null;
3024
+ const scheduler = getScheduler();
3025
+ const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
3026
+ id: priorityOrOptions.id,
3027
+ phase: priorityOrOptions.phase,
3028
+ priority: priorityOrOptions.priority,
3029
+ fps: priorityOrOptions.fps,
3030
+ drop: priorityOrOptions.drop,
3031
+ enabled: priorityOrOptions.enabled,
3032
+ before: priorityOrOptions.before,
3033
+ after: priorityOrOptions.after
3034
+ }) : "";
3035
+ const options = React__namespace.useMemo(() => {
3036
+ return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
3037
+ }, [optionsKey]);
3038
+ const reactId = React__namespace.useId();
3039
+ const id = options.id ?? reactId;
3040
+ const callbackRef = useMutableCallback(callback);
3041
+ const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
3042
+ useIsomorphicLayoutEffect(() => {
3043
+ if (!callback) return;
3044
+ if (isInsideCanvas) {
3045
+ const state = store.getState();
3046
+ const rootId = state.internal.rootId;
3047
+ if (isLegacyPriority) {
3048
+ state.internal.priority++;
3049
+ let parentRoot = state.previousRoot;
3050
+ while (parentRoot) {
2421
3051
  const parentState = parentRoot.getState();
2422
3052
  if (parentState?.internal) parentState.internal.priority++;
2423
3053
  parentRoot = parentState?.previousRoot;
@@ -2567,6 +3197,9 @@ function useTexture(input, optionsOrOnLoad) {
2567
3197
  const textureCache = useThree((state) => state.textures);
2568
3198
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2569
3199
  const { onLoad, cache = false } = options;
3200
+ const onLoadRef = React.useRef(onLoad);
3201
+ onLoadRef.current = onLoad;
3202
+ const onLoadCalledForRef = React.useRef(null);
2570
3203
  const urls = React.useMemo(() => getUrls(input), [input]);
2571
3204
  const cachedResult = React.useMemo(() => {
2572
3205
  if (!cache) return null;
@@ -2577,9 +3210,13 @@ function useTexture(input, optionsOrOnLoad) {
2577
3210
  webgpu.TextureLoader,
2578
3211
  IsObject(input) ? Object.values(input) : input
2579
3212
  );
3213
+ const inputKey = urls.join("\0");
2580
3214
  React.useLayoutEffect(() => {
2581
- if (!cachedResult) onLoad?.(loadedTextures);
2582
- }, [onLoad, cachedResult, loadedTextures]);
3215
+ if (cachedResult) return;
3216
+ if (onLoadCalledForRef.current === inputKey) return;
3217
+ onLoadCalledForRef.current = inputKey;
3218
+ onLoadRef.current?.(loadedTextures);
3219
+ }, [cachedResult, loadedTextures, inputKey]);
2583
3220
  React.useEffect(() => {
2584
3221
  if (cachedResult) return;
2585
3222
  if ("initTexture" in renderer) {
@@ -2746,14 +3383,31 @@ function useTextures() {
2746
3383
  }, [store]);
2747
3384
  }
2748
3385
 
2749
- function useRenderTarget(width, height, options) {
3386
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2750
3387
  const isLegacy = useThree((s) => s.isLegacy);
2751
3388
  const size = useThree((s) => s.size);
3389
+ let width;
3390
+ let height;
3391
+ let opts;
3392
+ if (typeof widthOrOptions === "object") {
3393
+ opts = widthOrOptions;
3394
+ } else if (typeof widthOrOptions === "number") {
3395
+ width = widthOrOptions;
3396
+ if (typeof heightOrOptions === "object") {
3397
+ height = widthOrOptions;
3398
+ opts = heightOrOptions;
3399
+ } else if (typeof heightOrOptions === "number") {
3400
+ height = heightOrOptions;
3401
+ opts = options;
3402
+ } else {
3403
+ height = widthOrOptions;
3404
+ }
3405
+ }
2752
3406
  return React.useMemo(() => {
2753
3407
  const w = width ?? size.width;
2754
3408
  const h = height ?? size.height;
2755
- return new webgpu.RenderTarget(w, h, options);
2756
- }, [width, height, size.width, size.height, options, isLegacy]);
3409
+ return new webgpu.RenderTarget(w, h, opts);
3410
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2757
3411
  }
2758
3412
 
2759
3413
  function useStore() {
@@ -2803,7 +3457,7 @@ function addTail(callback) {
2803
3457
  function invalidate(state, frames = 1, stackFrames = false) {
2804
3458
  getScheduler().invalidate(frames, stackFrames);
2805
3459
  }
2806
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3460
+ function advance(timestamp) {
2807
3461
  getScheduler().step(timestamp);
2808
3462
  }
2809
3463
 
@@ -14257,6 +14911,7 @@ function swapInstances() {
14257
14911
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14258
14912
  instance.object.__r3f = instance;
14259
14913
  setFiberRef(fiber, instance.object);
14914
+ delete instance.appliedOnce;
14260
14915
  applyProps(instance.object, instance.props);
14261
14916
  if (instance.props.attach) {
14262
14917
  attach(parent, instance);
@@ -14330,8 +14985,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14330
14985
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14331
14986
  if (isTailSibling) swapInstances();
14332
14987
  },
14333
- finalizeInitialChildren: () => false,
14334
- commitMount() {
14988
+ finalizeInitialChildren: (instance) => {
14989
+ for (const prop in instance.props) {
14990
+ if (isFromRef(instance.props[prop])) return true;
14991
+ }
14992
+ return false;
14993
+ },
14994
+ commitMount(instance) {
14995
+ const resolved = {};
14996
+ for (const prop in instance.props) {
14997
+ const value = instance.props[prop];
14998
+ if (isFromRef(value)) {
14999
+ const ref = value[FROM_REF];
15000
+ if (ref.current != null) resolved[prop] = ref.current;
15001
+ }
15002
+ }
15003
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14335
15004
  },
14336
15005
  getPublicInstance: (instance) => instance?.object,
14337
15006
  prepareForCommit: () => null,
@@ -14552,6 +15221,9 @@ function createRoot(canvas) {
14552
15221
  let resolve;
14553
15222
  pending = new Promise((_resolve) => resolve = _resolve);
14554
15223
  const {
15224
+ id: canvasId,
15225
+ primaryCanvas,
15226
+ scheduler: schedulerConfig,
14555
15227
  gl: glConfig,
14556
15228
  renderer: rendererConfig,
14557
15229
  size: propsSize,
@@ -14559,10 +15231,6 @@ function createRoot(canvas) {
14559
15231
  events,
14560
15232
  onCreated: onCreatedCallback,
14561
15233
  shadows = false,
14562
- linear = false,
14563
- flat = false,
14564
- textureColorSpace = webgpu.SRGBColorSpace,
14565
- legacy = false,
14566
15234
  orthographic = false,
14567
15235
  frameloop = "always",
14568
15236
  dpr = [1, 2],
@@ -14574,11 +15242,14 @@ function createRoot(canvas) {
14574
15242
  onDropMissed,
14575
15243
  autoUpdateFrustum = true,
14576
15244
  occlusion = false,
14577
- _sizeProps
15245
+ _sizeProps,
15246
+ forceEven
14578
15247
  } = props;
15248
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
14579
15249
  const state = store.getState();
14580
15250
  const defaultGPUProps = {
14581
- canvas
15251
+ canvas,
15252
+ antialias: true
14582
15253
  };
14583
15254
  if (glConfig && !R3F_BUILD_LEGACY) {
14584
15255
  throw new Error(
@@ -14589,15 +15260,52 @@ function createRoot(canvas) {
14589
15260
  throw new Error("Cannot use both gl and renderer props at the same time");
14590
15261
  }
14591
15262
  let renderer = state.internal.actualRenderer;
14592
- if (!state.internal.actualRenderer) {
15263
+ if (primaryCanvas && !state.internal.actualRenderer) {
15264
+ const primary = await waitForPrimary(primaryCanvas);
15265
+ renderer = primary.renderer;
15266
+ state.internal.actualRenderer = renderer;
15267
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15268
+ primary.store.setState((prev) => ({
15269
+ internal: { ...prev.internal, isMultiCanvas: true }
15270
+ }));
15271
+ state.set((prev) => ({
15272
+ webGPUSupported: primary.store.getState().webGPUSupported,
15273
+ renderer,
15274
+ primaryStore: primary.store,
15275
+ internal: {
15276
+ ...prev.internal,
15277
+ canvasTarget,
15278
+ isMultiCanvas: true,
15279
+ isSecondary: true,
15280
+ targetId: primaryCanvas
15281
+ }
15282
+ }));
15283
+ } else if (!state.internal.actualRenderer) {
14593
15284
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14594
15285
  if (!renderer.hasInitialized?.()) {
15286
+ const size2 = computeInitialSize(canvas, propsSize);
15287
+ if (size2.width > 0 && size2.height > 0) {
15288
+ const pixelRatio = calculateDpr(dpr);
15289
+ canvas.width = size2.width * pixelRatio;
15290
+ canvas.height = size2.height * pixelRatio;
15291
+ }
14595
15292
  await renderer.init();
14596
15293
  }
14597
15294
  const backend = renderer.backend;
14598
15295
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14599
15296
  state.internal.actualRenderer = renderer;
14600
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15297
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15298
+ if (canvasId && !state.internal.isSecondary) {
15299
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15300
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15301
+ state.set((prev) => ({
15302
+ internal: {
15303
+ ...prev.internal,
15304
+ canvasTarget,
15305
+ unregisterPrimary
15306
+ }
15307
+ }));
15308
+ }
14601
15309
  }
14602
15310
  let raycaster = state.raycaster;
14603
15311
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
@@ -14606,6 +15314,7 @@ function createRoot(canvas) {
14606
15314
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14607
15315
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14608
15316
  }
15317
+ let tempCamera = state.camera;
14609
15318
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14610
15319
  lastCamera = cameraOptions;
14611
15320
  const isCamera = cameraOptions?.isCamera;
@@ -14625,6 +15334,7 @@ function createRoot(canvas) {
14625
15334
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14626
15335
  }
14627
15336
  state.set({ camera });
15337
+ tempCamera = camera;
14628
15338
  raycaster.camera = camera;
14629
15339
  }
14630
15340
  if (!state.scene) {
@@ -14642,7 +15352,7 @@ function createRoot(canvas) {
14642
15352
  rootScene: scene,
14643
15353
  internal: { ...prev.internal, container: scene }
14644
15354
  }));
14645
- const camera = state.camera;
15355
+ const camera = tempCamera;
14646
15356
  if (camera && !camera.parent) scene.add(camera);
14647
15357
  }
14648
15358
  if (events && !state.events.handlers) {
@@ -14659,6 +15369,9 @@ function createRoot(canvas) {
14659
15369
  if (_sizeProps !== void 0) {
14660
15370
  state.set({ _sizeProps });
14661
15371
  }
15372
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15373
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15374
+ }
14662
15375
  const size = computeInitialSize(canvas, propsSize);
14663
15376
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14664
15377
  const wasImperative = state._sizeImperative;
@@ -14685,10 +15398,10 @@ function createRoot(canvas) {
14685
15398
  lastConfiguredProps.performance = performance;
14686
15399
  }
14687
15400
  if (!state.xr) {
14688
- const handleXRFrame = (timestamp, frame) => {
15401
+ const handleXRFrame = (timestamp, _frame) => {
14689
15402
  const state2 = store.getState();
14690
15403
  if (state2.frameloop === "never") return;
14691
- advance(timestamp, true);
15404
+ advance(timestamp);
14692
15405
  };
14693
15406
  const actualRenderer = state.internal.actualRenderer;
14694
15407
  const handleSessionChange = () => {
@@ -14700,16 +15413,16 @@ function createRoot(canvas) {
14700
15413
  };
14701
15414
  const xr = {
14702
15415
  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);
15416
+ const { gl, renderer: renderer2 } = store.getState();
15417
+ const xrManager = (renderer2 || gl).xr;
15418
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15419
+ xrManager.addEventListener("sessionend", handleSessionChange);
14707
15420
  },
14708
15421
  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);
15422
+ const { gl, renderer: renderer2 } = store.getState();
15423
+ const xrManager = (renderer2 || gl).xr;
15424
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15425
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14713
15426
  }
14714
15427
  };
14715
15428
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14721,15 +15434,22 @@ function createRoot(canvas) {
14721
15434
  const oldType = renderer.shadowMap.type;
14722
15435
  renderer.shadowMap.enabled = !!shadows;
14723
15436
  if (is.boo(shadows)) {
14724
- renderer.shadowMap.type = webgpu.PCFSoftShadowMap;
15437
+ renderer.shadowMap.type = webgpu.PCFShadowMap;
14725
15438
  } else if (is.str(shadows)) {
15439
+ if (shadows === "soft") {
15440
+ notifyDepreciated({
15441
+ heading: 'shadows="soft" is deprecated',
15442
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15443
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15444
+ });
15445
+ }
14726
15446
  const types = {
14727
15447
  basic: webgpu.BasicShadowMap,
14728
15448
  percentage: webgpu.PCFShadowMap,
14729
- soft: webgpu.PCFSoftShadowMap,
15449
+ soft: webgpu.PCFShadowMap,
14730
15450
  variance: webgpu.VSMShadowMap
14731
15451
  };
14732
- renderer.shadowMap.type = types[shadows] ?? webgpu.PCFSoftShadowMap;
15452
+ renderer.shadowMap.type = types[shadows] ?? webgpu.PCFShadowMap;
14733
15453
  } else if (is.obj(shadows)) {
14734
15454
  Object.assign(renderer.shadowMap, shadows);
14735
15455
  }
@@ -14737,27 +15457,69 @@ function createRoot(canvas) {
14737
15457
  renderer.shadowMap.needsUpdate = true;
14738
15458
  }
14739
15459
  }
15460
+ if (!configured) {
15461
+ renderer.outputColorSpace = webgpu.SRGBColorSpace;
15462
+ renderer.toneMapping = webgpu.ACESFilmicToneMapping;
15463
+ }
14740
15464
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14741
15465
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14742
15466
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14743
15467
  }
15468
+ const r3fProps = ["textureColorSpace"];
15469
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15470
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
14744
15471
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14745
- applyProps(renderer, glConfig);
15472
+ const glProps = {};
15473
+ for (const key in glConfig) {
15474
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15475
+ }
15476
+ applyProps(renderer, glProps);
14746
15477
  }
14747
15478
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14748
15479
  const currentRenderer = state.renderer;
14749
15480
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14750
- applyProps(currentRenderer, rendererConfig);
15481
+ const rendererProps = {};
15482
+ for (const key in rendererConfig) {
15483
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15484
+ }
15485
+ applyProps(currentRenderer, rendererProps);
14751
15486
  }
14752
15487
  }
14753
15488
  const scheduler = getScheduler();
14754
15489
  const rootId = state.internal.rootId;
14755
15490
  if (!rootId) {
14756
- const newRootId = scheduler.generateRootId();
15491
+ const newRootId = canvasId || scheduler.generateRootId();
14757
15492
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14758
15493
  getState: () => store.getState(),
14759
15494
  onError: (err) => store.getState().setError(err)
14760
15495
  });
15496
+ const unregisterCanvasTarget = scheduler.register(
15497
+ () => {
15498
+ const state2 = store.getState();
15499
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15500
+ const renderer2 = state2.internal.actualRenderer;
15501
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15502
+ }
15503
+ },
15504
+ {
15505
+ id: `${newRootId}_canvasTarget`,
15506
+ rootId: newRootId,
15507
+ phase: "start",
15508
+ system: true
15509
+ }
15510
+ );
15511
+ const unregisterEventsFlush = scheduler.register(
15512
+ () => {
15513
+ const state2 = store.getState();
15514
+ state2.events.flush?.();
15515
+ },
15516
+ {
15517
+ id: `${newRootId}_events`,
15518
+ rootId: newRootId,
15519
+ phase: "input",
15520
+ system: true
15521
+ }
15522
+ );
14761
15523
  const unregisterFrustum = scheduler.register(
14762
15524
  () => {
14763
15525
  const state2 = store.getState();
@@ -14792,18 +15554,22 @@ function createRoot(canvas) {
14792
15554
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14793
15555
  if (userHandlesRender || state2.internal.priority) return;
14794
15556
  try {
14795
- if (state2.postProcessing?.render) state2.postProcessing.render();
15557
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14796
15558
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14797
15559
  } catch (error) {
14798
15560
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14799
15561
  }
14800
15562
  },
14801
15563
  {
14802
- id: `${newRootId}_render`,
15564
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15565
+ id: canvasId || `${newRootId}_render`,
14803
15566
  rootId: newRootId,
14804
15567
  phase: "render",
14805
- system: true
15568
+ system: true,
14806
15569
  // Internal flag: this is a system job, not user-controlled
15570
+ // Apply scheduler config for render ordering and rate limiting
15571
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15572
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14807
15573
  }
14808
15574
  );
14809
15575
  state.set((state2) => ({
@@ -14812,6 +15578,8 @@ function createRoot(canvas) {
14812
15578
  rootId: newRootId,
14813
15579
  unregisterRoot: () => {
14814
15580
  unregisterRoot();
15581
+ unregisterCanvasTarget();
15582
+ unregisterEventsFlush();
14815
15583
  unregisterFrustum();
14816
15584
  unregisterVisibility();
14817
15585
  unregisterRender();
@@ -14870,15 +15638,24 @@ function unmountComponentAtNode(canvas, callback) {
14870
15638
  const renderer = state.internal.actualRenderer;
14871
15639
  const unregisterRoot = state.internal.unregisterRoot;
14872
15640
  if (unregisterRoot) unregisterRoot();
15641
+ const unregisterPrimary = state.internal.unregisterPrimary;
15642
+ if (unregisterPrimary) unregisterPrimary();
15643
+ const canvasTarget = state.internal.canvasTarget;
15644
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14873
15645
  state.events.disconnect?.();
14874
15646
  cleanupHelperGroup(root.store);
14875
- renderer?.renderLists?.dispose?.();
14876
- renderer?.forceContextLoss?.();
14877
- if (renderer?.xr) state.xr.disconnect();
15647
+ if (state.isLegacy && renderer) {
15648
+ ;
15649
+ renderer.renderLists?.dispose?.();
15650
+ renderer.forceContextLoss?.();
15651
+ }
15652
+ if (!state.internal.isSecondary) {
15653
+ if (renderer?.xr) state.xr.disconnect();
15654
+ }
14878
15655
  dispose(state.scene);
14879
15656
  _roots.delete(canvas);
14880
15657
  if (callback) callback(canvas);
14881
- } catch (e) {
15658
+ } catch {
14882
15659
  }
14883
15660
  }, 500);
14884
15661
  }
@@ -14886,36 +15663,34 @@ function unmountComponentAtNode(canvas, callback) {
14886
15663
  }
14887
15664
  }
14888
15665
  function createPortal(children, container, state) {
14889
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15666
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14890
15667
  }
14891
- function PortalWrapper({ children, container, state }) {
15668
+ function Portal({ children, container, state }) {
14892
15669
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14893
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15670
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14894
15671
  if (isRef(container)) return container.current ?? null;
14895
15672
  return container;
14896
15673
  });
15674
+ const setResolvedContainer = React.useCallback(
15675
+ (newContainer) => {
15676
+ if (!newContainer || newContainer === resolvedContainer) return;
15677
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15678
+ },
15679
+ [resolvedContainer, _setResolvedContainer, isRef]
15680
+ );
14897
15681
  React.useMemo(() => {
14898
- if (isRef(container)) {
14899
- const current = container.current;
14900
- if (!current) {
14901
- queueMicrotask(() => {
14902
- const updated = container.current;
14903
- if (updated && updated !== resolvedContainer) {
14904
- setResolvedContainer(updated);
14905
- }
14906
- });
14907
- } else if (current !== resolvedContainer) {
14908
- setResolvedContainer(current);
14909
- }
14910
- } else if (container !== resolvedContainer) {
14911
- setResolvedContainer(container);
15682
+ if (isRef(container) && !container.current) {
15683
+ return queueMicrotask(() => {
15684
+ setResolvedContainer(container.current);
15685
+ });
14912
15686
  }
14913
- }, [container, resolvedContainer, isRef]);
15687
+ setResolvedContainer(container);
15688
+ }, [container, isRef, setResolvedContainer]);
14914
15689
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14915
15690
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14916
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15691
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14917
15692
  }
14918
- function Portal({ state = {}, children, container }) {
15693
+ function PortalInner({ state = {}, children, container }) {
14919
15694
  const { events, size, injectScene = true, ...rest } = state;
14920
15695
  const previousRoot = useStore();
14921
15696
  const [raycaster] = React.useState(() => new webgpu.Raycaster());
@@ -14936,11 +15711,12 @@ function Portal({ state = {}, children, container }) {
14936
15711
  };
14937
15712
  }, [portalScene, container, injectScene]);
14938
15713
  const inject = useMutableCallback((rootState, injectState) => {
15714
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14939
15715
  let viewport = void 0;
14940
- if (injectState.camera && size) {
15716
+ if (injectState.camera && (size || injectState.size)) {
14941
15717
  const camera = injectState.camera;
14942
- viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), size);
14943
- if (camera !== rootState.camera) updateCamera(camera, size);
15718
+ viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
15719
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14944
15720
  }
14945
15721
  return {
14946
15722
  // The intersect consists of the previous root state
@@ -14957,7 +15733,7 @@ function Portal({ state = {}, children, container }) {
14957
15733
  previousRoot,
14958
15734
  // Events, size and viewport can be overridden by the inject layer
14959
15735
  events: { ...rootState.events, ...injectState.events, ...events },
14960
- size: { ...rootState.size, ...size },
15736
+ size: resolvedSize,
14961
15737
  viewport: { ...rootState.viewport, ...viewport },
14962
15738
  // Layers are allowed to override events
14963
15739
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14969,9 +15745,13 @@ function Portal({ state = {}, children, container }) {
14969
15745
  const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14970
15746
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14971
15747
  onMutate(previousRoot.getState());
14972
- previousRoot.subscribe(onMutate);
14973
15748
  return store;
14974
15749
  }, [previousRoot, container]);
15750
+ useIsomorphicLayoutEffect(() => {
15751
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15752
+ const unsubscribe = previousRoot.subscribe(onMutate);
15753
+ return unsubscribe;
15754
+ }, [previousRoot, usePortalStore]);
14975
15755
  return (
14976
15756
  // @ts-ignore, reconciler types are not maintained
14977
15757
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
@@ -14991,15 +15771,13 @@ function CanvasImpl({
14991
15771
  fallback,
14992
15772
  resize,
14993
15773
  style,
15774
+ id,
14994
15775
  gl,
14995
- renderer,
15776
+ renderer: rendererProp,
14996
15777
  events = createPointerEvents,
14997
15778
  eventSource,
14998
15779
  eventPrefix,
14999
15780
  shadows,
15000
- linear,
15001
- flat,
15002
- legacy,
15003
15781
  orthographic,
15004
15782
  frameloop,
15005
15783
  dpr,
@@ -15014,10 +15792,53 @@ function CanvasImpl({
15014
15792
  hmr,
15015
15793
  width,
15016
15794
  height,
15795
+ background,
15796
+ forceEven,
15017
15797
  ...props
15018
15798
  }) {
15799
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15800
+ let primaryCanvas;
15801
+ let scheduler;
15802
+ let renderer;
15803
+ if (isRendererConfig) {
15804
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15805
+ primaryCanvas = pc;
15806
+ scheduler = sc;
15807
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15808
+ } else {
15809
+ renderer = rendererProp;
15810
+ }
15019
15811
  React__namespace.useMemo(() => extend(THREE), []);
15020
15812
  const Bridge = useBridge();
15813
+ const backgroundProps = React__namespace.useMemo(() => {
15814
+ if (!background) return null;
15815
+ if (typeof background === "object" && !background.isColor) {
15816
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15817
+ return {
15818
+ ...rest,
15819
+ preset,
15820
+ files: envMap || files,
15821
+ backgroundFiles: backgroundMap,
15822
+ background: true
15823
+ };
15824
+ }
15825
+ if (typeof background === "number") {
15826
+ return { color: background, background: true };
15827
+ }
15828
+ if (typeof background === "string") {
15829
+ if (background in presetsObj) {
15830
+ return { preset: background, background: true };
15831
+ }
15832
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15833
+ return { files: background, background: true };
15834
+ }
15835
+ return { color: background, background: true };
15836
+ }
15837
+ if (background.isColor) {
15838
+ return { color: background, background: true };
15839
+ }
15840
+ return null;
15841
+ }, [background]);
15021
15842
  const hasInitialSizeRef = React__namespace.useRef(false);
15022
15843
  const measureConfig = React__namespace.useMemo(() => {
15023
15844
  if (!hasInitialSizeRef.current) {
@@ -15034,15 +15855,20 @@ function CanvasImpl({
15034
15855
  };
15035
15856
  }, [resize, hasInitialSizeRef.current]);
15036
15857
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15037
- const effectiveSize = React__namespace.useMemo(
15038
- () => ({
15039
- width: width ?? containerRect.width,
15040
- height: height ?? containerRect.height,
15858
+ const effectiveSize = React__namespace.useMemo(() => {
15859
+ let w = width ?? containerRect.width;
15860
+ let h = height ?? containerRect.height;
15861
+ if (forceEven) {
15862
+ w = Math.ceil(w / 2) * 2;
15863
+ h = Math.ceil(h / 2) * 2;
15864
+ }
15865
+ return {
15866
+ width: w,
15867
+ height: h,
15041
15868
  top: containerRect.top,
15042
15869
  left: containerRect.left
15043
- }),
15044
- [width, height, containerRect]
15045
- );
15870
+ };
15871
+ }, [width, height, containerRect, forceEven]);
15046
15872
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15047
15873
  hasInitialSizeRef.current = true;
15048
15874
  }
@@ -15082,14 +15908,14 @@ function CanvasImpl({
15082
15908
  async function run() {
15083
15909
  if (!effectActiveRef.current || !root.current) return;
15084
15910
  await root.current.configure({
15911
+ id,
15912
+ primaryCanvas,
15913
+ scheduler,
15085
15914
  gl,
15086
15915
  renderer,
15087
15916
  scene,
15088
15917
  events,
15089
15918
  shadows,
15090
- linear,
15091
- flat,
15092
- legacy,
15093
15919
  orthographic,
15094
15920
  frameloop,
15095
15921
  dpr,
@@ -15099,6 +15925,7 @@ function CanvasImpl({
15099
15925
  size: effectiveSize,
15100
15926
  // Store size props for reset functionality
15101
15927
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15928
+ forceEven,
15102
15929
  // Pass mutable reference to onPointerMissed so it's free to update
15103
15930
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15104
15931
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15122,7 +15949,10 @@ function CanvasImpl({
15122
15949
  });
15123
15950
  if (!effectActiveRef.current || !root.current) return;
15124
15951
  root.current.render(
15125
- /* @__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 }) }) })
15952
+ /* @__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: [
15953
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15954
+ children ?? null
15955
+ ] }) }) })
15126
15956
  );
15127
15957
  }
15128
15958
  run();
@@ -15149,20 +15979,22 @@ function CanvasImpl({
15149
15979
  const canvas = canvasRef.current;
15150
15980
  if (!canvas) return;
15151
15981
  const handleHMR = () => {
15152
- const rootEntry = _roots.get(canvas);
15153
- if (rootEntry?.store) {
15154
- rootEntry.store.setState((state) => ({
15155
- nodes: {},
15156
- uniforms: {},
15157
- _hmrVersion: state._hmrVersion + 1
15158
- }));
15159
- }
15982
+ queueMicrotask(() => {
15983
+ const rootEntry = _roots.get(canvas);
15984
+ if (rootEntry?.store) {
15985
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15986
+ rootEntry.store.setState((state) => ({
15987
+ nodes: {},
15988
+ uniforms: {},
15989
+ _hmrVersion: state._hmrVersion + 1
15990
+ }));
15991
+ }
15992
+ });
15160
15993
  };
15161
15994
  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) {
15162
15995
  const hot = undefined;
15163
15996
  hot.on("vite:afterUpdate", handleHMR);
15164
- return () => hot.dispose?.(() => {
15165
- });
15997
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15166
15998
  }
15167
15999
  if (typeof module !== "undefined" && module.hot) {
15168
16000
  const hot = module.hot;
@@ -15185,7 +16017,16 @@ function CanvasImpl({
15185
16017
  ...style
15186
16018
  },
15187
16019
  ...props,
15188
- 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 }) })
16020
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
16021
+ "canvas",
16022
+ {
16023
+ ref: canvasRef,
16024
+ id,
16025
+ className: "r3f-canvas",
16026
+ style: { display: "block", width: "100%", height: "100%" },
16027
+ children: fallback
16028
+ }
16029
+ ) })
15189
16030
  }
15190
16031
  );
15191
16032
  }
@@ -15258,6 +16099,34 @@ let ScopedStore = _ScopedStore;
15258
16099
  function createScopedStore(data) {
15259
16100
  return new ScopedStore(data);
15260
16101
  }
16102
+ function createLazyCreatorState(state) {
16103
+ let _uniforms = null;
16104
+ let _nodes = null;
16105
+ let _buffers = null;
16106
+ let _gpuStorage = null;
16107
+ return Object.create(state, {
16108
+ uniforms: {
16109
+ get() {
16110
+ return _uniforms ?? (_uniforms = createScopedStore(state.uniforms));
16111
+ }
16112
+ },
16113
+ nodes: {
16114
+ get() {
16115
+ return _nodes ?? (_nodes = createScopedStore(state.nodes));
16116
+ }
16117
+ },
16118
+ buffers: {
16119
+ get() {
16120
+ return _buffers ?? (_buffers = createScopedStore(state.buffers));
16121
+ }
16122
+ },
16123
+ gpuStorage: {
16124
+ get() {
16125
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
16126
+ }
16127
+ }
16128
+ });
16129
+ }
15261
16130
 
15262
16131
  function addTexture(set, key, value) {
15263
16132
  set((state) => {
@@ -15298,6 +16167,27 @@ function createTextureOperations(set) {
15298
16167
  removeMultiple: (keys) => removeTextures(set, keys)
15299
16168
  };
15300
16169
  }
16170
+ function extractTSLValue(value) {
16171
+ if (value === null || value === void 0) return value;
16172
+ if (typeof value !== "object") return value;
16173
+ const node = value;
16174
+ if (!node.isNode) return value;
16175
+ if (node.isConstNode) {
16176
+ return node.value;
16177
+ }
16178
+ if ("value" in node) {
16179
+ let extractedValue = node.value;
16180
+ if (typeof node.traverse === "function") {
16181
+ node.traverse((n) => {
16182
+ if (n.isConstNode) {
16183
+ extractedValue = n.value;
16184
+ }
16185
+ });
16186
+ }
16187
+ return extractedValue;
16188
+ }
16189
+ return value;
16190
+ }
15301
16191
  function vectorize(inObject) {
15302
16192
  if (inObject === null || inObject === void 0) return inObject;
15303
16193
  if (typeof inObject === "string") {
@@ -15310,9 +16200,16 @@ function vectorize(inObject) {
15310
16200
  }
15311
16201
  if (typeof inObject !== "object") return inObject;
15312
16202
  const obj = inObject;
16203
+ if (obj.isNode) {
16204
+ return extractTSLValue(inObject);
16205
+ }
15313
16206
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15314
16207
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15315
16208
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
16209
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
16210
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
16211
+ return new webgpu.Color(obj.r * scale, obj.g * scale, obj.b * scale);
16212
+ }
15316
16213
  if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
15317
16214
  if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
15318
16215
  return new webgpu.Vector4(obj.x, obj.y, obj.z, obj.w);
@@ -15375,17 +16272,14 @@ function useUniforms(creatorOrScope, scope) {
15375
16272
  const rebuildUniforms = React.useCallback(
15376
16273
  (targetScope) => {
15377
16274
  store.setState((state) => {
15378
- let newUniforms = state.uniforms;
16275
+ let newUniforms = {};
15379
16276
  if (targetScope && targetScope !== "root") {
15380
16277
  const { [targetScope]: _, ...rest } = state.uniforms;
15381
16278
  newUniforms = rest;
15382
16279
  } else if (targetScope === "root") {
15383
- newUniforms = {};
15384
16280
  for (const [key, value] of Object.entries(state.uniforms)) {
15385
16281
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15386
16282
  }
15387
- } else {
15388
- newUniforms = {};
15389
16283
  }
15390
16284
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15391
16285
  });
@@ -15393,20 +16287,26 @@ function useUniforms(creatorOrScope, scope) {
15393
16287
  [store]
15394
16288
  );
15395
16289
  const inputForMemoization = React.useMemo(() => {
16290
+ let raw = creatorOrScope;
15396
16291
  if (is.fun(creatorOrScope)) {
15397
- const state = store.getState();
15398
- const wrappedState = {
15399
- ...state,
15400
- uniforms: createScopedStore(state.uniforms),
15401
- nodes: createScopedStore(state.nodes)
15402
- };
15403
- return creatorOrScope(wrappedState);
16292
+ const wrappedState = createLazyCreatorState(store.getState());
16293
+ raw = creatorOrScope(wrappedState);
16294
+ }
16295
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
16296
+ const normalized = {};
16297
+ for (const [key, value] of Object.entries(raw)) {
16298
+ normalized[key] = vectorize(value);
16299
+ }
16300
+ return normalized;
15404
16301
  }
15405
- return creatorOrScope;
16302
+ return raw;
15406
16303
  }, [creatorOrScope, store]);
15407
16304
  const memoizedInput = useCompareMemoize(inputForMemoization);
15408
16305
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15409
16306
  const storeUniforms = useThree((s) => s.uniforms);
16307
+ const hmrVersion = useThree((s) => s._hmrVersion);
16308
+ const readerDep = isReader ? storeUniforms : null;
16309
+ const creatorDep = isReader ? null : hmrVersion;
15410
16310
  const uniforms = React.useMemo(() => {
15411
16311
  if (memoizedInput === void 0) {
15412
16312
  return storeUniforms;
@@ -15461,28 +16361,19 @@ function useUniforms(creatorOrScope, scope) {
15461
16361
  }
15462
16362
  }
15463
16363
  return result;
15464
- }, [
15465
- store,
15466
- memoizedInput,
15467
- scope,
15468
- // Only include storeUniforms in deps for reader modes to enable reactivity
15469
- isReader ? storeUniforms : null
15470
- ]);
16364
+ }, [store, memoizedInput, scope, readerDep, creatorDep]);
15471
16365
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15472
16366
  }
15473
16367
  function rebuildAllUniforms(store, scope) {
15474
16368
  store.setState((state) => {
15475
- let newUniforms = state.uniforms;
16369
+ let newUniforms = {};
15476
16370
  if (scope && scope !== "root") {
15477
16371
  const { [scope]: _, ...rest } = state.uniforms;
15478
16372
  newUniforms = rest;
15479
16373
  } else if (scope === "root") {
15480
- newUniforms = {};
15481
16374
  for (const [key, value] of Object.entries(state.uniforms)) {
15482
16375
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15483
16376
  }
15484
- } else {
15485
- newUniforms = {};
15486
16377
  }
15487
16378
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15488
16379
  });
@@ -15550,15 +16441,17 @@ function isSameThreeType(a, b) {
15550
16441
  }
15551
16442
 
15552
16443
  const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
16444
+ const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15553
16445
  function useUniform(name, value) {
15554
16446
  const store = useStore();
16447
+ const hmrVersion = useThree((s) => s._hmrVersion);
15555
16448
  return React.useMemo(() => {
15556
16449
  const state = store.getState();
15557
16450
  const set = store.setState;
15558
16451
  const existing = state.uniforms[name];
15559
16452
  if (existing && isUniformNode(existing)) {
15560
- if (value !== void 0) {
15561
- existing.value = value;
16453
+ if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
16454
+ existing.value = typeof value === "string" ? new webgpu.Color(value) : value;
15562
16455
  }
15563
16456
  return existing;
15564
16457
  }
@@ -15567,7 +16460,24 @@ function useUniform(name, value) {
15567
16460
  `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15568
16461
  );
15569
16462
  }
15570
- const node = tsl.uniform(value);
16463
+ if (isUniformNode(value)) {
16464
+ const node2 = value;
16465
+ if (typeof node2.setName === "function") {
16466
+ node2.setName(name);
16467
+ }
16468
+ set((s) => ({
16469
+ uniforms: { ...s.uniforms, [name]: node2 }
16470
+ }));
16471
+ return node2;
16472
+ }
16473
+ let node;
16474
+ if (isTSLNode$1(value)) {
16475
+ node = tsl.uniform(value);
16476
+ } else if (typeof value === "string") {
16477
+ node = tsl.uniform(new webgpu.Color(value));
16478
+ } else {
16479
+ node = tsl.uniform(value);
16480
+ }
15571
16481
  if (typeof node.setName === "function") {
15572
16482
  node.setName(name);
15573
16483
  }
@@ -15578,7 +16488,7 @@ function useUniform(name, value) {
15578
16488
  }
15579
16489
  }));
15580
16490
  return node;
15581
- }, [store, name]);
16491
+ }, [store, name, hmrVersion]);
15582
16492
  }
15583
16493
 
15584
16494
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
@@ -15642,6 +16552,9 @@ function useNodes(creatorOrScope, scope) {
15642
16552
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15643
16553
  const storeNodes = useThree((s) => s.nodes);
15644
16554
  const hmrVersion = useThree((s) => s._hmrVersion);
16555
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16556
+ const readerDep = isReader ? storeNodes : null;
16557
+ const creatorDep = isReader ? null : hmrVersion;
15645
16558
  const nodes = React.useMemo(() => {
15646
16559
  if (creatorOrScope === void 0) {
15647
16560
  return storeNodes;
@@ -15654,11 +16567,7 @@ function useNodes(creatorOrScope, scope) {
15654
16567
  const state = store.getState();
15655
16568
  const set = store.setState;
15656
16569
  const creator = creatorOrScope;
15657
- const wrappedState = {
15658
- ...state,
15659
- uniforms: createScopedStore(state.uniforms),
15660
- nodes: createScopedStore(state.nodes)
15661
- };
16570
+ const wrappedState = createLazyCreatorState(state);
15662
16571
  const created = creator(wrappedState);
15663
16572
  const result = {};
15664
16573
  let hasNewNodes = false;
@@ -15668,7 +16577,7 @@ function useNodes(creatorOrScope, scope) {
15668
16577
  if (currentScope[name]) {
15669
16578
  result[name] = currentScope[name];
15670
16579
  } else {
15671
- if (typeof node.label === "function") node.setName(`${scope}.${name}`);
16580
+ node.setName?.(`${scope}.${name}`);
15672
16581
  result[name] = node;
15673
16582
  hasNewNodes = true;
15674
16583
  }
@@ -15688,7 +16597,7 @@ function useNodes(creatorOrScope, scope) {
15688
16597
  if (existing && isTSLNode(existing)) {
15689
16598
  result[name] = existing;
15690
16599
  } else {
15691
- if (typeof node.label === "function") node.setName(name);
16600
+ node.setName?.(name);
15692
16601
  result[name] = node;
15693
16602
  hasNewNodes = true;
15694
16603
  }
@@ -15697,15 +16606,7 @@ function useNodes(creatorOrScope, scope) {
15697
16606
  set((s) => ({ nodes: { ...s.nodes, ...result } }));
15698
16607
  }
15699
16608
  return result;
15700
- }, [
15701
- store,
15702
- typeof creatorOrScope === "string" ? creatorOrScope : scope,
15703
- // Only include storeNodes in deps for reader modes to enable reactivity
15704
- // Creator mode intentionally excludes it to avoid re-running creator on unrelated changes
15705
- isReader ? storeNodes : null,
15706
- // Include hmrVersion for creator modes to allow rebuildNodes() to bust the cache
15707
- isReader ? null : hmrVersion
15708
- ]);
16609
+ }, [store, scopeDep, readerDep, creatorDep]);
15709
16610
  return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
15710
16611
  }
15711
16612
  function rebuildAllNodes(store, scope) {
@@ -15757,18 +16658,358 @@ function useLocalNodes(creator) {
15757
16658
  const uniforms = useThree((s) => s.uniforms);
15758
16659
  const nodes = useThree((s) => s.nodes);
15759
16660
  const textures = useThree((s) => s.textures);
16661
+ const hmrVersion = useThree((s) => s._hmrVersion);
15760
16662
  return React.useMemo(() => {
15761
- const state = store.getState();
15762
- const wrappedState = {
15763
- ...state,
15764
- uniforms: createScopedStore(state.uniforms),
15765
- nodes: createScopedStore(state.nodes)
15766
- };
16663
+ const wrappedState = createLazyCreatorState(store.getState());
15767
16664
  return creator(wrappedState);
15768
- }, [store, creator, uniforms, nodes, textures]);
16665
+ }, [store, creator, uniforms, nodes, textures, hmrVersion]);
16666
+ }
16667
+
16668
+ const isBufferLike = (value) => {
16669
+ if (value === null || typeof value !== "object") return false;
16670
+ if (ArrayBuffer.isView(value)) return true;
16671
+ if ("isBufferAttribute" in value) return true;
16672
+ if ("uuid" in value || "nodeType" in value) return true;
16673
+ return false;
16674
+ };
16675
+ const disposeBuffer = (buffer) => {
16676
+ if (buffer === null || typeof buffer !== "object") return;
16677
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
16678
+ buffer.dispose();
16679
+ }
16680
+ };
16681
+ function useBuffers(creatorOrScope, scope) {
16682
+ const store = useStore();
16683
+ const removeBuffers = React.useCallback(
16684
+ (names, targetScope) => {
16685
+ const nameArray = Array.isArray(names) ? names : [names];
16686
+ store.setState((state) => {
16687
+ if (targetScope) {
16688
+ const currentScope = { ...state.buffers[targetScope] };
16689
+ for (const name of nameArray) delete currentScope[name];
16690
+ return { buffers: { ...state.buffers, [targetScope]: currentScope } };
16691
+ }
16692
+ const buffers2 = { ...state.buffers };
16693
+ for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
16694
+ return { buffers: buffers2 };
16695
+ });
16696
+ },
16697
+ [store]
16698
+ );
16699
+ const clearBuffers = React.useCallback(
16700
+ (targetScope) => {
16701
+ store.setState((state) => {
16702
+ if (targetScope && targetScope !== "root") {
16703
+ const { [targetScope]: _, ...rest } = state.buffers;
16704
+ return { buffers: rest };
16705
+ }
16706
+ if (targetScope === "root") {
16707
+ const buffers2 = {};
16708
+ for (const [key, value] of Object.entries(state.buffers)) {
16709
+ if (!isBufferLike(value)) buffers2[key] = value;
16710
+ }
16711
+ return { buffers: buffers2 };
16712
+ }
16713
+ return { buffers: {} };
16714
+ });
16715
+ },
16716
+ [store]
16717
+ );
16718
+ const rebuildBuffers = React.useCallback(
16719
+ (targetScope) => {
16720
+ store.setState((state) => {
16721
+ let newBuffers = state.buffers;
16722
+ if (targetScope && targetScope !== "root") {
16723
+ const { [targetScope]: _, ...rest } = state.buffers;
16724
+ newBuffers = rest;
16725
+ } else if (targetScope === "root") {
16726
+ newBuffers = {};
16727
+ for (const [key, value] of Object.entries(state.buffers)) {
16728
+ if (!isBufferLike(value)) newBuffers[key] = value;
16729
+ }
16730
+ } else {
16731
+ newBuffers = {};
16732
+ }
16733
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16734
+ });
16735
+ },
16736
+ [store]
16737
+ );
16738
+ const disposeBuffers = React.useCallback(
16739
+ (names, targetScope) => {
16740
+ const nameArray = Array.isArray(names) ? names : [names];
16741
+ const state = store.getState();
16742
+ for (const name of nameArray) {
16743
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
16744
+ if (buffer && isBufferLike(buffer)) {
16745
+ disposeBuffer(buffer);
16746
+ }
16747
+ }
16748
+ removeBuffers(names, targetScope);
16749
+ },
16750
+ [store, removeBuffers]
16751
+ );
16752
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16753
+ const storeBuffers = useThree((s) => s.buffers);
16754
+ const hmrVersion = useThree((s) => s._hmrVersion);
16755
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16756
+ const readerDep = isReader ? storeBuffers : null;
16757
+ const creatorDep = isReader ? null : hmrVersion;
16758
+ const buffers = React.useMemo(() => {
16759
+ if (creatorOrScope === void 0) {
16760
+ return storeBuffers;
16761
+ }
16762
+ if (typeof creatorOrScope === "string") {
16763
+ const scopeData = storeBuffers[creatorOrScope];
16764
+ if (scopeData && !isBufferLike(scopeData)) return scopeData;
16765
+ return {};
16766
+ }
16767
+ const state = store.getState();
16768
+ const set = store.setState;
16769
+ const creator = creatorOrScope;
16770
+ const wrappedState = createLazyCreatorState(state);
16771
+ const created = creator(wrappedState);
16772
+ const result = {};
16773
+ let hasNewBuffers = false;
16774
+ if (scope) {
16775
+ const currentScope = state.buffers[scope] ?? {};
16776
+ for (const [name, buffer] of Object.entries(created)) {
16777
+ if (currentScope[name]) {
16778
+ result[name] = currentScope[name];
16779
+ } else {
16780
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16781
+ buffer.setName(`${scope}.${name}`);
16782
+ }
16783
+ result[name] = buffer;
16784
+ hasNewBuffers = true;
16785
+ }
16786
+ }
16787
+ if (hasNewBuffers) {
16788
+ set((s) => ({
16789
+ buffers: {
16790
+ ...s.buffers,
16791
+ [scope]: { ...s.buffers[scope], ...result }
16792
+ }
16793
+ }));
16794
+ }
16795
+ return result;
16796
+ }
16797
+ for (const [name, buffer] of Object.entries(created)) {
16798
+ const existing = state.buffers[name];
16799
+ if (existing && isBufferLike(existing)) {
16800
+ result[name] = existing;
16801
+ } else {
16802
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16803
+ buffer.setName(name);
16804
+ }
16805
+ result[name] = buffer;
16806
+ hasNewBuffers = true;
16807
+ }
16808
+ }
16809
+ if (hasNewBuffers) {
16810
+ set((s) => ({ buffers: { ...s.buffers, ...result } }));
16811
+ }
16812
+ return result;
16813
+ }, [store, scopeDep, readerDep, creatorDep]);
16814
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
16815
+ }
16816
+ function rebuildAllBuffers(store, scope) {
16817
+ store.setState((state) => {
16818
+ let newBuffers = state.buffers;
16819
+ if (scope && scope !== "root") {
16820
+ const { [scope]: _, ...rest } = state.buffers;
16821
+ newBuffers = rest;
16822
+ } else if (scope === "root") {
16823
+ newBuffers = {};
16824
+ for (const [key, value] of Object.entries(state.buffers)) {
16825
+ if (!isBufferLike(value)) newBuffers[key] = value;
16826
+ }
16827
+ } else {
16828
+ newBuffers = {};
16829
+ }
16830
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16831
+ });
16832
+ }
16833
+
16834
+ const isStorageLike = (value) => {
16835
+ if (value === null || typeof value !== "object") return false;
16836
+ if ("isTexture" in value) return true;
16837
+ if ("isData3DTexture" in value) return true;
16838
+ if ("uuid" in value || "nodeType" in value) return true;
16839
+ return false;
16840
+ };
16841
+ const disposeStorage = (storage) => {
16842
+ if (storage === null || typeof storage !== "object") return;
16843
+ if ("dispose" in storage && typeof storage.dispose === "function") {
16844
+ storage.dispose();
16845
+ }
16846
+ };
16847
+ function useGPUStorage(creatorOrScope, scope) {
16848
+ const store = useStore();
16849
+ const removeStorage = React.useCallback(
16850
+ (names, targetScope) => {
16851
+ const nameArray = Array.isArray(names) ? names : [names];
16852
+ store.setState((state) => {
16853
+ if (targetScope) {
16854
+ const currentScope = { ...state.gpuStorage[targetScope] };
16855
+ for (const name of nameArray) delete currentScope[name];
16856
+ return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
16857
+ }
16858
+ const gpuStorage2 = { ...state.gpuStorage };
16859
+ for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
16860
+ return { gpuStorage: gpuStorage2 };
16861
+ });
16862
+ },
16863
+ [store]
16864
+ );
16865
+ const clearStorage = React.useCallback(
16866
+ (targetScope) => {
16867
+ store.setState((state) => {
16868
+ if (targetScope && targetScope !== "root") {
16869
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16870
+ return { gpuStorage: rest };
16871
+ }
16872
+ if (targetScope === "root") {
16873
+ const gpuStorage2 = {};
16874
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16875
+ if (!isStorageLike(value)) gpuStorage2[key] = value;
16876
+ }
16877
+ return { gpuStorage: gpuStorage2 };
16878
+ }
16879
+ return { gpuStorage: {} };
16880
+ });
16881
+ },
16882
+ [store]
16883
+ );
16884
+ const rebuildStorage = React.useCallback(
16885
+ (targetScope) => {
16886
+ store.setState((state) => {
16887
+ let newStorage = state.gpuStorage;
16888
+ if (targetScope && targetScope !== "root") {
16889
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16890
+ newStorage = rest;
16891
+ } else if (targetScope === "root") {
16892
+ newStorage = {};
16893
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16894
+ if (!isStorageLike(value)) newStorage[key] = value;
16895
+ }
16896
+ } else {
16897
+ newStorage = {};
16898
+ }
16899
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16900
+ });
16901
+ },
16902
+ [store]
16903
+ );
16904
+ const disposeStorageFn = React.useCallback(
16905
+ (names, targetScope) => {
16906
+ const nameArray = Array.isArray(names) ? names : [names];
16907
+ const state = store.getState();
16908
+ for (const name of nameArray) {
16909
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
16910
+ if (storage && isStorageLike(storage)) {
16911
+ disposeStorage(storage);
16912
+ }
16913
+ }
16914
+ removeStorage(names, targetScope);
16915
+ },
16916
+ [store, removeStorage]
16917
+ );
16918
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16919
+ const storeStorage = useThree((s) => s.gpuStorage);
16920
+ const hmrVersion = useThree((s) => s._hmrVersion);
16921
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16922
+ const readerDep = isReader ? storeStorage : null;
16923
+ const creatorDep = isReader ? null : hmrVersion;
16924
+ const gpuStorage = React.useMemo(() => {
16925
+ if (creatorOrScope === void 0) {
16926
+ return storeStorage;
16927
+ }
16928
+ if (typeof creatorOrScope === "string") {
16929
+ const scopeData = storeStorage[creatorOrScope];
16930
+ if (scopeData && !isStorageLike(scopeData)) return scopeData;
16931
+ return {};
16932
+ }
16933
+ const state = store.getState();
16934
+ const set = store.setState;
16935
+ const creator = creatorOrScope;
16936
+ const wrappedState = createLazyCreatorState(state);
16937
+ const created = creator(wrappedState);
16938
+ const result = {};
16939
+ let hasNewStorage = false;
16940
+ if (scope) {
16941
+ const currentScope = state.gpuStorage[scope] ?? {};
16942
+ for (const [name, storage] of Object.entries(created)) {
16943
+ if (currentScope[name]) {
16944
+ result[name] = currentScope[name];
16945
+ } else {
16946
+ if ("setName" in storage && typeof storage.setName === "function") {
16947
+ storage.setName(`${scope}.${name}`);
16948
+ }
16949
+ if ("name" in storage && typeof storage.name === "string") {
16950
+ storage.name = `${scope}.${name}`;
16951
+ }
16952
+ result[name] = storage;
16953
+ hasNewStorage = true;
16954
+ }
16955
+ }
16956
+ if (hasNewStorage) {
16957
+ set((s) => ({
16958
+ gpuStorage: {
16959
+ ...s.gpuStorage,
16960
+ [scope]: { ...s.gpuStorage[scope], ...result }
16961
+ }
16962
+ }));
16963
+ }
16964
+ return result;
16965
+ }
16966
+ for (const [name, storage] of Object.entries(created)) {
16967
+ const existing = state.gpuStorage[name];
16968
+ if (existing && isStorageLike(existing)) {
16969
+ result[name] = existing;
16970
+ } else {
16971
+ if ("setName" in storage && typeof storage.setName === "function") {
16972
+ storage.setName(name);
16973
+ }
16974
+ if ("name" in storage && typeof storage.name === "string") {
16975
+ storage.name = name;
16976
+ }
16977
+ result[name] = storage;
16978
+ hasNewStorage = true;
16979
+ }
16980
+ }
16981
+ if (hasNewStorage) {
16982
+ set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
16983
+ }
16984
+ return result;
16985
+ }, [store, scopeDep, readerDep, creatorDep]);
16986
+ return {
16987
+ ...gpuStorage,
16988
+ removeStorage,
16989
+ clearStorage,
16990
+ rebuildStorage,
16991
+ disposeStorage: disposeStorageFn
16992
+ };
16993
+ }
16994
+ function rebuildAllStorage(store, scope) {
16995
+ store.setState((state) => {
16996
+ let newStorage = state.gpuStorage;
16997
+ if (scope && scope !== "root") {
16998
+ const { [scope]: _, ...rest } = state.gpuStorage;
16999
+ newStorage = rest;
17000
+ } else if (scope === "root") {
17001
+ newStorage = {};
17002
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
17003
+ if (!isStorageLike(value)) newStorage[key] = value;
17004
+ }
17005
+ } else {
17006
+ newStorage = {};
17007
+ }
17008
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
17009
+ });
15769
17010
  }
15770
17011
 
15771
- function usePostProcessing(mainCB, setupCB) {
17012
+ function useRenderPipeline(mainCB, setupCB) {
15772
17013
  const store = useStore();
15773
17014
  const { scene, camera, renderer, isLegacy } = useThree();
15774
17015
  const callbacksRanRef = React.useRef(false);
@@ -15787,7 +17028,7 @@ function usePostProcessing(mainCB, setupCB) {
15787
17028
  }, [store]);
15788
17029
  const reset = React.useCallback(() => {
15789
17030
  store.setState({
15790
- postProcessing: null,
17031
+ renderPipeline: null,
15791
17032
  passes: {}
15792
17033
  });
15793
17034
  callbacksRanRef.current = false;
@@ -15800,13 +17041,13 @@ function usePostProcessing(mainCB, setupCB) {
15800
17041
  }, []);
15801
17042
  React.useLayoutEffect(() => {
15802
17043
  if (isLegacy) {
15803
- throw new Error("usePostProcessing is only available with WebGPU renderer. Set renderer prop on Canvas.");
17044
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
15804
17045
  }
15805
17046
  if (!renderer || !scene || !camera) return;
15806
17047
  const state = store.getState();
15807
17048
  const set = store.setState;
15808
17049
  try {
15809
- let pp = state.postProcessing;
17050
+ let pp = state.renderPipeline;
15810
17051
  let currentPasses = { ...state.passes };
15811
17052
  let justCreatedPP = false;
15812
17053
  if (!pp) {
@@ -15823,7 +17064,7 @@ function usePostProcessing(mainCB, setupCB) {
15823
17064
  }
15824
17065
  currentPasses.scenePass = scenePass;
15825
17066
  if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
15826
- set({ postProcessing: pp, passes: currentPasses });
17067
+ set({ renderPipeline: pp, passes: currentPasses });
15827
17068
  const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
15828
17069
  if (shouldRunCallbacks) {
15829
17070
  if (setupCBRef.current) {
@@ -15845,19 +17086,19 @@ function usePostProcessing(mainCB, setupCB) {
15845
17086
  callbacksRanRef.current = true;
15846
17087
  }
15847
17088
  } catch (error) {
15848
- console.error("[usePostProcessing] Setup error:", error);
17089
+ console.error("[useRenderPipeline] Setup error:", error);
15849
17090
  }
15850
17091
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
15851
17092
  const passes = useThree((s) => s.passes);
15852
- const postProcessing = useThree((s) => s.postProcessing);
17093
+ const renderPipeline = useThree((s) => s.renderPipeline);
15853
17094
  return {
15854
17095
  passes,
15855
- postProcessing,
17096
+ renderPipeline,
15856
17097
  clearPasses,
15857
17098
  reset,
15858
17099
  rebuild,
15859
- // isReady indicates if PostProcessing is configured and ready for rendering
15860
- isReady: postProcessing !== null
17100
+ // isReady indicates if RenderPipeline is configured and ready for rendering
17101
+ isReady: renderPipeline !== null
15861
17102
  };
15862
17103
  }
15863
17104
 
@@ -15865,8 +17106,15 @@ extend(THREE);
15865
17106
 
15866
17107
  exports.Block = Block;
15867
17108
  exports.Canvas = Canvas;
17109
+ exports.Environment = Environment;
17110
+ exports.EnvironmentCube = EnvironmentCube;
17111
+ exports.EnvironmentMap = EnvironmentMap;
17112
+ exports.EnvironmentPortal = EnvironmentPortal;
15868
17113
  exports.ErrorBoundary = ErrorBoundary;
17114
+ exports.FROM_REF = FROM_REF;
15869
17115
  exports.IsObject = IsObject;
17116
+ exports.ONCE = ONCE;
17117
+ exports.Portal = Portal;
15870
17118
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15871
17119
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15872
17120
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15902,35 +17150,50 @@ exports.events = createPointerEvents;
15902
17150
  exports.extend = extend;
15903
17151
  exports.findInitialRoot = findInitialRoot;
15904
17152
  exports.flushSync = flushSync;
17153
+ exports.fromRef = fromRef;
15905
17154
  exports.getInstanceProps = getInstanceProps;
17155
+ exports.getPrimary = getPrimary;
17156
+ exports.getPrimaryIds = getPrimaryIds;
15906
17157
  exports.getRootState = getRootState;
15907
17158
  exports.getScheduler = getScheduler;
15908
17159
  exports.getUuidPrefix = getUuidPrefix;
15909
17160
  exports.hasConstructor = hasConstructor;
17161
+ exports.hasPrimary = hasPrimary;
15910
17162
  exports.invalidate = invalidate;
15911
17163
  exports.invalidateInstance = invalidateInstance;
15912
17164
  exports.is = is;
15913
17165
  exports.isColorRepresentation = isColorRepresentation;
15914
17166
  exports.isCopyable = isCopyable;
17167
+ exports.isFromRef = isFromRef;
15915
17168
  exports.isObject3D = isObject3D;
17169
+ exports.isOnce = isOnce;
15916
17170
  exports.isOrthographicCamera = isOrthographicCamera;
15917
17171
  exports.isRef = isRef;
15918
17172
  exports.isRenderer = isRenderer;
15919
17173
  exports.isTexture = isTexture;
15920
17174
  exports.isVectorLike = isVectorLike;
17175
+ exports.once = once;
15921
17176
  exports.prepare = prepare;
17177
+ exports.presetsObj = presetsObj;
17178
+ exports.rebuildAllBuffers = rebuildAllBuffers;
15922
17179
  exports.rebuildAllNodes = rebuildAllNodes;
17180
+ exports.rebuildAllStorage = rebuildAllStorage;
15923
17181
  exports.rebuildAllUniforms = rebuildAllUniforms;
15924
17182
  exports.reconciler = reconciler;
17183
+ exports.registerPrimary = registerPrimary;
15925
17184
  exports.removeInteractivity = removeInteractivity;
15926
17185
  exports.removeNodes = removeNodes;
15927
17186
  exports.removeUniforms = removeUniforms;
15928
17187
  exports.resolve = resolve;
15929
17188
  exports.unmountComponentAtNode = unmountComponentAtNode;
17189
+ exports.unregisterPrimary = unregisterPrimary;
15930
17190
  exports.updateCamera = updateCamera;
15931
17191
  exports.updateFrustum = updateFrustum;
15932
17192
  exports.useBridge = useBridge;
17193
+ exports.useBuffers = useBuffers;
17194
+ exports.useEnvironment = useEnvironment;
15933
17195
  exports.useFrame = useFrame;
17196
+ exports.useGPUStorage = useGPUStorage;
15934
17197
  exports.useGraph = useGraph;
15935
17198
  exports.useInstanceHandle = useInstanceHandle;
15936
17199
  exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
@@ -15938,7 +17201,7 @@ exports.useLoader = useLoader;
15938
17201
  exports.useLocalNodes = useLocalNodes;
15939
17202
  exports.useMutableCallback = useMutableCallback;
15940
17203
  exports.useNodes = useNodes;
15941
- exports.usePostProcessing = usePostProcessing;
17204
+ exports.useRenderPipeline = useRenderPipeline;
15942
17205
  exports.useRenderTarget = useRenderTarget;
15943
17206
  exports.useStore = useStore;
15944
17207
  exports.useTexture = useTexture;
@@ -15946,3 +17209,4 @@ exports.useTextures = useTextures;
15946
17209
  exports.useThree = useThree;
15947
17210
  exports.useUniform = useUniform;
15948
17211
  exports.useUniforms = useUniforms;
17212
+ exports.waitForPrimary = waitForPrimary;