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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,12 @@ const jsxRuntime = require('react/jsx-runtime');
6
6
  const React = require('react');
7
7
  const useMeasure = require('react-use-measure');
8
8
  const itsFine = require('its-fine');
9
+ const fiber = require('@react-three/fiber');
10
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
11
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
12
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
13
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
14
+ const gainmapJs = require('@monogrid/gainmap-js');
9
15
  const Tb = require('scheduler');
10
16
  const traditional = require('zustand/traditional');
11
17
  const suspendReact = require('suspend-react');
@@ -67,6 +73,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
67
73
  WebGLRenderer: WebGLRenderer
68
74
  }, [webgpu__namespace]);
69
75
 
76
+ const primaryRegistry = /* @__PURE__ */ new Map();
77
+ const pendingSubscribers = /* @__PURE__ */ new Map();
78
+ function registerPrimary(id, renderer, store) {
79
+ if (primaryRegistry.has(id)) {
80
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
81
+ }
82
+ const entry = { renderer, store };
83
+ primaryRegistry.set(id, entry);
84
+ const subscribers = pendingSubscribers.get(id);
85
+ if (subscribers) {
86
+ subscribers.forEach((callback) => callback(entry));
87
+ pendingSubscribers.delete(id);
88
+ }
89
+ return () => {
90
+ const currentEntry = primaryRegistry.get(id);
91
+ if (currentEntry?.renderer === renderer) {
92
+ primaryRegistry.delete(id);
93
+ }
94
+ };
95
+ }
96
+ function getPrimary(id) {
97
+ return primaryRegistry.get(id);
98
+ }
99
+ function waitForPrimary(id, timeout = 5e3) {
100
+ const existing = primaryRegistry.get(id);
101
+ if (existing) {
102
+ return Promise.resolve(existing);
103
+ }
104
+ return new Promise((resolve, reject) => {
105
+ const timeoutId = setTimeout(() => {
106
+ const subscribers = pendingSubscribers.get(id);
107
+ if (subscribers) {
108
+ const index = subscribers.indexOf(callback);
109
+ if (index !== -1) subscribers.splice(index, 1);
110
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
111
+ }
112
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
113
+ }, timeout);
114
+ const callback = (entry) => {
115
+ clearTimeout(timeoutId);
116
+ resolve(entry);
117
+ };
118
+ if (!pendingSubscribers.has(id)) {
119
+ pendingSubscribers.set(id, []);
120
+ }
121
+ pendingSubscribers.get(id).push(callback);
122
+ });
123
+ }
124
+ function hasPrimary(id) {
125
+ return primaryRegistry.has(id);
126
+ }
127
+ function unregisterPrimary(id) {
128
+ primaryRegistry.delete(id);
129
+ }
130
+ function getPrimaryIds() {
131
+ return Array.from(primaryRegistry.keys());
132
+ }
133
+
134
+ const presetsObj = {
135
+ apartment: "lebombo_1k.hdr",
136
+ city: "potsdamer_platz_1k.hdr",
137
+ dawn: "kiara_1_dawn_1k.hdr",
138
+ forest: "forest_slope_1k.hdr",
139
+ lobby: "st_fagans_interior_1k.hdr",
140
+ night: "dikhololo_night_1k.hdr",
141
+ park: "rooitou_park_1k.hdr",
142
+ studio: "studio_small_03_1k.hdr",
143
+ sunset: "venice_sunset_1k.hdr",
144
+ warehouse: "empty_warehouse_01_1k.hdr"
145
+ };
146
+
147
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
148
+ const isArray = (arr) => Array.isArray(arr);
149
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
150
+ function useEnvironment({
151
+ files = defaultFiles,
152
+ path = "",
153
+ preset = void 0,
154
+ colorSpace = void 0,
155
+ extensions
156
+ } = {}) {
157
+ if (preset) {
158
+ validatePreset(preset);
159
+ files = presetsObj[preset];
160
+ path = CUBEMAP_ROOT;
161
+ }
162
+ const multiFile = isArray(files);
163
+ const { extension, isCubemap } = getExtension(files);
164
+ const loader = getLoader$1(extension);
165
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
166
+ const renderer = fiber.useThree((state) => state.renderer);
167
+ React.useLayoutEffect(() => {
168
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
169
+ function clearGainmapTexture() {
170
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
171
+ }
172
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
173
+ }, [files, renderer.domElement]);
174
+ const loaderResult = fiber.useLoader(
175
+ loader,
176
+ multiFile ? [files] : files,
177
+ (loader2) => {
178
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
179
+ loader2.setRenderer?.(renderer);
180
+ }
181
+ loader2.setPath?.(path);
182
+ if (extensions) extensions(loader2);
183
+ }
184
+ );
185
+ let texture = multiFile ? (
186
+ // @ts-ignore
187
+ loaderResult[0]
188
+ ) : loaderResult;
189
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
190
+ texture = texture.renderTarget?.texture;
191
+ }
192
+ texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
193
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
194
+ return texture;
195
+ }
196
+ const preloadDefaultOptions = {
197
+ files: defaultFiles,
198
+ path: "",
199
+ preset: void 0,
200
+ extensions: void 0
201
+ };
202
+ useEnvironment.preload = (preloadOptions) => {
203
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
204
+ let { files, path = "" } = options;
205
+ const { preset, extensions } = options;
206
+ if (preset) {
207
+ validatePreset(preset);
208
+ files = presetsObj[preset];
209
+ path = CUBEMAP_ROOT;
210
+ }
211
+ const { extension } = getExtension(files);
212
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
213
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
214
+ }
215
+ const loader = getLoader$1(extension);
216
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
217
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
218
+ loader2.setPath?.(path);
219
+ if (extensions) extensions(loader2);
220
+ });
221
+ };
222
+ const clearDefaultOptins = {
223
+ files: defaultFiles,
224
+ preset: void 0
225
+ };
226
+ useEnvironment.clear = (clearOptions) => {
227
+ const options = { ...clearDefaultOptins, ...clearOptions };
228
+ let { files } = options;
229
+ const { preset } = options;
230
+ if (preset) {
231
+ validatePreset(preset);
232
+ files = presetsObj[preset];
233
+ }
234
+ const { extension } = getExtension(files);
235
+ const loader = getLoader$1(extension);
236
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
237
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
238
+ };
239
+ function validatePreset(preset) {
240
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
241
+ }
242
+ function getExtension(files) {
243
+ const isCubemap = isArray(files) && files.length === 6;
244
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
245
+ const firstEntry = isArray(files) ? files[0] : files;
246
+ const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
247
+ return { extension, isCubemap, isGainmap };
248
+ }
249
+ function getLoader$1(extension) {
250
+ const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
251
+ return loader;
252
+ }
253
+
254
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
255
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
256
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
257
+ sceneProps = {
258
+ backgroundBlurriness: 0,
259
+ backgroundIntensity: 1,
260
+ backgroundRotation: [0, 0, 0],
261
+ environmentIntensity: 1,
262
+ environmentRotation: [0, 0, 0],
263
+ ...sceneProps
264
+ };
265
+ const target = resolveScene(scene || defaultScene);
266
+ const oldbg = target.background;
267
+ const oldenv = target.environment;
268
+ const oldSceneProps = {
269
+ // @ts-ignore
270
+ backgroundBlurriness: target.backgroundBlurriness,
271
+ // @ts-ignore
272
+ backgroundIntensity: target.backgroundIntensity,
273
+ // @ts-ignore
274
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
275
+ // @ts-ignore
276
+ environmentIntensity: target.environmentIntensity,
277
+ // @ts-ignore
278
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
279
+ };
280
+ if (background !== "only") target.environment = texture;
281
+ if (background) target.background = texture;
282
+ fiber.applyProps(target, sceneProps);
283
+ return () => {
284
+ if (background !== "only") target.environment = oldenv;
285
+ if (background) target.background = oldbg;
286
+ fiber.applyProps(target, oldSceneProps);
287
+ };
288
+ }
289
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
290
+ const defaultScene = fiber.useThree((state) => state.scene);
291
+ React__namespace.useLayoutEffect(() => {
292
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
293
+ });
294
+ return null;
295
+ }
296
+ function EnvironmentCube({
297
+ background = false,
298
+ scene,
299
+ blur,
300
+ backgroundBlurriness,
301
+ backgroundIntensity,
302
+ backgroundRotation,
303
+ environmentIntensity,
304
+ environmentRotation,
305
+ ...rest
306
+ }) {
307
+ const texture = useEnvironment(rest);
308
+ const defaultScene = fiber.useThree((state) => state.scene);
309
+ React__namespace.useLayoutEffect(() => {
310
+ return setEnvProps(background, scene, defaultScene, texture, {
311
+ backgroundBlurriness: blur ?? backgroundBlurriness,
312
+ backgroundIntensity,
313
+ backgroundRotation,
314
+ environmentIntensity,
315
+ environmentRotation
316
+ });
317
+ });
318
+ React__namespace.useEffect(() => {
319
+ return () => {
320
+ texture.dispose();
321
+ };
322
+ }, [texture]);
323
+ return null;
324
+ }
325
+ function EnvironmentPortal({
326
+ children,
327
+ near = 0.1,
328
+ far = 1e3,
329
+ resolution = 256,
330
+ frames = 1,
331
+ map,
332
+ background = false,
333
+ blur,
334
+ backgroundBlurriness,
335
+ backgroundIntensity,
336
+ backgroundRotation,
337
+ environmentIntensity,
338
+ environmentRotation,
339
+ scene,
340
+ files,
341
+ path,
342
+ preset = void 0,
343
+ extensions
344
+ }) {
345
+ const gl = fiber.useThree((state) => state.gl);
346
+ const defaultScene = fiber.useThree((state) => state.scene);
347
+ const camera = React__namespace.useRef(null);
348
+ const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
349
+ const fbo = React__namespace.useMemo(() => {
350
+ const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
351
+ fbo2.texture.type = webgpu.HalfFloatType;
352
+ return fbo2;
353
+ }, [resolution]);
354
+ React__namespace.useEffect(() => {
355
+ return () => {
356
+ fbo.dispose();
357
+ };
358
+ }, [fbo]);
359
+ React__namespace.useLayoutEffect(() => {
360
+ if (frames === 1) {
361
+ const autoClear = gl.autoClear;
362
+ gl.autoClear = true;
363
+ camera.current.update(gl, virtualScene);
364
+ gl.autoClear = autoClear;
365
+ }
366
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
367
+ backgroundBlurriness: blur ?? backgroundBlurriness,
368
+ backgroundIntensity,
369
+ backgroundRotation,
370
+ environmentIntensity,
371
+ environmentRotation
372
+ });
373
+ }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
374
+ let count = 1;
375
+ fiber.useFrame(() => {
376
+ if (frames === Infinity || count < frames) {
377
+ const autoClear = gl.autoClear;
378
+ gl.autoClear = true;
379
+ camera.current.update(gl, virtualScene);
380
+ gl.autoClear = autoClear;
381
+ count++;
382
+ }
383
+ });
384
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
385
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
386
+ children,
387
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
388
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
389
+ ] }),
390
+ virtualScene
391
+ ) });
392
+ }
393
+ function EnvironmentGround(props) {
394
+ const textureDefault = useEnvironment(props);
395
+ const texture = props.map || textureDefault;
396
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
397
+ React__namespace.useEffect(() => {
398
+ return () => {
399
+ textureDefault.dispose();
400
+ };
401
+ }, [textureDefault]);
402
+ const height = props.ground?.height ?? 15;
403
+ const radius = props.ground?.radius ?? 60;
404
+ const scale = props.ground?.scale ?? 1e3;
405
+ const args = React__namespace.useMemo(
406
+ () => [texture, height, radius],
407
+ [texture, height, radius]
408
+ );
409
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
410
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
411
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
412
+ ] });
413
+ }
414
+ function EnvironmentColor({ color, scene }) {
415
+ const defaultScene = fiber.useThree((state) => state.scene);
416
+ React__namespace.useLayoutEffect(() => {
417
+ if (color === void 0) return;
418
+ const target = resolveScene(scene || defaultScene);
419
+ const oldBg = target.background;
420
+ target.background = new webgpu.Color(color);
421
+ return () => {
422
+ target.background = oldBg;
423
+ };
424
+ });
425
+ return null;
426
+ }
427
+ function EnvironmentDualSource(props) {
428
+ const { backgroundFiles, ...envProps } = props;
429
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
430
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
431
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
432
+ ] });
433
+ }
434
+ function Environment(props) {
435
+ if (props.color && !props.files && !props.preset && !props.map) {
436
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
437
+ }
438
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
439
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
440
+ }
441
+ return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
442
+ }
443
+
70
444
  var __defProp$3 = Object.defineProperty;
