@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.
@@ -1,11 +1,17 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, WebGPURenderer, Color, Vector4, PostProcessing } from 'three/webgpu';
2
+ import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, Vector4, PostProcessing } from 'three/webgpu';
3
3
  import { Inspector } from 'three/addons/inspector/Inspector.js';
4
- import { jsx, Fragment } from 'react/jsx-runtime';
4
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
  import * as React from 'react';
6
- import React__default, { useMemo, useLayoutEffect, useEffect, useContext, useRef, useImperativeHandle, useCallback, useState } from 'react';
6
+ import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
7
7
  import useMeasure from 'react-use-measure';
8
8
  import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
9
+ import { useThree as useThree$1, useLoader as useLoader$1, useFrame as useFrame$1, createPortal as createPortal$1, applyProps as applyProps$1, extend as extend$1 } from '@react-three/fiber';
10
+ import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
11
+ import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
12
+ import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
13
+ import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
14
+ import { GainMapLoader } from '@monogrid/gainmap-js';
9
15
  import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
10
16
  import { createWithEqualityFn } from 'zustand/traditional';
11
17
  import { suspend, preload, clear } from 'suspend-react';
@@ -46,6 +52,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
46
52
  WebGLRenderer: WebGLRenderer
47
53
  }, [webgpu]);
48
54
 
55
+ const primaryRegistry = /* @__PURE__ */ new Map();
56
+ const pendingSubscribers = /* @__PURE__ */ new Map();
57
+ function registerPrimary(id, renderer, store) {
58
+ if (primaryRegistry.has(id)) {
59
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
60
+ }
61
+ const entry = { renderer, store };
62
+ primaryRegistry.set(id, entry);
63
+ const subscribers = pendingSubscribers.get(id);
64
+ if (subscribers) {
65
+ subscribers.forEach((callback) => callback(entry));
66
+ pendingSubscribers.delete(id);
67
+ }
68
+ return () => {
69
+ const currentEntry = primaryRegistry.get(id);
70
+ if (currentEntry?.renderer === renderer) {
71
+ primaryRegistry.delete(id);
72
+ }
73
+ };
74
+ }
75
+ function getPrimary(id) {
76
+ return primaryRegistry.get(id);
77
+ }
78
+ function waitForPrimary(id, timeout = 5e3) {
79
+ const existing = primaryRegistry.get(id);
80
+ if (existing) {
81
+ return Promise.resolve(existing);
82
+ }
83
+ return new Promise((resolve, reject) => {
84
+ const timeoutId = setTimeout(() => {
85
+ const subscribers = pendingSubscribers.get(id);
86
+ if (subscribers) {
87
+ const index = subscribers.indexOf(callback);
88
+ if (index !== -1) subscribers.splice(index, 1);
89
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
90
+ }
91
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
92
+ }, timeout);
93
+ const callback = (entry) => {
94
+ clearTimeout(timeoutId);
95
+ resolve(entry);
96
+ };
97
+ if (!pendingSubscribers.has(id)) {
98
+ pendingSubscribers.set(id, []);
99
+ }
100
+ pendingSubscribers.get(id).push(callback);
101
+ });
102
+ }
103
+ function hasPrimary(id) {
104
+ return primaryRegistry.has(id);
105
+ }
106
+ function unregisterPrimary(id) {
107
+ primaryRegistry.delete(id);
108
+ }
109
+ function getPrimaryIds() {
110
+ return Array.from(primaryRegistry.keys());
111
+ }
112
+
113
+ const presetsObj = {
114
+ apartment: "lebombo_1k.hdr",
115
+ city: "potsdamer_platz_1k.hdr",
116
+ dawn: "kiara_1_dawn_1k.hdr",
117
+ forest: "forest_slope_1k.hdr",
118
+ lobby: "st_fagans_interior_1k.hdr",
119
+ night: "dikhololo_night_1k.hdr",
120
+ park: "rooitou_park_1k.hdr",
121
+ studio: "studio_small_03_1k.hdr",
122
+ sunset: "venice_sunset_1k.hdr",
123
+ warehouse: "empty_warehouse_01_1k.hdr"
124
+ };
125
+
126
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
127
+ const isArray = (arr) => Array.isArray(arr);
128
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
129
+ function useEnvironment({
130
+ files = defaultFiles,
131
+ path = "",
132
+ preset = void 0,
133
+ colorSpace = void 0,
134
+ extensions
135
+ } = {}) {
136
+ if (preset) {
137
+ validatePreset(preset);
138
+ files = presetsObj[preset];
139
+ path = CUBEMAP_ROOT;
140
+ }
141
+ const multiFile = isArray(files);
142
+ const { extension, isCubemap } = getExtension(files);
143
+ const loader = getLoader$1(extension);
144
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
145
+ const renderer = useThree$1((state) => state.renderer);
146
+ useLayoutEffect(() => {
147
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
148
+ function clearGainmapTexture() {
149
+ useLoader$1.clear(loader, multiFile ? [files] : files);
150
+ }
151
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
152
+ }, [extension, files, loader, multiFile, renderer.domElement]);
153
+ const loaderResult = useLoader$1(
154
+ loader,
155
+ multiFile ? [files] : files,
156
+ (loader2) => {
157
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
158
+ loader2.setRenderer?.(renderer);
159
+ }
160
+ loader2.setPath?.(path);
161
+ if (extensions) extensions(loader2);
162
+ }
163
+ );
164
+ let texture = multiFile ? (
165
+ // @ts-ignore
166
+ loaderResult[0]
167
+ ) : loaderResult;
168
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
169
+ texture = texture.renderTarget?.texture;
170
+ }
171
+ texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
172
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
173
+ return texture;
174
+ }
175
+ const preloadDefaultOptions = {
176
+ files: defaultFiles,
177
+ path: "",
178
+ preset: void 0,
179
+ extensions: void 0
180
+ };
181
+ useEnvironment.preload = (preloadOptions) => {
182
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
183
+ let { files, path = "" } = options;
184
+ const { preset, extensions } = options;
185
+ if (preset) {
186
+ validatePreset(preset);
187
+ files = presetsObj[preset];
188
+ path = CUBEMAP_ROOT;
189
+ }
190
+ const { extension } = getExtension(files);
191
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
192
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
193
+ }
194
+ const loader = getLoader$1(extension);
195
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
196
+ useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
197
+ loader2.setPath?.(path);
198
+ if (extensions) extensions(loader2);
199
+ });
200
+ };
201
+ const clearDefaultOptins = {
202
+ files: defaultFiles,
203
+ preset: void 0
204
+ };
205
+ useEnvironment.clear = (clearOptions) => {
206
+ const options = { ...clearDefaultOptins, ...clearOptions };
207
+ let { files } = options;
208
+ const { preset } = options;
209
+ if (preset) {
210
+ validatePreset(preset);
211
+ files = presetsObj[preset];
212
+ }
213
+ const { extension } = getExtension(files);
214
+ const loader = getLoader$1(extension);
215
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
216
+ useLoader$1.clear(loader, isArray(files) ? [files] : files);
217
+ };
218
+ function validatePreset(preset) {
219
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
220
+ }
221
+ function getExtension(files) {
222
+ const isCubemap = isArray(files) && files.length === 6;
223
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
224
+ const firstEntry = isArray(files) ? files[0] : files;
225
+ 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();
226
+ return { extension, isCubemap, isGainmap };
227
+ }
228
+ function getLoader$1(extension) {
229
+ const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
230
+ return loader;
231
+ }
232
+
233
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
234
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
235
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
236
+ sceneProps = {
237
+ backgroundBlurriness: 0,
238
+ backgroundIntensity: 1,
239
+ backgroundRotation: [0, 0, 0],
240
+ environmentIntensity: 1,
241
+ environmentRotation: [0, 0, 0],
242
+ ...sceneProps
243
+ };
244
+ const target = resolveScene(scene || defaultScene);
245
+ const oldbg = target.background;
246
+ const oldenv = target.environment;
247
+ const oldSceneProps = {
248
+ // @ts-ignore
249
+ backgroundBlurriness: target.backgroundBlurriness,
250
+ // @ts-ignore
251
+ backgroundIntensity: target.backgroundIntensity,
252
+ // @ts-ignore
253
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
254
+ // @ts-ignore
255
+ environmentIntensity: target.environmentIntensity,
256
+ // @ts-ignore
257
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
258
+ };
259
+ if (background !== "only") target.environment = texture;
260
+ if (background) target.background = texture;
261
+ applyProps$1(target, sceneProps);
262
+ return () => {
263
+ if (background !== "only") target.environment = oldenv;
264
+ if (background) target.background = oldbg;
265
+ applyProps$1(target, oldSceneProps);
266
+ };
267
+ }
268
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
269
+ const defaultScene = useThree$1((state) => state.scene);
270
+ React.useLayoutEffect(() => {
271
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
272
+ });
273
+ return null;
274
+ }
275
+ function EnvironmentCube({
276
+ background = false,
277
+ scene,
278
+ blur,
279
+ backgroundBlurriness,
280
+ backgroundIntensity,
281
+ backgroundRotation,
282
+ environmentIntensity,
283
+ environmentRotation,
284
+ ...rest
285
+ }) {
286
+ const texture = useEnvironment(rest);
287
+ const defaultScene = useThree$1((state) => state.scene);
288
+ React.useLayoutEffect(() => {
289
+ return setEnvProps(background, scene, defaultScene, texture, {
290
+ backgroundBlurriness: blur ?? backgroundBlurriness,
291
+ backgroundIntensity,
292
+ backgroundRotation,
293
+ environmentIntensity,
294
+ environmentRotation
295
+ });
296
+ });
297
+ React.useEffect(() => {
298
+ return () => {
299
+ texture.dispose();
300
+ };
301
+ }, [texture]);
302
+ return null;
303
+ }
304
+ function EnvironmentPortal({
305
+ children,
306
+ near = 0.1,
307
+ far = 1e3,
308
+ resolution = 256,
309
+ frames = 1,
310
+ map,
311
+ background = false,
312
+ blur,
313
+ backgroundBlurriness,
314
+ backgroundIntensity,
315
+ backgroundRotation,
316
+ environmentIntensity,
317
+ environmentRotation,
318
+ scene,
319
+ files,
320
+ path,
321
+ preset = void 0,
322
+ extensions
323
+ }) {
324
+ const gl = useThree$1((state) => state.gl);
325
+ const defaultScene = useThree$1((state) => state.scene);
326
+ const camera = React.useRef(null);
327
+ const [virtualScene] = React.useState(() => new Scene());
328
+ const fbo = React.useMemo(() => {
329
+ const fbo2 = new WebGLCubeRenderTarget(resolution);
330
+ fbo2.texture.type = HalfFloatType;
331
+ return fbo2;
332
+ }, [resolution]);
333
+ React.useEffect(() => {
334
+ return () => {
335
+ fbo.dispose();
336
+ };
337
+ }, [fbo]);
338
+ React.useLayoutEffect(() => {
339
+ if (frames === 1) {
340
+ const autoClear = gl.autoClear;
341
+ gl.autoClear = true;
342
+ camera.current.update(gl, virtualScene);
343
+ gl.autoClear = autoClear;
344
+ }
345
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
346
+ backgroundBlurriness: blur ?? backgroundBlurriness,
347
+ backgroundIntensity,
348
+ backgroundRotation,
349
+ environmentIntensity,
350
+ environmentRotation
351
+ });
352
+ }, [
353
+ children,
354
+ virtualScene,
355
+ fbo.texture,
356
+ scene,
357
+ defaultScene,
358
+ background,
359
+ frames,
360
+ gl,
361
+ blur,
362
+ backgroundBlurriness,
363
+ backgroundIntensity,
364
+ backgroundRotation,
365
+ environmentIntensity,
366
+ environmentRotation
367
+ ]);
368
+ let count = 1;
369
+ useFrame$1(() => {
370
+ if (frames === Infinity || count < frames) {
371
+ const autoClear = gl.autoClear;
372
+ gl.autoClear = true;
373
+ camera.current.update(gl, virtualScene);
374
+ gl.autoClear = autoClear;
375
+ count++;
376
+ }
377
+ });
378
+ return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
379
+ /* @__PURE__ */ jsxs(Fragment, { children: [
380
+ children,
381
+ /* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
382
+ files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
383
+ ] }),
384
+ virtualScene
385
+ ) });
386
+ }
387
+ function EnvironmentGround(props) {
388
+ const textureDefault = useEnvironment(props);
389
+ const texture = props.map || textureDefault;
390
+ React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
391
+ React.useEffect(() => {
392
+ return () => {
393
+ textureDefault.dispose();
394
+ };
395
+ }, [textureDefault]);
396
+ const height = props.ground?.height ?? 15;
397
+ const radius = props.ground?.radius ?? 60;
398
+ const scale = props.ground?.scale ?? 1e3;
399
+ const args = React.useMemo(
400
+ () => [texture, height, radius],
401
+ [texture, height, radius]
402
+ );
403
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
404
+ /* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
405
+ /* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
406
+ ] });
407
+ }
408
+ function EnvironmentColor({ color, scene }) {
409
+ const defaultScene = useThree$1((state) => state.scene);
410
+ React.useLayoutEffect(() => {
411
+ if (color === void 0) return;
412
+ const target = resolveScene(scene || defaultScene);
413
+ const oldBg = target.background;
414
+ target.background = new Color(color);
415
+ return () => {
416
+ target.background = oldBg;
417
+ };
418
+ });
419
+ return null;
420
+ }
421
+ function EnvironmentDualSource(props) {
422
+ const { backgroundFiles, ...envProps } = props;
423
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
425
+ /* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
426
+ ] });
427
+ }
428
+ function Environment(props) {
429
+ if (props.color && !props.files && !props.preset && !props.map) {
430
+ return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
431
+ }
432
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
433
+ return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
434
+ }
435
+ return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
436
+ }
437
+
49
438
  var __defProp$3 = Object.defineProperty;
50
439
  var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
51
440
  var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -225,7 +614,8 @@ function prepare(target, root, type, props) {
225
614
  object,
226
615
  eventCount: 0,
227
616
  handlers: {},
228
- isHidden: false
617
+ isHidden: false,
618
+ deferredRefs: []
229
619
  };
230
620
  if (object) object.__r3f = instance;
231
621
  }
@@ -274,7 +664,7 @@ function createOcclusionObserverNode(store, uniform) {
274
664
  let occlusionSetupPromise = null;
275
665
  function enableOcclusion(store) {
276
666
  const state = store.getState();
277
- const { internal, renderer, rootScene } = state;
667
+ const { internal, renderer } = state;
278
668
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
279
669
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
280
670
  if (!hasOcclusionSupport) {
@@ -437,6 +827,22 @@ function hasVisibilityHandlers(handlers) {
437
827
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
438
828
  }
439
829
 
830
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
831
+ function fromRef(ref) {
832
+ return { [FROM_REF]: ref };
833
+ }
834
+ function isFromRef(value) {
835
+ return value !== null && typeof value === "object" && FROM_REF in value;
836
+ }
837
+
838
+ const ONCE = Symbol.for("@react-three/fiber.once");
839
+ function once(...args) {
840
+ return { [ONCE]: args.length ? args : true };
841
+ }
842
+ function isOnce(value) {
843
+ return value !== null && typeof value === "object" && ONCE in value;
844
+ }
845
+
440
846
  const RESERVED_PROPS = [
441
847
  "children",
442
848
  "key",
@@ -507,7 +913,7 @@ function getMemoizedPrototype(root) {
507
913
  ctor = new root.constructor();
508
914
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
509
915
  }
510
- } catch (e) {
916
+ } catch {
511
917
  }
512
918
  return ctor;
513
919
  }
@@ -553,6 +959,25 @@ function applyProps(object, props) {
553
959
  continue;
554
960
  }
555
961
  if (value === void 0) continue;
962
+ if (isFromRef(value)) {
963
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
964
+ continue;
965
+ }
966
+ if (isOnce(value)) {
967
+ if (instance?.appliedOnce?.has(prop)) continue;
968
+ if (instance) {
969
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
970
+ instance.appliedOnce.add(prop);
971
+ }
972
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
973
+ const args = value[ONCE];
974
+ if (typeof targetRoot[targetKey] === "function") {
975
+ targetRoot[targetKey](...args === true ? [] : args);
976
+ } else if (args !== true && args.length > 0) {
977
+ targetRoot[targetKey] = args[0];
978
+ }
979
+ continue;
980
+ }
556
981
  let { root, key, target } = resolve(object, prop);
557
982
  if (target === void 0 && (typeof root !== "object" || root === null)) {
558
983
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -575,7 +1000,10 @@ function applyProps(object, props) {
575
1000
  else target.set(value);
576
1001
  } else {
577
1002
  root[key] = value;
578
- 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
1003
+ if (key.endsWith("Node") && root.isMaterial) {
1004
+ root.needsUpdate = true;
1005
+ }
1006
+ if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
579
1007
  root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
580
1008
  root[key].colorSpace = rootState.textureColorSpace;
581
1009
  }
@@ -608,38 +1036,60 @@ function applyProps(object, props) {
608
1036
  return object;
609
1037
  }
610
1038
 
1039
+ const DEFAULT_POINTER_ID = 0;
1040
+ const XR_POINTER_ID_START = 1e3;
1041
+ function getPointerState(internal, pointerId) {
1042
+ let state = internal.pointerMap.get(pointerId);
1043
+ if (!state) {
1044
+ state = {
1045
+ hovered: /* @__PURE__ */ new Map(),
1046
+ captured: /* @__PURE__ */ new Map(),
1047
+ initialClick: [0, 0],
1048
+ initialHits: []
1049
+ };
1050
+ internal.pointerMap.set(pointerId, state);
1051
+ }
1052
+ return state;
1053
+ }
1054
+ function getPointerId(event) {
1055
+ return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
1056
+ }
611
1057
  function makeId(event) {
612
1058
  return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
613
1059
  }
