@react-three/fiber 10.0.0-alpha.2 → 10.0.0-canary.604355a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/dist/index.cjs +1122 -558
- package/dist/index.d.cts +2001 -1285
- package/dist/index.d.mts +2001 -1285
- package/dist/index.d.ts +2001 -1285
- package/dist/index.mjs +1107 -562
- package/dist/legacy.cjs +1093 -547
- package/dist/legacy.d.cts +2002 -1286
- package/dist/legacy.d.mts +2002 -1286
- package/dist/legacy.d.ts +2002 -1286
- package/dist/legacy.mjs +1078 -551
- package/dist/webgpu/index.cjs +1176 -548
- package/dist/webgpu/index.d.cts +2097 -1300
- package/dist/webgpu/index.d.mts +2097 -1300
- package/dist/webgpu/index.d.ts +2097 -1300
- package/dist/webgpu/index.mjs +1161 -552
- package/package.json +3 -1
- package/readme.md +244 -318
package/dist/legacy.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import * as three from 'three';
|
|
2
|
-
import { WebGLRenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1,
|
|
3
|
-
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { WebGLRenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, Raycaster, OrthographicCamera, PerspectiveCamera, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGLRenderer } from 'three';
|
|
3
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import React__default, {
|
|
5
|
+
import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
|
|
6
6
|
import useMeasure from 'react-use-measure';
|
|
7
7
|
import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
|
|
8
|
+
import { useThree as useThree$1, useLoader as useLoader$1, useFrame as useFrame$1, createPortal as createPortal$1, applyProps as applyProps$1, extend as extend$1 } from '@react-three/fiber';
|
|
9
|
+
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
10
|
+
import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
|
|
11
|
+
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
|
|
12
|
+
import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
|
|
13
|
+
import { GainMapLoader } from '@monogrid/gainmap-js';
|
|
8
14
|
import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
|
|
9
15
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
10
16
|
import { suspend, preload, clear } from 'suspend-react';
|
|
@@ -45,6 +51,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
45
51
|
WebGPURenderer: WebGPURenderer
|
|
46
52
|
}, [three]);
|
|
47
53
|
|
|
54
|
+
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
55
|
+
const pendingSubscribers = /* @__PURE__ */ new Map();
|
|
56
|
+
function registerPrimary(id, renderer, store) {
|
|
57
|
+
if (primaryRegistry.has(id)) {
|
|
58
|
+
console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
|
|
59
|
+
}
|
|
60
|
+
const entry = { renderer, store };
|
|
61
|
+
primaryRegistry.set(id, entry);
|
|
62
|
+
const subscribers = pendingSubscribers.get(id);
|
|
63
|
+
if (subscribers) {
|
|
64
|
+
subscribers.forEach((callback) => callback(entry));
|
|
65
|
+
pendingSubscribers.delete(id);
|
|
66
|
+
}
|
|
67
|
+
return () => {
|
|
68
|
+
const currentEntry = primaryRegistry.get(id);
|
|
69
|
+
if (currentEntry?.renderer === renderer) {
|
|
70
|
+
primaryRegistry.delete(id);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function getPrimary(id) {
|
|
75
|
+
return primaryRegistry.get(id);
|
|
76
|
+
}
|
|
77
|
+
function waitForPrimary(id, timeout = 5e3) {
|
|
78
|
+
const existing = primaryRegistry.get(id);
|
|
79
|
+
if (existing) {
|
|
80
|
+
return Promise.resolve(existing);
|
|
81
|
+
}
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const timeoutId = setTimeout(() => {
|
|
84
|
+
const subscribers = pendingSubscribers.get(id);
|
|
85
|
+
if (subscribers) {
|
|
86
|
+
const index = subscribers.indexOf(callback);
|
|
87
|
+
if (index !== -1) subscribers.splice(index, 1);
|
|
88
|
+
if (subscribers.length === 0) pendingSubscribers.delete(id);
|
|
89
|
+
}
|
|
90
|
+
reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
|
|
91
|
+
}, timeout);
|
|
92
|
+
const callback = (entry) => {
|
|
93
|
+
clearTimeout(timeoutId);
|
|
94
|
+
resolve(entry);
|
|
95
|
+
};
|
|
96
|
+
if (!pendingSubscribers.has(id)) {
|
|
97
|
+
pendingSubscribers.set(id, []);
|
|
98
|
+
}
|
|
99
|
+
pendingSubscribers.get(id).push(callback);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function hasPrimary(id) {
|
|
103
|
+
return primaryRegistry.has(id);
|
|
104
|
+
}
|
|
105
|
+
function unregisterPrimary(id) {
|
|
106
|
+
primaryRegistry.delete(id);
|
|
107
|
+
}
|
|
108
|
+
function getPrimaryIds() {
|
|
109
|
+
return Array.from(primaryRegistry.keys());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const presetsObj = {
|
|
113
|
+
apartment: "lebombo_1k.hdr",
|
|
114
|
+
city: "potsdamer_platz_1k.hdr",
|
|
115
|
+
dawn: "kiara_1_dawn_1k.hdr",
|
|
116
|
+
forest: "forest_slope_1k.hdr",
|
|
117
|
+
lobby: "st_fagans_interior_1k.hdr",
|
|
118
|
+
night: "dikhololo_night_1k.hdr",
|
|
119
|
+
park: "rooitou_park_1k.hdr",
|
|
120
|
+
studio: "studio_small_03_1k.hdr",
|
|
121
|
+
sunset: "venice_sunset_1k.hdr",
|
|
122
|
+
warehouse: "empty_warehouse_01_1k.hdr"
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
|
|
126
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
127
|
+
const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
|
|
128
|
+
function useEnvironment({
|
|
129
|
+
files = defaultFiles,
|
|
130
|
+
path = "",
|
|
131
|
+
preset = void 0,
|
|
132
|
+
colorSpace = void 0,
|
|
133
|
+
extensions
|
|
134
|
+
} = {}) {
|
|
135
|
+
if (preset) {
|
|
136
|
+
validatePreset(preset);
|
|
137
|
+
files = presetsObj[preset];
|
|
138
|
+
path = CUBEMAP_ROOT;
|
|
139
|
+
}
|
|
140
|
+
const multiFile = isArray(files);
|
|
141
|
+
const { extension, isCubemap } = getExtension(files);
|
|
142
|
+
const loader = getLoader$1(extension);
|
|
143
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
144
|
+
const renderer = useThree$1((state) => state.renderer);
|
|
145
|
+
useLayoutEffect(() => {
|
|
146
|
+
if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
|
|
147
|
+
function clearGainmapTexture() {
|
|
148
|
+
useLoader$1.clear(loader, multiFile ? [files] : files);
|
|
149
|
+
}
|
|
150
|
+
renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
|
|
151
|
+
}, [files, renderer.domElement]);
|
|
152
|
+
const loaderResult = useLoader$1(
|
|
153
|
+
loader,
|
|
154
|
+
multiFile ? [files] : files,
|
|
155
|
+
(loader2) => {
|
|
156
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
157
|
+
loader2.setRenderer?.(renderer);
|
|
158
|
+
}
|
|
159
|
+
loader2.setPath?.(path);
|
|
160
|
+
if (extensions) extensions(loader2);
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
let texture = multiFile ? (
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
loaderResult[0]
|
|
166
|
+
) : loaderResult;
|
|
167
|
+
if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
|
|
168
|
+
texture = texture.renderTarget?.texture;
|
|
169
|
+
}
|
|
170
|
+
texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
|
|
171
|
+
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
172
|
+
return texture;
|
|
173
|
+
}
|
|
174
|
+
const preloadDefaultOptions = {
|
|
175
|
+
files: defaultFiles,
|
|
176
|
+
path: "",
|
|
177
|
+
preset: void 0,
|
|
178
|
+
extensions: void 0
|
|
179
|
+
};
|
|
180
|
+
useEnvironment.preload = (preloadOptions) => {
|
|
181
|
+
const options = { ...preloadDefaultOptions, ...preloadOptions };
|
|
182
|
+
let { files, path = "" } = options;
|
|
183
|
+
const { preset, extensions } = options;
|
|
184
|
+
if (preset) {
|
|
185
|
+
validatePreset(preset);
|
|
186
|
+
files = presetsObj[preset];
|
|
187
|
+
path = CUBEMAP_ROOT;
|
|
188
|
+
}
|
|
189
|
+
const { extension } = getExtension(files);
|
|
190
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
191
|
+
throw new Error("useEnvironment: Preloading gainmaps is not supported");
|
|
192
|
+
}
|
|
193
|
+
const loader = getLoader$1(extension);
|
|
194
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
195
|
+
useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
|
|
196
|
+
loader2.setPath?.(path);
|
|
197
|
+
if (extensions) extensions(loader2);
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const clearDefaultOptins = {
|
|
201
|
+
files: defaultFiles,
|
|
202
|
+
preset: void 0
|
|
203
|
+
};
|
|
204
|
+
useEnvironment.clear = (clearOptions) => {
|
|
205
|
+
const options = { ...clearDefaultOptins, ...clearOptions };
|
|
206
|
+
let { files } = options;
|
|
207
|
+
const { preset } = options;
|
|
208
|
+
if (preset) {
|
|
209
|
+
validatePreset(preset);
|
|
210
|
+
files = presetsObj[preset];
|
|
211
|
+
}
|
|
212
|
+
const { extension } = getExtension(files);
|
|
213
|
+
const loader = getLoader$1(extension);
|
|
214
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
215
|
+
useLoader$1.clear(loader, isArray(files) ? [files] : files);
|
|
216
|
+
};
|
|
217
|
+
function validatePreset(preset) {
|
|
218
|
+
if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
|
|
219
|
+
}
|
|
220
|
+
function getExtension(files) {
|
|
221
|
+
const isCubemap = isArray(files) && files.length === 6;
|
|
222
|
+
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
223
|
+
const firstEntry = isArray(files) ? files[0] : files;
|
|
224
|
+
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();
|
|
225
|
+
return { extension, isCubemap, isGainmap };
|
|
226
|
+
}
|
|
227
|
+
function getLoader$1(extension) {
|
|
228
|
+
const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
|
|
229
|
+
return loader;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const isRef$1 = (obj) => obj.current && obj.current.isScene;
|
|
233
|
+
const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
|
|
234
|
+
function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
|
|
235
|
+
sceneProps = {
|
|
236
|
+
backgroundBlurriness: 0,
|
|
237
|
+
backgroundIntensity: 1,
|
|
238
|
+
backgroundRotation: [0, 0, 0],
|
|
239
|
+
environmentIntensity: 1,
|
|
240
|
+
environmentRotation: [0, 0, 0],
|
|
241
|
+
...sceneProps
|
|
242
|
+
};
|
|
243
|
+
const target = resolveScene(scene || defaultScene);
|
|
244
|
+
const oldbg = target.background;
|
|
245
|
+
const oldenv = target.environment;
|
|
246
|
+
const oldSceneProps = {
|
|
247
|
+
// @ts-ignore
|
|
248
|
+
backgroundBlurriness: target.backgroundBlurriness,
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
backgroundIntensity: target.backgroundIntensity,
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
253
|
+
// @ts-ignore
|
|
254
|
+
environmentIntensity: target.environmentIntensity,
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
257
|
+
};
|
|
258
|
+
if (background !== "only") target.environment = texture;
|
|
259
|
+
if (background) target.background = texture;
|
|
260
|
+
applyProps$1(target, sceneProps);
|
|
261
|
+
return () => {
|
|
262
|
+
if (background !== "only") target.environment = oldenv;
|
|
263
|
+
if (background) target.background = oldbg;
|
|
264
|
+
applyProps$1(target, oldSceneProps);
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function EnvironmentMap({ scene, background = false, map, ...config }) {
|
|
268
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
269
|
+
React.useLayoutEffect(() => {
|
|
270
|
+
if (map) return setEnvProps(background, scene, defaultScene, map, config);
|
|
271
|
+
});
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
function EnvironmentCube({
|
|
275
|
+
background = false,
|
|
276
|
+
scene,
|
|
277
|
+
blur,
|
|
278
|
+
backgroundBlurriness,
|
|
279
|
+
backgroundIntensity,
|
|
280
|
+
backgroundRotation,
|
|
281
|
+
environmentIntensity,
|
|
282
|
+
environmentRotation,
|
|
283
|
+
...rest
|
|
284
|
+
}) {
|
|
285
|
+
const texture = useEnvironment(rest);
|
|
286
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
287
|
+
React.useLayoutEffect(() => {
|
|
288
|
+
return setEnvProps(background, scene, defaultScene, texture, {
|
|
289
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
290
|
+
backgroundIntensity,
|
|
291
|
+
backgroundRotation,
|
|
292
|
+
environmentIntensity,
|
|
293
|
+
environmentRotation
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
React.useEffect(() => {
|
|
297
|
+
return () => {
|
|
298
|
+
texture.dispose();
|
|
299
|
+
};
|
|
300
|
+
}, [texture]);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
function EnvironmentPortal({
|
|
304
|
+
children,
|
|
305
|
+
near = 0.1,
|
|
306
|
+
far = 1e3,
|
|
307
|
+
resolution = 256,
|
|
308
|
+
frames = 1,
|
|
309
|
+
map,
|
|
310
|
+
background = false,
|
|
311
|
+
blur,
|
|
312
|
+
backgroundBlurriness,
|
|
313
|
+
backgroundIntensity,
|
|
314
|
+
backgroundRotation,
|
|
315
|
+
environmentIntensity,
|
|
316
|
+
environmentRotation,
|
|
317
|
+
scene,
|
|
318
|
+
files,
|
|
319
|
+
path,
|
|
320
|
+
preset = void 0,
|
|
321
|
+
extensions
|
|
322
|
+
}) {
|
|
323
|
+
const gl = useThree$1((state) => state.gl);
|
|
324
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
325
|
+
const camera = React.useRef(null);
|
|
326
|
+
const [virtualScene] = React.useState(() => new Scene());
|
|
327
|
+
const fbo = React.useMemo(() => {
|
|
328
|
+
const fbo2 = new WebGLCubeRenderTarget(resolution);
|
|
329
|
+
fbo2.texture.type = HalfFloatType;
|
|
330
|
+
return fbo2;
|
|
331
|
+
}, [resolution]);
|
|
332
|
+
React.useEffect(() => {
|
|
333
|
+
return () => {
|
|
334
|
+
fbo.dispose();
|
|
335
|
+
};
|
|
336
|
+
}, [fbo]);
|
|
337
|
+
React.useLayoutEffect(() => {
|
|
338
|
+
if (frames === 1) {
|
|
339
|
+
const autoClear = gl.autoClear;
|
|
340
|
+
gl.autoClear = true;
|
|
341
|
+
camera.current.update(gl, virtualScene);
|
|
342
|
+
gl.autoClear = autoClear;
|
|
343
|
+
}
|
|
344
|
+
return setEnvProps(background, scene, defaultScene, fbo.texture, {
|
|
345
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
346
|
+
backgroundIntensity,
|
|
347
|
+
backgroundRotation,
|
|
348
|
+
environmentIntensity,
|
|
349
|
+
environmentRotation
|
|
350
|
+
});
|
|
351
|
+
}, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
|
|
352
|
+
let count = 1;
|
|
353
|
+
useFrame$1(() => {
|
|
354
|
+
if (frames === Infinity || count < frames) {
|
|
355
|
+
const autoClear = gl.autoClear;
|
|
356
|
+
gl.autoClear = true;
|
|
357
|
+
camera.current.update(gl, virtualScene);
|
|
358
|
+
gl.autoClear = autoClear;
|
|
359
|
+
count++;
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
|
|
363
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
364
|
+
children,
|
|
365
|
+
/* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
|
|
366
|
+
files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
|
|
367
|
+
] }),
|
|
368
|
+
virtualScene
|
|
369
|
+
) });
|
|
370
|
+
}
|
|
371
|
+
function EnvironmentGround(props) {
|
|
372
|
+
const textureDefault = useEnvironment(props);
|
|
373
|
+
const texture = props.map || textureDefault;
|
|
374
|
+
React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
|
|
375
|
+
React.useEffect(() => {
|
|
376
|
+
return () => {
|
|
377
|
+
textureDefault.dispose();
|
|
378
|
+
};
|
|
379
|
+
}, [textureDefault]);
|
|
380
|
+
const height = props.ground?.height ?? 15;
|
|
381
|
+
const radius = props.ground?.radius ?? 60;
|
|
382
|
+
const scale = props.ground?.scale ?? 1e3;
|
|
383
|
+
const args = React.useMemo(
|
|
384
|
+
() => [texture, height, radius],
|
|
385
|
+
[texture, height, radius]
|
|
386
|
+
);
|
|
387
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
388
|
+
/* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
|
|
389
|
+
/* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
|
|
390
|
+
] });
|
|
391
|
+
}
|
|
392
|
+
function EnvironmentColor({ color, scene }) {
|
|
393
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
394
|
+
React.useLayoutEffect(() => {
|
|
395
|
+
if (color === void 0) return;
|
|
396
|
+
const target = resolveScene(scene || defaultScene);
|
|
397
|
+
const oldBg = target.background;
|
|
398
|
+
target.background = new Color(color);
|
|
399
|
+
return () => {
|
|
400
|
+
target.background = oldBg;
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
function EnvironmentDualSource(props) {
|
|
406
|
+
const { backgroundFiles, ...envProps } = props;
|
|
407
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
408
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
|
|
409
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
|
|
410
|
+
] });
|
|
411
|
+
}
|
|
412
|
+
function Environment(props) {
|
|
413
|
+
if (props.color && !props.files && !props.preset && !props.map) {
|
|
414
|
+
return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
|
|
415
|
+
}
|
|
416
|
+
if (props.backgroundFiles && props.backgroundFiles !== props.files) {
|
|
417
|
+
return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
|
|
418
|
+
}
|
|
419
|
+
return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
|
|
420
|
+
}
|
|
421
|
+
|
|
48
422
|
var __defProp$2 = Object.defineProperty;
|
|
49
423
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
50
424
|
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -224,7 +598,8 @@ function prepare(target, root, type, props) {
|
|
|
224
598
|
object,
|
|
225
599
|
eventCount: 0,
|
|
226
600
|
handlers: {},
|
|
227
|
-
isHidden: false
|
|
601
|
+
isHidden: false,
|
|
602
|
+
deferredRefs: []
|
|
228
603
|
};
|
|
229
604
|
if (object) object.__r3f = instance;
|
|
230
605
|
}
|
|
@@ -273,7 +648,7 @@ function createOcclusionObserverNode(store, uniform) {
|
|
|
273
648
|
let occlusionSetupPromise = null;
|
|
274
649
|
function enableOcclusion(store) {
|
|
275
650
|
const state = store.getState();
|
|
276
|
-
const { internal, renderer
|
|
651
|
+
const { internal, renderer } = state;
|
|
277
652
|
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
278
653
|
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
279
654
|
if (!hasOcclusionSupport) {
|
|
@@ -436,6 +811,22 @@ function hasVisibilityHandlers(handlers) {
|
|
|
436
811
|
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
437
812
|
}
|
|
438
813
|
|
|
814
|
+
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
815
|
+
function fromRef(ref) {
|
|
816
|
+
return { [FROM_REF]: ref };
|
|
817
|
+
}
|
|
818
|
+
function isFromRef(value) {
|
|
819
|
+
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const ONCE = Symbol.for("@react-three/fiber.once");
|
|
823
|
+
function once(...args) {
|
|
824
|
+
return { [ONCE]: args.length ? args : true };
|
|
825
|
+
}
|
|
826
|
+
function isOnce(value) {
|
|
827
|
+
return value !== null && typeof value === "object" && ONCE in value;
|
|
828
|
+
}
|
|
829
|
+
|
|
439
830
|
const RESERVED_PROPS = [
|
|
440
831
|
"children",
|
|
441
832
|
"key",
|
|
@@ -506,7 +897,7 @@ function getMemoizedPrototype(root) {
|
|
|
506
897
|
ctor = new root.constructor();
|
|
507
898
|
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
508
899
|
}
|
|
509
|
-
} catch
|
|
900
|
+
} catch {
|
|
510
901
|
}
|
|
511
902
|
return ctor;
|
|
512
903
|
}
|
|
@@ -552,6 +943,25 @@ function applyProps(object, props) {
|
|
|
552
943
|
continue;
|
|
553
944
|
}
|
|
554
945
|
if (value === void 0) continue;
|
|
946
|
+
if (isFromRef(value)) {
|
|
947
|
+
instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
if (isOnce(value)) {
|
|
951
|
+
if (instance?.appliedOnce?.has(prop)) continue;
|
|
952
|
+
if (instance) {
|
|
953
|
+
instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
|
|
954
|
+
instance.appliedOnce.add(prop);
|
|
955
|
+
}
|
|
956
|
+
const { root: targetRoot, key: targetKey } = resolve(object, prop);
|
|
957
|
+
const args = value[ONCE];
|
|
958
|
+
if (typeof targetRoot[targetKey] === "function") {
|
|
959
|
+
targetRoot[targetKey](...args === true ? [] : args);
|
|
960
|
+
} else if (args !== true && args.length > 0) {
|
|
961
|
+
targetRoot[targetKey] = args[0];
|
|
962
|
+
}
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
555
965
|
let { root, key, target } = resolve(object, prop);
|
|
556
966
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
557
967
|
throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
|
|
@@ -574,7 +984,7 @@ function applyProps(object, props) {
|
|
|
574
984
|
else target.set(value);
|
|
575
985
|
} else {
|
|
576
986
|
root[key] = value;
|
|
577
|
-
if (rootState &&
|
|
987
|
+
if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
578
988
|
root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
|
|
579
989
|
root[key].colorSpace = rootState.textureColorSpace;
|
|
580
990
|
}
|
|
@@ -931,7 +1341,7 @@ function createPointerEvents(store) {
|
|
|
931
1341
|
return {
|
|
932
1342
|
priority: 1,
|
|
933
1343
|
enabled: true,
|
|
934
|
-
compute(event, state
|
|
1344
|
+
compute(event, state) {
|
|
935
1345
|
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
936
1346
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
937
1347
|
},
|
|
@@ -1029,331 +1439,26 @@ function notifyAlpha({ message, link }) {
|
|
|
1029
1439
|
}
|
|
1030
1440
|
}
|
|
1031
1441
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
let performanceTimeout = void 0;
|
|
1055
|
-
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
1056
|
-
const pointer = new Vector2();
|
|
1057
|
-
const rootState = {
|
|
1058
|
-
set,
|
|
1059
|
-
get,
|
|
1060
|
-
// Mock objects that have to be configured
|
|
1061
|
-
gl: null,
|
|
1062
|
-
renderer: null,
|
|
1063
|
-
camera: null,
|
|
1064
|
-
frustum: new Frustum(),
|
|
1065
|
-
autoUpdateFrustum: true,
|
|
1066
|
-
raycaster: null,
|
|
1067
|
-
events: { priority: 1, enabled: true, connected: false },
|
|
1068
|
-
scene: null,
|
|
1069
|
-
rootScene: null,
|
|
1070
|
-
xr: null,
|
|
1071
|
-
inspector: null,
|
|
1072
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
1073
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
1074
|
-
legacy: false,
|
|
1075
|
-
linear: false,
|
|
1076
|
-
flat: false,
|
|
1077
|
-
textureColorSpace: "srgb",
|
|
1078
|
-
isLegacy: false,
|
|
1079
|
-
webGPUSupported: false,
|
|
1080
|
-
isNative: false,
|
|
1081
|
-
controls: null,
|
|
1082
|
-
pointer,
|
|
1083
|
-
mouse: pointer,
|
|
1084
|
-
frameloop: "always",
|
|
1085
|
-
onPointerMissed: void 0,
|
|
1086
|
-
onDragOverMissed: void 0,
|
|
1087
|
-
onDropMissed: void 0,
|
|
1088
|
-
performance: {
|
|
1089
|
-
current: 1,
|
|
1090
|
-
min: 0.5,
|
|
1091
|
-
max: 1,
|
|
1092
|
-
debounce: 200,
|
|
1093
|
-
regress: () => {
|
|
1094
|
-
const state2 = get();
|
|
1095
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
1096
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
1097
|
-
performanceTimeout = setTimeout(
|
|
1098
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
1099
|
-
state2.performance.debounce
|
|
1100
|
-
);
|
|
1101
|
-
}
|
|
1102
|
-
},
|
|
1103
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1104
|
-
viewport: {
|
|
1105
|
-
initialDpr: 0,
|
|
1106
|
-
dpr: 0,
|
|
1107
|
-
width: 0,
|
|
1108
|
-
height: 0,
|
|
1109
|
-
top: 0,
|
|
1110
|
-
left: 0,
|
|
1111
|
-
aspect: 0,
|
|
1112
|
-
distance: 0,
|
|
1113
|
-
factor: 0,
|
|
1114
|
-
getCurrentViewport
|
|
1115
|
-
},
|
|
1116
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
1117
|
-
setSize: (width, height, top, left) => {
|
|
1118
|
-
const state2 = get();
|
|
1119
|
-
if (width === void 0) {
|
|
1120
|
-
set({ _sizeImperative: false });
|
|
1121
|
-
if (state2._sizeProps) {
|
|
1122
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
1123
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
1124
|
-
const currentSize = state2.size;
|
|
1125
|
-
const newSize = {
|
|
1126
|
-
width: propW ?? currentSize.width,
|
|
1127
|
-
height: propH ?? currentSize.height,
|
|
1128
|
-
top: currentSize.top,
|
|
1129
|
-
left: currentSize.left
|
|
1130
|
-
};
|
|
1131
|
-
set((s) => ({
|
|
1132
|
-
size: newSize,
|
|
1133
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
1134
|
-
}));
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return;
|
|
1138
|
-
}
|
|
1139
|
-
const w = width;
|
|
1140
|
-
const h = height ?? width;
|
|
1141
|
-
const t = top ?? state2.size.top;
|
|
1142
|
-
const l = left ?? state2.size.left;
|
|
1143
|
-
const size = { width: w, height: h, top: t, left: l };
|
|
1144
|
-
set((s) => ({
|
|
1145
|
-
size,
|
|
1146
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
1147
|
-
_sizeImperative: true
|
|
1148
|
-
}));
|
|
1149
|
-
},
|
|
1150
|
-
setDpr: (dpr) => set((state2) => {
|
|
1151
|
-
const resolved = calculateDpr(dpr);
|
|
1152
|
-
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
1153
|
-
}),
|
|
1154
|
-
setFrameloop: (frameloop = "always") => {
|
|
1155
|
-
set(() => ({ frameloop }));
|
|
1156
|
-
},
|
|
1157
|
-
setError: (error) => set(() => ({ error })),
|
|
1158
|
-
error: null,
|
|
1159
|
-
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
1160
|
-
uniforms: {},
|
|
1161
|
-
nodes: {},
|
|
1162
|
-
textures: /* @__PURE__ */ new Map(),
|
|
1163
|
-
postProcessing: null,
|
|
1164
|
-
passes: {},
|
|
1165
|
-
_hmrVersion: 0,
|
|
1166
|
-
_sizeImperative: false,
|
|
1167
|
-
_sizeProps: null,
|
|
1168
|
-
previousRoot: void 0,
|
|
1169
|
-
internal: {
|
|
1170
|
-
// Events
|
|
1171
|
-
interaction: [],
|
|
1172
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
1173
|
-
subscribers: [],
|
|
1174
|
-
initialClick: [0, 0],
|
|
1175
|
-
initialHits: [],
|
|
1176
|
-
capturedMap: /* @__PURE__ */ new Map(),
|
|
1177
|
-
lastEvent: React.createRef(),
|
|
1178
|
-
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1179
|
-
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1180
|
-
// Occlusion system (WebGPU only)
|
|
1181
|
-
occlusionEnabled: false,
|
|
1182
|
-
occlusionObserver: null,
|
|
1183
|
-
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1184
|
-
helperGroup: null,
|
|
1185
|
-
// Updates
|
|
1186
|
-
active: false,
|
|
1187
|
-
frames: 0,
|
|
1188
|
-
priority: 0,
|
|
1189
|
-
subscribe: (ref, priority, store) => {
|
|
1190
|
-
const internal = get().internal;
|
|
1191
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1192
|
-
internal.subscribers.push({ ref, priority, store });
|
|
1193
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
1194
|
-
return () => {
|
|
1195
|
-
const internal2 = get().internal;
|
|
1196
|
-
if (internal2?.subscribers) {
|
|
1197
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
1198
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
1199
|
-
}
|
|
1200
|
-
};
|
|
1201
|
-
},
|
|
1202
|
-
// Renderer Storage (single source of truth)
|
|
1203
|
-
actualRenderer: null,
|
|
1204
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
1205
|
-
scheduler: null
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
return rootState;
|
|
1209
|
-
});
|
|
1210
|
-
const state = rootStore.getState();
|
|
1211
|
-
Object.defineProperty(state, "gl", {
|
|
1212
|
-
get() {
|
|
1213
|
-
const currentState = rootStore.getState();
|
|
1214
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
1215
|
-
const stack = new Error().stack || "";
|
|
1216
|
-
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
1217
|
-
if (!isInternalAccess) {
|
|
1218
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1219
|
-
notifyDepreciated({
|
|
1220
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1221
|
-
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
|
|
1222
|
-
});
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
return currentState.internal.actualRenderer;
|
|
1226
|
-
},
|
|
1227
|
-
set(value) {
|
|
1228
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1229
|
-
},
|
|
1230
|
-
enumerable: true,
|
|
1231
|
-
configurable: true
|
|
1232
|
-
});
|
|
1233
|
-
Object.defineProperty(state, "renderer", {
|
|
1234
|
-
get() {
|
|
1235
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1236
|
-
},
|
|
1237
|
-
set(value) {
|
|
1238
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1239
|
-
},
|
|
1240
|
-
enumerable: true,
|
|
1241
|
-
configurable: true
|
|
1242
|
-
});
|
|
1243
|
-
let oldScene = state.scene;
|
|
1244
|
-
rootStore.subscribe(() => {
|
|
1245
|
-
const currentState = rootStore.getState();
|
|
1246
|
-
const { scene, rootScene, set } = currentState;
|
|
1247
|
-
if (scene !== oldScene) {
|
|
1248
|
-
oldScene = scene;
|
|
1249
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1250
|
-
set({ rootScene: scene });
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
let oldSize = state.size;
|
|
1255
|
-
let oldDpr = state.viewport.dpr;
|
|
1256
|
-
let oldCamera = state.camera;
|
|
1257
|
-
rootStore.subscribe(() => {
|
|
1258
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1259
|
-
const actualRenderer = internal.actualRenderer;
|
|
1260
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1261
|
-
oldSize = size;
|
|
1262
|
-
oldDpr = viewport.dpr;
|
|
1263
|
-
updateCamera(camera, size);
|
|
1264
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1265
|
-
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
1266
|
-
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
1267
|
-
}
|
|
1268
|
-
if (camera !== oldCamera) {
|
|
1269
|
-
oldCamera = camera;
|
|
1270
|
-
const { rootScene } = rootStore.getState();
|
|
1271
|
-
if (camera && rootScene && !camera.parent) {
|
|
1272
|
-
rootScene.add(camera);
|
|
1273
|
-
}
|
|
1274
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1275
|
-
const currentState = rootStore.getState();
|
|
1276
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1277
|
-
updateFrustum(camera, currentState.frustum);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1282
|
-
return rootStore;
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
1286
|
-
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
1287
|
-
function getLoader(Proto) {
|
|
1288
|
-
if (isConstructor$1(Proto)) {
|
|
1289
|
-
let loader = memoizedLoaders.get(Proto);
|
|
1290
|
-
if (!loader) {
|
|
1291
|
-
loader = new Proto();
|
|
1292
|
-
memoizedLoaders.set(Proto, loader);
|
|
1293
|
-
}
|
|
1294
|
-
return loader;
|
|
1295
|
-
}
|
|
1296
|
-
return Proto;
|
|
1297
|
-
}
|
|
1298
|
-
function loadingFn(extensions, onProgress) {
|
|
1299
|
-
return function(Proto, input) {
|
|
1300
|
-
const loader = getLoader(Proto);
|
|
1301
|
-
if (extensions) extensions(loader);
|
|
1302
|
-
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
1303
|
-
return loader.loadAsync(input, onProgress).then((data) => {
|
|
1304
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1305
|
-
return data;
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
return new Promise(
|
|
1309
|
-
(res, reject) => loader.load(
|
|
1310
|
-
input,
|
|
1311
|
-
(data) => {
|
|
1312
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1313
|
-
res(data);
|
|
1314
|
-
},
|
|
1315
|
-
onProgress,
|
|
1316
|
-
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
1317
|
-
)
|
|
1318
|
-
);
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
function useLoader(loader, input, extensions, onProgress) {
|
|
1322
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1323
|
-
const fn = loadingFn(extensions, onProgress);
|
|
1324
|
-
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
1325
|
-
return Array.isArray(input) ? results : results[0];
|
|
1326
|
-
}
|
|
1327
|
-
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
1328
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1329
|
-
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
1330
|
-
};
|
|
1331
|
-
useLoader.clear = function(loader, input) {
|
|
1332
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1333
|
-
keys.forEach((key) => clear([loader, key]));
|
|
1334
|
-
};
|
|
1335
|
-
useLoader.loader = getLoader;
|
|
1336
|
-
|
|
1337
|
-
var __defProp$1 = Object.defineProperty;
|
|
1338
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1339
|
-
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1340
|
-
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1341
|
-
class PhaseGraph {
|
|
1342
|
-
constructor() {
|
|
1343
|
-
/** Ordered list of phase nodes */
|
|
1344
|
-
__publicField$1(this, "phases", []);
|
|
1345
|
-
/** Quick lookup by name */
|
|
1346
|
-
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1347
|
-
/** Cached ordered names (invalidated on changes) */
|
|
1348
|
-
__publicField$1(this, "orderedNamesCache", null);
|
|
1349
|
-
this.initializeDefaultPhases();
|
|
1350
|
-
}
|
|
1351
|
-
//* Initialization --------------------------------
|
|
1352
|
-
initializeDefaultPhases() {
|
|
1353
|
-
for (const name of DEFAULT_PHASES) {
|
|
1354
|
-
const node = { name, isAutoGenerated: false };
|
|
1355
|
-
this.phases.push(node);
|
|
1356
|
-
this.phaseMap.set(name, node);
|
|
1442
|
+
var __defProp$1 = Object.defineProperty;
|
|
1443
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1444
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1445
|
+
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1446
|
+
class PhaseGraph {
|
|
1447
|
+
constructor() {
|
|
1448
|
+
/** Ordered list of phase nodes */
|
|
1449
|
+
__publicField$1(this, "phases", []);
|
|
1450
|
+
/** Quick lookup by name */
|
|
1451
|
+
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1452
|
+
/** Cached ordered names (invalidated on changes) */
|
|
1453
|
+
__publicField$1(this, "orderedNamesCache", null);
|
|
1454
|
+
this.initializeDefaultPhases();
|
|
1455
|
+
}
|
|
1456
|
+
//* Initialization --------------------------------
|
|
1457
|
+
initializeDefaultPhases() {
|
|
1458
|
+
for (const name of DEFAULT_PHASES) {
|
|
1459
|
+
const node = { name, isAutoGenerated: false };
|
|
1460
|
+
this.phases.push(node);
|
|
1461
|
+
this.phaseMap.set(name, node);
|
|
1357
1462
|
}
|
|
1358
1463
|
this.invalidateCache();
|
|
1359
1464
|
}
|
|
@@ -2239,132 +2344,445 @@ const _Scheduler = class _Scheduler {
|
|
|
2239
2344
|
console.error(`[Scheduler] Error in global job "${job.id}":`, error);
|
|
2240
2345
|
}
|
|
2241
2346
|
}
|
|
2242
|
-
}
|
|
2243
|
-
/**
|
|
2244
|
-
* Execute all jobs for a single root in sorted order.
|
|
2245
|
-
* Rebuilds sorted job list if needed, then dispatches each job.
|
|
2246
|
-
* Errors are caught and propagated via triggerError.
|
|
2247
|
-
* @param {RootEntry} root - The root entry to tick
|
|
2248
|
-
* @param {number} timestamp - RAF timestamp in milliseconds
|
|
2249
|
-
* @param {number} delta - Time since last frame in seconds
|
|
2250
|
-
* @returns {void}
|
|
2251
|
-
* @private
|
|
2252
|
-
*/
|
|
2253
|
-
tickRoot(root, timestamp, delta) {
|
|
2254
|
-
if (root.needsRebuild) {
|
|
2255
|
-
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2256
|
-
root.needsRebuild = false;
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* Execute all jobs for a single root in sorted order.
|
|
2350
|
+
* Rebuilds sorted job list if needed, then dispatches each job.
|
|
2351
|
+
* Errors are caught and propagated via triggerError.
|
|
2352
|
+
* @param {RootEntry} root - The root entry to tick
|
|
2353
|
+
* @param {number} timestamp - RAF timestamp in milliseconds
|
|
2354
|
+
* @param {number} delta - Time since last frame in seconds
|
|
2355
|
+
* @returns {void}
|
|
2356
|
+
* @private
|
|
2357
|
+
*/
|
|
2358
|
+
tickRoot(root, timestamp, delta) {
|
|
2359
|
+
if (root.needsRebuild) {
|
|
2360
|
+
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2361
|
+
root.needsRebuild = false;
|
|
2362
|
+
}
|
|
2363
|
+
const providedState = root.getState?.() ?? {};
|
|
2364
|
+
const frameState = {
|
|
2365
|
+
...providedState,
|
|
2366
|
+
time: timestamp,
|
|
2367
|
+
delta,
|
|
2368
|
+
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2369
|
+
// Convert ms to seconds
|
|
2370
|
+
frame: this.loopState.frameCount
|
|
2371
|
+
};
|
|
2372
|
+
for (const job of root.sortedJobs) {
|
|
2373
|
+
if (!shouldRun(job, timestamp)) continue;
|
|
2374
|
+
try {
|
|
2375
|
+
job.callback(frameState, delta);
|
|
2376
|
+
} catch (error) {
|
|
2377
|
+
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2378
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
//* Debug & Inspection Methods ================================
|
|
2383
|
+
/**
|
|
2384
|
+
* Get the total number of registered jobs across all roots.
|
|
2385
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2386
|
+
* @returns {number} Total job count
|
|
2387
|
+
*/
|
|
2388
|
+
getJobCount() {
|
|
2389
|
+
let count = 0;
|
|
2390
|
+
for (const root of this.roots.values()) {
|
|
2391
|
+
count += root.jobs.size;
|
|
2392
|
+
}
|
|
2393
|
+
return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Get all registered job IDs across all roots.
|
|
2397
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2398
|
+
* @returns {string[]} Array of all job IDs
|
|
2399
|
+
*/
|
|
2400
|
+
getJobIds() {
|
|
2401
|
+
const ids = [];
|
|
2402
|
+
for (const root of this.roots.values()) {
|
|
2403
|
+
ids.push(...root.jobs.keys());
|
|
2404
|
+
}
|
|
2405
|
+
ids.push(...this.globalBeforeJobs.keys());
|
|
2406
|
+
ids.push(...this.globalAfterJobs.keys());
|
|
2407
|
+
return ids;
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Get the number of registered roots (Canvas instances).
|
|
2411
|
+
* @returns {number} Number of registered roots
|
|
2412
|
+
*/
|
|
2413
|
+
getRootCount() {
|
|
2414
|
+
return this.roots.size;
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2418
|
+
* Used by the default render job to know if a user has taken over rendering.
|
|
2419
|
+
*
|
|
2420
|
+
* @param phase The phase to check
|
|
2421
|
+
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2422
|
+
* @returns true if any user jobs exist in the phase
|
|
2423
|
+
*/
|
|
2424
|
+
hasUserJobsInPhase(phase, rootId) {
|
|
2425
|
+
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2426
|
+
return rootsToCheck.some((root) => {
|
|
2427
|
+
if (!root) return false;
|
|
2428
|
+
for (const job of root.jobs.values()) {
|
|
2429
|
+
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2430
|
+
}
|
|
2431
|
+
return false;
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
//* Utility Methods ================================
|
|
2435
|
+
/**
|
|
2436
|
+
* Generate a unique root ID for automatic root registration.
|
|
2437
|
+
* @returns {string} A unique root ID in the format 'root_N'
|
|
2438
|
+
*/
|
|
2439
|
+
generateRootId() {
|
|
2440
|
+
return `root_${this.nextRootIndex++}`;
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Generate a unique job ID.
|
|
2444
|
+
* @returns {string} A unique job ID in the format 'job_N'
|
|
2445
|
+
* @private
|
|
2446
|
+
*/
|
|
2447
|
+
generateJobId() {
|
|
2448
|
+
return `job_${this.nextJobIndex}`;
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Normalize before/after constraints to a Set.
|
|
2452
|
+
* Handles undefined, single string, or array inputs.
|
|
2453
|
+
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2454
|
+
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2455
|
+
* @private
|
|
2456
|
+
*/
|
|
2457
|
+
normalizeConstraints(value) {
|
|
2458
|
+
if (!value) return /* @__PURE__ */ new Set();
|
|
2459
|
+
if (Array.isArray(value)) return new Set(value);
|
|
2460
|
+
return /* @__PURE__ */ new Set([value]);
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2463
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2464
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2465
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2466
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2467
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2468
|
+
let Scheduler = _Scheduler;
|
|
2469
|
+
const getScheduler = () => Scheduler.get();
|
|
2470
|
+
if (hmrData) {
|
|
2471
|
+
hmrData.accept?.();
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
2475
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
|
|
2476
|
+
const createStore = (invalidate, advance) => {
|
|
2477
|
+
const rootStore = createWithEqualityFn((set, get) => {
|
|
2478
|
+
const position = new Vector3();
|
|
2479
|
+
const defaultTarget = new Vector3();
|
|
2480
|
+
const tempTarget = new Vector3();
|
|
2481
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
2482
|
+
const { width, height, top, left } = size;
|
|
2483
|
+
const aspect = width / height;
|
|
2484
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
2485
|
+
else tempTarget.set(...target);
|
|
2486
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2487
|
+
if (isOrthographicCamera(camera)) {
|
|
2488
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
2489
|
+
} else {
|
|
2490
|
+
const fov = camera.fov * Math.PI / 180;
|
|
2491
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
2492
|
+
const w = h * (width / height);
|
|
2493
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
let performanceTimeout = void 0;
|
|
2497
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
2498
|
+
const pointer = new Vector2();
|
|
2499
|
+
const rootState = {
|
|
2500
|
+
set,
|
|
2501
|
+
get,
|
|
2502
|
+
// Mock objects that have to be configured
|
|
2503
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
2504
|
+
primaryStore: null,
|
|
2505
|
+
gl: null,
|
|
2506
|
+
renderer: null,
|
|
2507
|
+
camera: null,
|
|
2508
|
+
frustum: new Frustum(),
|
|
2509
|
+
autoUpdateFrustum: true,
|
|
2510
|
+
raycaster: null,
|
|
2511
|
+
events: { priority: 1, enabled: true, connected: false },
|
|
2512
|
+
scene: null,
|
|
2513
|
+
rootScene: null,
|
|
2514
|
+
xr: null,
|
|
2515
|
+
inspector: null,
|
|
2516
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
2517
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
2518
|
+
textureColorSpace: SRGBColorSpace,
|
|
2519
|
+
isLegacy: false,
|
|
2520
|
+
webGPUSupported: false,
|
|
2521
|
+
isNative: false,
|
|
2522
|
+
controls: null,
|
|
2523
|
+
pointer,
|
|
2524
|
+
mouse: pointer,
|
|
2525
|
+
frameloop: "always",
|
|
2526
|
+
onPointerMissed: void 0,
|
|
2527
|
+
onDragOverMissed: void 0,
|
|
2528
|
+
onDropMissed: void 0,
|
|
2529
|
+
performance: {
|
|
2530
|
+
current: 1,
|
|
2531
|
+
min: 0.5,
|
|
2532
|
+
max: 1,
|
|
2533
|
+
debounce: 200,
|
|
2534
|
+
regress: () => {
|
|
2535
|
+
const state2 = get();
|
|
2536
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
2537
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
2538
|
+
performanceTimeout = setTimeout(
|
|
2539
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
2540
|
+
state2.performance.debounce
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
},
|
|
2544
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2545
|
+
viewport: {
|
|
2546
|
+
initialDpr: 0,
|
|
2547
|
+
dpr: 0,
|
|
2548
|
+
width: 0,
|
|
2549
|
+
height: 0,
|
|
2550
|
+
top: 0,
|
|
2551
|
+
left: 0,
|
|
2552
|
+
aspect: 0,
|
|
2553
|
+
distance: 0,
|
|
2554
|
+
factor: 0,
|
|
2555
|
+
getCurrentViewport
|
|
2556
|
+
},
|
|
2557
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
2558
|
+
setSize: (width, height, top, left) => {
|
|
2559
|
+
const state2 = get();
|
|
2560
|
+
if (width === void 0) {
|
|
2561
|
+
set({ _sizeImperative: false });
|
|
2562
|
+
if (state2._sizeProps) {
|
|
2563
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
2564
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
2565
|
+
const currentSize = state2.size;
|
|
2566
|
+
const newSize = {
|
|
2567
|
+
width: propW ?? currentSize.width,
|
|
2568
|
+
height: propH ?? currentSize.height,
|
|
2569
|
+
top: currentSize.top,
|
|
2570
|
+
left: currentSize.left
|
|
2571
|
+
};
|
|
2572
|
+
set((s) => ({
|
|
2573
|
+
size: newSize,
|
|
2574
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
2575
|
+
}));
|
|
2576
|
+
getScheduler().invalidate();
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
const w = width;
|
|
2582
|
+
const h = height ?? width;
|
|
2583
|
+
const t = top ?? state2.size.top;
|
|
2584
|
+
const l = left ?? state2.size.left;
|
|
2585
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
2586
|
+
set((s) => ({
|
|
2587
|
+
size,
|
|
2588
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
2589
|
+
_sizeImperative: true
|
|
2590
|
+
}));
|
|
2591
|
+
getScheduler().invalidate();
|
|
2592
|
+
},
|
|
2593
|
+
setDpr: (dpr) => set((state2) => {
|
|
2594
|
+
const resolved = calculateDpr(dpr);
|
|
2595
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
2596
|
+
}),
|
|
2597
|
+
setFrameloop: (frameloop = "always") => {
|
|
2598
|
+
set(() => ({ frameloop }));
|
|
2599
|
+
},
|
|
2600
|
+
setError: (error) => set(() => ({ error })),
|
|
2601
|
+
error: null,
|
|
2602
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
2603
|
+
uniforms: {},
|
|
2604
|
+
nodes: {},
|
|
2605
|
+
textures: /* @__PURE__ */ new Map(),
|
|
2606
|
+
postProcessing: null,
|
|
2607
|
+
passes: {},
|
|
2608
|
+
_hmrVersion: 0,
|
|
2609
|
+
_sizeImperative: false,
|
|
2610
|
+
_sizeProps: null,
|
|
2611
|
+
previousRoot: void 0,
|
|
2612
|
+
internal: {
|
|
2613
|
+
// Events
|
|
2614
|
+
interaction: [],
|
|
2615
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2616
|
+
subscribers: [],
|
|
2617
|
+
initialClick: [0, 0],
|
|
2618
|
+
initialHits: [],
|
|
2619
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
2620
|
+
lastEvent: React.createRef(),
|
|
2621
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2622
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2623
|
+
// Occlusion system (WebGPU only)
|
|
2624
|
+
occlusionEnabled: false,
|
|
2625
|
+
occlusionObserver: null,
|
|
2626
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
2627
|
+
helperGroup: null,
|
|
2628
|
+
// Updates
|
|
2629
|
+
active: false,
|
|
2630
|
+
frames: 0,
|
|
2631
|
+
priority: 0,
|
|
2632
|
+
subscribe: (ref, priority, store) => {
|
|
2633
|
+
const internal = get().internal;
|
|
2634
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2635
|
+
internal.subscribers.push({ ref, priority, store });
|
|
2636
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
2637
|
+
return () => {
|
|
2638
|
+
const internal2 = get().internal;
|
|
2639
|
+
if (internal2?.subscribers) {
|
|
2640
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
2641
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
},
|
|
2645
|
+
// Renderer Storage (single source of truth)
|
|
2646
|
+
actualRenderer: null,
|
|
2647
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
2648
|
+
scheduler: null
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
return rootState;
|
|
2652
|
+
});
|
|
2653
|
+
const state = rootStore.getState();
|
|
2654
|
+
Object.defineProperty(state, "gl", {
|
|
2655
|
+
get() {
|
|
2656
|
+
const currentState = rootStore.getState();
|
|
2657
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
2658
|
+
const stack = new Error().stack || "";
|
|
2659
|
+
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
2660
|
+
if (!isInternalAccess) {
|
|
2661
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
2662
|
+
notifyDepreciated({
|
|
2663
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
2664
|
+
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
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return currentState.internal.actualRenderer;
|
|
2669
|
+
},
|
|
2670
|
+
set(value) {
|
|
2671
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2672
|
+
},
|
|
2673
|
+
enumerable: true,
|
|
2674
|
+
configurable: true
|
|
2675
|
+
});
|
|
2676
|
+
Object.defineProperty(state, "renderer", {
|
|
2677
|
+
get() {
|
|
2678
|
+
return rootStore.getState().internal.actualRenderer;
|
|
2679
|
+
},
|
|
2680
|
+
set(value) {
|
|
2681
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2682
|
+
},
|
|
2683
|
+
enumerable: true,
|
|
2684
|
+
configurable: true
|
|
2685
|
+
});
|
|
2686
|
+
let oldScene = state.scene;
|
|
2687
|
+
rootStore.subscribe(() => {
|
|
2688
|
+
const currentState = rootStore.getState();
|
|
2689
|
+
const { scene, rootScene, set } = currentState;
|
|
2690
|
+
if (scene !== oldScene) {
|
|
2691
|
+
oldScene = scene;
|
|
2692
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
2693
|
+
set({ rootScene: scene });
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
let oldSize = state.size;
|
|
2698
|
+
let oldDpr = state.viewport.dpr;
|
|
2699
|
+
let oldCamera = state.camera;
|
|
2700
|
+
rootStore.subscribe(() => {
|
|
2701
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
2702
|
+
const actualRenderer = internal.actualRenderer;
|
|
2703
|
+
const canvasTarget = internal.canvasTarget;
|
|
2704
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
2705
|
+
oldSize = size;
|
|
2706
|
+
oldDpr = viewport.dpr;
|
|
2707
|
+
updateCamera(camera, size);
|
|
2708
|
+
if (canvasTarget) {
|
|
2709
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2710
|
+
const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
|
|
2711
|
+
canvasTarget.setSize(size.width, size.height, updateStyle);
|
|
2712
|
+
} else {
|
|
2713
|
+
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2714
|
+
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
2715
|
+
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
2716
|
+
}
|
|
2257
2717
|
}
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
if (!shouldRun(job, timestamp)) continue;
|
|
2269
|
-
try {
|
|
2270
|
-
job.callback(frameState, delta);
|
|
2271
|
-
} catch (error) {
|
|
2272
|
-
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2273
|
-
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2718
|
+
if (camera !== oldCamera) {
|
|
2719
|
+
oldCamera = camera;
|
|
2720
|
+
const { rootScene } = rootStore.getState();
|
|
2721
|
+
if (camera && rootScene && !camera.parent) {
|
|
2722
|
+
rootScene.add(camera);
|
|
2723
|
+
}
|
|
2724
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
2725
|
+
const currentState = rootStore.getState();
|
|
2726
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
2727
|
+
updateFrustum(camera, currentState.frustum);
|
|
2274
2728
|
}
|
|
2275
2729
|
}
|
|
2276
|
-
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2730
|
+
});
|
|
2731
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
2732
|
+
return rootStore;
|
|
2733
|
+
};
|
|
2734
|
+
|
|
2735
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
2736
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
2737
|
+
function getLoader(Proto) {
|
|
2738
|
+
if (isConstructor$1(Proto)) {
|
|
2739
|
+
let loader = memoizedLoaders.get(Proto);
|
|
2740
|
+
if (!loader) {
|
|
2741
|
+
loader = new Proto();
|
|
2742
|
+
memoizedLoaders.set(Proto, loader);
|
|
2287
2743
|
}
|
|
2288
|
-
return
|
|
2744
|
+
return loader;
|
|
2289
2745
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2746
|
+
return Proto;
|
|
2747
|
+
}
|
|
2748
|
+
function loadingFn(extensions, onProgress) {
|
|
2749
|
+
return function(Proto, input) {
|
|
2750
|
+
const loader = getLoader(Proto);
|
|
2751
|
+
if (extensions) extensions(loader);
|
|
2752
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
2753
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
2754
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2755
|
+
return data;
|
|
2756
|
+
});
|
|
2299
2757
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
*/
|
|
2319
|
-
hasUserJobsInPhase(phase, rootId) {
|
|
2320
|
-
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2321
|
-
return rootsToCheck.some((root) => {
|
|
2322
|
-
if (!root) return false;
|
|
2323
|
-
for (const job of root.jobs.values()) {
|
|
2324
|
-
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2325
|
-
}
|
|
2326
|
-
return false;
|
|
2327
|
-
});
|
|
2328
|
-
}
|
|
2329
|
-
//* Utility Methods ================================
|
|
2330
|
-
/**
|
|
2331
|
-
* Generate a unique root ID for automatic root registration.
|
|
2332
|
-
* @returns {string} A unique root ID in the format 'root_N'
|
|
2333
|
-
*/
|
|
2334
|
-
generateRootId() {
|
|
2335
|
-
return `root_${this.nextRootIndex++}`;
|
|
2336
|
-
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Generate a unique job ID.
|
|
2339
|
-
* @returns {string} A unique job ID in the format 'job_N'
|
|
2340
|
-
* @private
|
|
2341
|
-
*/
|
|
2342
|
-
generateJobId() {
|
|
2343
|
-
return `job_${this.nextJobIndex}`;
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Normalize before/after constraints to a Set.
|
|
2347
|
-
* Handles undefined, single string, or array inputs.
|
|
2348
|
-
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2349
|
-
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2350
|
-
* @private
|
|
2351
|
-
*/
|
|
2352
|
-
normalizeConstraints(value) {
|
|
2353
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
2354
|
-
if (Array.isArray(value)) return new Set(value);
|
|
2355
|
-
return /* @__PURE__ */ new Set([value]);
|
|
2356
|
-
}
|
|
2357
|
-
};
|
|
2358
|
-
//* Static State & Methods (Singleton Usage) ================================
|
|
2359
|
-
//* Cross-Bundle Singleton Key ==============================
|
|
2360
|
-
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2361
|
-
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2362
|
-
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2363
|
-
let Scheduler = _Scheduler;
|
|
2364
|
-
const getScheduler = () => Scheduler.get();
|
|
2365
|
-
if (hmrData) {
|
|
2366
|
-
hmrData.accept?.();
|
|
2758
|
+
return new Promise(
|
|
2759
|
+
(res, reject) => loader.load(
|
|
2760
|
+
input,
|
|
2761
|
+
(data) => {
|
|
2762
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2763
|
+
res(data);
|
|
2764
|
+
},
|
|
2765
|
+
onProgress,
|
|
2766
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
2767
|
+
)
|
|
2768
|
+
);
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
2772
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2773
|
+
const fn = loadingFn(extensions, onProgress);
|
|
2774
|
+
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
2775
|
+
return Array.isArray(input) ? results : results[0];
|
|
2367
2776
|
}
|
|
2777
|
+
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
2778
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2779
|
+
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
2780
|
+
};
|
|
2781
|
+
useLoader.clear = function(loader, input) {
|
|
2782
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2783
|
+
keys.forEach((key) => clear([loader, key]));
|
|
2784
|
+
};
|
|
2785
|
+
useLoader.loader = getLoader;
|
|
2368
2786
|
|
|
2369
2787
|
function useFrame(callback, priorityOrOptions) {
|
|
2370
2788
|
const store = React.useContext(context);
|
|
@@ -2545,6 +2963,9 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2545
2963
|
const textureCache = useThree((state) => state.textures);
|
|
2546
2964
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2547
2965
|
const { onLoad, cache = false } = options;
|
|
2966
|
+
const onLoadRef = useRef(onLoad);
|
|
2967
|
+
onLoadRef.current = onLoad;
|
|
2968
|
+
const onLoadCalledForRef = useRef(null);
|
|
2548
2969
|
const urls = useMemo(() => getUrls(input), [input]);
|
|
2549
2970
|
const cachedResult = useMemo(() => {
|
|
2550
2971
|
if (!cache) return null;
|
|
@@ -2555,9 +2976,13 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2555
2976
|
TextureLoader,
|
|
2556
2977
|
IsObject(input) ? Object.values(input) : input
|
|
2557
2978
|
);
|
|
2979
|
+
const inputKey = urls.join("\0");
|
|
2558
2980
|
useLayoutEffect(() => {
|
|
2559
|
-
if (
|
|
2560
|
-
|
|
2981
|
+
if (cachedResult) return;
|
|
2982
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
2983
|
+
onLoadCalledForRef.current = inputKey;
|
|
2984
|
+
onLoadRef.current?.(loadedTextures);
|
|
2985
|
+
}, [cachedResult, loadedTextures, inputKey]);
|
|
2561
2986
|
useEffect(() => {
|
|
2562
2987
|
if (cachedResult) return;
|
|
2563
2988
|
if ("initTexture" in renderer) {
|
|
@@ -2724,14 +3149,31 @@ function useTextures() {
|
|
|
2724
3149
|
}, [store]);
|
|
2725
3150
|
}
|
|
2726
3151
|
|
|
2727
|
-
function useRenderTarget(
|
|
3152
|
+
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
2728
3153
|
const isLegacy = useThree((s) => s.isLegacy);
|
|
2729
3154
|
const size = useThree((s) => s.size);
|
|
3155
|
+
let width;
|
|
3156
|
+
let height;
|
|
3157
|
+
let opts;
|
|
3158
|
+
if (typeof widthOrOptions === "object") {
|
|
3159
|
+
opts = widthOrOptions;
|
|
3160
|
+
} else if (typeof widthOrOptions === "number") {
|
|
3161
|
+
width = widthOrOptions;
|
|
3162
|
+
if (typeof heightOrOptions === "object") {
|
|
3163
|
+
height = widthOrOptions;
|
|
3164
|
+
opts = heightOrOptions;
|
|
3165
|
+
} else if (typeof heightOrOptions === "number") {
|
|
3166
|
+
height = heightOrOptions;
|
|
3167
|
+
opts = options;
|
|
3168
|
+
} else {
|
|
3169
|
+
height = widthOrOptions;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
2730
3172
|
return useMemo(() => {
|
|
2731
3173
|
const w = width ?? size.width;
|
|
2732
3174
|
const h = height ?? size.height;
|
|
2733
|
-
return new WebGLRenderTarget(w, h,
|
|
2734
|
-
}, [width, height, size.width, size.height,
|
|
3175
|
+
return new WebGLRenderTarget(w, h, opts);
|
|
3176
|
+
}, [width, height, size.width, size.height, opts, isLegacy]);
|
|
2735
3177
|
}
|
|
2736
3178
|
|
|
2737
3179
|
function useStore() {
|
|
@@ -2781,7 +3223,7 @@ function addTail(callback) {
|
|
|
2781
3223
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
2782
3224
|
getScheduler().invalidate(frames, stackFrames);
|
|
2783
3225
|
}
|
|
2784
|
-
function advance(timestamp
|
|
3226
|
+
function advance(timestamp) {
|
|
2785
3227
|
getScheduler().step(timestamp);
|
|
2786
3228
|
}
|
|
2787
3229
|
|
|
@@ -14235,6 +14677,7 @@ function swapInstances() {
|
|
|
14235
14677
|
instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
|
|
14236
14678
|
instance.object.__r3f = instance;
|
|
14237
14679
|
setFiberRef(fiber, instance.object);
|
|
14680
|
+
delete instance.appliedOnce;
|
|
14238
14681
|
applyProps(instance.object, instance.props);
|
|
14239
14682
|
if (instance.props.attach) {
|
|
14240
14683
|
attach(parent, instance);
|
|
@@ -14308,8 +14751,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
14308
14751
|
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
14309
14752
|
if (isTailSibling) swapInstances();
|
|
14310
14753
|
},
|
|
14311
|
-
finalizeInitialChildren: () =>
|
|
14312
|
-
|
|
14754
|
+
finalizeInitialChildren: (instance) => {
|
|
14755
|
+
for (const prop in instance.props) {
|
|
14756
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
14757
|
+
}
|
|
14758
|
+
return false;
|
|
14759
|
+
},
|
|
14760
|
+
commitMount(instance) {
|
|
14761
|
+
const resolved = {};
|
|
14762
|
+
for (const prop in instance.props) {
|
|
14763
|
+
const value = instance.props[prop];
|
|
14764
|
+
if (isFromRef(value)) {
|
|
14765
|
+
const ref = value[FROM_REF];
|
|
14766
|
+
if (ref.current != null) resolved[prop] = ref.current;
|
|
14767
|
+
}
|
|
14768
|
+
}
|
|
14769
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
14313
14770
|
},
|
|
14314
14771
|
getPublicInstance: (instance) => instance?.object,
|
|
14315
14772
|
prepareForCommit: () => null,
|
|
@@ -14530,6 +14987,9 @@ function createRoot(canvas) {
|
|
|
14530
14987
|
let resolve;
|
|
14531
14988
|
pending = new Promise((_resolve) => resolve = _resolve);
|
|
14532
14989
|
const {
|
|
14990
|
+
id: canvasId,
|
|
14991
|
+
primaryCanvas,
|
|
14992
|
+
scheduler: schedulerConfig,
|
|
14533
14993
|
gl: glConfig,
|
|
14534
14994
|
renderer: rendererConfig,
|
|
14535
14995
|
size: propsSize,
|
|
@@ -14537,10 +14997,7 @@ function createRoot(canvas) {
|
|
|
14537
14997
|
events,
|
|
14538
14998
|
onCreated: onCreatedCallback,
|
|
14539
14999
|
shadows = false,
|
|
14540
|
-
linear = false,
|
|
14541
|
-
flat = false,
|
|
14542
15000
|
textureColorSpace = SRGBColorSpace,
|
|
14543
|
-
legacy = false,
|
|
14544
15001
|
orthographic = false,
|
|
14545
15002
|
frameloop = "always",
|
|
14546
15003
|
dpr = [1, 2],
|
|
@@ -14552,7 +15009,8 @@ function createRoot(canvas) {
|
|
|
14552
15009
|
onDropMissed,
|
|
14553
15010
|
autoUpdateFrustum = true,
|
|
14554
15011
|
occlusion = false,
|
|
14555
|
-
_sizeProps
|
|
15012
|
+
_sizeProps,
|
|
15013
|
+
forceEven
|
|
14556
15014
|
} = props;
|
|
14557
15015
|
const state = store.getState();
|
|
14558
15016
|
const defaultGLProps = {
|
|
@@ -14566,15 +15024,25 @@ function createRoot(canvas) {
|
|
|
14566
15024
|
"WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
|
|
14567
15025
|
);
|
|
14568
15026
|
}
|
|
14569
|
-
(state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
15027
|
+
const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
14570
15028
|
if (glConfig && rendererConfig) {
|
|
14571
15029
|
throw new Error("Cannot use both gl and renderer props at the same time");
|
|
14572
15030
|
}
|
|
14573
15031
|
let renderer = state.internal.actualRenderer;
|
|
15032
|
+
if (primaryCanvas && !R3F_BUILD_WEBGPU) {
|
|
15033
|
+
throw new Error(
|
|
15034
|
+
"The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
|
|
15035
|
+
);
|
|
15036
|
+
}
|
|
15037
|
+
if (primaryCanvas && wantsGL) {
|
|
15038
|
+
throw new Error(
|
|
15039
|
+
"The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
|
|
15040
|
+
);
|
|
15041
|
+
}
|
|
14574
15042
|
if (!state.internal.actualRenderer) {
|
|
14575
15043
|
renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
|
|
14576
15044
|
state.internal.actualRenderer = renderer;
|
|
14577
|
-
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
15045
|
+
state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
|
|
14578
15046
|
}
|
|
14579
15047
|
let raycaster = state.raycaster;
|
|
14580
15048
|
if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
|
|
@@ -14583,6 +15051,7 @@ function createRoot(canvas) {
|
|
|
14583
15051
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
14584
15052
|
applyProps(raycaster, { params: { ...raycaster.params, ...params } });
|
|
14585
15053
|
}
|
|
15054
|
+
let tempCamera = state.camera;
|
|
14586
15055
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
14587
15056
|
lastCamera = cameraOptions;
|
|
14588
15057
|
const isCamera = cameraOptions?.isCamera;
|
|
@@ -14602,6 +15071,7 @@ function createRoot(canvas) {
|
|
|
14602
15071
|
if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
|
|
14603
15072
|
}
|
|
14604
15073
|
state.set({ camera });
|
|
15074
|
+
tempCamera = camera;
|
|
14605
15075
|
raycaster.camera = camera;
|
|
14606
15076
|
}
|
|
14607
15077
|
if (!state.scene) {
|
|
@@ -14619,7 +15089,7 @@ function createRoot(canvas) {
|
|
|
14619
15089
|
rootScene: scene,
|
|
14620
15090
|
internal: { ...prev.internal, container: scene }
|
|
14621
15091
|
}));
|
|
14622
|
-
const camera =
|
|
15092
|
+
const camera = tempCamera;
|
|
14623
15093
|
if (camera && !camera.parent) scene.add(camera);
|
|
14624
15094
|
}
|
|
14625
15095
|
if (events && !state.events.handlers) {
|
|
@@ -14636,6 +15106,9 @@ function createRoot(canvas) {
|
|
|
14636
15106
|
if (_sizeProps !== void 0) {
|
|
14637
15107
|
state.set({ _sizeProps });
|
|
14638
15108
|
}
|
|
15109
|
+
if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
|
|
15110
|
+
state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
|
|
15111
|
+
}
|
|
14639
15112
|
const size = computeInitialSize(canvas, propsSize);
|
|
14640
15113
|
if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
|
|
14641
15114
|
const wasImperative = state._sizeImperative;
|
|
@@ -14665,7 +15138,7 @@ function createRoot(canvas) {
|
|
|
14665
15138
|
const handleXRFrame = (timestamp, frame) => {
|
|
14666
15139
|
const state2 = store.getState();
|
|
14667
15140
|
if (state2.frameloop === "never") return;
|
|
14668
|
-
advance(timestamp
|
|
15141
|
+
advance(timestamp);
|
|
14669
15142
|
};
|
|
14670
15143
|
const actualRenderer = state.internal.actualRenderer;
|
|
14671
15144
|
const handleSessionChange = () => {
|
|
@@ -14677,16 +15150,16 @@ function createRoot(canvas) {
|
|
|
14677
15150
|
};
|
|
14678
15151
|
const xr = {
|
|
14679
15152
|
connect() {
|
|
14680
|
-
const { gl, renderer: renderer2
|
|
14681
|
-
const
|
|
14682
|
-
|
|
14683
|
-
|
|
15153
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15154
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15155
|
+
xrManager.addEventListener("sessionstart", handleSessionChange);
|
|
15156
|
+
xrManager.addEventListener("sessionend", handleSessionChange);
|
|
14684
15157
|
},
|
|
14685
15158
|
disconnect() {
|
|
14686
|
-
const { gl, renderer: renderer2
|
|
14687
|
-
const
|
|
14688
|
-
|
|
14689
|
-
|
|
15159
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15160
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15161
|
+
xrManager.removeEventListener("sessionstart", handleSessionChange);
|
|
15162
|
+
xrManager.removeEventListener("sessionend", handleSessionChange);
|
|
14690
15163
|
}
|
|
14691
15164
|
};
|
|
14692
15165
|
if (typeof renderer.xr?.addEventListener === "function") xr.connect();
|
|
@@ -14714,25 +15187,9 @@ function createRoot(canvas) {
|
|
|
14714
15187
|
renderer.shadowMap.needsUpdate = true;
|
|
14715
15188
|
}
|
|
14716
15189
|
}
|
|
14717
|
-
{
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
-
const flatChanged = flat !== lastConfiguredProps.flat;
|
|
14721
|
-
if (legacyChanged) {
|
|
14722
|
-
ColorManagement.enabled = !legacy;
|
|
14723
|
-
lastConfiguredProps.legacy = legacy;
|
|
14724
|
-
}
|
|
14725
|
-
if (!configured || linearChanged) {
|
|
14726
|
-
renderer.outputColorSpace = linear ? LinearSRGBColorSpace : SRGBColorSpace;
|
|
14727
|
-
lastConfiguredProps.linear = linear;
|
|
14728
|
-
}
|
|
14729
|
-
if (!configured || flatChanged) {
|
|
14730
|
-
renderer.toneMapping = flat ? NoToneMapping : ACESFilmicToneMapping;
|
|
14731
|
-
lastConfiguredProps.flat = flat;
|
|
14732
|
-
}
|
|
14733
|
-
if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
|
|
14734
|
-
if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
|
|
14735
|
-
if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
|
|
15190
|
+
if (!configured) {
|
|
15191
|
+
renderer.outputColorSpace = SRGBColorSpace;
|
|
15192
|
+
renderer.toneMapping = ACESFilmicToneMapping;
|
|
14736
15193
|
}
|
|
14737
15194
|
if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
|
|
14738
15195
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
@@ -14750,11 +15207,26 @@ function createRoot(canvas) {
|
|
|
14750
15207
|
const scheduler = getScheduler();
|
|
14751
15208
|
const rootId = state.internal.rootId;
|
|
14752
15209
|
if (!rootId) {
|
|
14753
|
-
const newRootId = scheduler.generateRootId();
|
|
15210
|
+
const newRootId = canvasId || scheduler.generateRootId();
|
|
14754
15211
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14755
15212
|
getState: () => store.getState(),
|
|
14756
15213
|
onError: (err) => store.getState().setError(err)
|
|
14757
15214
|
});
|
|
15215
|
+
const unregisterCanvasTarget = scheduler.register(
|
|
15216
|
+
() => {
|
|
15217
|
+
const state2 = store.getState();
|
|
15218
|
+
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
15219
|
+
const renderer2 = state2.internal.actualRenderer;
|
|
15220
|
+
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
15221
|
+
}
|
|
15222
|
+
},
|
|
15223
|
+
{
|
|
15224
|
+
id: `${newRootId}_canvasTarget`,
|
|
15225
|
+
rootId: newRootId,
|
|
15226
|
+
phase: "start",
|
|
15227
|
+
system: true
|
|
15228
|
+
}
|
|
15229
|
+
);
|
|
14758
15230
|
const unregisterFrustum = scheduler.register(
|
|
14759
15231
|
() => {
|
|
14760
15232
|
const state2 = store.getState();
|
|
@@ -14796,11 +15268,15 @@ function createRoot(canvas) {
|
|
|
14796
15268
|
}
|
|
14797
15269
|
},
|
|
14798
15270
|
{
|
|
14799
|
-
|
|
15271
|
+
// Use canvas ID directly as job ID if available, otherwise use generated rootId
|
|
15272
|
+
id: canvasId || `${newRootId}_render`,
|
|
14800
15273
|
rootId: newRootId,
|
|
14801
15274
|
phase: "render",
|
|
14802
|
-
system: true
|
|
15275
|
+
system: true,
|
|
14803
15276
|
// Internal flag: this is a system job, not user-controlled
|
|
15277
|
+
// Apply scheduler config for render ordering and rate limiting
|
|
15278
|
+
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
15279
|
+
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14804
15280
|
}
|
|
14805
15281
|
);
|
|
14806
15282
|
state.set((state2) => ({
|
|
@@ -14809,6 +15285,7 @@ function createRoot(canvas) {
|
|
|
14809
15285
|
rootId: newRootId,
|
|
14810
15286
|
unregisterRoot: () => {
|
|
14811
15287
|
unregisterRoot();
|
|
15288
|
+
unregisterCanvasTarget();
|
|
14812
15289
|
unregisterFrustum();
|
|
14813
15290
|
unregisterVisibility();
|
|
14814
15291
|
unregisterRender();
|
|
@@ -14867,15 +15344,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14867
15344
|
const renderer = state.internal.actualRenderer;
|
|
14868
15345
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14869
15346
|
if (unregisterRoot) unregisterRoot();
|
|
15347
|
+
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
15348
|
+
if (unregisterPrimary) unregisterPrimary();
|
|
15349
|
+
const canvasTarget = state.internal.canvasTarget;
|
|
15350
|
+
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14870
15351
|
state.events.disconnect?.();
|
|
14871
15352
|
cleanupHelperGroup(root.store);
|
|
14872
|
-
renderer
|
|
14873
|
-
|
|
14874
|
-
|
|
15353
|
+
if (state.isLegacy && renderer) {
|
|
15354
|
+
;
|
|
15355
|
+
renderer.renderLists?.dispose?.();
|
|
15356
|
+
renderer.forceContextLoss?.();
|
|
15357
|
+
}
|
|
15358
|
+
if (!state.internal.isSecondary) {
|
|
15359
|
+
if (renderer?.xr) state.xr.disconnect();
|
|
15360
|
+
}
|
|
14875
15361
|
dispose(state.scene);
|
|
14876
15362
|
_roots.delete(canvas);
|
|
14877
15363
|
if (callback) callback(canvas);
|
|
14878
|
-
} catch
|
|
15364
|
+
} catch {
|
|
14879
15365
|
}
|
|
14880
15366
|
}, 500);
|
|
14881
15367
|
}
|
|
@@ -14883,36 +15369,34 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14883
15369
|
}
|
|
14884
15370
|
}
|
|
14885
15371
|
function createPortal(children, container, state) {
|
|
14886
|
-
return /* @__PURE__ */ jsx(
|
|
15372
|
+
return /* @__PURE__ */ jsx(Portal, { children, container, state });
|
|
14887
15373
|
}
|
|
14888
|
-
function
|
|
15374
|
+
function Portal({ children, container, state }) {
|
|
14889
15375
|
const isRef = useCallback((obj) => obj && "current" in obj, []);
|
|
14890
|
-
const [resolvedContainer,
|
|
15376
|
+
const [resolvedContainer, _setResolvedContainer] = useState(() => {
|
|
14891
15377
|
if (isRef(container)) return container.current ?? null;
|
|
14892
15378
|
return container;
|
|
14893
15379
|
});
|
|
15380
|
+
const setResolvedContainer = useCallback(
|
|
15381
|
+
(newContainer) => {
|
|
15382
|
+
if (!newContainer || newContainer === resolvedContainer) return;
|
|
15383
|
+
_setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
|
|
15384
|
+
},
|
|
15385
|
+
[resolvedContainer, _setResolvedContainer, isRef]
|
|
15386
|
+
);
|
|
14894
15387
|
useMemo(() => {
|
|
14895
|
-
if (isRef(container)) {
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
const updated = container.current;
|
|
14900
|
-
if (updated && updated !== resolvedContainer) {
|
|
14901
|
-
setResolvedContainer(updated);
|
|
14902
|
-
}
|
|
14903
|
-
});
|
|
14904
|
-
} else if (current !== resolvedContainer) {
|
|
14905
|
-
setResolvedContainer(current);
|
|
14906
|
-
}
|
|
14907
|
-
} else if (container !== resolvedContainer) {
|
|
14908
|
-
setResolvedContainer(container);
|
|
15388
|
+
if (isRef(container) && !container.current) {
|
|
15389
|
+
return queueMicrotask(() => {
|
|
15390
|
+
setResolvedContainer(container.current);
|
|
15391
|
+
});
|
|
14909
15392
|
}
|
|
14910
|
-
|
|
15393
|
+
setResolvedContainer(container);
|
|
15394
|
+
}, [container, isRef, setResolvedContainer]);
|
|
14911
15395
|
if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
|
|
14912
15396
|
const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
|
|
14913
|
-
return /* @__PURE__ */ jsx(
|
|
15397
|
+
return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
|
|
14914
15398
|
}
|
|
14915
|
-
function
|
|
15399
|
+
function PortalInner({ state = {}, children, container }) {
|
|
14916
15400
|
const { events, size, injectScene = true, ...rest } = state;
|
|
14917
15401
|
const previousRoot = useStore();
|
|
14918
15402
|
const [raycaster] = useState(() => new Raycaster());
|
|
@@ -14933,11 +15417,12 @@ function Portal({ state = {}, children, container }) {
|
|
|
14933
15417
|
};
|
|
14934
15418
|
}, [portalScene, container, injectScene]);
|
|
14935
15419
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
15420
|
+
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14936
15421
|
let viewport = void 0;
|
|
14937
|
-
if (injectState.camera && size) {
|
|
15422
|
+
if (injectState.camera && (size || injectState.size)) {
|
|
14938
15423
|
const camera = injectState.camera;
|
|
14939
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(),
|
|
14940
|
-
if (camera !== rootState.camera) updateCamera(camera,
|
|
15424
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
|
|
15425
|
+
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14941
15426
|
}
|
|
14942
15427
|
return {
|
|
14943
15428
|
// The intersect consists of the previous root state
|
|
@@ -14954,7 +15439,7 @@ function Portal({ state = {}, children, container }) {
|
|
|
14954
15439
|
previousRoot,
|
|
14955
15440
|
// Events, size and viewport can be overridden by the inject layer
|
|
14956
15441
|
events: { ...rootState.events, ...injectState.events, ...events },
|
|
14957
|
-
size:
|
|
15442
|
+
size: resolvedSize,
|
|
14958
15443
|
viewport: { ...rootState.viewport, ...viewport },
|
|
14959
15444
|
// Layers are allowed to override events
|
|
14960
15445
|
setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
|
|
@@ -14988,15 +15473,13 @@ function CanvasImpl({
|
|
|
14988
15473
|
fallback,
|
|
14989
15474
|
resize,
|
|
14990
15475
|
style,
|
|
15476
|
+
id,
|
|
14991
15477
|
gl,
|
|
14992
|
-
renderer,
|
|
15478
|
+
renderer: rendererProp,
|
|
14993
15479
|
events = createPointerEvents,
|
|
14994
15480
|
eventSource,
|
|
14995
15481
|
eventPrefix,
|
|
14996
15482
|
shadows,
|
|
14997
|
-
linear,
|
|
14998
|
-
flat,
|
|
14999
|
-
legacy,
|
|
15000
15483
|
orthographic,
|
|
15001
15484
|
frameloop,
|
|
15002
15485
|
dpr,
|
|
@@ -15011,10 +15494,43 @@ function CanvasImpl({
|
|
|
15011
15494
|
hmr,
|
|
15012
15495
|
width,
|
|
15013
15496
|
height,
|
|
15497
|
+
background,
|
|
15498
|
+
forceEven,
|
|
15014
15499
|
...props
|
|
15015
15500
|
}) {
|
|
15501
|
+
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 };
|
|
15502
|
+
const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
|
|
15016
15503
|
React.useMemo(() => extend(THREE), []);
|
|
15017
15504
|
const Bridge = useBridge();
|
|
15505
|
+
const backgroundProps = React.useMemo(() => {
|
|
15506
|
+
if (!background) return null;
|
|
15507
|
+
if (typeof background === "object" && !background.isColor) {
|
|
15508
|
+
const { backgroundMap, envMap, files, preset, ...rest } = background;
|
|
15509
|
+
return {
|
|
15510
|
+
...rest,
|
|
15511
|
+
preset,
|
|
15512
|
+
files: envMap || files,
|
|
15513
|
+
backgroundFiles: backgroundMap,
|
|
15514
|
+
background: true
|
|
15515
|
+
};
|
|
15516
|
+
}
|
|
15517
|
+
if (typeof background === "number") {
|
|
15518
|
+
return { color: background, background: true };
|
|
15519
|
+
}
|
|
15520
|
+
if (typeof background === "string") {
|
|
15521
|
+
if (background in presetsObj) {
|
|
15522
|
+
return { preset: background, background: true };
|
|
15523
|
+
}
|
|
15524
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
|
|
15525
|
+
return { files: background, background: true };
|
|
15526
|
+
}
|
|
15527
|
+
return { color: background, background: true };
|
|
15528
|
+
}
|
|
15529
|
+
if (background.isColor) {
|
|
15530
|
+
return { color: background, background: true };
|
|
15531
|
+
}
|
|
15532
|
+
return null;
|
|
15533
|
+
}, [background]);
|
|
15018
15534
|
const hasInitialSizeRef = React.useRef(false);
|
|
15019
15535
|
const measureConfig = React.useMemo(() => {
|
|
15020
15536
|
if (!hasInitialSizeRef.current) {
|
|
@@ -15031,15 +15547,20 @@ function CanvasImpl({
|
|
|
15031
15547
|
};
|
|
15032
15548
|
}, [resize, hasInitialSizeRef.current]);
|
|
15033
15549
|
const [containerRef, containerRect] = useMeasure(measureConfig);
|
|
15034
|
-
const effectiveSize = React.useMemo(
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15550
|
+
const effectiveSize = React.useMemo(() => {
|
|
15551
|
+
let w = width ?? containerRect.width;
|
|
15552
|
+
let h = height ?? containerRect.height;
|
|
15553
|
+
if (forceEven) {
|
|
15554
|
+
w = Math.ceil(w / 2) * 2;
|
|
15555
|
+
h = Math.ceil(h / 2) * 2;
|
|
15556
|
+
}
|
|
15557
|
+
return {
|
|
15558
|
+
width: w,
|
|
15559
|
+
height: h,
|
|
15038
15560
|
top: containerRect.top,
|
|
15039
15561
|
left: containerRect.left
|
|
15040
|
-
}
|
|
15041
|
-
|
|
15042
|
-
);
|
|
15562
|
+
};
|
|
15563
|
+
}, [width, height, containerRect, forceEven]);
|
|
15043
15564
|
if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
|
|
15044
15565
|
hasInitialSizeRef.current = true;
|
|
15045
15566
|
}
|
|
@@ -15079,14 +15600,14 @@ function CanvasImpl({
|
|
|
15079
15600
|
async function run() {
|
|
15080
15601
|
if (!effectActiveRef.current || !root.current) return;
|
|
15081
15602
|
await root.current.configure({
|
|
15603
|
+
id,
|
|
15604
|
+
primaryCanvas,
|
|
15605
|
+
scheduler,
|
|
15082
15606
|
gl,
|
|
15083
15607
|
renderer,
|
|
15084
15608
|
scene,
|
|
15085
15609
|
events,
|
|
15086
15610
|
shadows,
|
|
15087
|
-
linear,
|
|
15088
|
-
flat,
|
|
15089
|
-
legacy,
|
|
15090
15611
|
orthographic,
|
|
15091
15612
|
frameloop,
|
|
15092
15613
|
dpr,
|
|
@@ -15096,6 +15617,7 @@ function CanvasImpl({
|
|
|
15096
15617
|
size: effectiveSize,
|
|
15097
15618
|
// Store size props for reset functionality
|
|
15098
15619
|
_sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
|
|
15620
|
+
forceEven,
|
|
15099
15621
|
// Pass mutable reference to onPointerMissed so it's free to update
|
|
15100
15622
|
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
|
|
15101
15623
|
onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
|
|
@@ -15119,7 +15641,10 @@ function CanvasImpl({
|
|
|
15119
15641
|
});
|
|
15120
15642
|
if (!effectActiveRef.current || !root.current) return;
|
|
15121
15643
|
root.current.render(
|
|
15122
|
-
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */
|
|
15644
|
+
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
|
|
15645
|
+
backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
|
|
15646
|
+
children ?? null
|
|
15647
|
+
] }) }) })
|
|
15123
15648
|
);
|
|
15124
15649
|
}
|
|
15125
15650
|
run();
|
|
@@ -15146,14 +15671,16 @@ function CanvasImpl({
|
|
|
15146
15671
|
const canvas = canvasRef.current;
|
|
15147
15672
|
if (!canvas) return;
|
|
15148
15673
|
const handleHMR = () => {
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
rootEntry
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15674
|
+
queueMicrotask(() => {
|
|
15675
|
+
const rootEntry = _roots.get(canvas);
|
|
15676
|
+
if (rootEntry?.store) {
|
|
15677
|
+
rootEntry.store.setState((state) => ({
|
|
15678
|
+
nodes: {},
|
|
15679
|
+
uniforms: {},
|
|
15680
|
+
_hmrVersion: state._hmrVersion + 1
|
|
15681
|
+
}));
|
|
15682
|
+
}
|
|
15683
|
+
});
|
|
15157
15684
|
};
|
|
15158
15685
|
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
15159
15686
|
const hot = import.meta.hot;
|
|
@@ -15182,7 +15709,7 @@ function CanvasImpl({
|
|
|
15182
15709
|
...style
|
|
15183
15710
|
},
|
|
15184
15711
|
...props,
|
|
15185
|
-
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
|
|
15712
|
+
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
|
|
15186
15713
|
}
|
|
15187
15714
|
);
|
|
15188
15715
|
}
|
|
@@ -15192,4 +15719,4 @@ function Canvas(props) {
|
|
|
15192
15719
|
|
|
15193
15720
|
extend(THREE);
|
|
15194
15721
|
|
|
15195
|
-
export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree };
|
|
15722
|
+
export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getScheduler, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, reconciler, registerPrimary, removeInteractivity, resolve, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useEnvironment, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree, waitForPrimary };
|