71
445
  var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
72
446
  var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -246,7 +620,8 @@ function prepare(target, root, type, props) {
246
620
  object,
247
621
  eventCount: 0,
248
622
  handlers: {},
249
- isHidden: false
623
+ isHidden: false,
624
+ deferredRefs: []
250
625
  };
251
626
  if (object) object.__r3f = instance;
252
627
  }
@@ -295,7 +670,7 @@ function createOcclusionObserverNode(store, uniform) {
295
670
  let occlusionSetupPromise = null;
296
671
  function enableOcclusion(store) {
297
672
  const state = store.getState();
298
- const { internal, renderer, rootScene } = state;
673
+ const { internal, renderer } = state;
299
674
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
300
675
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
301
676
  if (!hasOcclusionSupport) {
@@ -458,6 +833,22 @@ function hasVisibilityHandlers(handlers) {
458
833
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
459
834
  }
460
835
 
836
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
837
+ function fromRef(ref) {
838
+ return { [FROM_REF]: ref };
839
+ }
840
+ function isFromRef(value) {
841
+ return value !== null && typeof value === "object" && FROM_REF in value;
842
+ }
843
+
844
+ const ONCE = Symbol.for("@react-three/fiber.once");
845
+ function once(...args) {
846
+ return { [ONCE]: args.length ? args : true };
847
+ }
848
+ function isOnce(value) {
849
+ return value !== null && typeof value === "object" && ONCE in value;
850
+ }
851
+
461
852
  const RESERVED_PROPS = [
462
853
  "children",
463
854
  "key",
@@ -528,7 +919,7 @@ function getMemoizedPrototype(root) {
528
919
  ctor = new root.constructor();
529
920
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
530
921
  }
531
- } catch (e) {
922
+ } catch {
532
923
  }
533
924
  return ctor;
534
925
  }
@@ -574,6 +965,25 @@ function applyProps(object, props) {
574
965
  continue;
575
966
  }
576
967
  if (value === void 0) continue;
968
+ if (isFromRef(value)) {
969
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
970
+ continue;
971
+ }
972
+ if (isOnce(value)) {
973
+ if (instance?.appliedOnce?.has(prop)) continue;
974
+ if (instance) {
975
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
976
+ instance.appliedOnce.add(prop);
977
+ }
978
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
979
+ const args = value[ONCE];
980
+ if (typeof targetRoot[targetKey] === "function") {
981
+ targetRoot[targetKey](...args === true ? [] : args);
982
+ } else if (args !== true && args.length > 0) {
983
+ targetRoot[targetKey] = args[0];
984
+ }
985
+ continue;
986
+ }
577
987
  let { root, key, target } = resolve(object, prop);
578
988
  if (target === void 0 && (typeof root !== "object" || root === null)) {
579
989
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -596,7 +1006,7 @@ function applyProps(object, props) {
596
1006
  else target.set(value);
597
1007
  } else {
598
1008
  root[key] = value;
599
- if (rootState && !rootState.linear && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
1009
+ if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
600
1010
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
601
1011
  root[key].colorSpace = rootState.textureColorSpace;
602
1012
  }
@@ -953,7 +1363,7 @@ function createPointerEvents(store) {
953
1363
  return {
954
1364
  priority: 1,
955
1365
  enabled: true,
956
- compute(event, state, previous) {
1366
+ compute(event, state) {
957
1367
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
958
1368
  state.raycaster.setFromCamera(state.pointer, state.camera);
959
1369
  },
@@ -1051,331 +1461,26 @@ function notifyAlpha({ message, link }) {
1051
1461
  }
1052
1462
  }
1053
1463
 
1054
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1055
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1056
- const createStore = (invalidate, advance) => {
1057
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1058
- const position = new webgpu.Vector3();
1059
- const defaultTarget = new webgpu.Vector3();
1060
- const tempTarget = new webgpu.Vector3();
1061
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1062
- const { width, height, top, left } = size;
1063
- const aspect = width / height;
1064
- if (target.isVector3) tempTarget.copy(target);
1065
- else tempTarget.set(...target);
1066
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1067
- if (isOrthographicCamera(camera)) {
1068
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1069
- } else {
1070
- const fov = camera.fov * Math.PI / 180;
1071
- const h = 2 * Math.tan(fov / 2) * distance;
1072
- const w = h * (width / height);
1073
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1074
- }
1075
- }
1076
- let performanceTimeout = void 0;
1077
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1078
- const pointer = new webgpu.Vector2();
1079
- const rootState = {
1080
- set,
1081
- get,
1082
- // Mock objects that have to be configured
1083
- gl: null,
1084
- renderer: null,
1085
- camera: null,
1086
- frustum: new webgpu.Frustum(),
1087
- autoUpdateFrustum: true,
1088
- raycaster: null,
1089
- events: { priority: 1, enabled: true, connected: false },
1090
- scene: null,
1091
- rootScene: null,
1092
- xr: null,
1093
- inspector: null,
1094
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1095
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1096
- legacy: false,
1097
- linear: false,
1098
- flat: false,
1099
- textureColorSpace: "srgb",
1100
- isLegacy: false,
1101
- webGPUSupported: false,
1102
- isNative: false,
1103
- controls: null,
1104
- pointer,
1105
- mouse: pointer,
1106
- frameloop: "always",
1107
- onPointerMissed: void 0,
1108
- onDragOverMissed: void 0,
1109
- onDropMissed: void 0,
1110
- performance: {
1111
- current: 1,
1112
- min: 0.5,
1113
- max: 1,
1114
- debounce: 200,
1115
- regress: () => {
1116
- const state2 = get();
1117
- if (performanceTimeout) clearTimeout(performanceTimeout);
1118
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1119
- performanceTimeout = setTimeout(
1120
- () => setPerformanceCurrent(get().performance.max),
1121
- state2.performance.debounce
1122
- );
1123
- }
1124
- },
1125
- size: { width: 0, height: 0, top: 0, left: 0 },
1126
- viewport: {
1127
- initialDpr: 0,
1128
- dpr: 0,
1129
- width: 0,
1130
- height: 0,
1131
- top: 0,
1132
- left: 0,
1133
- aspect: 0,
1134
- distance: 0,
1135
- factor: 0,
1136
- getCurrentViewport
1137
- },
1138
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1139
- setSize: (width, height, top, left) => {
1140
- const state2 = get();
1141
- if (width === void 0) {
1142
- set({ _sizeImperative: false });
1143
- if (state2._sizeProps) {
1144
- const { width: propW, height: propH } = state2._sizeProps;
1145
- if (propW !== void 0 || propH !== void 0) {
1146
- const currentSize = state2.size;
1147
- const newSize = {
1148
- width: propW ?? currentSize.width,
1149
- height: propH ?? currentSize.height,
1150
- top: currentSize.top,
1151
- left: currentSize.left
1152
- };
1153
- set((s) => ({
1154
- size: newSize,
1155
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1156
- }));
1157
- }
1158
- }
1159
- return;
1160
- }
1161
- const w = width;
1162
- const h = height ?? width;
1163
- const t = top ?? state2.size.top;
1164
- const l = left ?? state2.size.left;
1165
- const size = { width: w, height: h, top: t, left: l };
1166
- set((s) => ({
1167
- size,
1168
- viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1169
- _sizeImperative: true
1170
- }));
1171
- },
1172
- setDpr: (dpr) => set((state2) => {
1173
- const resolved = calculateDpr(dpr);
1174
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1175
- }),
1176
- setFrameloop: (frameloop = "always") => {
1177
- set(() => ({ frameloop }));
1178
- },
1179
- setError: (error) => set(() => ({ error })),
1180
- error: null,
1181
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1182
- uniforms: {},
1183
- nodes: {},
1184
- textures: /* @__PURE__ */ new Map(),
1185
- postProcessing: null,
1186
- passes: {},
1187
- _hmrVersion: 0,
1188
- _sizeImperative: false,
1189
- _sizeProps: null,
1190
- previousRoot: void 0,
1191
- internal: {
1192
- // Events
1193
- interaction: [],
1194
- hovered: /* @__PURE__ */ new Map(),
1195
- subscribers: [],
1196
- initialClick: [0, 0],
1197
- initialHits: [],
1198
- capturedMap: /* @__PURE__ */ new Map(),
1199
- lastEvent: React__namespace.createRef(),
1200
- // Visibility tracking (onFramed, onOccluded, onVisible)
1201
- visibilityRegistry: /* @__PURE__ */ new Map(),
1202
- // Occlusion system (WebGPU only)
1203
- occlusionEnabled: false,
1204
- occlusionObserver: null,
1205
- occlusionCache: /* @__PURE__ */ new Map(),
1206
- helperGroup: null,
1207
- // Updates
1208
- active: false,
1209
- frames: 0,
1210
- priority: 0,
1211
- subscribe: (ref, priority, store) => {
1212
- const internal = get().internal;
1213
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1214
- internal.subscribers.push({ ref, priority, store });
1215
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1216
- return () => {
1217
- const internal2 = get().internal;
1218
- if (internal2?.subscribers) {
1219
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1220
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1221
- }
1222
- };
1223
- },
1224
- // Renderer Storage (single source of truth)
1225
- actualRenderer: null,
1226
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1227
- scheduler: null
1228
- }
1229
- };
1230
- return rootState;
1231
- });
1232
- const state = rootStore.getState();
1233
- Object.defineProperty(state, "gl", {
1234
- get() {
1235
- const currentState = rootStore.getState();
1236
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1237
- const stack = new Error().stack || "";
1238
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1239
- if (!isInternalAccess) {
1240
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1241
- notifyDepreciated({
1242
- heading: "Accessing state.gl in WebGPU mode",
1243
- body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
1244
- });
1245
- }
1246
- }
1247
- return currentState.internal.actualRenderer;
1248
- },
1249
- set(value) {
1250
- rootStore.getState().internal.actualRenderer = value;
1251
- },
1252
- enumerable: true,
1253
- configurable: true
1254
- });
1255
- Object.defineProperty(state, "renderer", {
1256
- get() {
1257
- return rootStore.getState().internal.actualRenderer;
1258
- },
1259
- set(value) {
1260
- rootStore.getState().internal.actualRenderer = value;
1261
- },
1262
- enumerable: true,
1263
- configurable: true
1264
- });
1265
- let oldScene = state.scene;
1266
- rootStore.subscribe(() => {
1267
- const currentState = rootStore.getState();
1268
- const { scene, rootScene, set } = currentState;
1269
- if (scene !== oldScene) {
1270
- oldScene = scene;
1271
- if (scene?.isScene && scene !== rootScene) {
1272
- set({ rootScene: scene });
1273
- }
1274
- }
1275
- });
1276
- let oldSize = state.size;
1277
- let oldDpr = state.viewport.dpr;
1278
- let oldCamera = state.camera;
1279
- rootStore.subscribe(() => {
1280
- const { camera, size, viewport, set, internal } = rootStore.getState();
1281
- const actualRenderer = internal.actualRenderer;
1282
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1283
- oldSize = size;
1284
- oldDpr = viewport.dpr;
1285
- updateCamera(camera, size);
1286
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1287
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1288
- actualRenderer.setSize(size.width, size.height, updateStyle);
1289
- }
1290
- if (camera !== oldCamera) {
1291
- oldCamera = camera;
1292
- const { rootScene } = rootStore.getState();
1293
- if (camera && rootScene && !camera.parent) {
1294
- rootScene.add(camera);
1295
- }
1296
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1297
- const currentState = rootStore.getState();
1298
- if (currentState.autoUpdateFrustum && camera) {
1299
- updateFrustum(camera, currentState.frustum);
1300
- }
1301
- }
1302
- });
1303
- rootStore.subscribe((state2) => invalidate(state2));
1304
- return rootStore;
1305
- };
1306
-
1307
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1308
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1309
- function getLoader(Proto) {
1310
- if (isConstructor$1(Proto)) {
1311
- let loader = memoizedLoaders.get(Proto);
1312
- if (!loader) {
1313
- loader = new Proto();
1314
- memoizedLoaders.set(Proto, loader);
1315
- }
1316
- return loader;
1317
- }
1318
- return Proto;
1319
- }
1320
- function loadingFn(extensions, onProgress) {
1321
- return function(Proto, input) {
1322
- const loader = getLoader(Proto);
1323
- if (extensions) extensions(loader);
1324
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1325
- return loader.loadAsync(input, onProgress).then((data) => {
1326
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1327
- return data;
1328
- });
1329
- }
1330
- return new Promise(
1331
- (res, reject) => loader.load(
1332
- input,
1333
- (data) => {
1334
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1335
- res(data);
1336
- },
1337
- onProgress,
1338
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1339
- )
1340
- );
1341
- };
1342
- }
1343
- function useLoader(loader, input, extensions, onProgress) {
1344
- const keys = Array.isArray(input) ? input : [input];
1345
- const fn = loadingFn(extensions, onProgress);
1346
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1347
- return Array.isArray(input) ? results : results[0];
1348
- }
1349
- useLoader.preload = function(loader, input, extensions, onProgress) {
1350
- const keys = Array.isArray(input) ? input : [input];
1351
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1352
- };
1353
- useLoader.clear = function(loader, input) {
1354
- const keys = Array.isArray(input) ? input : [input];
1355
- keys.forEach((key) => suspendReact.clear([loader, key]));
1356
- };
1357
- useLoader.loader = getLoader;
1358
-
1359
- var __defProp$2 = Object.defineProperty;
1360
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1361
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1362
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1363
- class PhaseGraph {
1364
- constructor() {
1365
- /** Ordered list of phase nodes */
1366
- __publicField$2(this, "phases", []);
1367
- /** Quick lookup by name */
1368
- __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1369
- /** Cached ordered names (invalidated on changes) */
1370
- __publicField$2(this, "orderedNamesCache", null);
1371
- this.initializeDefaultPhases();
1372
- }
1373
- //* Initialization --------------------------------
1374
- initializeDefaultPhases() {
1375
- for (const name of DEFAULT_PHASES) {
1376
- const node = { name, isAutoGenerated: false };
1377
- this.phases.push(node);
1378
- this.phaseMap.set(name, node);
1464
+ var __defProp$2 = Object.defineProperty;
1465
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1466
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1467
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1468
+ class PhaseGraph {
1469
+ constructor() {
1470
+ /** Ordered list of phase nodes */
1471
+ __publicField$2(this, "phases", []);
1472
+ /** Quick lookup by name */
1473
+ __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1474
+ /** Cached ordered names (invalidated on changes) */
1475
+ __publicField$2(this, "orderedNamesCache", null);
1476
+ this.initializeDefaultPhases();
1477
+ }
1478
+ //* Initialization --------------------------------
1479
+ initializeDefaultPhases() {
1480
+ for (const name of DEFAULT_PHASES) {
1481
+ const node = { name, isAutoGenerated: false };
1482
+ this.phases.push(node);
1483
+ this.phaseMap.set(name, node);
1379
1484
  }
1380
1485
  this.invalidateCache();
1381
1486
  }
@@ -2295,98 +2400,411 @@ const _Scheduler = class _Scheduler {
2295
2400
  this.triggerError(error instanceof Error ? error : new Error(String(error)));
2296
2401
  }
2297
2402
  }
2298
- }
2299
- //* Debug & Inspection Methods ================================
2300
- /**
2301
- * Get the total number of registered jobs across all roots.
2302
- * Includes both per-root jobs and global before/after jobs.
2303
- * @returns {number} Total job count
2304
- */
2305
- getJobCount() {
2306
- let count = 0;
2307
- for (const root of this.roots.values()) {
2308
- count += root.jobs.size;
2403
+ }
2404
+ //* Debug & Inspection Methods ================================
2405
+ /**
2406
+ * Get the total number of registered jobs across all roots.
2407
+ * Includes both per-root jobs and global before/after jobs.
2408
+ * @returns {number} Total job count
2409
+ */
2410
+ getJobCount() {
2411
+ let count = 0;
2412
+ for (const root of this.roots.values()) {
2413
+ count += root.jobs.size;
2414
+ }
2415
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2416
+ }
2417
+ /**
2418
+ * Get all registered job IDs across all roots.
2419
+ * Includes both per-root jobs and global before/after jobs.
2420
+ * @returns {string[]} Array of all job IDs
2421
+ */
2422
+ getJobIds() {
2423
+ const ids = [];
2424
+ for (const root of this.roots.values()) {
2425
+ ids.push(...root.jobs.keys());
2426
+ }
2427
+ ids.push(...this.globalBeforeJobs.keys());
2428
+ ids.push(...this.globalAfterJobs.keys());
2429
+ return ids;
2430
+ }
2431
+ /**
2432
+ * Get the number of registered roots (Canvas instances).
2433
+ * @returns {number} Number of registered roots
2434
+ */
2435
+ getRootCount() {
2436
+ return this.roots.size;
2437
+ }
2438
+ /**
2439
+ * Check if any user (non-system) jobs are registered in a specific phase.
2440
+ * Used by the default render job to know if a user has taken over rendering.
2441
+ *
2442
+ * @param phase The phase to check
2443
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2444
+ * @returns true if any user jobs exist in the phase
2445
+ */
2446
+ hasUserJobsInPhase(phase, rootId) {
2447
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2448
+ return rootsToCheck.some((root) => {
2449
+ if (!root) return false;
2450
+ for (const job of root.jobs.values()) {
2451
+ if (job.phase === phase && !job.system && job.enabled) return true;
2452
+ }
2453
+ return false;
2454
+ });
2455
+ }
2456
+ //* Utility Methods ================================
2457
+ /**
2458
+ * Generate a unique root ID for automatic root registration.
2459
+ * @returns {string} A unique root ID in the format 'root_N'
2460
+ */
2461
+ generateRootId() {
2462
+ return `root_${this.nextRootIndex++}`;
2463
+ }
2464
+ /**
2465
+ * Generate a unique job ID.
2466
+ * @returns {string} A unique job ID in the format 'job_N'
2467
+ * @private
2468
+ */
2469
+ generateJobId() {
2470
+ return `job_${this.nextJobIndex}`;
2471
+ }
2472
+ /**
2473
+ * Normalize before/after constraints to a Set.
2474
+ * Handles undefined, single string, or array inputs.
2475
+ * @param {string | string[] | undefined} value - The constraint value(s)
2476
+ * @returns {Set<string>} Normalized Set of constraint strings
2477
+ * @private
2478
+ */
2479
+ normalizeConstraints(value) {
2480
+ if (!value) return /* @__PURE__ */ new Set();
2481
+ if (Array.isArray(value)) return new Set(value);
2482
+ return /* @__PURE__ */ new Set([value]);
2483
+ }
2484
+ };
2485
+ //* Static State & Methods (Singleton Usage) ================================
2486
+ //* Cross-Bundle Singleton Key ==============================
2487
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2488
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2489
+ __publicField$1(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2490
+ let Scheduler = _Scheduler;
2491
+ const getScheduler = () => Scheduler.get();
2492
+ if (hmrData) {
2493
+ hmrData.accept?.();
2494
+ }
2495
+
2496
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2497
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2498
+ const createStore = (invalidate, advance) => {
2499
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2500
+ const position = new webgpu.Vector3();
2501
+ const defaultTarget = new webgpu.Vector3();
2502
+ const tempTarget = new webgpu.Vector3();
2503
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2504
+ const { width, height, top, left } = size;
2505
+ const aspect = width / height;
2506
+ if (target.isVector3) tempTarget.copy(target);
2507
+ else tempTarget.set(...target);
2508
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2509
+ if (isOrthographicCamera(camera)) {
2510
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2511
+ } else {
2512
+ const fov = camera.fov * Math.PI / 180;
2513
+ const h = 2 * Math.tan(fov / 2) * distance;
2514
+ const w = h * (width / height);
2515
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2516
+ }
2517
+ }
2518
+ let performanceTimeout = void 0;
2519
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2520
+ const pointer = new webgpu.Vector2();
2521
+ const rootState = {
2522
+ set,
2523
+ get,
2524
+ // Mock objects that have to be configured
2525
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2526
+ primaryStore: null,
2527
+ gl: null,
2528
+ renderer: null,
2529
+ camera: null,
2530
+ frustum: new webgpu.Frustum(),
2531
+ autoUpdateFrustum: true,
2532
+ raycaster: null,
2533
+ events: { priority: 1, enabled: true, connected: false },
2534
+ scene: null,
2535
+ rootScene: null,
2536
+ xr: null,
2537
+ inspector: null,
2538
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2539
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2540
+ textureColorSpace: webgpu.SRGBColorSpace,
2541
+ isLegacy: false,
2542
+ webGPUSupported: false,
2543
+ isNative: false,
2544
+ controls: null,
2545
+ pointer,
2546
+ mouse: pointer,
2547
+ frameloop: "always",
2548
+ onPointerMissed: void 0,
2549
+ onDragOverMissed: void 0,
2550
+ onDropMissed: void 0,
2551
+ performance: {
2552
+ current: 1,
2553
+ min: 0.5,
2554
+ max: 1,
2555
+ debounce: 200,
2556
+ regress: () => {
2557
+ const state2 = get();
2558
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2559
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2560
+ performanceTimeout = setTimeout(
2561
+ () => setPerformanceCurrent(get().performance.max),
2562
+ state2.performance.debounce
2563
+ );
2564
+ }
2565
+ },
2566
+ size: { width: 0, height: 0, top: 0, left: 0 },
2567
+ viewport: {
2568
+ initialDpr: 0,
2569
+ dpr: 0,
2570
+ width: 0,
2571
+ height: 0,
2572
+ top: 0,
2573
+ left: 0,
2574
+ aspect: 0,
2575
+ distance: 0,
2576
+ factor: 0,
2577
+ getCurrentViewport
2578
+ },
2579
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2580
+ setSize: (width, height, top, left) => {
2581
+ const state2 = get();
2582
+ if (width === void 0) {
2583
+ set({ _sizeImperative: false });
2584
+ if (state2._sizeProps) {
2585
+ const { width: propW, height: propH } = state2._sizeProps;
2586
+ if (propW !== void 0 || propH !== void 0) {
2587
+ const currentSize = state2.size;
2588
+ const newSize = {
2589
+ width: propW ?? currentSize.width,
2590
+ height: propH ?? currentSize.height,
2591
+ top: currentSize.top,
2592
+ left: currentSize.left
2593
+ };
2594
+ set((s) => ({
2595
+ size: newSize,
2596
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2597
+ }));
2598
+ getScheduler().invalidate();
2599
+ }
2600
+ }
2601
+ return;
2602
+ }
2603
+ const w = width;
2604
+ const h = height ?? width;
2605
+ const t = top ?? state2.size.top;
2606
+ const l = left ?? state2.size.left;
2607
+ const size = { width: w, height: h, top: t, left: l };
2608
+ set((s) => ({
2609
+ size,
2610
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2611
+ _sizeImperative: true
2612
+ }));
2613
+ getScheduler().invalidate();
2614
+ },
2615
+ setDpr: (dpr) => set((state2) => {
2616
+ const resolved = calculateDpr(dpr);
2617
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2618
+ }),
2619
+ setFrameloop: (frameloop = "always") => {
2620
+ set(() => ({ frameloop }));
2621
+ },
2622
+ setError: (error) => set(() => ({ error })),
2623
+ error: null,
2624
+ //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2625
+ uniforms: {},
2626
+ nodes: {},
2627
+ textures: /* @__PURE__ */ new Map(),
2628
+ postProcessing: null,
2629
+ passes: {},
2630
+ _hmrVersion: 0,
2631
+ _sizeImperative: false,
2632
+ _sizeProps: null,
2633
+ previousRoot: void 0,
2634
+ internal: {
2635
+ // Events
2636
+ interaction: [],
2637
+ hovered: /* @__PURE__ */ new Map(),
2638
+ subscribers: [],
2639
+ initialClick: [0, 0],
2640
+ initialHits: [],
2641
+ capturedMap: /* @__PURE__ */ new Map(),
2642
+ lastEvent: React__namespace.createRef(),
2643
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2644
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2645
+ // Occlusion system (WebGPU only)
2646
+ occlusionEnabled: false,
2647
+ occlusionObserver: null,
2648
+ occlusionCache: /* @__PURE__ */ new Map(),
2649
+ helperGroup: null,
2650
+ // Updates
2651
+ active: false,
2652
+ frames: 0,
2653
+ priority: 0,
2654
+ subscribe: (ref, priority, store) => {
2655
+ const internal = get().internal;
2656
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2657
+ internal.subscribers.push({ ref, priority, store });
2658
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2659
+ return () => {
2660
+ const internal2 = get().internal;
2661
+ if (internal2?.subscribers) {
2662
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2663
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2664
+ }
2665
+ };
2666
+ },
2667
+ // Renderer Storage (single source of truth)
2668
+ actualRenderer: null,
2669
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2670
+ scheduler: null
2671
+ }
2672
+ };
2673
+ return rootState;
2674
+ });
2675
+ const state = rootStore.getState();
2676
+ Object.defineProperty(state, "gl", {
2677
+ get() {
2678
+ const currentState = rootStore.getState();
2679
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2680
+ const stack = new Error().stack || "";
2681
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2682
+ if (!isInternalAccess) {
2683
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2684
+ notifyDepreciated({
2685
+ heading: "Accessing state.gl in WebGPU mode",
2686
+ 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
2687
+ });
2688
+ }
2689
+ }
2690
+ return currentState.internal.actualRenderer;
2691
+ },
2692
+ set(value) {
2693
+ rootStore.getState().internal.actualRenderer = value;
2694
+ },
2695
+ enumerable: true,
2696
+ configurable: true
2697
+ });
2698
+ Object.defineProperty(state, "renderer", {
2699
+ get() {
2700
+ return rootStore.getState().internal.actualRenderer;
2701
+ },
2702
+ set(value) {
2703
+ rootStore.getState().internal.actualRenderer = value;
2704
+ },
2705
+ enumerable: true,
2706
+ configurable: true
2707
+ });
2708
+ let oldScene = state.scene;
2709
+ rootStore.subscribe(() => {
2710
+ const currentState = rootStore.getState();
2711
+ const { scene, rootScene, set } = currentState;
2712
+ if (scene !== oldScene) {
2713
+ oldScene = scene;
2714
+ if (scene?.isScene && scene !== rootScene) {
2715
+ set({ rootScene: scene });
2716
+ }
2717
+ }
2718
+ });
2719
+ let oldSize = state.size;
2720
+ let oldDpr = state.viewport.dpr;
2721
+ let oldCamera = state.camera;
2722
+ rootStore.subscribe(() => {
2723
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2724
+ const actualRenderer = internal.actualRenderer;
2725
+ const canvasTarget = internal.canvasTarget;
2726
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2727
+ oldSize = size;
2728
+ oldDpr = viewport.dpr;
2729
+ updateCamera(camera, size);
2730
+ if (canvasTarget) {
2731
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2732
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2733
+ canvasTarget.setSize(size.width, size.height, updateStyle);
2734
+ } else {
2735
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2736
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2737
+ actualRenderer.setSize(size.width, size.height, updateStyle);
2738
+ }
2739
+ }
2740
+ if (camera !== oldCamera) {
2741
+ oldCamera = camera;
2742
+ const { rootScene } = rootStore.getState();
2743
+ if (camera && rootScene && !camera.parent) {
2744
+ rootScene.add(camera);
2745
+ }
2746
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2747
+ const currentState = rootStore.getState();
2748
+ if (currentState.autoUpdateFrustum && camera) {
2749
+ updateFrustum(camera, currentState.frustum);
2750
+ }
2309
2751
  }
2310
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2311
- }
2312
- /**
2313
- * Get all registered job IDs across all roots.
2314
- * Includes both per-root jobs and global before/after jobs.
2315
- * @returns {string[]} Array of all job IDs
2316
- */
2317
- getJobIds() {
2318
- const ids = [];
2319
- for (const root of this.roots.values()) {
2320
- ids.push(...root.jobs.keys());
2752
+ });
2753
+ rootStore.subscribe((state2) => invalidate(state2));
2754
+ return rootStore;
2755
+ };
2756
+
2757
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2758
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2759
+ function getLoader(Proto) {
2760
+ if (isConstructor$1(Proto)) {
2761
+ let loader = memoizedLoaders.get(Proto);
2762
+ if (!loader) {
2763
+ loader = new Proto();
2764
+ memoizedLoaders.set(Proto, loader);
2321
2765
  }
2322
- ids.push(...this.globalBeforeJobs.keys());
2323
- ids.push(...this.globalAfterJobs.keys());
2324
- return ids;
2325
- }
2326
- /**
2327
- * Get the number of registered roots (Canvas instances).
2328
- * @returns {number} Number of registered roots
2329
- */
2330
- getRootCount() {
2331
- return this.roots.size;
2332
- }
2333
- /**
2334
- * Check if any user (non-system) jobs are registered in a specific phase.
2335
- * Used by the default render job to know if a user has taken over rendering.
2336
- *
2337
- * @param phase The phase to check
2338
- * @param rootId Optional root ID to check (checks all roots if not provided)
2339
- * @returns true if any user jobs exist in the phase
2340
- */
2341
- hasUserJobsInPhase(phase, rootId) {
2342
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2343
- return rootsToCheck.some((root) => {
2344
- if (!root) return false;
2345
- for (const job of root.jobs.values()) {
2346
- if (job.phase === phase && !job.system && job.enabled) return true;
2347
- }
2348
- return false;
2349
- });
2350
- }
2351
- //* Utility Methods ================================
2352
- /**
2353
- * Generate a unique root ID for automatic root registration.
2354
- * @returns {string} A unique root ID in the format 'root_N'
2355
- */
2356
- generateRootId() {
2357
- return `root_${this.nextRootIndex++}`;
2358
- }
2359
- /**
2360
- * Generate a unique job ID.
2361
- * @returns {string} A unique job ID in the format 'job_N'
2362
- * @private
2363
- */
2364
- generateJobId() {
2365
- return `job_${this.nextJobIndex}`;
2366
- }
2367
- /**
2368
- * Normalize before/after constraints to a Set.
2369
- * Handles undefined, single string, or array inputs.
2370
- * @param {string | string[] | undefined} value - The constraint value(s)
2371
- * @returns {Set<string>} Normalized Set of constraint strings
2372
- * @private
2373
- */
2374
- normalizeConstraints(value) {
2375
- if (!value) return /* @__PURE__ */ new Set();
2376
- if (Array.isArray(value)) return new Set(value);
2377
- return /* @__PURE__ */ new Set([value]);
2766
+ return loader;
2378
2767
  }
2379
- };
2380
- //* Static State & Methods (Singleton Usage) ================================
2381
- //* Cross-Bundle Singleton Key ==============================
2382
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2383
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2384
- __publicField$1(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2385
- let Scheduler = _Scheduler;
2386
- const getScheduler = () => Scheduler.get();
2387
- if (hmrData) {
2388
- hmrData.accept?.();
2768
+ return Proto;
2769
+ }
2770
+ function loadingFn(extensions, onProgress) {
2771
+ return function(Proto, input) {
2772
+ const loader = getLoader(Proto);
2773
+ if (extensions) extensions(loader);
2774
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2775
+ return loader.loadAsync(input, onProgress).then((data) => {
2776
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2777
+ return data;
2778
+ });
2779
+ }
2780
+ return new Promise(
2781
+ (res, reject) => loader.load(
2782
+ input,
2783
+ (data) => {
2784
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2785
+ res(data);
2786
+ },
2787
+ onProgress,
2788
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2789
+ )
2790
+ );
2791
+ };
2792
+ }
2793
+ function useLoader(loader, input, extensions, onProgress) {
2794
+ const keys = Array.isArray(input) ? input : [input];
2795
+ const fn = loadingFn(extensions, onProgress);
2796
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2797
+ return Array.isArray(input) ? results : results[0];
2389
2798
  }