614
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
615
- const captureData = captures.get(obj);
1060
+ function releaseInternalPointerCapture(internal, obj, pointerId) {
1061
+ const pointerState = internal.pointerMap.get(pointerId);
1062
+ if (!pointerState) return;
1063
+ const captureData = pointerState.captured.get(obj);
616
1064
  if (captureData) {
617
- captures.delete(obj);
618
- if (captures.size === 0) {
619
- capturedMap.delete(pointerId);
620
- captureData.target.releasePointerCapture(pointerId);
621
- }
1065
+ pointerState.captured.delete(obj);
1066
+ captureData.target.releasePointerCapture(pointerId);
622
1067
  }
623
1068
  }
624
1069
  function removeInteractivity(store, object) {
625
1070
  const { internal } = store.getState();
626
1071
  internal.interaction = internal.interaction.filter((o) => o !== object);
627
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
628
- internal.hovered.forEach((value, key) => {
629
- if (value.eventObject === object || value.object === object) {
630
- internal.hovered.delete(key);
1072
+ for (const [pointerId, pointerState] of internal.pointerMap) {
1073
+ pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
1074
+ pointerState.hovered.forEach((value, key) => {
1075
+ if (value.eventObject === object || value.object === object) {
1076
+ pointerState.hovered.delete(key);
1077
+ }
1078
+ });
1079
+ if (pointerState.captured.has(object)) {
1080
+ releaseInternalPointerCapture(internal, object, pointerId);
631
1081
  }
632
- });
633
- internal.capturedMap.forEach((captures, pointerId) => {
634
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
635
- });
1082
+ }
636
1083
  unregisterVisibility(store, object);
637
1084
  }
638
1085
  function createEvents(store) {
639
- function calculateDistance(event) {
1086
+ function calculateDistance(event, pointerId) {
640
1087
  const { internal } = store.getState();
641
- const dx = event.offsetX - internal.initialClick[0];
642
- const dy = event.offsetY - internal.initialClick[1];
1088
+ const pointerState = internal.pointerMap.get(pointerId);
1089
+ if (!pointerState) return 0;
1090
+ const [initialX, initialY] = pointerState.initialClick;
1091
+ const dx = event.offsetX - initialX;
1092
+ const dy = event.offsetY - initialY;
643
1093
  return Math.round(Math.sqrt(dx * dx + dy * dy));
644
1094
  }
645
1095
  function filterPointerEvents(objects) {
@@ -675,6 +1125,15 @@ function createEvents(store) {
675
1125
  return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
676
1126
  }
677
1127
  let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
1128
+ const aInteractivePriority = a.object.userData?.interactivePriority;
1129
+ const bInteractivePriority = b.object.userData?.interactivePriority;
1130
+ if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
1131
+ if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
1132
+ if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
1133
+ if (aInteractivePriority !== bInteractivePriority) {
1134
+ return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
1135
+ }
1136
+ }
678
1137
  const aState = getRootState(a.object);
679
1138
  const bState = getRootState(b.object);
680
1139
  const aPriority = aState?.events?.priority ?? 1;
@@ -696,9 +1155,13 @@ function createEvents(store) {
696
1155
  eventObject = eventObject.parent;
697
1156
  }
698
1157
  }
699
- if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
700
- for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
701
- if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1158
+ if ("pointerId" in event) {
1159
+ const pointerId = event.pointerId;
1160
+ const pointerState = state.internal.pointerMap.get(pointerId);
1161
+ if (pointerState?.captured.size) {
1162
+ for (const captureData of pointerState.captured.values()) {
1163
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1164
+ }
702
1165
  }
703
1166
  }
704
1167
  return intersections;
@@ -711,27 +1174,25 @@ function createEvents(store) {
711
1174
  if (state) {
712
1175
  const { raycaster, pointer, camera, internal } = state;
713
1176
  const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
714
- const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
1177
+ const hasPointerCapture = (id) => {
1178
+ const pointerState = internal.pointerMap.get(id);
1179
+ return pointerState?.captured.has(hit.eventObject) ?? false;
1180
+ };
715
1181
  const setPointerCapture = (id) => {
716
1182
  const captureData = { intersection: hit, target: event.target };
717
- if (internal.capturedMap.has(id)) {
718
- internal.capturedMap.get(id).set(hit.eventObject, captureData);
719
- } else {
720
- internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
721
- }
1183
+ const pointerState = getPointerState(internal, id);
1184
+ pointerState.captured.set(hit.eventObject, captureData);
722
1185
  event.target.setPointerCapture(id);
723
1186
  };
724
1187
  const releasePointerCapture = (id) => {
725
- const captures = internal.capturedMap.get(id);
726
- if (captures) {
727
- releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
728
- }
1188
+ releaseInternalPointerCapture(internal, hit.eventObject, id);
729
1189
  };
730
1190
  const extractEventProps = {};
731
1191
  for (const prop in event) {
732
1192
  const property = event[prop];
733
1193
  if (typeof property !== "function") extractEventProps[prop] = property;
734
1194
  }
1195
+ const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
735
1196
  const raycastEvent = {
736
1197
  ...hit,
737
1198
  ...extractEventProps,
@@ -742,18 +1203,19 @@ function createEvents(store) {
742
1203
  unprojectedPoint,
743
1204
  ray: raycaster.ray,
744
1205
  camera,
1206
+ pointerId: eventPointerId,
745
1207
  // Hijack stopPropagation, which just sets a flag
746
1208
  stopPropagation() {
747
- const capturesForPointer = "pointerId" in event && internal.capturedMap.get(event.pointerId);
1209
+ const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
748
1210
  if (
749
1211
  // ...if this pointer hasn't been captured
750
- !capturesForPointer || // ... or if the hit object is capturing the pointer
751
- capturesForPointer.has(hit.eventObject)
1212
+ !pointerState?.captured.size || // ... or if the hit object is capturing the pointer
1213
+ pointerState.captured.has(hit.eventObject)
752
1214
  ) {
753
1215
  raycastEvent.stopped = localState.stopped = true;
754
- if (internal.hovered.size && Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
1216
+ if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
755
1217
  const higher = intersections.slice(0, intersections.indexOf(hit));
756
- cancelPointer([...higher, hit]);
1218
+ cancelPointer([...higher, hit], eventPointerId);
757
1219
  }
758
1220
  }
759
1221
  },
@@ -769,15 +1231,18 @@ function createEvents(store) {
769
1231
  }
770
1232
  return intersections;
771
1233
  }
772
- function cancelPointer(intersections) {
1234
+ function cancelPointer(intersections, pointerId) {
773
1235
  const { internal } = store.getState();
774
- for (const hoveredObj of internal.hovered.values()) {
1236
+ const pid = pointerId ?? DEFAULT_POINTER_ID;
1237
+ const pointerState = internal.pointerMap.get(pid);
1238
+ if (!pointerState) return;
1239
+ for (const [hoveredId, hoveredObj] of pointerState.hovered) {
775
1240
  if (!intersections.length || !intersections.find(
776
1241
  (hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
777
1242
  )) {
778
1243
  const eventObject = hoveredObj.eventObject;
779
1244
  const instance = eventObject.__r3f;
780
- internal.hovered.delete(makeId(hoveredObj));
1245
+ pointerState.hovered.delete(hoveredId);
781
1246
  if (instance?.eventCount) {
782
1247
  const handlers = instance.handlers;
783
1248
  const data = { ...hoveredObj, intersections };
@@ -806,41 +1271,118 @@ function createEvents(store) {
806
1271
  instance?.handlers.onDropMissed?.(event);
807
1272
  }
808
1273
  }
1274
+ function cleanupPointer(pointerId) {
1275
+ const { internal } = store.getState();
1276
+ const pointerState = internal.pointerMap.get(pointerId);
1277
+ if (pointerState) {
1278
+ for (const [, hoveredObj] of pointerState.hovered) {
1279
+ const eventObject = hoveredObj.eventObject;
1280
+ const instance = eventObject.__r3f;
1281
+ if (instance?.eventCount) {
1282
+ const handlers = instance.handlers;
1283
+ const data = { ...hoveredObj, intersections: [] };
1284
+ handlers.onPointerOut?.(data);
1285
+ handlers.onPointerLeave?.(data);
1286
+ }
1287
+ }
1288
+ internal.pointerMap.delete(pointerId);
1289
+ }
1290
+ internal.pointerDirty.delete(pointerId);
1291
+ }
1292
+ function processDeferredPointer(event, pointerId) {
1293
+ const state = store.getState();
1294
+ const { internal } = state;
1295
+ if (!state.events.enabled) return;
1296
+ const filter = filterPointerEvents;
1297
+ const hits = intersect(event, filter);
1298
+ cancelPointer(hits, pointerId);
1299
+ function onIntersect(data) {
1300
+ const eventObject = data.eventObject;
1301
+ const instance = eventObject.__r3f;
1302
+ if (!instance?.eventCount) return;
1303
+ const handlers = instance.handlers;
1304
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1305
+ const id = makeId(data);
1306
+ const pointerState = getPointerState(internal, pointerId);
1307
+ const hoveredItem = pointerState.hovered.get(id);
1308
+ if (!hoveredItem) {
1309
+ pointerState.hovered.set(id, data);
1310
+ handlers.onPointerOver?.(data);
1311
+ handlers.onPointerEnter?.(data);
1312
+ } else if (hoveredItem.stopped) {
1313
+ data.stopPropagation();
1314
+ }
1315
+ }
1316
+ handlers.onPointerMove?.(data);
1317
+ }
1318
+ handleIntersects(hits, event, 0, onIntersect);
1319
+ }
809
1320
  function handlePointer(name) {
810
1321
  switch (name) {
811
1322
  case "onPointerLeave":
812
- case "onPointerCancel":
813
1323
  case "onDragLeave":
814
1324
  return () => cancelPointer([]);
1325
+ // Global cancel of these events
1326
+ case "onPointerCancel":
1327
+ return (event) => {
1328
+ const pointerId = getPointerId(event);
1329
+ cleanupPointer(pointerId);
1330
+ };
815
1331
  case "onLostPointerCapture":
816
1332
  return (event) => {
817
1333
  const { internal } = store.getState();
818
- if ("pointerId" in event && internal.capturedMap.has(event.pointerId)) {
1334
+ const pointerId = getPointerId(event);
1335
+ const pointerState = internal.pointerMap.get(pointerId);
1336
+ if (pointerState?.captured.size) {
819
1337
  requestAnimationFrame(() => {
820
- if (internal.capturedMap.has(event.pointerId)) {
821
- internal.capturedMap.delete(event.pointerId);
822
- cancelPointer([]);
1338
+ const pointerState2 = internal.pointerMap.get(pointerId);
1339
+ if (pointerState2?.captured.size) {
1340
+ pointerState2.captured.clear();
823
1341
  }
1342
+ cancelPointer([], pointerId);
824
1343
  });
825
1344
  }
826
1345
  };
827
1346
  }
828
1347
  return function handleEvent(event) {
829
1348
  const state = store.getState();
830
- const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
1349
+ const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
1350
+ const pointerId = getPointerId(event);
831
1351
  internal.lastEvent.current = event;
832
- if (!state.events.enabled) return;
1352
+ if (!events.enabled) return;
833
1353
  const isPointerMove = name === "onPointerMove";
834
1354
  const isDragOver = name === "onDragOver";
835
1355
  const isDrop = name === "onDrop";
836
1356
  const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
1357
+ const isPointerDown = name === "onPointerDown";
1358
+ const isPointerUp = name === "onPointerUp";
1359
+ const isWheel = name === "onWheel";
1360
+ const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
1361
+ if (isPointerMove && canDeferRaycasts) {
1362
+ events.compute?.(event, state);
1363
+ internal.pointerDirty.set(pointerId, event);
1364
+ return;
1365
+ }
1366
+ if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
1367
+ events.compute?.(event, state);
1368
+ internal.pointerDirty.set(pointerId, event);
1369
+ return;
1370
+ }
1371
+ if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
1372
+ const deferredEvent = internal.pointerDirty.get(pointerId);
1373
+ internal.pointerDirty.delete(pointerId);
1374
+ processDeferredPointer(deferredEvent, pointerId);
1375
+ }
837
1376
  const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
838
1377
  const hits = intersect(event, filter);
839
- const delta = isClickEvent ? calculateDistance(event) : 0;
840
- if (name === "onPointerDown") {
841
- internal.initialClick = [event.offsetX, event.offsetY];
842
- internal.initialHits = hits.map((hit) => hit.eventObject);
843
- }
1378
+ const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
1379
+ if (isPointerDown) {
1380
+ const pointerState2 = getPointerState(internal, pointerId);
1381
+ pointerState2.initialClick = [event.offsetX, event.offsetY];
1382
+ pointerState2.initialHits = hits.map((hit) => hit.eventObject);
1383
+ }
1384
+ const pointerState = internal.pointerMap.get(pointerId);
1385
+ const initialHits = pointerState?.initialHits ?? [];
844
1386
  if (isClickEvent && !hits.length) {
845
1387
  if (delta <= 2) {
846
1388
  pointerMissed(event, internal.interaction);
@@ -855,7 +1397,9 @@ function createEvents(store) {
855
1397
  dropMissed(event, internal.interaction);
856
1398
  if (onDropMissed) onDropMissed(event);
857
1399
  }
858
- if (isPointerMove || isDragOver) cancelPointer(hits);
1400
+ if (isPointerMove || isDragOver) {
1401
+ cancelPointer(hits, pointerId);
1402
+ }
859
1403
  function onIntersect(data) {
860
1404
  const eventObject = data.eventObject;
861
1405
  const instance = eventObject.__r3f;
@@ -864,9 +1408,10 @@ function createEvents(store) {
864
1408
  if (isPointerMove) {
865
1409
  if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
866
1410
  const id = makeId(data);
867
- const hoveredItem = internal.hovered.get(id);
1411
+ const pointerState2 = getPointerState(internal, pointerId);
1412
+ const hoveredItem = pointerState2.hovered.get(id);
868
1413
  if (!hoveredItem) {
869
- internal.hovered.set(id, data);
1414
+ pointerState2.hovered.set(id, data);
870
1415
  handlers.onPointerOver?.(data);
871
1416
  handlers.onPointerEnter?.(data);
872
1417
  } else if (hoveredItem.stopped) {
@@ -876,9 +1421,10 @@ function createEvents(store) {
876
1421
  handlers.onPointerMove?.(data);
877
1422
  } else if (isDragOver) {
878
1423
  const id = makeId(data);
879
- const hoveredItem = internal.hovered.get(id);
1424
+ const pointerState2 = getPointerState(internal, pointerId);
1425
+ const hoveredItem = pointerState2.hovered.get(id);
880
1426
  if (!hoveredItem) {
881
- internal.hovered.set(id, data);
1427
+ pointerState2.hovered.set(id, data);
882
1428
  handlers.onDragOverEnter?.(data);
883
1429
  } else if (hoveredItem.stopped) {
884
1430
  data.stopPropagation();
@@ -889,18 +1435,18 @@ function createEvents(store) {
889
1435
  } else {
890
1436
  const handler = handlers[name];
891
1437
  if (handler) {
892
- if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1438
+ if (!isClickEvent || initialHits.includes(eventObject)) {
893
1439
  pointerMissed(
894
1440
  event,
895
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1441
+ internal.interaction.filter((object) => !initialHits.includes(object))
896
1442
  );
897
1443
  handler(data);
898
1444
  }
899
1445
  } else {
900
- if (isClickEvent && internal.initialHits.includes(eventObject)) {
1446
+ if (isClickEvent && initialHits.includes(eventObject)) {
901
1447
  pointerMissed(
902
1448
  event,
903
- internal.interaction.filter((object) => !internal.initialHits.includes(object))
1449
+ internal.interaction.filter((object) => !initialHits.includes(object))
904
1450
  );
905
1451
  }
906
1452
  }
@@ -909,7 +1455,15 @@ function createEvents(store) {
909
1455
  handleIntersects(hits, event, delta, onIntersect);
910
1456
  };
911
1457
  }
912
- return { handlePointer };
1458
+ function flushDeferredPointers() {
1459
+ const { internal, events } = store.getState();
1460
+ if (!events.frameTimedRaycasts) return;
1461
+ for (const [pointerId, event] of internal.pointerDirty) {
1462
+ processDeferredPointer(event, pointerId);
1463
+ }
1464
+ internal.pointerDirty.clear();
1465
+ }
1466
+ return { handlePointer, flushDeferredPointers, processDeferredPointer };
913
1467
  }
914
1468
  const DOM_EVENTS = {
915
1469
  onClick: ["click", false],
@@ -928,11 +1482,16 @@ const DOM_EVENTS = {
928
1482
  onLostPointerCapture: ["lostpointercapture", true]
929
1483
  };
930
1484
  function createPointerEvents(store) {
931
- const { handlePointer } = createEvents(store);
1485
+ const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
1486
+ let nextXRPointerId = XR_POINTER_ID_START;
1487
+ const xrPointers = /* @__PURE__ */ new Map();
932
1488
  return {
933
1489
  priority: 1,
934
1490
  enabled: true,
935
- compute(event, state, previous) {
1491
+ frameTimedRaycasts: true,
1492
+ alwaysFireOnScroll: true,
1493
+ updateOnFrame: false,
1494
+ compute(event, state) {
936
1495
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
937
1496
  state.raycaster.setFromCamera(state.pointer, state.camera);
938
1497
  },
@@ -941,11 +1500,33 @@ function createPointerEvents(store) {
941
1500
  (acc, key) => ({ ...acc, [key]: handlePointer(key) }),
942
1501
  {}
943
1502
  ),
944
- update: () => {
1503
+ update: (pointerId) => {
1504
+ const { events, internal } = store.getState();
1505
+ if (!events.handlers) return;
1506
+ if (pointerId !== void 0) {
1507
+ const event = internal.pointerDirty.get(pointerId);
1508
+ if (event) {
1509
+ internal.pointerDirty.delete(pointerId);
1510
+ processDeferredPointer(event, pointerId);
1511
+ } else if (internal.lastEvent?.current) {
1512
+ processDeferredPointer(internal.lastEvent.current, pointerId);
1513
+ }
1514
+ } else {
1515
+ flushDeferredPointers();
1516
+ if (internal.lastEvent?.current) {
1517
+ events.handlers.onPointerMove(internal.lastEvent.current);
1518
+ }
1519
+ }
1520
+ },
1521
+ flush: () => {
945
1522
  const { events, internal } = store.getState();
946
- if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current);
1523
+ flushDeferredPointers();
1524
+ if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
1525
+ events.handlers.onPointerMove(internal.lastEvent.current);
1526
+ }
947
1527
  },
948
1528
  connect: (target) => {
1529
+ if (!target) return;
949
1530
  const { set, events } = store.getState();
950
1531
  events.disconnect?.();
951
1532
  set((state) => ({ events: { ...state.events, connected: target } }));
@@ -969,6 +1550,32 @@ function createPointerEvents(store) {
969
1550
  }
970
1551
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
971
1552
  }
1553
+ },
1554
+ registerPointer: (config) => {
1555
+ const pointerId = nextXRPointerId++;
1556
+ xrPointers.set(pointerId, config);
1557
+ const { internal } = store.getState();
1558
+ getPointerState(internal, pointerId);
1559
+ return pointerId;
1560
+ },
1561
+ unregisterPointer: (pointerId) => {
1562
+ xrPointers.delete(pointerId);
1563
+ const { internal } = store.getState();
1564
+ const pointerState = internal.pointerMap.get(pointerId);
1565
+ if (pointerState) {
1566
+ for (const [, hoveredObj] of pointerState.hovered) {
1567
+ const eventObject = hoveredObj.eventObject;
1568
+ const instance = eventObject.__r3f;
1569
+ if (instance?.eventCount) {
1570
+ const handlers = instance.handlers;
1571
+ const data = { ...hoveredObj, intersections: [] };
1572
+ handlers.onPointerOut?.(data);
1573
+ handlers.onPointerLeave?.(data);
1574
+ }
1575
+ }
1576
+ internal.pointerMap.delete(pointerId);
1577
+ }
1578
+ internal.pointerDirty.delete(pointerId);
972
1579
  }
973
1580
  };
974
1581
  }
@@ -1030,331 +1637,26 @@ function notifyAlpha({ message, link }) {
1030
1637
  }
1031
1638
  }
1032
1639
 
1033
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1034
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1035
- const createStore = (invalidate, advance) => {
1036
- const rootStore = createWithEqualityFn((set, get) => {
1037
- const position = new Vector3();
1038
- const defaultTarget = new Vector3();
1039
- const tempTarget = new Vector3();
1040
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1041
- const { width, height, top, left } = size;
1042
- const aspect = width / height;
1043
- if (target.isVector3) tempTarget.copy(target);
1044
- else tempTarget.set(...target);
1045
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1046
- if (isOrthographicCamera(camera)) {
1047
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1048
- } else {
1049
- const fov = camera.fov * Math.PI / 180;
1050
- const h = 2 * Math.tan(fov / 2) * distance;
1051
- const w = h * (width / height);
1052
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1053
- }
1054
- }
1055
- let performanceTimeout = void 0;
1056
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1057
- const pointer = new Vector2();
1058
- const rootState = {
1059
- set,
1060
- get,
1061
- // Mock objects that have to be configured
1062
- gl: null,
1063
- renderer: null,
1064
- camera: null,
1065
- frustum: new Frustum(),
1066
- autoUpdateFrustum: true,
1067
- raycaster: null,
1068
- events: { priority: 1, enabled: true, connected: false },
1069
- scene: null,
1070
- rootScene: null,
1071
- xr: null,
1072
- inspector: null,
1073
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1074
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1075
- legacy: false,
1076
- linear: false,
1077
- flat: false,
1078
- textureColorSpace: "srgb",
1079
- isLegacy: false,
1080
- webGPUSupported: false,
1081
- isNative: false,
1082
- controls: null,
1083
- pointer,
1084
- mouse: pointer,
1085
- frameloop: "always",
1086
- onPointerMissed: void 0,
1087
- onDragOverMissed: void 0,
1088
- onDropMissed: void 0,
1089
- performance: {
1090
- current: 1,
1091
- min: 0.5,
1092
- max: 1,
1093
- debounce: 200,
1094
- regress: () => {
1095
- const state2 = get();
1096
- if (performanceTimeout) clearTimeout(performanceTimeout);
1097
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1098
- performanceTimeout = setTimeout(
1099
- () => setPerformanceCurrent(get().performance.max),
1100
- state2.performance.debounce
1101
- );
1102
- }
1103
- },
1104
- size: { width: 0, height: 0, top: 0, left: 0 },
1105
- viewport: {
1106
- initialDpr: 0,
1107
- dpr: 0,
1108
- width: 0,
1109
- height: 0,
1110
- top: 0,
1111
- left: 0,
1112
- aspect: 0,
1113
- distance: 0,
1114
- factor: 0,
1115
- getCurrentViewport
1116
- },
1117
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1118
- setSize: (width, height, top, left) => {
1119
- const state2 = get();
1120
- if (width === void 0) {
1121
- set({ _sizeImperative: false });
1122
- if (state2._sizeProps) {
1123
- const { width: propW, height: propH } = state2._sizeProps;
1124
- if (propW !== void 0 || propH !== void 0) {
1125
- const currentSize = state2.size;
1126
- const newSize = {
1127
- width: propW ?? currentSize.width,
1128
- height: propH ?? currentSize.height,
1129
- top: currentSize.top,
1130
- left: currentSize.left
1131
- };
1132
- set((s) => ({
1133
- size: newSize,
1134
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1135
- }));
1136
- }
1137
- }
1138
- return;
1139
- }
1140
- const w = width;
1141
- const h = height ?? width;
1142
- const t = top ?? state2.size.top;
1143
- const l = left ?? state2.size.left;
1144
- const size = { width: w, height: h, top: t, left: l };
1145
- set((s) => ({
1146
- size,
1147
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1148
- _sizeImperative: true
1149
- }));
1150
- },
1151
- setDpr: (dpr) => set((state2) => {
1152
- const resolved = calculateDpr(dpr);
1153
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1154
- }),
1155
- setFrameloop: (frameloop = "always") => {
1156
- set(() => ({ frameloop }));
1157
- },
1158
- setError: (error) => set(() => ({ error })),
1159
- error: null,
1160
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1161
- uniforms: {},
1162
- nodes: {},
1163
- textures: /* @__PURE__ */ new Map(),
1164
- postProcessing: null,
1165
- passes: {},
1166
- _hmrVersion: 0,
1167
- _sizeImperative: false,
1168
- _sizeProps: null,
1169
- previousRoot: void 0,
1170
- internal: {
1171
- // Events
1172
- interaction: [],
1173
- hovered: /* @__PURE__ */ new Map(),
1174
- subscribers: [],
1175
- initialClick: [0, 0],
1176
- initialHits: [],
1177
- capturedMap: /* @__PURE__ */ new Map(),
1178
- lastEvent: React.createRef(),
1179
- // Visibility tracking (onFramed, onOccluded, onVisible)
1180
- visibilityRegistry: /* @__PURE__ */ new Map(),
1181
- // Occlusion system (WebGPU only)
1182
- occlusionEnabled: false,
1183
- occlusionObserver: null,
1184
- occlusionCache: /* @__PURE__ */ new Map(),
1185
- helperGroup: null,
1186
- // Updates
1187
- active: false,
1188
- frames: 0,
1189
- priority: 0,
1190
- subscribe: (ref, priority, store) => {
1191
- const internal = get().internal;
1192
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1193
- internal.subscribers.push({ ref, priority, store });
1194
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1195
- return () => {
1196
- const internal2 = get().internal;
1197
- if (internal2?.subscribers) {
1198
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1199
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1200
- }
1201
- };
1202
- },
1203
- // Renderer Storage (single source of truth)
1204
- actualRenderer: null,
1205
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1206
- scheduler: null
1207
- }
1208
- };
1209
- return rootState;
1210
- });
1211
- const state = rootStore.getState();
1212
- Object.defineProperty(state, "gl", {
1213
- get() {
1214
- const currentState = rootStore.getState();
1215
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1216
- const stack = new Error().stack || "";
1217
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1218
- if (!isInternalAccess) {
1219
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1220
- notifyDepreciated({
1221
- heading: "Accessing state.gl in WebGPU mode",
1222
- 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
1223
- });
1224
- }
1225
- }
1226
- return currentState.internal.actualRenderer;
1227
- },
1228
- set(value) {
1229
- rootStore.getState().internal.actualRenderer = value;
1230
- },
1231
- enumerable: true,
1232
- configurable: true
1233
- });
1234
- Object.defineProperty(state, "renderer", {
1235
- get() {
1236
- return rootStore.getState().internal.actualRenderer;
1237
- },
1238
- set(value) {
1239
- rootStore.getState().internal.actualRenderer = value;
1240
- },
1241
- enumerable: true,
1242
- configurable: true
1243
- });
1244
- let oldScene = state.scene;
1245
- rootStore.subscribe(() => {
1246
- const currentState = rootStore.getState();
1247
- const { scene, rootScene, set } = currentState;
1248
- if (scene !== oldScene) {
1249
- oldScene = scene;
1250
- if (scene?.isScene && scene !== rootScene) {
1251
- set({ rootScene: scene });
1252
- }
1253
- }
1254
- });
1255
- let oldSize = state.size;
1256
- let oldDpr = state.viewport.dpr;
1257
- let oldCamera = state.camera;
1258
- rootStore.subscribe(() => {
1259
- const { camera, size, viewport, set, internal } = rootStore.getState();
1260
- const actualRenderer = internal.actualRenderer;
1261
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1262
- oldSize = size;
1263
- oldDpr = viewport.dpr;
1264
- updateCamera(camera, size);
1265
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1266
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1267
- actualRenderer.setSize(size.width, size.height, updateStyle);
1268
- }
1269
- if (camera !== oldCamera) {
1270
- oldCamera = camera;
1271
- const { rootScene } = rootStore.getState();
1272
- if (camera && rootScene && !camera.parent) {
1273
- rootScene.add(camera);
1274
- }
1275
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1276
- const currentState = rootStore.getState();
1277
- if (currentState.autoUpdateFrustum && camera) {
1278
- updateFrustum(camera, currentState.frustum);
1279
- }
1280
- }
1281
- });
1282
- rootStore.subscribe((state2) => invalidate(state2));
1283
- return rootStore;
1284
- };
1285
-
1286
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1287
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1288
- function getLoader(Proto) {
1289
- if (isConstructor$1(Proto)) {
1290
- let loader = memoizedLoaders.get(Proto);
1291
- if (!loader) {
1292
- loader = new Proto();
1293
- memoizedLoaders.set(Proto, loader);
1294
- }
1295
- return loader;
1296
- }
1297
- return Proto;
1298
- }
1299
- function loadingFn(extensions, onProgress) {
1300
- return function(Proto, input) {
1301
- const loader = getLoader(Proto);
1302
- if (extensions) extensions(loader);
1303
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1304
- return loader.loadAsync(input, onProgress).then((data) => {
1305
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1306
- return data;
1307
- });
1308
- }
1309
- return new Promise(
1310
- (res, reject) => loader.load(
1311
- input,
1312
- (data) => {
1313
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1314
- res(data);
1315
- },
1316
- onProgress,
1317
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1318
- )
1319
- );
1320
- };
1321
- }
1322
- function useLoader(loader, input, extensions, onProgress) {
1323
- const keys = Array.isArray(input) ? input : [input];
1324
- const fn = loadingFn(extensions, onProgress);
1325
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1326
- return Array.isArray(input) ? results : results[0];
1327
- }
1328
- useLoader.preload = function(loader, input, extensions, onProgress) {
1329
- const keys = Array.isArray(input) ? input : [input];
1330
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
1331
- };
1332
- useLoader.clear = function(loader, input) {
1333
- const keys = Array.isArray(input) ? input : [input];
1334
- keys.forEach((key) => clear([loader, key]));
1335
- };
1336
- useLoader.loader = getLoader;
1337
-
1338
- var __defProp$2 = Object.defineProperty;
1339
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1340
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1341
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1342
- class PhaseGraph {
1343
- constructor() {
1344
- /** Ordered list of phase nodes */
1345
- __publicField$2(this, "phases", []);
1346
- /** Quick lookup by name */
1347
- __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1348
- /** Cached ordered names (invalidated on changes) */
1349
- __publicField$2(this, "orderedNamesCache", null);
1350
- this.initializeDefaultPhases();
1351
- }
1352
- //* Initialization --------------------------------
1353
- initializeDefaultPhases() {
1354
- for (const name of DEFAULT_PHASES) {
1355
- const node = { name, isAutoGenerated: false };
1356
- this.phases.push(node);
1357
- this.phaseMap.set(name, node);
1640
+ var __defProp$2 = Object.defineProperty;
1641
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1642
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1643
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1644
+ class PhaseGraph {
1645
+ constructor() {
1646
+ /** Ordered list of phase nodes */
1647
+ __publicField$2(this, "phases", []);
1648
+ /** Quick lookup by name */
1649
+ __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1650
+ /** Cached ordered names (invalidated on changes) */
1651
+ __publicField$2(this, "orderedNamesCache", null);
1652
+ this.initializeDefaultPhases();
1653
+ }
1654
+ //* Initialization --------------------------------
1655
+ initializeDefaultPhases() {
1656
+ for (const name of DEFAULT_PHASES) {
1657
+ const node = { name, isAutoGenerated: false };
1658
+ this.phases.push(node);
1659
+ this.phaseMap.set(name, node);
1358
1660
  }
1359
1661
  this.invalidateCache();
1360
1662
  }
@@ -1587,7 +1889,7 @@ function shouldRun(job, now) {
1587
1889
  const minInterval = 1e3 / job.fps;
1588
1890
  const lastRun = job.lastRun ?? 0;
1589
1891
  const elapsed = now - lastRun;
1590
- if (elapsed < minInterval) return false;
1892
+ if (elapsed < minInterval - 1) return false;
1591
1893
  if (job.drop) {
1592
1894
  job.lastRun = now;
1593
1895
  } else {
@@ -2367,36 +2669,364 @@ if (hmrData) {
2367
2669
  hmrData.accept?.();
2368
2670
  }
2369
2671
 
2370
- function useFrame(callback, priorityOrOptions) {
2371
- const store = React.useContext(context);
2372
- const isInsideCanvas = store !== null;
2373
- const scheduler = getScheduler();
2374
- const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
2375
- id: priorityOrOptions.id,
2376
- phase: priorityOrOptions.phase,
2377
- priority: priorityOrOptions.priority,
2378
- fps: priorityOrOptions.fps,
2379
- drop: priorityOrOptions.drop,
2380
- enabled: priorityOrOptions.enabled,
2381
- before: priorityOrOptions.before,
2382
- after: priorityOrOptions.after
2383
- }) : "";
2384
- const options = React.useMemo(() => {
2385
- return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
2386
- }, [optionsKey]);
2387
- const reactId = React.useId();
2388
- const id = options.id ?? reactId;
2389
- const callbackRef = useMutableCallback(callback);
2390
- const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
2391
- useIsomorphicLayoutEffect(() => {
2392
- if (!callback) return;
2393
- if (isInsideCanvas) {
2394
- const state = store.getState();
2395
- const rootId = state.internal.rootId;
2396
- if (isLegacyPriority) {
2397
- state.internal.priority++;
2398
- let parentRoot = state.previousRoot;
2399
- while (parentRoot) {
2672
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2673
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
2674
+ const createStore = (invalidate, advance) => {
2675
+ const rootStore = createWithEqualityFn((set, get) => {
2676
+ const position = new Vector3();
2677
+ const defaultTarget = new Vector3();
2678
+ const tempTarget = new Vector3();
2679
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2680
+ const { width, height, top, left } = size;
2681
+ const aspect = width / height;
2682
+ if (target.isVector3) tempTarget.copy(target);
2683
+ else tempTarget.set(...target);
2684
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2685
+ if (isOrthographicCamera(camera)) {
2686
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2687
+ } else {
2688
+ const fov = camera.fov * Math.PI / 180;
2689
+ const h = 2 * Math.tan(fov / 2) * distance;
2690
+ const w = h * (width / height);
2691
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2692
+ }
2693
+ }
2694
+ let performanceTimeout = void 0;
2695
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2696
+ const pointer = new Vector2();
2697
+ const rootState = {
2698
+ set,
2699
+ get,
2700
+ // Mock objects that have to be configured
2701
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2702
+ primaryStore: null,
2703
+ gl: null,
2704
+ renderer: null,
2705
+ camera: null,
2706
+ frustum: new Frustum(),
2707
+ autoUpdateFrustum: true,
2708
+ raycaster: null,
2709
+ events: {
2710
+ priority: 1,
2711
+ enabled: true,
2712
+ connected: false,
2713
+ frameTimedRaycasts: true,
2714
+ alwaysFireOnScroll: true,
2715
+ updateOnFrame: false
2716
+ },
2717
+ scene: null,
2718
+ rootScene: null,
2719
+ xr: null,
2720
+ inspector: null,
2721
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2722
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2723
+ textureColorSpace: SRGBColorSpace,
2724
+ isLegacy: false,
2725
+ webGPUSupported: false,
2726
+ isNative: false,
2727
+ controls: null,
2728
+ pointer,
2729
+ mouse: pointer,
2730
+ frameloop: "always",
2731
+ onPointerMissed: void 0,
2732
+ onDragOverMissed: void 0,
2733
+ onDropMissed: void 0,
2734
+ performance: {
2735
+ current: 1,
2736
+ min: 0.5,
2737
+ max: 1,
2738
+ debounce: 200,
2739
+ regress: () => {
2740
+ const state2 = get();
2741
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2742
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2743
+ performanceTimeout = setTimeout(
2744
+ () => setPerformanceCurrent(get().performance.max),
2745
+ state2.performance.debounce
2746
+ );
2747
+ }
2748
+ },
2749
+ size: { width: 0, height: 0, top: 0, left: 0 },
2750
+ viewport: {
2751
+ initialDpr: 0,
2752
+ dpr: 0,
2753
+ width: 0,
2754
+ height: 0,
2755
+ top: 0,
2756
+ left: 0,
2757
+ aspect: 0,
2758
+ distance: 0,
2759
+ factor: 0,
2760
+ getCurrentViewport
2761
+ },
2762
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2763
+ setSize: (width, height, top, left) => {
2764
+ const state2 = get();
2765
+ if (width === void 0) {
2766
+ set({ _sizeImperative: false });
2767
+ if (state2._sizeProps) {
2768
+ const { width: propW, height: propH } = state2._sizeProps;
2769
+ if (propW !== void 0 || propH !== void 0) {
2770
+ const currentSize = state2.size;
2771
+ const newSize = {
2772
+ width: propW ?? currentSize.width,
2773
+ height: propH ?? currentSize.height,
2774
+ top: currentSize.top,
2775
+ left: currentSize.left
2776
+ };
2777
+ set((s) => ({
2778
+ size: newSize,
2779
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2780
+ }));
2781
+ getScheduler().invalidate();
2782
+ }
2783
+ }
2784
+ return;
2785
+ }
2786
+ const w = width;
2787
+ const h = height ?? width;
2788
+ const t = top ?? state2.size.top;
2789
+ const l = left ?? state2.size.left;
2790
+ const size = { width: w, height: h, top: t, left: l };
2791
+ set((s) => ({
2792
+ size,
2793
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2794
+ _sizeImperative: true
2795
+ }));
2796
+ getScheduler().invalidate();
2797
+ },
2798
+ setDpr: (dpr) => set((state2) => {
2799
+ const resolved = calculateDpr(dpr);
2800
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2801
+ }),
2802
+ setFrameloop: (frameloop = "always") => {
2803
+ set(() => ({ frameloop }));
2804
+ },
2805
+ setError: (error) => set(() => ({ error })),
2806
+ error: null,
2807
+ //* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
2808
+ uniforms: {},
2809
+ nodes: {},
2810
+ buffers: {},
2811
+ gpuStorage: {},
2812
+ textures: /* @__PURE__ */ new Map(),
2813
+ renderPipeline: null,
2814
+ passes: {},
2815
+ _hmrVersion: 0,
2816
+ _sizeImperative: false,
2817
+ _sizeProps: null,
2818
+ previousRoot: void 0,
2819
+ internal: {
2820
+ // Events
2821
+ interaction: [],
2822
+ subscribers: [],
2823
+ // Per-pointer state (new unified structure)
2824
+ pointerMap: /* @__PURE__ */ new Map(),
2825
+ pointerDirty: /* @__PURE__ */ new Map(),
2826
+ lastEvent: React.createRef(),
2827
+ // Deprecated but kept for backwards compatibility
2828
+ hovered: /* @__PURE__ */ new Map(),
2829
+ initialClick: [0, 0],
2830
+ initialHits: [],
2831
+ capturedMap: /* @__PURE__ */ new Map(),
2832
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2833
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2834
+ // Occlusion system (WebGPU only)
2835
+ occlusionEnabled: false,
2836
+ occlusionObserver: null,
2837
+ occlusionCache: /* @__PURE__ */ new Map(),
2838
+ helperGroup: null,
2839
+ // Updates
2840
+ active: false,
2841
+ frames: 0,
2842
+ priority: 0,
2843
+ subscribe: (ref, priority, store) => {
2844
+ const internal = get().internal;
2845
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2846
+ internal.subscribers.push({ ref, priority, store });
2847
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2848
+ return () => {
2849
+ const internal2 = get().internal;
2850
+ if (internal2?.subscribers) {
2851
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2852
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2853
+ }
2854
+ };
2855
+ },
2856
+ // Renderer Storage (single source of truth)
2857
+ actualRenderer: null,
2858
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2859
+ scheduler: null
2860
+ }
2861
+ };
2862
+ return rootState;
2863
+ });
2864
+ const state = rootStore.getState();
2865
+ Object.defineProperty(state, "gl", {
2866
+ get() {
2867
+ const currentState = rootStore.getState();
2868
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2869
+ const stack = new Error().stack || "";
2870
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2871
+ if (!isInternalAccess) {
2872
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2873
+ notifyDepreciated({
2874
+ heading: "Accessing state.gl in WebGPU mode",
2875
+ 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
2876
+ });
2877
+ }
2878
+ }
2879
+ return currentState.internal.actualRenderer;
2880
+ },
2881
+ set(value) {
2882
+ rootStore.getState().internal.actualRenderer = value;
2883
+ },
2884
+ enumerable: true,
2885
+ configurable: true
2886
+ });
2887
+ Object.defineProperty(state, "renderer", {
2888
+ get() {
2889
+ return rootStore.getState().internal.actualRenderer;
2890
+ },
2891
+ set(value) {
2892
+ rootStore.getState().internal.actualRenderer = value;
2893
+ },
2894
+ enumerable: true,
2895
+ configurable: true
2896
+ });
2897
+ let oldScene = state.scene;
2898
+ rootStore.subscribe(() => {
2899
+ const currentState = rootStore.getState();
2900
+ const { scene, rootScene, set } = currentState;
2901
+ if (scene !== oldScene) {
2902
+ oldScene = scene;
2903
+ if (scene?.isScene && scene !== rootScene) {
2904
+ set({ rootScene: scene });
2905
+ }
2906
+ }
2907
+ });
2908
+ let oldSize = state.size;
2909
+ let oldDpr = state.viewport.dpr;
2910
+ let oldCamera = state.camera;
2911
+ rootStore.subscribe(() => {
2912
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2913
+ const actualRenderer = internal.actualRenderer;
2914
+ const canvasTarget = internal.canvasTarget;
2915
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2916
+ oldSize = size;
2917
+ oldDpr = viewport.dpr;
2918
+ updateCamera(camera, size);
2919
+ if (internal.isSecondary && canvasTarget) {
2920
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2921
+ canvasTarget.setSize(size.width, size.height, false);
2922
+ } else {
2923
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2924
+ actualRenderer.setSize(size.width, size.height, false);
2925
+ if (canvasTarget) {
2926
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2927
+ canvasTarget.setSize(size.width, size.height, false);
2928
+ }
2929
+ }
2930
+ }
2931
+ if (camera !== oldCamera) {
2932
+ oldCamera = camera;
2933
+ const { rootScene } = rootStore.getState();
2934
+ if (camera && rootScene && !camera.parent) {
2935
+ rootScene.add(camera);
2936
+ }
2937
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2938
+ const currentState = rootStore.getState();
2939
+ if (currentState.autoUpdateFrustum && camera) {
2940
+ updateFrustum(camera, currentState.frustum);
2941
+ }
2942
+ }
2943
+ });
2944
+ rootStore.subscribe((state2) => invalidate(state2));
2945
+ return rootStore;
2946
+ };
2947
+
2948
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2949
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2950
+ function getLoader(Proto) {
2951
+ if (isConstructor$1(Proto)) {
2952
+ let loader = memoizedLoaders.get(Proto);
2953
+ if (!loader) {
2954
+ loader = new Proto();
2955
+ memoizedLoaders.set(Proto, loader);
2956
+ }
2957
+ return loader;
2958
+ }
2959
+ return Proto;
2960
+ }
2961
+ function loadingFn(extensions, onProgress) {
2962
+ return function(Proto, input) {
2963
+ const loader = getLoader(Proto);
2964
+ if (extensions) extensions(loader);
2965
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2966
+ return loader.loadAsync(input, onProgress).then((data) => {
2967
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2968
+ return data;
2969
+ });
2970
+ }
2971
+ return new Promise(
2972
+ (res, reject) => loader.load(
2973
+ input,
2974
+ (data) => {
2975
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2976
+ res(data);
2977
+ },
2978
+ onProgress,
2979
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2980
+ )
2981
+ );
2982
+ };
2983
+ }
2984
+ function useLoader(loader, input, extensions, onProgress) {
2985
+ const keys = Array.isArray(input) ? input : [input];
2986
+ const fn = loadingFn(extensions, onProgress);
2987
+ const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
2988
+ return Array.isArray(input) ? results : results[0];
2989
+ }
2990
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2991
+ const keys = Array.isArray(input) ? input : [input];
2992
+ keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
2993
+ };
2994
+ useLoader.clear = function(loader, input) {
2995
+ const keys = Array.isArray(input) ? input : [input];
2996
+ keys.forEach((key) => clear([loader, key]));
2997
+ };
2998
+ useLoader.loader = getLoader;
2999
+
3000
+ function useFrame(callback, priorityOrOptions) {
3001
+ const store = React.useContext(context);
3002
+ const isInsideCanvas = store !== null;
3003
+ const scheduler = getScheduler();
3004
+ const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
3005
+ id: priorityOrOptions.id,
3006
+ phase: priorityOrOptions.phase,
3007
+ priority: priorityOrOptions.priority,
3008
+ fps: priorityOrOptions.fps,
3009
+ drop: priorityOrOptions.drop,
3010
+ enabled: priorityOrOptions.enabled,
3011
+ before: priorityOrOptions.before,
3012
+ after: priorityOrOptions.after
3013
+ }) : "";
3014
+ const options = React.useMemo(() => {
3015
+ return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
3016
+ }, [optionsKey]);
3017
+ const reactId = React.useId();
3018
+ const id = options.id ?? reactId;
3019
+ const callbackRef = useMutableCallback(callback);
3020
+ const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
3021
+ useIsomorphicLayoutEffect(() => {
3022
+ if (!callback) return;
3023
+ if (isInsideCanvas) {
3024
+ const state = store.getState();
3025
+ const rootId = state.internal.rootId;
3026
+ if (isLegacyPriority) {
3027
+ state.internal.priority++;
3028
+ let parentRoot = state.previousRoot;
3029
+ while (parentRoot) {
2400
3030
  const parentState = parentRoot.getState();
2401
3031
  if (parentState?.internal) parentState.internal.priority++;
2402
3032
  parentRoot = parentState?.previousRoot;
@@ -2546,6 +3176,9 @@ function useTexture(input, optionsOrOnLoad) {
2546
3176
  const textureCache = useThree((state) => state.textures);
2547
3177
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2548
3178
  const { onLoad, cache = false } = options;
3179
+ const onLoadRef = useRef(onLoad);
3180
+ onLoadRef.current = onLoad;
3181
+ const onLoadCalledForRef = useRef(null);
2549
3182
  const urls = useMemo(() => getUrls(input), [input]);
2550
3183
  const cachedResult = useMemo(() => {
2551
3184
  if (!cache) return null;
@@ -2556,9 +3189,13 @@ function useTexture(input, optionsOrOnLoad) {
2556
3189
  TextureLoader,
2557
3190
  IsObject(input) ? Object.values(input) : input
2558
3191
  );
3192
+ const inputKey = urls.join("\0");
2559
3193
  useLayoutEffect(() => {
2560
- if (!cachedResult) onLoad?.(loadedTextures);
2561
- }, [onLoad, cachedResult, loadedTextures]);
3194
+ if (cachedResult) return;
3195
+ if (onLoadCalledForRef.current === inputKey) return;
3196
+ onLoadCalledForRef.current = inputKey;
3197
+ onLoadRef.current?.(loadedTextures);
3198
+ }, [cachedResult, loadedTextures, inputKey]);
2562
3199
  useEffect(() => {
2563
3200
  if (cachedResult) return;
2564
3201
  if ("initTexture" in renderer) {
@@ -2725,14 +3362,31 @@ function useTextures() {
2725
3362
  }, [store]);
2726
3363
  }
2727
3364
 
2728
- function useRenderTarget(width, height, options) {
3365
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2729
3366
  const isLegacy = useThree((s) => s.isLegacy);
2730
3367
  const size = useThree((s) => s.size);
3368
+ let width;
3369
+ let height;
3370
+ let opts;
3371
+ if (typeof widthOrOptions === "object") {
3372
+ opts = widthOrOptions;
3373
+ } else if (typeof widthOrOptions === "number") {
3374
+ width = widthOrOptions;
3375
+ if (typeof heightOrOptions === "object") {
3376
+ height = widthOrOptions;
3377
+ opts = heightOrOptions;
3378
+ } else if (typeof heightOrOptions === "number") {
3379
+ height = heightOrOptions;
3380
+ opts = options;
3381
+ } else {
3382
+ height = widthOrOptions;
3383
+ }
3384
+ }
2731
3385
  return useMemo(() => {
2732
3386
  const w = width ?? size.width;
2733
3387
  const h = height ?? size.height;
2734
- return new RenderTarget(w, h, options);
2735
- }, [width, height, size.width, size.height, options, isLegacy]);
3388
+ return new RenderTarget(w, h, opts);
3389
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2736
3390
  }
2737
3391
 
2738
3392
  function useStore() {
@@ -2782,7 +3436,7 @@ function addTail(callback) {
2782
3436
  function invalidate(state, frames = 1, stackFrames = false) {
2783
3437
  getScheduler().invalidate(frames, stackFrames);
2784
3438
  }
2785
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3439
+ function advance(timestamp) {
2786
3440
  getScheduler().step(timestamp);
2787
3441
  }
2788
3442
 
@@ -14236,6 +14890,7 @@ function swapInstances() {
14236
14890
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14237
14891
  instance.object.__r3f = instance;
14238
14892
  setFiberRef(fiber, instance.object);
14893
+ delete instance.appliedOnce;
14239
14894
  applyProps(instance.object, instance.props);
14240
14895
  if (instance.props.attach) {
14241
14896
  attach(parent, instance);
@@ -14309,8 +14964,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14309
14964
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14310
14965
  if (isTailSibling) swapInstances();
14311
14966
  },
14312
- finalizeInitialChildren: () => false,
14313
- commitMount() {
14967
+ finalizeInitialChildren: (instance) => {
14968
+ for (const prop in instance.props) {
14969
+ if (isFromRef(instance.props[prop])) return true;
14970
+ }
14971
+ return false;
14972
+ },
14973
+ commitMount(instance) {
14974
+ const resolved = {};
14975
+ for (const prop in instance.props) {
14976
+ const value = instance.props[prop];
14977
+ if (isFromRef(value)) {
14978
+ const ref = value[FROM_REF];
14979
+ if (ref.current != null) resolved[prop] = ref.current;
14980
+ }
14981
+ }
14982
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14314
14983
  },
14315
14984
  getPublicInstance: (instance) => instance?.object,
14316
14985
  prepareForCommit: () => null,
@@ -14531,6 +15200,9 @@ function createRoot(canvas) {
14531
15200
  let resolve;
14532
15201
  pending = new Promise((_resolve) => resolve = _resolve);
14533
15202
  const {
15203
+ id: canvasId,
15204
+ primaryCanvas,
15205
+ scheduler: schedulerConfig,
14534
15206
  gl: glConfig,
14535
15207
  renderer: rendererConfig,
14536
15208
  size: propsSize,
@@ -14538,10 +15210,6 @@ function createRoot(canvas) {
14538
15210
  events,
14539
15211
  onCreated: onCreatedCallback,
14540
15212
  shadows = false,
14541
- linear = false,
14542
- flat = false,
14543
- textureColorSpace = SRGBColorSpace,
14544
- legacy = false,
14545
15213
  orthographic = false,
14546
15214
  frameloop = "always",
14547
15215
  dpr = [1, 2],
@@ -14553,11 +15221,14 @@ function createRoot(canvas) {
14553
15221
  onDropMissed,
14554
15222
  autoUpdateFrustum = true,
14555
15223
  occlusion = false,
14556
- _sizeProps
15224
+ _sizeProps,
15225
+ forceEven
14557
15226
  } = props;
15227
+ const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
14558
15228
  const state = store.getState();
14559
15229
  const defaultGPUProps = {
14560
- canvas
15230
+ canvas,
15231
+ antialias: true
14561
15232
  };
14562
15233
  if (glConfig && !R3F_BUILD_LEGACY) {
14563
15234
  throw new Error(
@@ -14568,15 +15239,52 @@ function createRoot(canvas) {
14568
15239
  throw new Error("Cannot use both gl and renderer props at the same time");
14569
15240
  }
14570
15241
  let renderer = state.internal.actualRenderer;
14571
- if (!state.internal.actualRenderer) {
15242
+ if (primaryCanvas && !state.internal.actualRenderer) {
15243
+ const primary = await waitForPrimary(primaryCanvas);
15244
+ renderer = primary.renderer;
15245
+ state.internal.actualRenderer = renderer;
15246
+ const canvasTarget = new CanvasTarget(canvas);
15247
+ primary.store.setState((prev) => ({
15248
+ internal: { ...prev.internal, isMultiCanvas: true }
15249
+ }));
15250
+ state.set((prev) => ({
15251
+ webGPUSupported: primary.store.getState().webGPUSupported,
15252
+ renderer,
15253
+ primaryStore: primary.store,
15254
+ internal: {
15255
+ ...prev.internal,
15256
+ canvasTarget,
15257
+ isMultiCanvas: true,
15258
+ isSecondary: true,
15259
+ targetId: primaryCanvas
15260
+ }
15261
+ }));
15262
+ } else if (!state.internal.actualRenderer) {
14572
15263
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14573
15264
  if (!renderer.hasInitialized?.()) {
15265
+ const size2 = computeInitialSize(canvas, propsSize);
15266
+ if (size2.width > 0 && size2.height > 0) {
15267
+ const pixelRatio = calculateDpr(dpr);
15268
+ canvas.width = size2.width * pixelRatio;
15269
+ canvas.height = size2.height * pixelRatio;
15270
+ }
14574
15271
  await renderer.init();
14575
15272
  }
14576
15273
  const backend = renderer.backend;
14577
15274
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14578
15275
  state.internal.actualRenderer = renderer;
14579
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15276
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15277
+ if (canvasId && !state.internal.isSecondary) {
15278
+ const canvasTarget = new CanvasTarget(canvas);
15279
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15280
+ state.set((prev) => ({
15281
+ internal: {
15282
+ ...prev.internal,
15283
+ canvasTarget,
15284
+ unregisterPrimary
15285
+ }
15286
+ }));
15287
+ }
14580
15288
  }
14581
15289
  let raycaster = state.raycaster;
14582
15290
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
@@ -14585,6 +15293,7 @@ function createRoot(canvas) {
14585
15293
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14586
15294
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14587
15295
  }
15296
+ let tempCamera = state.camera;
14588
15297
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14589
15298
  lastCamera = cameraOptions;
14590
15299
  const isCamera = cameraOptions?.isCamera;
@@ -14604,6 +15313,7 @@ function createRoot(canvas) {
14604
15313
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14605
15314
  }
14606
15315
  state.set({ camera });
15316
+ tempCamera = camera;
14607
15317
  raycaster.camera = camera;
14608
15318
  }
14609
15319
  if (!state.scene) {
@@ -14621,7 +15331,7 @@ function createRoot(canvas) {
14621
15331
  rootScene: scene,
14622
15332
  internal: { ...prev.internal, container: scene }
14623
15333
  }));
14624
- const camera = state.camera;
15334
+ const camera = tempCamera;
14625
15335
  if (camera && !camera.parent) scene.add(camera);
14626
15336
  }
14627
15337
  if (events && !state.events.handlers) {
@@ -14638,6 +15348,9 @@ function createRoot(canvas) {
14638
15348
  if (_sizeProps !== void 0) {
14639
15349
  state.set({ _sizeProps });
14640
15350
  }
15351
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15352
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15353
+ }
14641
15354
  const size = computeInitialSize(canvas, propsSize);
14642
15355
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14643
15356
  const wasImperative = state._sizeImperative;
@@ -14664,10 +15377,10 @@ function createRoot(canvas) {
14664
15377
  lastConfiguredProps.performance = performance;
14665
15378
  }
14666
15379
  if (!state.xr) {
14667
- const handleXRFrame = (timestamp, frame) => {
15380
+ const handleXRFrame = (timestamp, _frame) => {
14668
15381
  const state2 = store.getState();
14669
15382
  if (state2.frameloop === "never") return;
14670
- advance(timestamp, true);
15383
+ advance(timestamp);
14671
15384
  };
14672
15385
  const actualRenderer = state.internal.actualRenderer;
14673
15386
  const handleSessionChange = () => {
@@ -14679,16 +15392,16 @@ function createRoot(canvas) {
14679
15392
  };
14680
15393
  const xr = {
14681
15394
  connect() {
14682
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14683
- const actualRenderer2 = renderer2 || gl;
14684
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14685
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15395
+ const { gl, renderer: renderer2 } = store.getState();
15396
+ const xrManager = (renderer2 || gl).xr;
15397
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15398
+ xrManager.addEventListener("sessionend", handleSessionChange);
14686
15399
  },
14687
15400
  disconnect() {
14688
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14689
- const actualRenderer2 = renderer2 || gl;
14690
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14691
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15401
+ const { gl, renderer: renderer2 } = store.getState();
15402
+ const xrManager = (renderer2 || gl).xr;
15403
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15404
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14692
15405
  }
14693
15406
  };
14694
15407
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14700,15 +15413,22 @@ function createRoot(canvas) {
14700
15413
  const oldType = renderer.shadowMap.type;
14701
15414
  renderer.shadowMap.enabled = !!shadows;
14702
15415
  if (is.boo(shadows)) {
14703
- renderer.shadowMap.type = PCFSoftShadowMap;
15416
+ renderer.shadowMap.type = PCFShadowMap;
14704
15417
  } else if (is.str(shadows)) {
15418
+ if (shadows === "soft") {
15419
+ notifyDepreciated({
15420
+ heading: 'shadows="soft" is deprecated',
15421
+ body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
15422
+ link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
15423
+ });
15424
+ }
14705
15425
  const types = {
14706
15426
  basic: BasicShadowMap,
14707
15427
  percentage: PCFShadowMap,
14708
- soft: PCFSoftShadowMap,
15428
+ soft: PCFShadowMap,
14709
15429
  variance: VSMShadowMap
14710
15430
  };
14711
- renderer.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
15431
+ renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
14712
15432
  } else if (is.obj(shadows)) {
14713
15433
  Object.assign(renderer.shadowMap, shadows);
14714
15434
  }
@@ -14716,27 +15436,69 @@ function createRoot(canvas) {
14716
15436
  renderer.shadowMap.needsUpdate = true;
14717
15437
  }
14718
15438
  }
15439
+ if (!configured) {
15440
+ renderer.outputColorSpace = SRGBColorSpace;
15441
+ renderer.toneMapping = ACESFilmicToneMapping;
15442
+ }
14719
15443
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14720
15444
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14721
15445
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14722
15446
  }
15447
+ const r3fProps = ["textureColorSpace"];
15448
+ const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15449
+ const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
14723
15450
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14724
- applyProps(renderer, glConfig);
15451
+ const glProps = {};
15452
+ for (const key in glConfig) {
15453
+ if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
15454
+ }
15455
+ applyProps(renderer, glProps);
14725
15456
  }
14726
15457
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14727
15458
  const currentRenderer = state.renderer;
14728
15459
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
14729
- applyProps(currentRenderer, rendererConfig);
15460
+ const rendererProps = {};
15461
+ for (const key in rendererConfig) {
15462
+ if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
15463
+ }
15464
+ applyProps(currentRenderer, rendererProps);
14730
15465
  }
14731
15466
  }
14732
15467
  const scheduler = getScheduler();
14733
15468
  const rootId = state.internal.rootId;
14734
15469
  if (!rootId) {
14735
- const newRootId = scheduler.generateRootId();
15470
+ const newRootId = canvasId || scheduler.generateRootId();
14736
15471
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14737
15472
  getState: () => store.getState(),
14738
15473
  onError: (err) => store.getState().setError(err)
14739
15474
  });
15475
+ const unregisterCanvasTarget = scheduler.register(
15476
+ () => {
15477
+ const state2 = store.getState();
15478
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15479
+ const renderer2 = state2.internal.actualRenderer;
15480
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15481
+ }
15482
+ },
15483
+ {
15484
+ id: `${newRootId}_canvasTarget`,
15485
+ rootId: newRootId,
15486
+ phase: "start",
15487
+ system: true
15488
+ }
15489
+ );
15490
+ const unregisterEventsFlush = scheduler.register(
15491
+ () => {
15492
+ const state2 = store.getState();
15493
+ state2.events.flush?.();
15494
+ },
15495
+ {
15496
+ id: `${newRootId}_events`,
15497
+ rootId: newRootId,
15498
+ phase: "input",
15499
+ system: true
15500
+ }
15501
+ );
14740
15502
  const unregisterFrustum = scheduler.register(
14741
15503
  () => {
14742
15504
  const state2 = store.getState();
@@ -14771,18 +15533,22 @@ function createRoot(canvas) {
14771
15533
  const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
14772
15534
  if (userHandlesRender || state2.internal.priority) return;
14773
15535
  try {
14774
- if (state2.postProcessing?.render) state2.postProcessing.render();
15536
+ if (state2.renderPipeline?.render) state2.renderPipeline.render();
14775
15537
  else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
14776
15538
  } catch (error) {
14777
15539
  state2.setError(error instanceof Error ? error : new Error(String(error)));
14778
15540
  }
14779
15541
  },
14780
15542
  {
14781
- id: `${newRootId}_render`,
15543
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15544
+ id: canvasId || `${newRootId}_render`,
14782
15545
  rootId: newRootId,
14783
15546
  phase: "render",
14784
- system: true
15547
+ system: true,
14785
15548
  // Internal flag: this is a system job, not user-controlled
15549
+ // Apply scheduler config for render ordering and rate limiting
15550
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15551
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14786
15552
  }
14787
15553
  );
14788
15554
  state.set((state2) => ({
@@ -14791,6 +15557,8 @@ function createRoot(canvas) {
14791
15557
  rootId: newRootId,
14792
15558
  unregisterRoot: () => {
14793
15559
  unregisterRoot();
15560
+ unregisterCanvasTarget();
15561
+ unregisterEventsFlush();
14794
15562
  unregisterFrustum();
14795
15563
  unregisterVisibility();
14796
15564
  unregisterRender();
@@ -14849,15 +15617,24 @@ function unmountComponentAtNode(canvas, callback) {
14849
15617
  const renderer = state.internal.actualRenderer;
14850
15618
  const unregisterRoot = state.internal.unregisterRoot;
14851
15619
  if (unregisterRoot) unregisterRoot();
15620
+ const unregisterPrimary = state.internal.unregisterPrimary;
15621
+ if (unregisterPrimary) unregisterPrimary();
15622
+ const canvasTarget = state.internal.canvasTarget;
15623
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14852
15624
  state.events.disconnect?.();
14853
15625
  cleanupHelperGroup(root.store);
14854
- renderer?.renderLists?.dispose?.();
14855
- renderer?.forceContextLoss?.();
14856
- if (renderer?.xr) state.xr.disconnect();
15626
+ if (state.isLegacy && renderer) {
15627
+ ;
15628
+ renderer.renderLists?.dispose?.();
15629
+ renderer.forceContextLoss?.();
15630
+ }
15631
+ if (!state.internal.isSecondary) {
15632
+ if (renderer?.xr) state.xr.disconnect();
15633
+ }
14857
15634
  dispose(state.scene);
14858
15635
  _roots.delete(canvas);
14859
15636
  if (callback) callback(canvas);
14860
- } catch (e) {
15637
+ } catch {
14861
15638
  }
14862
15639
  }, 500);
14863
15640
  }
@@ -14865,36 +15642,34 @@ function unmountComponentAtNode(canvas, callback) {
14865
15642
  }
14866
15643
  }
14867
15644
  function createPortal(children, container, state) {
14868
- return /* @__PURE__ */ jsx(PortalWrapper, { children, container, state });
15645
+ return /* @__PURE__ */ jsx(Portal, { children, container, state });
14869
15646
  }
14870
- function PortalWrapper({ children, container, state }) {
15647
+ function Portal({ children, container, state }) {
14871
15648
  const isRef = useCallback((obj) => obj && "current" in obj, []);
14872
- const [resolvedContainer, setResolvedContainer] = useState(() => {
15649
+ const [resolvedContainer, _setResolvedContainer] = useState(() => {
14873
15650
  if (isRef(container)) return container.current ?? null;
14874
15651
  return container;
14875
15652
  });
15653
+ const setResolvedContainer = useCallback(
15654
+ (newContainer) => {
15655
+ if (!newContainer || newContainer === resolvedContainer) return;
15656
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15657
+ },
15658
+ [resolvedContainer, _setResolvedContainer, isRef]
15659
+ );
14876
15660
  useMemo(() => {
14877
- if (isRef(container)) {
14878
- const current = container.current;
14879
- if (!current) {
14880
- queueMicrotask(() => {
14881
- const updated = container.current;
14882
- if (updated && updated !== resolvedContainer) {
14883
- setResolvedContainer(updated);
14884
- }
14885
- });
14886
- } else if (current !== resolvedContainer) {
14887
- setResolvedContainer(current);
14888
- }
14889
- } else if (container !== resolvedContainer) {
14890
- setResolvedContainer(container);
15661
+ if (isRef(container) && !container.current) {
15662
+ return queueMicrotask(() => {
15663
+ setResolvedContainer(container.current);
15664
+ });
14891
15665
  }
14892
- }, [container, resolvedContainer, isRef]);
15666
+ setResolvedContainer(container);
15667
+ }, [container, isRef, setResolvedContainer]);
14893
15668
  if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
14894
15669
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14895
- return /* @__PURE__ */ jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15670
+ return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14896
15671
  }
14897
- function Portal({ state = {}, children, container }) {
15672
+ function PortalInner({ state = {}, children, container }) {
14898
15673
  const { events, size, injectScene = true, ...rest } = state;
14899
15674
  const previousRoot = useStore();
14900
15675
  const [raycaster] = useState(() => new Raycaster());
@@ -14915,11 +15690,12 @@ function Portal({ state = {}, children, container }) {
14915
15690
  };
14916
15691
  }, [portalScene, container, injectScene]);
14917
15692
  const inject = useMutableCallback((rootState, injectState) => {
15693
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14918
15694
  let viewport = void 0;
14919
- if (injectState.camera && size) {
15695
+ if (injectState.camera && (size || injectState.size)) {
14920
15696
  const camera = injectState.camera;
14921
- viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), size);
14922
- if (camera !== rootState.camera) updateCamera(camera, size);
15697
+ viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
15698
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14923
15699
  }
14924
15700
  return {
14925
15701
  // The intersect consists of the previous root state
@@ -14936,7 +15712,7 @@ function Portal({ state = {}, children, container }) {
14936
15712
  previousRoot,
14937
15713
  // Events, size and viewport can be overridden by the inject layer
14938
15714
  events: { ...rootState.events, ...injectState.events, ...events },
14939
- size: { ...rootState.size, ...size },
15715
+ size: resolvedSize,
14940
15716
  viewport: { ...rootState.viewport, ...viewport },
14941
15717
  // Layers are allowed to override events
14942
15718
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14948,9 +15724,13 @@ function Portal({ state = {}, children, container }) {
14948
15724
  const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
14949
15725
  const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
14950
15726
  onMutate(previousRoot.getState());
14951
- previousRoot.subscribe(onMutate);
14952
15727
  return store;
14953
15728
  }, [previousRoot, container]);
15729
+ useIsomorphicLayoutEffect(() => {
15730
+ const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
15731
+ const unsubscribe = previousRoot.subscribe(onMutate);
15732
+ return unsubscribe;
15733
+ }, [previousRoot, usePortalStore]);
14954
15734
  return (
14955
15735
  // @ts-ignore, reconciler types are not maintained
14956
15736
  /* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
@@ -14970,15 +15750,13 @@ function CanvasImpl({
14970
15750
  fallback,
14971
15751
  resize,
14972
15752
  style,
15753
+ id,
14973
15754
  gl,
14974
- renderer,
15755
+ renderer: rendererProp,
14975
15756
  events = createPointerEvents,
14976
15757
  eventSource,
14977
15758
  eventPrefix,
14978
15759
  shadows,
14979
- linear,
14980
- flat,
14981
- legacy,
14982
15760
  orthographic,
14983
15761
  frameloop,
14984
15762
  dpr,
@@ -14993,10 +15771,53 @@ function CanvasImpl({
14993
15771
  hmr,
14994
15772
  width,
14995
15773
  height,
15774
+ background,
15775
+ forceEven,
14996
15776
  ...props
14997
15777
  }) {
15778
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
15779
+ let primaryCanvas;
15780
+ let scheduler;
15781
+ let renderer;
15782
+ if (isRendererConfig) {
15783
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
15784
+ primaryCanvas = pc;
15785
+ scheduler = sc;
15786
+ renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
15787
+ } else {
15788
+ renderer = rendererProp;
15789
+ }
14998
15790
  React.useMemo(() => extend(THREE), []);
14999
15791
  const Bridge = useBridge();
15792
+ const backgroundProps = React.useMemo(() => {
15793
+ if (!background) return null;
15794
+ if (typeof background === "object" && !background.isColor) {
15795
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15796
+ return {
15797
+ ...rest,
15798
+ preset,
15799
+ files: envMap || files,
15800
+ backgroundFiles: backgroundMap,
15801
+ background: true
15802
+ };
15803
+ }
15804
+ if (typeof background === "number") {
15805
+ return { color: background, background: true };
15806
+ }
15807
+ if (typeof background === "string") {
15808
+ if (background in presetsObj) {
15809
+ return { preset: background, background: true };
15810
+ }
15811
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15812
+ return { files: background, background: true };
15813
+ }
15814
+ return { color: background, background: true };
15815
+ }
15816
+ if (background.isColor) {
15817
+ return { color: background, background: true };
15818
+ }
15819
+ return null;
15820
+ }, [background]);
15000
15821
  const hasInitialSizeRef = React.useRef(false);
15001
15822
  const measureConfig = React.useMemo(() => {
15002
15823
  if (!hasInitialSizeRef.current) {
@@ -15013,15 +15834,20 @@ function CanvasImpl({
15013
15834
  };
15014
15835
  }, [resize, hasInitialSizeRef.current]);
15015
15836
  const [containerRef, containerRect] = useMeasure(measureConfig);
15016
- const effectiveSize = React.useMemo(
15017
- () => ({
15018
- width: width ?? containerRect.width,
15019
- height: height ?? containerRect.height,
15837
+ const effectiveSize = React.useMemo(() => {
15838
+ let w = width ?? containerRect.width;
15839
+ let h = height ?? containerRect.height;
15840
+ if (forceEven) {
15841
+ w = Math.ceil(w / 2) * 2;
15842
+ h = Math.ceil(h / 2) * 2;
15843
+ }
15844
+ return {
15845
+ width: w,
15846
+ height: h,
15020
15847
  top: containerRect.top,
15021
15848
  left: containerRect.left
15022
- }),
15023
- [width, height, containerRect]
15024
- );
15849
+ };
15850
+ }, [width, height, containerRect, forceEven]);
15025
15851
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15026
15852
  hasInitialSizeRef.current = true;
15027
15853
  }
@@ -15061,14 +15887,14 @@ function CanvasImpl({
15061
15887
  async function run() {
15062
15888
  if (!effectActiveRef.current || !root.current) return;
15063
15889
  await root.current.configure({
15890
+ id,
15891
+ primaryCanvas,
15892
+ scheduler,
15064
15893
  gl,
15065
15894
  renderer,
15066
15895
  scene,
15067
15896
  events,
15068
15897
  shadows,
15069
- linear,
15070
- flat,
15071
- legacy,
15072
15898
  orthographic,
15073
15899
  frameloop,
15074
15900
  dpr,
@@ -15078,6 +15904,7 @@ function CanvasImpl({
15078
15904
  size: effectiveSize,
15079
15905
  // Store size props for reset functionality
15080
15906
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15907
+ forceEven,
15081
15908
  // Pass mutable reference to onPointerMissed so it's free to update
15082
15909
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15083
15910
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15101,7 +15928,10 @@ function CanvasImpl({
15101
15928
  });
15102
15929
  if (!effectActiveRef.current || !root.current) return;
15103
15930
  root.current.render(
15104
- /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsx(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15931
+ /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
15932
+ backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
15933
+ children ?? null
15934
+ ] }) }) })
15105
15935
  );
15106
15936
  }
15107
15937
  run();
@@ -15128,20 +15958,22 @@ function CanvasImpl({
15128
15958
  const canvas = canvasRef.current;
15129
15959
  if (!canvas) return;
15130
15960
  const handleHMR = () => {
15131
- const rootEntry = _roots.get(canvas);
15132
- if (rootEntry?.store) {
15133
- rootEntry.store.setState((state) => ({
15134
- nodes: {},
15135
- uniforms: {},
15136
- _hmrVersion: state._hmrVersion + 1
15137
- }));
15138
- }
15961
+ queueMicrotask(() => {
15962
+ const rootEntry = _roots.get(canvas);
15963
+ if (rootEntry?.store) {
15964
+ console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
15965
+ rootEntry.store.setState((state) => ({
15966
+ nodes: {},
15967
+ uniforms: {},
15968
+ _hmrVersion: state._hmrVersion + 1
15969
+ }));
15970
+ }
15971
+ });
15139
15972
  };
15140
15973
  if (typeof import.meta !== "undefined" && import.meta.hot) {
15141
15974
  const hot = import.meta.hot;
15142
15975
  hot.on("vite:afterUpdate", handleHMR);
15143
- return () => hot.dispose?.(() => {
15144
- });
15976
+ return () => hot.off?.("vite:afterUpdate", handleHMR);
15145
15977
  }
15146
15978
  if (typeof module !== "undefined" && module.hot) {
15147
15979
  const hot = module.hot;
@@ -15164,7 +15996,16 @@ function CanvasImpl({
15164
15996
  ...style
15165
15997
  },
15166
15998
  ...props,
15167
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15999
+ children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
16000
+ "canvas",
16001
+ {
16002
+ ref: canvasRef,
16003
+ id,
16004
+ className: "r3f-canvas",
16005
+ style: { display: "block", width: "100%", height: "100%" },
16006
+ children: fallback
16007
+ }
16008
+ ) })
15168
16009
  }
15169
16010
  );
15170
16011
  }
@@ -15237,6 +16078,34 @@ let ScopedStore = _ScopedStore;
15237
16078
  function createScopedStore(data) {
15238
16079
  return new ScopedStore(data);
15239
16080
  }
16081
+ function createLazyCreatorState(state) {
16082
+ let _uniforms = null;
16083
+ let _nodes = null;
16084
+ let _buffers = null;
16085
+ let _gpuStorage = null;
16086
+ return Object.create(state, {
16087
+ uniforms: {
16088
+ get() {
16089
+ return _uniforms ?? (_uniforms = createScopedStore(state.uniforms));
16090
+ }
16091
+ },
16092
+ nodes: {
16093
+ get() {
16094
+ return _nodes ?? (_nodes = createScopedStore(state.nodes));
16095
+ }
16096
+ },
16097
+ buffers: {
16098
+ get() {
16099
+ return _buffers ?? (_buffers = createScopedStore(state.buffers));
16100
+ }
16101
+ },
16102
+ gpuStorage: {
16103
+ get() {
16104
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(state.gpuStorage));
16105
+ }
16106
+ }
16107
+ });
16108
+ }
15240
16109
 
15241
16110
  function addTexture(set, key, value) {
15242
16111
  set((state) => {
@@ -15277,6 +16146,27 @@ function createTextureOperations(set) {
15277
16146
  removeMultiple: (keys) => removeTextures(set, keys)
15278
16147
  };
15279
16148
  }
16149
+ function extractTSLValue(value) {
16150
+ if (value === null || value === void 0) return value;
16151
+ if (typeof value !== "object") return value;
16152
+ const node = value;
16153
+ if (!node.isNode) return value;
16154
+ if (node.isConstNode) {
16155
+ return node.value;
16156
+ }
16157
+ if ("value" in node) {
16158
+ let extractedValue = node.value;
16159
+ if (typeof node.traverse === "function") {
16160
+ node.traverse((n) => {
16161
+ if (n.isConstNode) {
16162
+ extractedValue = n.value;
16163
+ }
16164
+ });
16165
+ }
16166
+ return extractedValue;
16167
+ }
16168
+ return value;
16169
+ }
15280
16170
  function vectorize(inObject) {
15281
16171
  if (inObject === null || inObject === void 0) return inObject;
15282
16172
  if (typeof inObject === "string") {
@@ -15289,9 +16179,16 @@ function vectorize(inObject) {
15289
16179
  }
15290
16180
  if (typeof inObject !== "object") return inObject;
15291
16181
  const obj = inObject;
16182
+ if (obj.isNode) {
16183
+ return extractTSLValue(inObject);
16184
+ }
15292
16185
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15293
16186
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15294
16187
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
16188
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
16189
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
16190
+ return new Color(obj.r * scale, obj.g * scale, obj.b * scale);
16191
+ }
15295
16192
  if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
15296
16193
  if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
15297
16194
  return new Vector4(obj.x, obj.y, obj.z, obj.w);
@@ -15354,17 +16251,14 @@ function useUniforms(creatorOrScope, scope) {
15354
16251
  const rebuildUniforms = useCallback(
15355
16252
  (targetScope) => {
15356
16253
  store.setState((state) => {
15357
- let newUniforms = state.uniforms;
16254
+ let newUniforms = {};
15358
16255
  if (targetScope && targetScope !== "root") {
15359
16256
  const { [targetScope]: _, ...rest } = state.uniforms;
15360
16257
  newUniforms = rest;
15361
16258
  } else if (targetScope === "root") {
15362
- newUniforms = {};
15363
16259
  for (const [key, value] of Object.entries(state.uniforms)) {
15364
16260
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15365
16261
  }
15366
- } else {
15367
- newUniforms = {};
15368
16262
  }
15369
16263
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15370
16264
  });
@@ -15372,20 +16266,26 @@ function useUniforms(creatorOrScope, scope) {
15372
16266
  [store]
15373
16267
  );
15374
16268
  const inputForMemoization = useMemo(() => {
16269
+ let raw = creatorOrScope;
15375
16270
  if (is.fun(creatorOrScope)) {
15376
- const state = store.getState();
15377
- const wrappedState = {
15378
- ...state,
15379
- uniforms: createScopedStore(state.uniforms),
15380
- nodes: createScopedStore(state.nodes)
15381
- };
15382
- return creatorOrScope(wrappedState);
16271
+ const wrappedState = createLazyCreatorState(store.getState());
16272
+ raw = creatorOrScope(wrappedState);
16273
+ }
16274
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
16275
+ const normalized = {};
16276
+ for (const [key, value] of Object.entries(raw)) {
16277
+ normalized[key] = vectorize(value);
16278
+ }
16279
+ return normalized;
15383
16280
  }
15384
- return creatorOrScope;
16281
+ return raw;
15385
16282
  }, [creatorOrScope, store]);
15386
16283
  const memoizedInput = useCompareMemoize(inputForMemoization);
15387
16284
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15388
16285
  const storeUniforms = useThree((s) => s.uniforms);
16286
+ const hmrVersion = useThree((s) => s._hmrVersion);
16287
+ const readerDep = isReader ? storeUniforms : null;
16288
+ const creatorDep = isReader ? null : hmrVersion;
15389
16289
  const uniforms = useMemo(() => {
15390
16290
  if (memoizedInput === void 0) {
15391
16291
  return storeUniforms;
@@ -15440,28 +16340,19 @@ function useUniforms(creatorOrScope, scope) {
15440
16340
  }
15441
16341
  }
15442
16342
  return result;
15443
- }, [
15444
- store,
15445
- memoizedInput,
15446
- scope,
15447
- // Only include storeUniforms in deps for reader modes to enable reactivity
15448
- isReader ? storeUniforms : null
15449
- ]);
16343
+ }, [store, memoizedInput, scope, readerDep, creatorDep]);
15450
16344
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15451
16345
  }
15452
16346
  function rebuildAllUniforms(store, scope) {
15453
16347
  store.setState((state) => {
15454
- let newUniforms = state.uniforms;
16348
+ let newUniforms = {};
15455
16349
  if (scope && scope !== "root") {
15456
16350
  const { [scope]: _, ...rest } = state.uniforms;
15457
16351
  newUniforms = rest;
15458
16352
  } else if (scope === "root") {
15459
- newUniforms = {};
15460
16353
  for (const [key, value] of Object.entries(state.uniforms)) {
15461
16354
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15462
16355
  }
15463
- } else {
15464
- newUniforms = {};
15465
16356
  }
15466
16357
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15467
16358
  });
@@ -15529,15 +16420,17 @@ function isSameThreeType(a, b) {
15529
16420
  }
15530
16421
 
15531
16422
  const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
16423
+ const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15532
16424
  function useUniform(name, value) {
15533
16425
  const store = useStore();
16426
+ const hmrVersion = useThree((s) => s._hmrVersion);
15534
16427
  return useMemo(() => {
15535
16428
  const state = store.getState();
15536
16429
  const set = store.setState;
15537
16430
  const existing = state.uniforms[name];
15538
16431
  if (existing && isUniformNode(existing)) {
15539
- if (value !== void 0) {
15540
- existing.value = value;
16432
+ if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
16433
+ existing.value = typeof value === "string" ? new Color(value) : value;
15541
16434
  }
15542
16435
  return existing;
15543
16436
  }
@@ -15546,7 +16439,24 @@ function useUniform(name, value) {
15546
16439
  `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15547
16440
  );
15548
16441
  }
15549
- const node = uniform(value);
16442
+ if (isUniformNode(value)) {
16443
+ const node2 = value;
16444
+ if (typeof node2.setName === "function") {
16445
+ node2.setName(name);
16446
+ }
16447
+ set((s) => ({
16448
+ uniforms: { ...s.uniforms, [name]: node2 }
16449
+ }));
16450
+ return node2;
16451
+ }
16452
+ let node;
16453
+ if (isTSLNode$1(value)) {
16454
+ node = uniform(value);
16455
+ } else if (typeof value === "string") {
16456
+ node = uniform(new Color(value));
16457
+ } else {
16458
+ node = uniform(value);
16459
+ }
15550
16460
  if (typeof node.setName === "function") {
15551
16461
  node.setName(name);
15552
16462
  }
@@ -15557,7 +16467,7 @@ function useUniform(name, value) {
15557
16467
  }
15558
16468
  }));
15559
16469
  return node;
15560
- }, [store, name]);
16470
+ }, [store, name, hmrVersion]);
15561
16471
  }
15562
16472
 
15563
16473
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
@@ -15621,6 +16531,9 @@ function useNodes(creatorOrScope, scope) {
15621
16531
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15622
16532
  const storeNodes = useThree((s) => s.nodes);
15623
16533
  const hmrVersion = useThree((s) => s._hmrVersion);
16534
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16535
+ const readerDep = isReader ? storeNodes : null;
16536
+ const creatorDep = isReader ? null : hmrVersion;
15624
16537
  const nodes = useMemo(() => {
15625
16538
  if (creatorOrScope === void 0) {
15626
16539
  return storeNodes;
@@ -15633,11 +16546,7 @@ function useNodes(creatorOrScope, scope) {
15633
16546
  const state = store.getState();
15634
16547
  const set = store.setState;
15635
16548
  const creator = creatorOrScope;
15636
- const wrappedState = {
15637
- ...state,
15638
- uniforms: createScopedStore(state.uniforms),
15639
- nodes: createScopedStore(state.nodes)
15640
- };
16549
+ const wrappedState = createLazyCreatorState(state);
15641
16550
  const created = creator(wrappedState);
15642
16551
  const result = {};
15643
16552
  let hasNewNodes = false;
@@ -15647,7 +16556,7 @@ function useNodes(creatorOrScope, scope) {
15647
16556
  if (currentScope[name]) {
15648
16557
  result[name] = currentScope[name];
15649
16558
  } else {
15650
- if (typeof node.label === "function") node.setName(`${scope}.${name}`);
16559
+ node.setName?.(`${scope}.${name}`);
15651
16560
  result[name] = node;
15652
16561
  hasNewNodes = true;
15653
16562
  }
@@ -15667,7 +16576,7 @@ function useNodes(creatorOrScope, scope) {
15667
16576
  if (existing && isTSLNode(existing)) {
15668
16577
  result[name] = existing;
15669
16578
  } else {
15670
- if (typeof node.label === "function") node.setName(name);
16579
+ node.setName?.(name);
15671
16580
  result[name] = node;
15672
16581
  hasNewNodes = true;
15673
16582
  }
@@ -15676,15 +16585,7 @@ function useNodes(creatorOrScope, scope) {
15676
16585
  set((s) => ({ nodes: { ...s.nodes, ...result } }));
15677
16586
  }
15678
16587
  return result;
15679
- }, [
15680
- store,
15681
- typeof creatorOrScope === "string" ? creatorOrScope : scope,
15682
- // Only include storeNodes in deps for reader modes to enable reactivity
15683
- // Creator mode intentionally excludes it to avoid re-running creator on unrelated changes
15684
- isReader ? storeNodes : null,
15685
- // Include hmrVersion for creator modes to allow rebuildNodes() to bust the cache
15686
- isReader ? null : hmrVersion
15687
- ]);
16588
+ }, [store, scopeDep, readerDep, creatorDep]);
15688
16589
  return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
15689
16590
  }
15690
16591
  function rebuildAllNodes(store, scope) {
@@ -15736,18 +16637,358 @@ function useLocalNodes(creator) {
15736
16637
  const uniforms = useThree((s) => s.uniforms);
15737
16638
  const nodes = useThree((s) => s.nodes);
15738
16639
  const textures = useThree((s) => s.textures);
16640
+ const hmrVersion = useThree((s) => s._hmrVersion);
15739
16641
  return useMemo(() => {
15740
- const state = store.getState();
15741
- const wrappedState = {
15742
- ...state,
15743
- uniforms: createScopedStore(state.uniforms),
15744
- nodes: createScopedStore(state.nodes)
15745
- };
16642
+ const wrappedState = createLazyCreatorState(store.getState());
15746
16643
  return creator(wrappedState);
15747
- }, [store, creator, uniforms, nodes, textures]);
16644
+ }, [store, creator, uniforms, nodes, textures, hmrVersion]);
16645
+ }
16646
+
16647
+ const isBufferLike = (value) => {
16648
+ if (value === null || typeof value !== "object") return false;
16649
+ if (ArrayBuffer.isView(value)) return true;
16650
+ if ("isBufferAttribute" in value) return true;
16651
+ if ("uuid" in value || "nodeType" in value) return true;
16652
+ return false;
16653
+ };
16654
+ const disposeBuffer = (buffer) => {
16655
+ if (buffer === null || typeof buffer !== "object") return;
16656
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
16657
+ buffer.dispose();
16658
+ }
16659
+ };
16660
+ function useBuffers(creatorOrScope, scope) {
16661
+ const store = useStore();
16662
+ const removeBuffers = useCallback(
16663
+ (names, targetScope) => {
16664
+ const nameArray = Array.isArray(names) ? names : [names];
16665
+ store.setState((state) => {
16666
+ if (targetScope) {
16667
+ const currentScope = { ...state.buffers[targetScope] };
16668
+ for (const name of nameArray) delete currentScope[name];
16669
+ return { buffers: { ...state.buffers, [targetScope]: currentScope } };
16670
+ }
16671
+ const buffers2 = { ...state.buffers };
16672
+ for (const name of nameArray) if (isBufferLike(buffers2[name])) delete buffers2[name];
16673
+ return { buffers: buffers2 };
16674
+ });
16675
+ },
16676
+ [store]
16677
+ );
16678
+ const clearBuffers = useCallback(
16679
+ (targetScope) => {
16680
+ store.setState((state) => {
16681
+ if (targetScope && targetScope !== "root") {
16682
+ const { [targetScope]: _, ...rest } = state.buffers;
16683
+ return { buffers: rest };
16684
+ }
16685
+ if (targetScope === "root") {
16686
+ const buffers2 = {};
16687
+ for (const [key, value] of Object.entries(state.buffers)) {
16688
+ if (!isBufferLike(value)) buffers2[key] = value;
16689
+ }
16690
+ return { buffers: buffers2 };
16691
+ }
16692
+ return { buffers: {} };
16693
+ });
16694
+ },
16695
+ [store]
16696
+ );
16697
+ const rebuildBuffers = useCallback(
16698
+ (targetScope) => {
16699
+ store.setState((state) => {
16700
+ let newBuffers = state.buffers;
16701
+ if (targetScope && targetScope !== "root") {
16702
+ const { [targetScope]: _, ...rest } = state.buffers;
16703
+ newBuffers = rest;
16704
+ } else if (targetScope === "root") {
16705
+ newBuffers = {};
16706
+ for (const [key, value] of Object.entries(state.buffers)) {
16707
+ if (!isBufferLike(value)) newBuffers[key] = value;
16708
+ }
16709
+ } else {
16710
+ newBuffers = {};
16711
+ }
16712
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16713
+ });
16714
+ },
16715
+ [store]
16716
+ );
16717
+ const disposeBuffers = useCallback(
16718
+ (names, targetScope) => {
16719
+ const nameArray = Array.isArray(names) ? names : [names];
16720
+ const state = store.getState();
16721
+ for (const name of nameArray) {
16722
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
16723
+ if (buffer && isBufferLike(buffer)) {
16724
+ disposeBuffer(buffer);
16725
+ }
16726
+ }
16727
+ removeBuffers(names, targetScope);
16728
+ },
16729
+ [store, removeBuffers]
16730
+ );
16731
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16732
+ const storeBuffers = useThree((s) => s.buffers);
16733
+ const hmrVersion = useThree((s) => s._hmrVersion);
16734
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16735
+ const readerDep = isReader ? storeBuffers : null;
16736
+ const creatorDep = isReader ? null : hmrVersion;
16737
+ const buffers = useMemo(() => {
16738
+ if (creatorOrScope === void 0) {
16739
+ return storeBuffers;
16740
+ }
16741
+ if (typeof creatorOrScope === "string") {
16742
+ const scopeData = storeBuffers[creatorOrScope];
16743
+ if (scopeData && !isBufferLike(scopeData)) return scopeData;
16744
+ return {};
16745
+ }
16746
+ const state = store.getState();
16747
+ const set = store.setState;
16748
+ const creator = creatorOrScope;
16749
+ const wrappedState = createLazyCreatorState(state);
16750
+ const created = creator(wrappedState);
16751
+ const result = {};
16752
+ let hasNewBuffers = false;
16753
+ if (scope) {
16754
+ const currentScope = state.buffers[scope] ?? {};
16755
+ for (const [name, buffer] of Object.entries(created)) {
16756
+ if (currentScope[name]) {
16757
+ result[name] = currentScope[name];
16758
+ } else {
16759
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16760
+ buffer.setName(`${scope}.${name}`);
16761
+ }
16762
+ result[name] = buffer;
16763
+ hasNewBuffers = true;
16764
+ }
16765
+ }
16766
+ if (hasNewBuffers) {
16767
+ set((s) => ({
16768
+ buffers: {
16769
+ ...s.buffers,
16770
+ [scope]: { ...s.buffers[scope], ...result }
16771
+ }
16772
+ }));
16773
+ }
16774
+ return result;
16775
+ }
16776
+ for (const [name, buffer] of Object.entries(created)) {
16777
+ const existing = state.buffers[name];
16778
+ if (existing && isBufferLike(existing)) {
16779
+ result[name] = existing;
16780
+ } else {
16781
+ if ("setName" in buffer && typeof buffer.setName === "function") {
16782
+ buffer.setName(name);
16783
+ }
16784
+ result[name] = buffer;
16785
+ hasNewBuffers = true;
16786
+ }
16787
+ }
16788
+ if (hasNewBuffers) {
16789
+ set((s) => ({ buffers: { ...s.buffers, ...result } }));
16790
+ }
16791
+ return result;
16792
+ }, [store, scopeDep, readerDep, creatorDep]);
16793
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
16794
+ }
16795
+ function rebuildAllBuffers(store, scope) {
16796
+ store.setState((state) => {
16797
+ let newBuffers = state.buffers;
16798
+ if (scope && scope !== "root") {
16799
+ const { [scope]: _, ...rest } = state.buffers;
16800
+ newBuffers = rest;
16801
+ } else if (scope === "root") {
16802
+ newBuffers = {};
16803
+ for (const [key, value] of Object.entries(state.buffers)) {
16804
+ if (!isBufferLike(value)) newBuffers[key] = value;
16805
+ }
16806
+ } else {
16807
+ newBuffers = {};
16808
+ }
16809
+ return { buffers: newBuffers, _hmrVersion: state._hmrVersion + 1 };
16810
+ });
16811
+ }
16812
+
16813
+ const isStorageLike = (value) => {
16814
+ if (value === null || typeof value !== "object") return false;
16815
+ if ("isTexture" in value) return true;
16816
+ if ("isData3DTexture" in value) return true;
16817
+ if ("uuid" in value || "nodeType" in value) return true;
16818
+ return false;
16819
+ };
16820
+ const disposeStorage = (storage) => {
16821
+ if (storage === null || typeof storage !== "object") return;
16822
+ if ("dispose" in storage && typeof storage.dispose === "function") {
16823
+ storage.dispose();
16824
+ }
16825
+ };
16826
+ function useGPUStorage(creatorOrScope, scope) {
16827
+ const store = useStore();
16828
+ const removeStorage = useCallback(
16829
+ (names, targetScope) => {
16830
+ const nameArray = Array.isArray(names) ? names : [names];
16831
+ store.setState((state) => {
16832
+ if (targetScope) {
16833
+ const currentScope = { ...state.gpuStorage[targetScope] };
16834
+ for (const name of nameArray) delete currentScope[name];
16835
+ return { gpuStorage: { ...state.gpuStorage, [targetScope]: currentScope } };
16836
+ }
16837
+ const gpuStorage2 = { ...state.gpuStorage };
16838
+ for (const name of nameArray) if (isStorageLike(gpuStorage2[name])) delete gpuStorage2[name];
16839
+ return { gpuStorage: gpuStorage2 };
16840
+ });
16841
+ },
16842
+ [store]
16843
+ );
16844
+ const clearStorage = useCallback(
16845
+ (targetScope) => {
16846
+ store.setState((state) => {
16847
+ if (targetScope && targetScope !== "root") {
16848
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16849
+ return { gpuStorage: rest };
16850
+ }
16851
+ if (targetScope === "root") {
16852
+ const gpuStorage2 = {};
16853
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16854
+ if (!isStorageLike(value)) gpuStorage2[key] = value;
16855
+ }
16856
+ return { gpuStorage: gpuStorage2 };
16857
+ }
16858
+ return { gpuStorage: {} };
16859
+ });
16860
+ },
16861
+ [store]
16862
+ );
16863
+ const rebuildStorage = useCallback(
16864
+ (targetScope) => {
16865
+ store.setState((state) => {
16866
+ let newStorage = state.gpuStorage;
16867
+ if (targetScope && targetScope !== "root") {
16868
+ const { [targetScope]: _, ...rest } = state.gpuStorage;
16869
+ newStorage = rest;
16870
+ } else if (targetScope === "root") {
16871
+ newStorage = {};
16872
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16873
+ if (!isStorageLike(value)) newStorage[key] = value;
16874
+ }
16875
+ } else {
16876
+ newStorage = {};
16877
+ }
16878
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16879
+ });
16880
+ },
16881
+ [store]
16882
+ );
16883
+ const disposeStorageFn = useCallback(
16884
+ (names, targetScope) => {
16885
+ const nameArray = Array.isArray(names) ? names : [names];
16886
+ const state = store.getState();
16887
+ for (const name of nameArray) {
16888
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
16889
+ if (storage && isStorageLike(storage)) {
16890
+ disposeStorage(storage);
16891
+ }
16892
+ }
16893
+ removeStorage(names, targetScope);
16894
+ },
16895
+ [store, removeStorage]
16896
+ );
16897
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
16898
+ const storeStorage = useThree((s) => s.gpuStorage);
16899
+ const hmrVersion = useThree((s) => s._hmrVersion);
16900
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16901
+ const readerDep = isReader ? storeStorage : null;
16902
+ const creatorDep = isReader ? null : hmrVersion;
16903
+ const gpuStorage = useMemo(() => {
16904
+ if (creatorOrScope === void 0) {
16905
+ return storeStorage;
16906
+ }
16907
+ if (typeof creatorOrScope === "string") {
16908
+ const scopeData = storeStorage[creatorOrScope];
16909
+ if (scopeData && !isStorageLike(scopeData)) return scopeData;
16910
+ return {};
16911
+ }
16912
+ const state = store.getState();
16913
+ const set = store.setState;
16914
+ const creator = creatorOrScope;
16915
+ const wrappedState = createLazyCreatorState(state);
16916
+ const created = creator(wrappedState);
16917
+ const result = {};
16918
+ let hasNewStorage = false;
16919
+ if (scope) {
16920
+ const currentScope = state.gpuStorage[scope] ?? {};
16921
+ for (const [name, storage] of Object.entries(created)) {
16922
+ if (currentScope[name]) {
16923
+ result[name] = currentScope[name];
16924
+ } else {
16925
+ if ("setName" in storage && typeof storage.setName === "function") {
16926
+ storage.setName(`${scope}.${name}`);
16927
+ }
16928
+ if ("name" in storage && typeof storage.name === "string") {
16929
+ storage.name = `${scope}.${name}`;
16930
+ }
16931
+ result[name] = storage;
16932
+ hasNewStorage = true;
16933
+ }
16934
+ }
16935
+ if (hasNewStorage) {
16936
+ set((s) => ({
16937
+ gpuStorage: {
16938
+ ...s.gpuStorage,
16939
+ [scope]: { ...s.gpuStorage[scope], ...result }
16940
+ }
16941
+ }));
16942
+ }
16943
+ return result;
16944
+ }
16945
+ for (const [name, storage] of Object.entries(created)) {
16946
+ const existing = state.gpuStorage[name];
16947
+ if (existing && isStorageLike(existing)) {
16948
+ result[name] = existing;
16949
+ } else {
16950
+ if ("setName" in storage && typeof storage.setName === "function") {
16951
+ storage.setName(name);
16952
+ }
16953
+ if ("name" in storage && typeof storage.name === "string") {
16954
+ storage.name = name;
16955
+ }
16956
+ result[name] = storage;
16957
+ hasNewStorage = true;
16958
+ }
16959
+ }
16960
+ if (hasNewStorage) {
16961
+ set((s) => ({ gpuStorage: { ...s.gpuStorage, ...result } }));
16962
+ }
16963
+ return result;
16964
+ }, [store, scopeDep, readerDep, creatorDep]);
16965
+ return {
16966
+ ...gpuStorage,
16967
+ removeStorage,
16968
+ clearStorage,
16969
+ rebuildStorage,
16970
+ disposeStorage: disposeStorageFn
16971
+ };
16972
+ }
16973
+ function rebuildAllStorage(store, scope) {
16974
+ store.setState((state) => {
16975
+ let newStorage = state.gpuStorage;
16976
+ if (scope && scope !== "root") {
16977
+ const { [scope]: _, ...rest } = state.gpuStorage;
16978
+ newStorage = rest;
16979
+ } else if (scope === "root") {
16980
+ newStorage = {};
16981
+ for (const [key, value] of Object.entries(state.gpuStorage)) {
16982
+ if (!isStorageLike(value)) newStorage[key] = value;
16983
+ }
16984
+ } else {
16985
+ newStorage = {};
16986
+ }
16987
+ return { gpuStorage: newStorage, _hmrVersion: state._hmrVersion + 1 };
16988
+ });
15748
16989
  }
15749
16990
 
15750
- function usePostProcessing(mainCB, setupCB) {
16991
+ function useRenderPipeline(mainCB, setupCB) {
15751
16992
  const store = useStore();
15752
16993
  const { scene, camera, renderer, isLegacy } = useThree();
15753
16994
  const callbacksRanRef = useRef(false);
@@ -15766,7 +17007,7 @@ function usePostProcessing(mainCB, setupCB) {
15766
17007
  }, [store]);
15767
17008
  const reset = useCallback(() => {
15768
17009
  store.setState({
15769
- postProcessing: null,
17010
+ renderPipeline: null,
15770
17011
  passes: {}
15771
17012
  });
15772
17013
  callbacksRanRef.current = false;
@@ -15779,13 +17020,13 @@ function usePostProcessing(mainCB, setupCB) {
15779
17020
  }, []);
15780
17021
  useLayoutEffect(() => {
15781
17022
  if (isLegacy) {
15782
- throw new Error("usePostProcessing is only available with WebGPU renderer. Set renderer prop on Canvas.");
17023
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
15783
17024
  }
15784
17025
  if (!renderer || !scene || !camera) return;
15785
17026
  const state = store.getState();
15786
17027
  const set = store.setState;
15787
17028
  try {
15788
- let pp = state.postProcessing;
17029
+ let pp = state.renderPipeline;
15789
17030
  let currentPasses = { ...state.passes };
15790
17031
  let justCreatedPP = false;
15791
17032
  if (!pp) {
@@ -15802,7 +17043,7 @@ function usePostProcessing(mainCB, setupCB) {
15802
17043
  }
15803
17044
  currentPasses.scenePass = scenePass;
15804
17045
  if (!pp.outputNode || justCreatedPP) pp.outputNode = scenePass;
15805
- set({ postProcessing: pp, passes: currentPasses });
17046
+ set({ renderPipeline: pp, passes: currentPasses });
15806
17047
  const shouldRunCallbacks = justCreatedPP || !callbacksRanRef.current || !cacheValid;
15807
17048
  if (shouldRunCallbacks) {
15808
17049
  if (setupCBRef.current) {
@@ -15824,22 +17065,22 @@ function usePostProcessing(mainCB, setupCB) {
15824
17065
  callbacksRanRef.current = true;
15825
17066
  }
15826
17067
  } catch (error) {
15827
- console.error("[usePostProcessing] Setup error:", error);
17068
+ console.error("[useRenderPipeline] Setup error:", error);
15828
17069
  }
15829
17070
  }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
15830
17071
  const passes = useThree((s) => s.passes);
15831
- const postProcessing = useThree((s) => s.postProcessing);
17072
+ const renderPipeline = useThree((s) => s.renderPipeline);
15832
17073
  return {
15833
17074
  passes,
15834
- postProcessing,
17075
+ renderPipeline,
15835
17076
  clearPasses,
15836
17077
  reset,
15837
17078
  rebuild,
15838
- // isReady indicates if PostProcessing is configured and ready for rendering
15839
- isReady: postProcessing !== null
17079
+ // isReady indicates if RenderPipeline is configured and ready for rendering
17080
+ isReady: renderPipeline !== null
15840
17081
  };
15841
17082
  }
15842
17083
 
15843
17084
  extend(THREE);
15844
17085
 
15845
- export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, rebuildAllNodes, rebuildAllUniforms, reconciler, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, usePostProcessing, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms };
17086
+ export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, rebuildAllBuffers, rebuildAllNodes, rebuildAllStorage, rebuildAllUniforms, reconciler, registerPrimary, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useBuffers, useEnvironment, useFrame, useGPUStorage, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, useRenderPipeline, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms, waitForPrimary };