2799
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2800
+ const keys = Array.isArray(input) ? input : [input];
2801
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2802
+ };
2803
+ useLoader.clear = function(loader, input) {
2804
+ const keys = Array.isArray(input) ? input : [input];
2805
+ keys.forEach((key) => suspendReact.clear([loader, key]));
2806
+ };
2807
+ useLoader.loader = getLoader;
2390
2808
 
2391
2809
  function useFrame(callback, priorityOrOptions) {
2392
2810
  const store = React__namespace.useContext(context);
@@ -2567,6 +2985,9 @@ function useTexture(input, optionsOrOnLoad) {
2567
2985
  const textureCache = useThree((state) => state.textures);
2568
2986
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2569
2987
  const { onLoad, cache = false } = options;
2988
+ const onLoadRef = React.useRef(onLoad);
2989
+ onLoadRef.current = onLoad;
2990
+ const onLoadCalledForRef = React.useRef(null);
2570
2991
  const urls = React.useMemo(() => getUrls(input), [input]);
2571
2992
  const cachedResult = React.useMemo(() => {
2572
2993
  if (!cache) return null;
@@ -2577,9 +2998,13 @@ function useTexture(input, optionsOrOnLoad) {
2577
2998
  webgpu.TextureLoader,
2578
2999
  IsObject(input) ? Object.values(input) : input
2579
3000
  );
3001
+ const inputKey = urls.join("\0");
2580
3002
  React.useLayoutEffect(() => {
2581
- if (!cachedResult) onLoad?.(loadedTextures);
2582
- }, [onLoad, cachedResult, loadedTextures]);
3003
+ if (cachedResult) return;
3004
+ if (onLoadCalledForRef.current === inputKey) return;
3005
+ onLoadCalledForRef.current = inputKey;
3006
+ onLoadRef.current?.(loadedTextures);
3007
+ }, [cachedResult, loadedTextures, inputKey]);
2583
3008
  React.useEffect(() => {
2584
3009
  if (cachedResult) return;
2585
3010
  if ("initTexture" in renderer) {
@@ -2746,14 +3171,31 @@ function useTextures() {
2746
3171
  }, [store]);
2747
3172
  }
2748
3173
 
2749
- function useRenderTarget(width, height, options) {
3174
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2750
3175
  const isLegacy = useThree((s) => s.isLegacy);
2751
3176
  const size = useThree((s) => s.size);
3177
+ let width;
3178
+ let height;
3179
+ let opts;
3180
+ if (typeof widthOrOptions === "object") {
3181
+ opts = widthOrOptions;
3182
+ } else if (typeof widthOrOptions === "number") {
3183
+ width = widthOrOptions;
3184
+ if (typeof heightOrOptions === "object") {
3185
+ height = widthOrOptions;
3186
+ opts = heightOrOptions;
3187
+ } else if (typeof heightOrOptions === "number") {
3188
+ height = heightOrOptions;
3189
+ opts = options;
3190
+ } else {
3191
+ height = widthOrOptions;
3192
+ }
3193
+ }
2752
3194
  return React.useMemo(() => {
2753
3195
  const w = width ?? size.width;
2754
3196
  const h = height ?? size.height;
2755
- return new webgpu.RenderTarget(w, h, options);
2756
- }, [width, height, size.width, size.height, options, isLegacy]);
3197
+ return new webgpu.RenderTarget(w, h, opts);
3198
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2757
3199
  }
2758
3200
 
2759
3201
  function useStore() {
@@ -2803,7 +3245,7 @@ function addTail(callback) {
2803
3245
  function invalidate(state, frames = 1, stackFrames = false) {
2804
3246
  getScheduler().invalidate(frames, stackFrames);
2805
3247
  }
2806
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3248
+ function advance(timestamp) {
2807
3249
  getScheduler().step(timestamp);
2808
3250
  }
2809
3251
 
@@ -14257,6 +14699,7 @@ function swapInstances() {
14257
14699
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14258
14700
  instance.object.__r3f = instance;
14259
14701
  setFiberRef(fiber, instance.object);
14702
+ delete instance.appliedOnce;
14260
14703
  applyProps(instance.object, instance.props);
14261
14704
  if (instance.props.attach) {
14262
14705
  attach(parent, instance);
@@ -14330,8 +14773,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14330
14773
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14331
14774
  if (isTailSibling) swapInstances();
14332
14775
  },
14333
- finalizeInitialChildren: () => false,
14334
- commitMount() {
14776
+ finalizeInitialChildren: (instance) => {
14777
+ for (const prop in instance.props) {
14778
+ if (isFromRef(instance.props[prop])) return true;
14779
+ }
14780
+ return false;
14781
+ },
14782
+ commitMount(instance) {
14783
+ const resolved = {};
14784
+ for (const prop in instance.props) {
14785
+ const value = instance.props[prop];
14786
+ if (isFromRef(value)) {
14787
+ const ref = value[FROM_REF];
14788
+ if (ref.current != null) resolved[prop] = ref.current;
14789
+ }
14790
+ }
14791
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14335
14792
  },
14336
14793
  getPublicInstance: (instance) => instance?.object,
14337
14794
  prepareForCommit: () => null,
@@ -14552,6 +15009,9 @@ function createRoot(canvas) {
14552
15009
  let resolve;
14553
15010
  pending = new Promise((_resolve) => resolve = _resolve);
14554
15011
  const {
15012
+ id: canvasId,
15013
+ primaryCanvas,
15014
+ scheduler: schedulerConfig,
14555
15015
  gl: glConfig,
14556
15016
  renderer: rendererConfig,
14557
15017
  size: propsSize,
@@ -14559,10 +15019,7 @@ function createRoot(canvas) {
14559
15019
  events,
14560
15020
  onCreated: onCreatedCallback,
14561
15021
  shadows = false,
14562
- linear = false,
14563
- flat = false,
14564
15022
  textureColorSpace = webgpu.SRGBColorSpace,
14565
- legacy = false,
14566
15023
  orthographic = false,
14567
15024
  frameloop = "always",
14568
15025
  dpr = [1, 2],
@@ -14574,11 +15031,13 @@ function createRoot(canvas) {
14574
15031
  onDropMissed,
14575
15032
  autoUpdateFrustum = true,
14576
15033
  occlusion = false,
14577
- _sizeProps
15034
+ _sizeProps,
15035
+ forceEven
14578
15036
  } = props;
14579
15037
  const state = store.getState();
14580
15038
  const defaultGPUProps = {
14581
- canvas
15039
+ canvas,
15040
+ antialias: true
14582
15041
  };
14583
15042
  if (glConfig && !R3F_BUILD_LEGACY) {
14584
15043
  throw new Error(
@@ -14589,7 +15048,27 @@ function createRoot(canvas) {
14589
15048
  throw new Error("Cannot use both gl and renderer props at the same time");
14590
15049
  }
14591
15050
  let renderer = state.internal.actualRenderer;
14592
- if (!state.internal.actualRenderer) {
15051
+ if (primaryCanvas && !state.internal.actualRenderer) {
15052
+ const primary = await waitForPrimary(primaryCanvas);
15053
+ renderer = primary.renderer;
15054
+ state.internal.actualRenderer = renderer;
15055
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15056
+ primary.store.setState((prev) => ({
15057
+ internal: { ...prev.internal, isMultiCanvas: true }
15058
+ }));
15059
+ state.set((prev) => ({
15060
+ webGPUSupported: primary.store.getState().webGPUSupported,
15061
+ renderer,
15062
+ primaryStore: primary.store,
15063
+ internal: {
15064
+ ...prev.internal,
15065
+ canvasTarget,
15066
+ isMultiCanvas: true,
15067
+ isSecondary: true,
15068
+ targetId: primaryCanvas
15069
+ }
15070
+ }));
15071
+ } else if (!state.internal.actualRenderer) {
14593
15072
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14594
15073
  if (!renderer.hasInitialized?.()) {
14595
15074
  await renderer.init();
@@ -14597,7 +15076,18 @@ function createRoot(canvas) {
14597
15076
  const backend = renderer.backend;
14598
15077
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14599
15078
  state.internal.actualRenderer = renderer;
14600
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15079
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15080
+ if (canvasId && !state.internal.isSecondary) {
15081
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15082
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15083
+ state.set((prev) => ({
15084
+ internal: {
15085
+ ...prev.internal,
15086
+ canvasTarget,
15087
+ unregisterPrimary
15088
+ }
15089
+ }));
15090
+ }
14601
15091
  }
14602
15092
  let raycaster = state.raycaster;
14603
15093
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
@@ -14606,6 +15096,7 @@ function createRoot(canvas) {
14606
15096
  if (!is.equ(params, raycaster.params, shallowLoose)) {
14607
15097
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14608
15098
  }
15099
+ let tempCamera = state.camera;
14609
15100
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14610
15101
  lastCamera = cameraOptions;
14611
15102
  const isCamera = cameraOptions?.isCamera;
@@ -14625,6 +15116,7 @@ function createRoot(canvas) {
14625
15116
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14626
15117
  }
14627
15118
  state.set({ camera });
15119
+ tempCamera = camera;
14628
15120
  raycaster.camera = camera;
14629
15121
  }
14630
15122
  if (!state.scene) {
@@ -14642,7 +15134,7 @@ function createRoot(canvas) {
14642
15134
  rootScene: scene,
14643
15135
  internal: { ...prev.internal, container: scene }
14644
15136
  }));
14645
- const camera = state.camera;
15137
+ const camera = tempCamera;
14646
15138
  if (camera && !camera.parent) scene.add(camera);
14647
15139
  }
14648
15140
  if (events && !state.events.handlers) {
@@ -14659,6 +15151,9 @@ function createRoot(canvas) {
14659
15151
  if (_sizeProps !== void 0) {
14660
15152
  state.set({ _sizeProps });
14661
15153
  }
15154
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15155
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15156
+ }
14662
15157
  const size = computeInitialSize(canvas, propsSize);
14663
15158
  if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14664
15159
  const wasImperative = state._sizeImperative;
@@ -14688,7 +15183,7 @@ function createRoot(canvas) {
14688
15183
  const handleXRFrame = (timestamp, frame) => {
14689
15184
  const state2 = store.getState();
14690
15185
  if (state2.frameloop === "never") return;
14691
- advance(timestamp, true);
15186
+ advance(timestamp);
14692
15187
  };
14693
15188
  const actualRenderer = state.internal.actualRenderer;
14694
15189
  const handleSessionChange = () => {
@@ -14700,16 +15195,16 @@ function createRoot(canvas) {
14700
15195
  };
14701
15196
  const xr = {
14702
15197
  connect() {
14703
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14704
- const actualRenderer2 = renderer2 || gl;
14705
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14706
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15198
+ const { gl, renderer: renderer2 } = store.getState();
15199
+ const xrManager = (renderer2 || gl).xr;
15200
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15201
+ xrManager.addEventListener("sessionend", handleSessionChange);
14707
15202
  },
14708
15203
  disconnect() {
14709
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14710
- const actualRenderer2 = renderer2 || gl;
14711
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14712
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15204
+ const { gl, renderer: renderer2 } = store.getState();
15205
+ const xrManager = (renderer2 || gl).xr;
15206
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15207
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14713
15208
  }
14714
15209
  };
14715
15210
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14737,6 +15232,10 @@ function createRoot(canvas) {
14737
15232
  renderer.shadowMap.needsUpdate = true;
14738
15233
  }
14739
15234
  }
15235
+ if (!configured) {
15236
+ renderer.outputColorSpace = webgpu.SRGBColorSpace;
15237
+ renderer.toneMapping = webgpu.ACESFilmicToneMapping;
15238
+ }
14740
15239
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14741
15240
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14742
15241
  lastConfiguredProps.textureColorSpace = textureColorSpace;
@@ -14753,11 +15252,26 @@ function createRoot(canvas) {
14753
15252
  const scheduler = getScheduler();
14754
15253
  const rootId = state.internal.rootId;
14755
15254
  if (!rootId) {
14756
- const newRootId = scheduler.generateRootId();
15255
+ const newRootId = canvasId || scheduler.generateRootId();
14757
15256
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14758
15257
  getState: () => store.getState(),
14759
15258
  onError: (err) => store.getState().setError(err)
14760
15259
  });
15260
+ const unregisterCanvasTarget = scheduler.register(
15261
+ () => {
15262
+ const state2 = store.getState();
15263
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15264
+ const renderer2 = state2.internal.actualRenderer;
15265
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15266
+ }
15267
+ },
15268
+ {
15269
+ id: `${newRootId}_canvasTarget`,
15270
+ rootId: newRootId,
15271
+ phase: "start",
15272
+ system: true
15273
+ }
15274
+ );
14761
15275
  const unregisterFrustum = scheduler.register(
14762
15276
  () => {
14763
15277
  const state2 = store.getState();
@@ -14799,11 +15313,15 @@ function createRoot(canvas) {
14799
15313
  }
14800
15314
  },
14801
15315
  {
14802
- id: `${newRootId}_render`,
15316
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15317
+ id: canvasId || `${newRootId}_render`,
14803
15318
  rootId: newRootId,
14804
15319
  phase: "render",
14805
- system: true
15320
+ system: true,
14806
15321
  // Internal flag: this is a system job, not user-controlled
15322
+ // Apply scheduler config for render ordering and rate limiting
15323
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15324
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14807
15325
  }
14808
15326
  );
14809
15327
  state.set((state2) => ({
@@ -14812,6 +15330,7 @@ function createRoot(canvas) {
14812
15330
  rootId: newRootId,
14813
15331
  unregisterRoot: () => {
14814
15332
  unregisterRoot();
15333
+ unregisterCanvasTarget();
14815
15334
  unregisterFrustum();
14816
15335
  unregisterVisibility();
14817
15336
  unregisterRender();
@@ -14870,15 +15389,24 @@ function unmountComponentAtNode(canvas, callback) {
14870
15389
  const renderer = state.internal.actualRenderer;
14871
15390
  const unregisterRoot = state.internal.unregisterRoot;
14872
15391
  if (unregisterRoot) unregisterRoot();
15392
+ const unregisterPrimary = state.internal.unregisterPrimary;
15393
+ if (unregisterPrimary) unregisterPrimary();
15394
+ const canvasTarget = state.internal.canvasTarget;
15395
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14873
15396
  state.events.disconnect?.();
14874
15397
  cleanupHelperGroup(root.store);
14875
- renderer?.renderLists?.dispose?.();
14876
- renderer?.forceContextLoss?.();
14877
- if (renderer?.xr) state.xr.disconnect();
15398
+ if (state.isLegacy && renderer) {
15399
+ ;
15400
+ renderer.renderLists?.dispose?.();
15401
+ renderer.forceContextLoss?.();
15402
+ }
15403
+ if (!state.internal.isSecondary) {
15404
+ if (renderer?.xr) state.xr.disconnect();
15405
+ }
14878
15406
  dispose(state.scene);
14879
15407
  _roots.delete(canvas);
14880
15408
  if (callback) callback(canvas);
14881
- } catch (e) {
15409
+ } catch {
14882
15410
  }
14883
15411
  }, 500);
14884
15412
  }
@@ -14886,36 +15414,34 @@ function unmountComponentAtNode(canvas, callback) {
14886
15414
  }
14887
15415
  }
14888
15416
  function createPortal(children, container, state) {
14889
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15417
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14890
15418
  }
14891
- function PortalWrapper({ children, container, state }) {
15419
+ function Portal({ children, container, state }) {
14892
15420
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14893
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15421
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14894
15422
  if (isRef(container)) return container.current ?? null;
14895
15423
  return container;
14896
15424
  });
15425
+ const setResolvedContainer = React.useCallback(
15426
+ (newContainer) => {
15427
+ if (!newContainer || newContainer === resolvedContainer) return;
15428
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15429
+ },
15430
+ [resolvedContainer, _setResolvedContainer, isRef]
15431
+ );
14897
15432
  React.useMemo(() => {
14898
- if (isRef(container)) {
14899
- const current = container.current;
14900
- if (!current) {
14901
- queueMicrotask(() => {
14902
- const updated = container.current;
14903
- if (updated && updated !== resolvedContainer) {
14904
- setResolvedContainer(updated);
14905
- }
14906
- });
14907
- } else if (current !== resolvedContainer) {
14908
- setResolvedContainer(current);
14909
- }
14910
- } else if (container !== resolvedContainer) {
14911
- setResolvedContainer(container);
15433
+ if (isRef(container) && !container.current) {
15434
+ return queueMicrotask(() => {
15435
+ setResolvedContainer(container.current);
15436
+ });
14912
15437
  }
14913
- }, [container, resolvedContainer, isRef]);
15438
+ setResolvedContainer(container);
15439
+ }, [container, isRef, setResolvedContainer]);
14914
15440
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14915
15441
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14916
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15442
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14917
15443
  }
14918
- function Portal({ state = {}, children, container }) {
15444
+ function PortalInner({ state = {}, children, container }) {
14919
15445
  const { events, size, injectScene = true, ...rest } = state;
14920
15446
  const previousRoot = useStore();
14921
15447
  const [raycaster] = React.useState(() => new webgpu.Raycaster());
@@ -14936,11 +15462,12 @@ function Portal({ state = {}, children, container }) {
14936
15462
  };
14937
15463
  }, [portalScene, container, injectScene]);
14938
15464
  const inject = useMutableCallback((rootState, injectState) => {
15465
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14939
15466
  let viewport = void 0;
14940
- if (injectState.camera && size) {
15467
+ if (injectState.camera && (size || injectState.size)) {
14941
15468
  const camera = injectState.camera;
14942
- viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), size);
14943
- if (camera !== rootState.camera) updateCamera(camera, size);
15469
+ viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
15470
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14944
15471
  }
14945
15472
  return {
14946
15473
  // The intersect consists of the previous root state
@@ -14957,7 +15484,7 @@ function Portal({ state = {}, children, container }) {
14957
15484
  previousRoot,
14958
15485
  // Events, size and viewport can be overridden by the inject layer
14959
15486
  events: { ...rootState.events, ...injectState.events, ...events },
14960
- size: { ...rootState.size, ...size },
15487
+ size: resolvedSize,
14961
15488
  viewport: { ...rootState.viewport, ...viewport },
14962
15489
  // Layers are allowed to override events
14963
15490
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -14991,15 +15518,13 @@ function CanvasImpl({
14991
15518
  fallback,
14992
15519
  resize,
14993
15520
  style,
15521
+ id,
14994
15522
  gl,
14995
- renderer,
15523
+ renderer: rendererProp,
14996
15524
  events = createPointerEvents,
14997
15525
  eventSource,
14998
15526
  eventPrefix,
14999
15527
  shadows,
15000
- linear,
15001
- flat,
15002
- legacy,
15003
15528
  orthographic,
15004
15529
  frameloop,
15005
15530
  dpr,
@@ -15014,10 +15539,43 @@ function CanvasImpl({
15014
15539
  hmr,
15015
15540
  width,
15016
15541
  height,
15542
+ background,
15543
+ forceEven,
15017
15544
  ...props
15018
15545
  }) {
15546
+ const { primaryCanvas, scheduler, ...rendererConfig } = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp) ? rendererProp : { primaryCanvas: void 0, scheduler: void 0 };
15547
+ const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15019
15548
  React__namespace.useMemo(() => extend(THREE), []);
15020
15549
  const Bridge = useBridge();
15550
+ const backgroundProps = React__namespace.useMemo(() => {
15551
+ if (!background) return null;
15552
+ if (typeof background === "object" && !background.isColor) {
15553
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15554
+ return {
15555
+ ...rest,
15556
+ preset,
15557
+ files: envMap || files,
15558
+ backgroundFiles: backgroundMap,
15559
+ background: true
15560
+ };
15561
+ }
15562
+ if (typeof background === "number") {
15563
+ return { color: background, background: true };
15564
+ }
15565
+ if (typeof background === "string") {
15566
+ if (background in presetsObj) {
15567
+ return { preset: background, background: true };
15568
+ }
15569
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15570
+ return { files: background, background: true };
15571
+ }
15572
+ return { color: background, background: true };
15573
+ }
15574
+ if (background.isColor) {
15575
+ return { color: background, background: true };
15576
+ }
15577
+ return null;
15578
+ }, [background]);
15021
15579
  const hasInitialSizeRef = React__namespace.useRef(false);
15022
15580
  const measureConfig = React__namespace.useMemo(() => {
15023
15581
  if (!hasInitialSizeRef.current) {
@@ -15034,15 +15592,20 @@ function CanvasImpl({
15034
15592
  };
15035
15593
  }, [resize, hasInitialSizeRef.current]);
15036
15594
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15037
- const effectiveSize = React__namespace.useMemo(
15038
- () => ({
15039
- width: width ?? containerRect.width,
15040
- height: height ?? containerRect.height,
15595
+ const effectiveSize = React__namespace.useMemo(() => {
15596
+ let w = width ?? containerRect.width;
15597
+ let h = height ?? containerRect.height;
15598
+ if (forceEven) {
15599
+ w = Math.ceil(w / 2) * 2;
15600
+ h = Math.ceil(h / 2) * 2;
15601
+ }
15602
+ return {
15603
+ width: w,
15604
+ height: h,
15041
15605
  top: containerRect.top,
15042
15606
  left: containerRect.left
15043
- }),
15044
- [width, height, containerRect]
15045
- );
15607
+ };
15608
+ }, [width, height, containerRect, forceEven]);
15046
15609
  if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15047
15610
  hasInitialSizeRef.current = true;
15048
15611
  }
@@ -15082,14 +15645,14 @@ function CanvasImpl({
15082
15645
  async function run() {
15083
15646
  if (!effectActiveRef.current || !root.current) return;
15084
15647
  await root.current.configure({
15648
+ id,
15649
+ primaryCanvas,
15650
+ scheduler,
15085
15651
  gl,
15086
15652
  renderer,
15087
15653
  scene,
15088
15654
  events,
15089
15655
  shadows,
15090
- linear,
15091
- flat,
15092
- legacy,
15093
15656
  orthographic,
15094
15657
  frameloop,
15095
15658
  dpr,
@@ -15099,6 +15662,7 @@ function CanvasImpl({
15099
15662
  size: effectiveSize,
15100
15663
  // Store size props for reset functionality
15101
15664
  _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15665
+ forceEven,
15102
15666
  // Pass mutable reference to onPointerMissed so it's free to update
15103
15667
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15104
15668
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15122,7 +15686,10 @@ function CanvasImpl({
15122
15686
  });
15123
15687
  if (!effectActiveRef.current || !root.current) return;
15124
15688
  root.current.render(
15125
- /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15689
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
15690
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15691
+ children ?? null
15692
+ ] }) }) })
15126
15693
  );
15127
15694
  }
15128
15695
  run();
@@ -15149,14 +15716,16 @@ function CanvasImpl({
15149
15716
  const canvas = canvasRef.current;
15150
15717
  if (!canvas) return;
15151
15718
  const handleHMR = () => {
15152
- const rootEntry = _roots.get(canvas);
15153
- if (rootEntry?.store) {
15154
- rootEntry.store.setState((state) => ({
15155
- nodes: {},
15156
- uniforms: {},
15157
- _hmrVersion: state._hmrVersion + 1
15158
- }));
15159
- }
15719
+ queueMicrotask(() => {
15720
+ const rootEntry = _roots.get(canvas);
15721
+ if (rootEntry?.store) {
15722
+ rootEntry.store.setState((state) => ({
15723
+ nodes: {},
15724
+ uniforms: {},
15725
+ _hmrVersion: state._hmrVersion + 1
15726
+ }));
15727
+ }
15728
+ });
15160
15729
  };
15161
15730
  if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15162
15731
  const hot = undefined;
@@ -15185,7 +15754,7 @@ function CanvasImpl({
15185
15754
  ...style
15186
15755
  },
15187
15756
  ...props,
15188
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15757
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15189
15758
  }
15190
15759
  );
15191
15760
  }
@@ -15258,6 +15827,22 @@ let ScopedStore = _ScopedStore;
15258
15827
  function createScopedStore(data) {
15259
15828
  return new ScopedStore(data);
15260
15829
  }
15830
+ function createLazyCreatorState(state) {
15831
+ let _uniforms = null;
15832
+ let _nodes = null;
15833
+ return Object.create(state, {
15834
+ uniforms: {
15835
+ get() {
15836
+ return _uniforms ?? (_uniforms = createScopedStore(state.uniforms));
15837
+ }
15838
+ },
15839
+ nodes: {
15840
+ get() {
15841
+ return _nodes ?? (_nodes = createScopedStore(state.nodes));
15842
+ }
15843
+ }
15844
+ });
15845
+ }
15261
15846
 
15262
15847
  function addTexture(set, key, value) {
15263
15848
  set((state) => {
@@ -15298,6 +15883,27 @@ function createTextureOperations(set) {
15298
15883
  removeMultiple: (keys) => removeTextures(set, keys)
15299
15884
  };
15300
15885
  }
15886
+ function extractTSLValue(value) {
15887
+ if (value === null || value === void 0) return value;
15888
+ if (typeof value !== "object") return value;
15889
+ const node = value;
15890
+ if (!node.isNode) return value;
15891
+ if (node.isConstNode) {
15892
+ return node.value;
15893
+ }
15894
+ if ("value" in node) {
15895
+ let extractedValue = node.value;
15896
+ if (typeof node.traverse === "function") {
15897
+ node.traverse((n) => {
15898
+ if (n.isConstNode) {
15899
+ extractedValue = n.value;
15900
+ }
15901
+ });
15902
+ }
15903
+ return extractedValue;
15904
+ }
15905
+ return value;
15906
+ }
15301
15907
  function vectorize(inObject) {
15302
15908
  if (inObject === null || inObject === void 0) return inObject;
15303
15909
  if (typeof inObject === "string") {
@@ -15310,6 +15916,9 @@ function vectorize(inObject) {
15310
15916
  }
15311
15917
  if (typeof inObject !== "object") return inObject;
15312
15918
  const obj = inObject;
15919
+ if (obj.isNode) {
15920
+ return extractTSLValue(inObject);
15921
+ }
15313
15922
  if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
15314
15923
  if (obj.isMatrix3 || obj.isMatrix4) return inObject;
15315
15924
  if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
@@ -15375,17 +15984,14 @@ function useUniforms(creatorOrScope, scope) {
15375
15984
  const rebuildUniforms = React.useCallback(
15376
15985
  (targetScope) => {
15377
15986
  store.setState((state) => {
15378
- let newUniforms = state.uniforms;
15987
+ let newUniforms = {};
15379
15988
  if (targetScope && targetScope !== "root") {
15380
15989
  const { [targetScope]: _, ...rest } = state.uniforms;
15381
15990
  newUniforms = rest;
15382
15991
  } else if (targetScope === "root") {
15383
- newUniforms = {};
15384
15992
  for (const [key, value] of Object.entries(state.uniforms)) {
15385
15993
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15386
15994
  }
15387
- } else {
15388
- newUniforms = {};
15389
15995
  }
15390
15996
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15391
15997
  });
@@ -15393,20 +15999,26 @@ function useUniforms(creatorOrScope, scope) {
15393
15999
  [store]
15394
16000
  );
15395
16001
  const inputForMemoization = React.useMemo(() => {
16002
+ let raw = creatorOrScope;
15396
16003
  if (is.fun(creatorOrScope)) {
15397
- const state = store.getState();
15398
- const wrappedState = {
15399
- ...state,
15400
- uniforms: createScopedStore(state.uniforms),
15401
- nodes: createScopedStore(state.nodes)
15402
- };
15403
- return creatorOrScope(wrappedState);
16004
+ const wrappedState = createLazyCreatorState(store.getState());
16005
+ raw = creatorOrScope(wrappedState);
16006
+ }
16007
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
16008
+ const normalized = {};
16009
+ for (const [key, value] of Object.entries(raw)) {
16010
+ normalized[key] = vectorize(value);
16011
+ }
16012
+ return normalized;
15404
16013
  }
15405
- return creatorOrScope;
16014
+ return raw;
15406
16015
  }, [creatorOrScope, store]);
15407
16016
  const memoizedInput = useCompareMemoize(inputForMemoization);
15408
16017
  const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15409
16018
  const storeUniforms = useThree((s) => s.uniforms);
16019
+ const hmrVersion = useThree((s) => s._hmrVersion);
16020
+ const readerDep = isReader ? storeUniforms : null;
16021
+ const creatorDep = isReader ? null : hmrVersion;
15410
16022
  const uniforms = React.useMemo(() => {
15411
16023
  if (memoizedInput === void 0) {
15412
16024
  return storeUniforms;
@@ -15461,28 +16073,19 @@ function useUniforms(creatorOrScope, scope) {
15461
16073
  }
15462
16074
  }
15463
16075
  return result;
15464
- }, [
15465
- store,
15466
- memoizedInput,
15467
- scope,
15468
- // Only include storeUniforms in deps for reader modes to enable reactivity
15469
- isReader ? storeUniforms : null
15470
- ]);
16076
+ }, [store, memoizedInput, scope, readerDep, creatorDep]);
15471
16077
  return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15472
16078
  }
15473
16079
  function rebuildAllUniforms(store, scope) {
15474
16080
  store.setState((state) => {
15475
- let newUniforms = state.uniforms;
16081
+ let newUniforms = {};
15476
16082
  if (scope && scope !== "root") {
15477
16083
  const { [scope]: _, ...rest } = state.uniforms;
15478
16084
  newUniforms = rest;
15479
16085
  } else if (scope === "root") {
15480
- newUniforms = {};
15481
16086
  for (const [key, value] of Object.entries(state.uniforms)) {
15482
16087
  if (!isUniformNode$1(value)) newUniforms[key] = value;
15483
16088
  }
15484
- } else {
15485
- newUniforms = {};
15486
16089
  }
15487
16090
  return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15488
16091
  });
@@ -15550,15 +16153,17 @@ function isSameThreeType(a, b) {
15550
16153
  }
15551
16154
 
15552
16155
  const isUniformNode = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
16156
+ const isTSLNode$1 = (value) => value !== null && typeof value === "object" && "uuid" in value && "nodeType" in value;
15553
16157
  function useUniform(name, value) {
15554
16158
  const store = useStore();
16159
+ const hmrVersion = useThree((s) => s._hmrVersion);
15555
16160
  return React.useMemo(() => {
15556
16161
  const state = store.getState();
15557
16162
  const set = store.setState;
15558
16163
  const existing = state.uniforms[name];
15559
16164
  if (existing && isUniformNode(existing)) {
15560
- if (value !== void 0) {
15561
- existing.value = value;
16165
+ if (value !== void 0 && !isTSLNode$1(value) && !isUniformNode(value)) {
16166
+ existing.value = typeof value === "string" ? new webgpu.Color(value) : value;
15562
16167
  }
15563
16168
  return existing;
15564
16169
  }
@@ -15567,7 +16172,24 @@ function useUniform(name, value) {
15567
16172
  `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
15568
16173
  );
15569
16174
  }
15570
- const node = tsl.uniform(value);
16175
+ if (isUniformNode(value)) {
16176
+ const node2 = value;
16177
+ if (typeof node2.setName === "function") {
16178
+ node2.setName(name);
16179
+ }
16180
+ set((s) => ({
16181
+ uniforms: { ...s.uniforms, [name]: node2 }
16182
+ }));
16183
+ return node2;
16184
+ }
16185
+ let node;
16186
+ if (isTSLNode$1(value)) {
16187
+ node = tsl.uniform(value);
16188
+ } else if (typeof value === "string") {
16189
+ node = tsl.uniform(new webgpu.Color(value));
16190
+ } else {
16191
+ node = tsl.uniform(value);
16192
+ }
15571
16193
  if (typeof node.setName === "function") {
15572
16194
  node.setName(name);
15573
16195
  }
@@ -15578,7 +16200,7 @@ function useUniform(name, value) {
15578
16200
  }
15579
16201
  }));
15580
16202
  return node;
15581
- }, [store, name]);
16203
+ }, [store, name, hmrVersion]);
15582
16204
  }
15583
16205
 
15584
16206
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
@@ -15642,6 +16264,9 @@ function useNodes(creatorOrScope, scope) {
15642
16264
  const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15643
16265
  const storeNodes = useThree((s) => s.nodes);
15644
16266
  const hmrVersion = useThree((s) => s._hmrVersion);
16267
+ const scopeDep = typeof creatorOrScope === "string" ? creatorOrScope : scope;
16268
+ const readerDep = isReader ? storeNodes : null;
16269
+ const creatorDep = isReader ? null : hmrVersion;
15645
16270
  const nodes = React.useMemo(() => {
15646
16271
  if (creatorOrScope === void 0) {
15647
16272
  return storeNodes;
@@ -15654,11 +16279,7 @@ function useNodes(creatorOrScope, scope) {
15654
16279
  const state = store.getState();
15655
16280
  const set = store.setState;
15656
16281
  const creator = creatorOrScope;
15657
- const wrappedState = {
15658
- ...state,
15659
- uniforms: createScopedStore(state.uniforms),
15660
- nodes: createScopedStore(state.nodes)
15661
- };
16282
+ const wrappedState = createLazyCreatorState(state);
15662
16283
  const created = creator(wrappedState);
15663
16284
  const result = {};
15664
16285
  let hasNewNodes = false;
@@ -15668,7 +16289,7 @@ function useNodes(creatorOrScope, scope) {
15668
16289
  if (currentScope[name]) {
15669
16290
  result[name] = currentScope[name];
15670
16291
  } else {
15671
- if (typeof node.label === "function") node.setName(`${scope}.${name}`);
16292
+ node.setName?.(`${scope}.${name}`);
15672
16293
  result[name] = node;
15673
16294
  hasNewNodes = true;
15674
16295
  }
@@ -15688,7 +16309,7 @@ function useNodes(creatorOrScope, scope) {
15688
16309
  if (existing && isTSLNode(existing)) {
15689
16310
  result[name] = existing;
15690
16311
  } else {
15691
- if (typeof node.label === "function") node.setName(name);
16312
+ node.setName?.(name);
15692
16313
  result[name] = node;
15693
16314
  hasNewNodes = true;
15694
16315
  }
@@ -15697,15 +16318,7 @@ function useNodes(creatorOrScope, scope) {
15697
16318
  set((s) => ({ nodes: { ...s.nodes, ...result } }));
15698
16319
  }
15699
16320
  return result;
15700
- }, [
15701
- store,
15702
- typeof creatorOrScope === "string" ? creatorOrScope : scope,
15703
- // Only include storeNodes in deps for reader modes to enable reactivity
15704
- // Creator mode intentionally excludes it to avoid re-running creator on unrelated changes
15705
- isReader ? storeNodes : null,
15706
- // Include hmrVersion for creator modes to allow rebuildNodes() to bust the cache
15707
- isReader ? null : hmrVersion
15708
- ]);
16321
+ }, [store, scopeDep, readerDep, creatorDep]);
15709
16322
  return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
15710
16323
  }
15711
16324
  function rebuildAllNodes(store, scope) {
@@ -15757,15 +16370,11 @@ function useLocalNodes(creator) {
15757
16370
  const uniforms = useThree((s) => s.uniforms);
15758
16371
  const nodes = useThree((s) => s.nodes);
15759
16372
  const textures = useThree((s) => s.textures);
16373
+ const hmrVersion = useThree((s) => s._hmrVersion);
15760
16374
  return React.useMemo(() => {
15761
- const state = store.getState();
15762
- const wrappedState = {
15763
- ...state,
15764
- uniforms: createScopedStore(state.uniforms),
15765
- nodes: createScopedStore(state.nodes)
15766
- };
16375
+ const wrappedState = createLazyCreatorState(store.getState());
15767
16376
  return creator(wrappedState);
15768
- }, [store, creator, uniforms, nodes, textures]);
16377
+ }, [store, creator, uniforms, nodes, textures, hmrVersion]);
15769
16378
  }
15770
16379
 
15771
16380
  function usePostProcessing(mainCB, setupCB) {
@@ -15865,8 +16474,15 @@ extend(THREE);
15865
16474
 
15866
16475
  exports.Block = Block;
15867
16476
  exports.Canvas = Canvas;
16477
+ exports.Environment = Environment;
16478
+ exports.EnvironmentCube = EnvironmentCube;
16479
+ exports.EnvironmentMap = EnvironmentMap;
16480
+ exports.EnvironmentPortal = EnvironmentPortal;
15868
16481
  exports.ErrorBoundary = ErrorBoundary;
16482
+ exports.FROM_REF = FROM_REF;
15869
16483
  exports.IsObject = IsObject;
16484
+ exports.ONCE = ONCE;
16485
+ exports.Portal = Portal;
15870
16486
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15871
16487
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15872
16488
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15902,34 +16518,45 @@ exports.events = createPointerEvents;
15902
16518
  exports.extend = extend;
15903
16519
  exports.findInitialRoot = findInitialRoot;
15904
16520
  exports.flushSync = flushSync;
16521
+ exports.fromRef = fromRef;
15905
16522
  exports.getInstanceProps = getInstanceProps;
16523
+ exports.getPrimary = getPrimary;
16524
+ exports.getPrimaryIds = getPrimaryIds;
15906
16525
  exports.getRootState = getRootState;
15907
16526
  exports.getScheduler = getScheduler;
15908
16527
  exports.getUuidPrefix = getUuidPrefix;
15909
16528
  exports.hasConstructor = hasConstructor;
16529
+ exports.hasPrimary = hasPrimary;
15910
16530
  exports.invalidate = invalidate;
15911
16531
  exports.invalidateInstance = invalidateInstance;
15912
16532
  exports.is = is;
15913
16533
  exports.isColorRepresentation = isColorRepresentation;
15914
16534
  exports.isCopyable = isCopyable;
16535
+ exports.isFromRef = isFromRef;
15915
16536
  exports.isObject3D = isObject3D;
16537
+ exports.isOnce = isOnce;
15916
16538
  exports.isOrthographicCamera = isOrthographicCamera;
15917
16539
  exports.isRef = isRef;
15918
16540
  exports.isRenderer = isRenderer;
15919
16541
  exports.isTexture = isTexture;
15920
16542
  exports.isVectorLike = isVectorLike;
16543
+ exports.once = once;
15921
16544
  exports.prepare = prepare;
16545
+ exports.presetsObj = presetsObj;
15922
16546
  exports.rebuildAllNodes = rebuildAllNodes;
15923
16547
  exports.rebuildAllUniforms = rebuildAllUniforms;
15924
16548
  exports.reconciler = reconciler;
16549
+ exports.registerPrimary = registerPrimary;
15925
16550
  exports.removeInteractivity = removeInteractivity;
15926
16551
  exports.removeNodes = removeNodes;
15927
16552
  exports.removeUniforms = removeUniforms;
15928
16553
  exports.resolve = resolve;
15929
16554
  exports.unmountComponentAtNode = unmountComponentAtNode;
16555
+ exports.unregisterPrimary = unregisterPrimary;
15930
16556
  exports.updateCamera = updateCamera;
15931
16557
  exports.updateFrustum = updateFrustum;
15932
16558
  exports.useBridge = useBridge;
16559
+ exports.useEnvironment = useEnvironment;
15933
16560
  exports.useFrame = useFrame;
15934
16561
  exports.useGraph = useGraph;
15935
16562
  exports.useInstanceHandle = useInstanceHandle;
@@ -15946,3 +16573,4 @@ exports.useTextures = useTextures;
15946
16573
  exports.useThree = useThree;
15947
16574
  exports.useUniform = useUniform;
15948
16575
  exports.useUniforms = useUniforms;
16576
+ exports.waitForPrimary = waitForPrimary;