@react-three/fiber 10.0.0-alpha.2 → 10.0.0-canary.1b98c17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/dist/index.cjs +1454 -618
- package/dist/index.d.cts +2130 -1285
- package/dist/index.d.mts +2130 -1285
- package/dist/index.d.ts +2130 -1285
- package/dist/index.mjs +1439 -622
- package/dist/legacy.cjs +1420 -608
- package/dist/legacy.d.cts +2131 -1286
- package/dist/legacy.d.mts +2131 -1286
- package/dist/legacy.d.ts +2131 -1286
- package/dist/legacy.mjs +1405 -612
- package/dist/webgpu/index.cjs +1841 -577
- package/dist/webgpu/index.d.cts +2310 -1312
- package/dist/webgpu/index.d.mts +2310 -1312
- package/dist/webgpu/index.d.ts +2310 -1312
- package/dist/webgpu/index.mjs +1821 -580
- package/package.json +3 -1
- package/readme.md +244 -318
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import * as webgpu from 'three/webgpu';
|
|
2
|
-
import { RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1,
|
|
2
|
+
import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
|
|
3
3
|
import { WebGLRenderTarget, WebGLRenderer } from 'three';
|
|
4
4
|
import { Inspector } from 'three/addons/inspector/Inspector.js';
|
|
5
|
-
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
import * as React from 'react';
|
|
7
|
-
import React__default, {
|
|
7
|
+
import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
|
|
8
8
|
import useMeasure from 'react-use-measure';
|
|
9
9
|
import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
|
|
10
|
+
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';
|
|
11
|
+
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
12
|
+
import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
|
|
13
|
+
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
|
|
14
|
+
import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
|
|
15
|
+
import { GainMapLoader } from '@monogrid/gainmap-js';
|
|
10
16
|
import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
|
|
11
17
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
12
18
|
import { suspend, preload, clear } from 'suspend-react';
|
|
@@ -36,6 +42,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
36
42
|
WebGLRenderer: WebGLRenderer
|
|
37
43
|
}, [webgpu]);
|
|
38
44
|
|
|
45
|
+
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
46
|
+
const pendingSubscribers = /* @__PURE__ */ new Map();
|
|
47
|
+
function registerPrimary(id, renderer, store) {
|
|
48
|
+
if (primaryRegistry.has(id)) {
|
|
49
|
+
console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
|
|
50
|
+
}
|
|
51
|
+
const entry = { renderer, store };
|
|
52
|
+
primaryRegistry.set(id, entry);
|
|
53
|
+
const subscribers = pendingSubscribers.get(id);
|
|
54
|
+
if (subscribers) {
|
|
55
|
+
subscribers.forEach((callback) => callback(entry));
|
|
56
|
+
pendingSubscribers.delete(id);
|
|
57
|
+
}
|
|
58
|
+
return () => {
|
|
59
|
+
const currentEntry = primaryRegistry.get(id);
|
|
60
|
+
if (currentEntry?.renderer === renderer) {
|
|
61
|
+
primaryRegistry.delete(id);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function getPrimary(id) {
|
|
66
|
+
return primaryRegistry.get(id);
|
|
67
|
+
}
|
|
68
|
+
function waitForPrimary(id, timeout = 5e3) {
|
|
69
|
+
const existing = primaryRegistry.get(id);
|
|
70
|
+
if (existing) {
|
|
71
|
+
return Promise.resolve(existing);
|
|
72
|
+
}
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const timeoutId = setTimeout(() => {
|
|
75
|
+
const subscribers = pendingSubscribers.get(id);
|
|
76
|
+
if (subscribers) {
|
|
77
|
+
const index = subscribers.indexOf(callback);
|
|
78
|
+
if (index !== -1) subscribers.splice(index, 1);
|
|
79
|
+
if (subscribers.length === 0) pendingSubscribers.delete(id);
|
|
80
|
+
}
|
|
81
|
+
reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
|
|
82
|
+
}, timeout);
|
|
83
|
+
const callback = (entry) => {
|
|
84
|
+
clearTimeout(timeoutId);
|
|
85
|
+
resolve(entry);
|
|
86
|
+
};
|
|
87
|
+
if (!pendingSubscribers.has(id)) {
|
|
88
|
+
pendingSubscribers.set(id, []);
|
|
89
|
+
}
|
|
90
|
+
pendingSubscribers.get(id).push(callback);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function hasPrimary(id) {
|
|
94
|
+
return primaryRegistry.has(id);
|
|
95
|
+
}
|
|
96
|
+
function unregisterPrimary(id) {
|
|
97
|
+
primaryRegistry.delete(id);
|
|
98
|
+
}
|
|
99
|
+
function getPrimaryIds() {
|
|
100
|
+
return Array.from(primaryRegistry.keys());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const presetsObj = {
|
|
104
|
+
apartment: "lebombo_1k.hdr",
|
|
105
|
+
city: "potsdamer_platz_1k.hdr",
|
|
106
|
+
dawn: "kiara_1_dawn_1k.hdr",
|
|
107
|
+
forest: "forest_slope_1k.hdr",
|
|
108
|
+
lobby: "st_fagans_interior_1k.hdr",
|
|
109
|
+
night: "dikhololo_night_1k.hdr",
|
|
110
|
+
park: "rooitou_park_1k.hdr",
|
|
111
|
+
studio: "studio_small_03_1k.hdr",
|
|
112
|
+
sunset: "venice_sunset_1k.hdr",
|
|
113
|
+
warehouse: "empty_warehouse_01_1k.hdr"
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
|
|
117
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
118
|
+
const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
|
|
119
|
+
function useEnvironment({
|
|
120
|
+
files = defaultFiles,
|
|
121
|
+
path = "",
|
|
122
|
+
preset = void 0,
|
|
123
|
+
colorSpace = void 0,
|
|
124
|
+
extensions
|
|
125
|
+
} = {}) {
|
|
126
|
+
if (preset) {
|
|
127
|
+
validatePreset(preset);
|
|
128
|
+
files = presetsObj[preset];
|
|
129
|
+
path = CUBEMAP_ROOT;
|
|
130
|
+
}
|
|
131
|
+
const multiFile = isArray(files);
|
|
132
|
+
const { extension, isCubemap } = getExtension(files);
|
|
133
|
+
const loader = getLoader$1(extension);
|
|
134
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
135
|
+
const renderer = useThree$1((state) => state.renderer);
|
|
136
|
+
useLayoutEffect(() => {
|
|
137
|
+
if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
|
|
138
|
+
function clearGainmapTexture() {
|
|
139
|
+
useLoader$1.clear(loader, multiFile ? [files] : files);
|
|
140
|
+
}
|
|
141
|
+
renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
|
|
142
|
+
}, [extension, files, loader, multiFile, renderer.domElement]);
|
|
143
|
+
const loaderResult = useLoader$1(
|
|
144
|
+
loader,
|
|
145
|
+
multiFile ? [files] : files,
|
|
146
|
+
(loader2) => {
|
|
147
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
148
|
+
loader2.setRenderer?.(renderer);
|
|
149
|
+
}
|
|
150
|
+
loader2.setPath?.(path);
|
|
151
|
+
if (extensions) extensions(loader2);
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
let texture = multiFile ? (
|
|
155
|
+
// @ts-ignore
|
|
156
|
+
loaderResult[0]
|
|
157
|
+
) : loaderResult;
|
|
158
|
+
if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
|
|
159
|
+
texture = texture.renderTarget?.texture;
|
|
160
|
+
}
|
|
161
|
+
texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
|
|
162
|
+
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
163
|
+
return texture;
|
|
164
|
+
}
|
|
165
|
+
const preloadDefaultOptions = {
|
|
166
|
+
files: defaultFiles,
|
|
167
|
+
path: "",
|
|
168
|
+
preset: void 0,
|
|
169
|
+
extensions: void 0
|
|
170
|
+
};
|
|
171
|
+
useEnvironment.preload = (preloadOptions) => {
|
|
172
|
+
const options = { ...preloadDefaultOptions, ...preloadOptions };
|
|
173
|
+
let { files, path = "" } = options;
|
|
174
|
+
const { preset, extensions } = options;
|
|
175
|
+
if (preset) {
|
|
176
|
+
validatePreset(preset);
|
|
177
|
+
files = presetsObj[preset];
|
|
178
|
+
path = CUBEMAP_ROOT;
|
|
179
|
+
}
|
|
180
|
+
const { extension } = getExtension(files);
|
|
181
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
182
|
+
throw new Error("useEnvironment: Preloading gainmaps is not supported");
|
|
183
|
+
}
|
|
184
|
+
const loader = getLoader$1(extension);
|
|
185
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
186
|
+
useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
|
|
187
|
+
loader2.setPath?.(path);
|
|
188
|
+
if (extensions) extensions(loader2);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
const clearDefaultOptins = {
|
|
192
|
+
files: defaultFiles,
|
|
193
|
+
preset: void 0
|
|
194
|
+
};
|
|
195
|
+
useEnvironment.clear = (clearOptions) => {
|
|
196
|
+
const options = { ...clearDefaultOptins, ...clearOptions };
|
|
197
|
+
let { files } = options;
|
|
198
|
+
const { preset } = options;
|
|
199
|
+
if (preset) {
|
|
200
|
+
validatePreset(preset);
|
|
201
|
+
files = presetsObj[preset];
|
|
202
|
+
}
|
|
203
|
+
const { extension } = getExtension(files);
|
|
204
|
+
const loader = getLoader$1(extension);
|
|
205
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
206
|
+
useLoader$1.clear(loader, isArray(files) ? [files] : files);
|
|
207
|
+
};
|
|
208
|
+
function validatePreset(preset) {
|
|
209
|
+
if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
|
|
210
|
+
}
|
|
211
|
+
function getExtension(files) {
|
|
212
|
+
const isCubemap = isArray(files) && files.length === 6;
|
|
213
|
+
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
214
|
+
const firstEntry = isArray(files) ? files[0] : files;
|
|
215
|
+
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();
|
|
216
|
+
return { extension, isCubemap, isGainmap };
|
|
217
|
+
}
|
|
218
|
+
function getLoader$1(extension) {
|
|
219
|
+
const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
|
|
220
|
+
return loader;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const isRef$1 = (obj) => obj.current && obj.current.isScene;
|
|
224
|
+
const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
|
|
225
|
+
function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
|
|
226
|
+
sceneProps = {
|
|
227
|
+
backgroundBlurriness: 0,
|
|
228
|
+
backgroundIntensity: 1,
|
|
229
|
+
backgroundRotation: [0, 0, 0],
|
|
230
|
+
environmentIntensity: 1,
|
|
231
|
+
environmentRotation: [0, 0, 0],
|
|
232
|
+
...sceneProps
|
|
233
|
+
};
|
|
234
|
+
const target = resolveScene(scene || defaultScene);
|
|
235
|
+
const oldbg = target.background;
|
|
236
|
+
const oldenv = target.environment;
|
|
237
|
+
const oldSceneProps = {
|
|
238
|
+
// @ts-ignore
|
|
239
|
+
backgroundBlurriness: target.backgroundBlurriness,
|
|
240
|
+
// @ts-ignore
|
|
241
|
+
backgroundIntensity: target.backgroundIntensity,
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
244
|
+
// @ts-ignore
|
|
245
|
+
environmentIntensity: target.environmentIntensity,
|
|
246
|
+
// @ts-ignore
|
|
247
|
+
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
248
|
+
};
|
|
249
|
+
if (background !== "only") target.environment = texture;
|
|
250
|
+
if (background) target.background = texture;
|
|
251
|
+
applyProps$1(target, sceneProps);
|
|
252
|
+
return () => {
|
|
253
|
+
if (background !== "only") target.environment = oldenv;
|
|
254
|
+
if (background) target.background = oldbg;
|
|
255
|
+
applyProps$1(target, oldSceneProps);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function EnvironmentMap({ scene, background = false, map, ...config }) {
|
|
259
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
260
|
+
React.useLayoutEffect(() => {
|
|
261
|
+
if (map) return setEnvProps(background, scene, defaultScene, map, config);
|
|
262
|
+
});
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
function EnvironmentCube({
|
|
266
|
+
background = false,
|
|
267
|
+
scene,
|
|
268
|
+
blur,
|
|
269
|
+
backgroundBlurriness,
|
|
270
|
+
backgroundIntensity,
|
|
271
|
+
backgroundRotation,
|
|
272
|
+
environmentIntensity,
|
|
273
|
+
environmentRotation,
|
|
274
|
+
...rest
|
|
275
|
+
}) {
|
|
276
|
+
const texture = useEnvironment(rest);
|
|
277
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
278
|
+
React.useLayoutEffect(() => {
|
|
279
|
+
return setEnvProps(background, scene, defaultScene, texture, {
|
|
280
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
281
|
+
backgroundIntensity,
|
|
282
|
+
backgroundRotation,
|
|
283
|
+
environmentIntensity,
|
|
284
|
+
environmentRotation
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
React.useEffect(() => {
|
|
288
|
+
return () => {
|
|
289
|
+
texture.dispose();
|
|
290
|
+
};
|
|
291
|
+
}, [texture]);
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
function EnvironmentPortal({
|
|
295
|
+
children,
|
|
296
|
+
near = 0.1,
|
|
297
|
+
far = 1e3,
|
|
298
|
+
resolution = 256,
|
|
299
|
+
frames = 1,
|
|
300
|
+
map,
|
|
301
|
+
background = false,
|
|
302
|
+
blur,
|
|
303
|
+
backgroundBlurriness,
|
|
304
|
+
backgroundIntensity,
|
|
305
|
+
backgroundRotation,
|
|
306
|
+
environmentIntensity,
|
|
307
|
+
environmentRotation,
|
|
308
|
+
scene,
|
|
309
|
+
files,
|
|
310
|
+
path,
|
|
311
|
+
preset = void 0,
|
|
312
|
+
extensions
|
|
313
|
+
}) {
|
|
314
|
+
const gl = useThree$1((state) => state.gl);
|
|
315
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
316
|
+
const camera = React.useRef(null);
|
|
317
|
+
const [virtualScene] = React.useState(() => new Scene());
|
|
318
|
+
const fbo = React.useMemo(() => {
|
|
319
|
+
const fbo2 = new WebGLCubeRenderTarget(resolution);
|
|
320
|
+
fbo2.texture.type = HalfFloatType;
|
|
321
|
+
return fbo2;
|
|
322
|
+
}, [resolution]);
|
|
323
|
+
React.useEffect(() => {
|
|
324
|
+
return () => {
|
|
325
|
+
fbo.dispose();
|
|
326
|
+
};
|
|
327
|
+
}, [fbo]);
|
|
328
|
+
React.useLayoutEffect(() => {
|
|
329
|
+
if (frames === 1) {
|
|
330
|
+
const autoClear = gl.autoClear;
|
|
331
|
+
gl.autoClear = true;
|
|
332
|
+
camera.current.update(gl, virtualScene);
|
|
333
|
+
gl.autoClear = autoClear;
|
|
334
|
+
}
|
|
335
|
+
return setEnvProps(background, scene, defaultScene, fbo.texture, {
|
|
336
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
337
|
+
backgroundIntensity,
|
|
338
|
+
backgroundRotation,
|
|
339
|
+
environmentIntensity,
|
|
340
|
+
environmentRotation
|
|
341
|
+
});
|
|
342
|
+
}, [
|
|
343
|
+
children,
|
|
344
|
+
virtualScene,
|
|
345
|
+
fbo.texture,
|
|
346
|
+
scene,
|
|
347
|
+
defaultScene,
|
|
348
|
+
background,
|
|
349
|
+
frames,
|
|
350
|
+
gl,
|
|
351
|
+
blur,
|
|
352
|
+
backgroundBlurriness,
|
|
353
|
+
backgroundIntensity,
|
|
354
|
+
backgroundRotation,
|
|
355
|
+
environmentIntensity,
|
|
356
|
+
environmentRotation
|
|
357
|
+
]);
|
|
358
|
+
let count = 1;
|
|
359
|
+
useFrame$1(() => {
|
|
360
|
+
if (frames === Infinity || count < frames) {
|
|
361
|
+
const autoClear = gl.autoClear;
|
|
362
|
+
gl.autoClear = true;
|
|
363
|
+
camera.current.update(gl, virtualScene);
|
|
364
|
+
gl.autoClear = autoClear;
|
|
365
|
+
count++;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
|
|
369
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
370
|
+
children,
|
|
371
|
+
/* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
|
|
372
|
+
files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
|
|
373
|
+
] }),
|
|
374
|
+
virtualScene
|
|
375
|
+
) });
|
|
376
|
+
}
|
|
377
|
+
function EnvironmentGround(props) {
|
|
378
|
+
const textureDefault = useEnvironment(props);
|
|
379
|
+
const texture = props.map || textureDefault;
|
|
380
|
+
React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
|
|
381
|
+
React.useEffect(() => {
|
|
382
|
+
return () => {
|
|
383
|
+
textureDefault.dispose();
|
|
384
|
+
};
|
|
385
|
+
}, [textureDefault]);
|
|
386
|
+
const height = props.ground?.height ?? 15;
|
|
387
|
+
const radius = props.ground?.radius ?? 60;
|
|
388
|
+
const scale = props.ground?.scale ?? 1e3;
|
|
389
|
+
const args = React.useMemo(
|
|
390
|
+
() => [texture, height, radius],
|
|
391
|
+
[texture, height, radius]
|
|
392
|
+
);
|
|
393
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
394
|
+
/* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
|
|
395
|
+
/* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
|
|
396
|
+
] });
|
|
397
|
+
}
|
|
398
|
+
function EnvironmentColor({ color, scene }) {
|
|
399
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
400
|
+
React.useLayoutEffect(() => {
|
|
401
|
+
if (color === void 0) return;
|
|
402
|
+
const target = resolveScene(scene || defaultScene);
|
|
403
|
+
const oldBg = target.background;
|
|
404
|
+
target.background = new Color(color);
|
|
405
|
+
return () => {
|
|
406
|
+
target.background = oldBg;
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
function EnvironmentDualSource(props) {
|
|
412
|
+
const { backgroundFiles, ...envProps } = props;
|
|
413
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
414
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
|
|
415
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
|
|
416
|
+
] });
|
|
417
|
+
}
|
|
418
|
+
function Environment(props) {
|
|
419
|
+
if (props.color && !props.files && !props.preset && !props.map) {
|
|
420
|
+
return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
|
|
421
|
+
}
|
|
422
|
+
if (props.backgroundFiles && props.backgroundFiles !== props.files) {
|
|
423
|
+
return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
|
|
424
|
+
}
|
|
425
|
+
return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
|
|
426
|
+
}
|
|
427
|
+
|
|
39
428
|
var __defProp$2 = Object.defineProperty;
|
|
40
429
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
41
430
|
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -215,7 +604,8 @@ function prepare(target, root, type, props) {
|
|
|
215
604
|
object,
|
|
216
605
|
eventCount: 0,
|
|
217
606
|
handlers: {},
|
|
218
|
-
isHidden: false
|
|
607
|
+
isHidden: false,
|
|
608
|
+
deferredRefs: []
|
|
219
609
|
};
|
|
220
610
|
if (object) object.__r3f = instance;
|
|
221
611
|
}
|
|
@@ -264,7 +654,7 @@ function createOcclusionObserverNode(store, uniform) {
|
|
|
264
654
|
let occlusionSetupPromise = null;
|
|
265
655
|
function enableOcclusion(store) {
|
|
266
656
|
const state = store.getState();
|
|
267
|
-
const { internal, renderer
|
|
657
|
+
const { internal, renderer } = state;
|
|
268
658
|
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
269
659
|
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
270
660
|
if (!hasOcclusionSupport) {
|
|
@@ -427,6 +817,22 @@ function hasVisibilityHandlers(handlers) {
|
|
|
427
817
|
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
428
818
|
}
|
|
429
819
|
|
|
820
|
+
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
821
|
+
function fromRef(ref) {
|
|
822
|
+
return { [FROM_REF]: ref };
|
|
823
|
+
}
|
|
824
|
+
function isFromRef(value) {
|
|
825
|
+
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const ONCE = Symbol.for("@react-three/fiber.once");
|
|
829
|
+
function once(...args) {
|
|
830
|
+
return { [ONCE]: args.length ? args : true };
|
|
831
|
+
}
|
|
832
|
+
function isOnce(value) {
|
|
833
|
+
return value !== null && typeof value === "object" && ONCE in value;
|
|
834
|
+
}
|
|
835
|
+
|
|
430
836
|
const RESERVED_PROPS = [
|
|
431
837
|
"children",
|
|
432
838
|
"key",
|
|
@@ -497,7 +903,7 @@ function getMemoizedPrototype(root) {
|
|
|
497
903
|
ctor = new root.constructor();
|
|
498
904
|
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
499
905
|
}
|
|
500
|
-
} catch
|
|
906
|
+
} catch {
|
|
501
907
|
}
|
|
502
908
|
return ctor;
|
|
503
909
|
}
|
|
@@ -543,6 +949,25 @@ function applyProps(object, props) {
|
|
|
543
949
|
continue;
|
|
544
950
|
}
|
|
545
951
|
if (value === void 0) continue;
|
|
952
|
+
if (isFromRef(value)) {
|
|
953
|
+
instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
if (isOnce(value)) {
|
|
957
|
+
if (instance?.appliedOnce?.has(prop)) continue;
|
|
958
|
+
if (instance) {
|
|
959
|
+
instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
|
|
960
|
+
instance.appliedOnce.add(prop);
|
|
961
|
+
}
|
|
962
|
+
const { root: targetRoot, key: targetKey } = resolve(object, prop);
|
|
963
|
+
const args = value[ONCE];
|
|
964
|
+
if (typeof targetRoot[targetKey] === "function") {
|
|
965
|
+
targetRoot[targetKey](...args === true ? [] : args);
|
|
966
|
+
} else if (args !== true && args.length > 0) {
|
|
967
|
+
targetRoot[targetKey] = args[0];
|
|
968
|
+
}
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
546
971
|
let { root, key, target } = resolve(object, prop);
|
|
547
972
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
548
973
|
throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
|
|
@@ -565,7 +990,10 @@ function applyProps(object, props) {
|
|
|
565
990
|
else target.set(value);
|
|
566
991
|
} else {
|
|
567
992
|
root[key] = value;
|
|
568
|
-
if (
|
|
993
|
+
if (key.endsWith("Node") && root.isMaterial) {
|
|
994
|
+
root.needsUpdate = true;
|
|
995
|
+
}
|
|
996
|
+
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
|
|
569
997
|
root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
|
|
570
998
|
root[key].colorSpace = rootState.textureColorSpace;
|
|
571
999
|
}
|
|
@@ -598,38 +1026,60 @@ function applyProps(object, props) {
|
|
|
598
1026
|
return object;
|
|
599
1027
|
}
|
|
600
1028
|
|
|
1029
|
+
const DEFAULT_POINTER_ID = 0;
|
|
1030
|
+
const XR_POINTER_ID_START = 1e3;
|
|
1031
|
+
function getPointerState(internal, pointerId) {
|
|
1032
|
+
let state = internal.pointerMap.get(pointerId);
|
|
1033
|
+
if (!state) {
|
|
1034
|
+
state = {
|
|
1035
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
1036
|
+
captured: /* @__PURE__ */ new Map(),
|
|
1037
|
+
initialClick: [0, 0],
|
|
1038
|
+
initialHits: []
|
|
1039
|
+
};
|
|
1040
|
+
internal.pointerMap.set(pointerId, state);
|
|
1041
|
+
}
|
|
1042
|
+
return state;
|
|
1043
|
+
}
|
|
1044
|
+
function getPointerId(event) {
|
|
1045
|
+
return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
|
|
1046
|
+
}
|
|
601
1047
|
function makeId(event) {
|
|
602
1048
|
return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
|
|
603
1049
|
}
|
|
604
|
-
function releaseInternalPointerCapture(
|
|
605
|
-
const
|
|
1050
|
+
function releaseInternalPointerCapture(internal, obj, pointerId) {
|
|
1051
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1052
|
+
if (!pointerState) return;
|
|
1053
|
+
const captureData = pointerState.captured.get(obj);
|
|
606
1054
|
if (captureData) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
capturedMap.delete(pointerId);
|
|
610
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
611
|
-
}
|
|
1055
|
+
pointerState.captured.delete(obj);
|
|
1056
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
612
1057
|
}
|
|
613
1058
|
}
|
|
614
1059
|
function removeInteractivity(store, object) {
|
|
615
1060
|
const { internal } = store.getState();
|
|
616
1061
|
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
1062
|
+
for (const [pointerId, pointerState] of internal.pointerMap) {
|
|
1063
|
+
pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
|
|
1064
|
+
pointerState.hovered.forEach((value, key) => {
|
|
1065
|
+
if (value.eventObject === object || value.object === object) {
|
|
1066
|
+
pointerState.hovered.delete(key);
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
if (pointerState.captured.has(object)) {
|
|
1070
|
+
releaseInternalPointerCapture(internal, object, pointerId);
|
|
621
1071
|
}
|
|
622
|
-
}
|
|
623
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
624
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
625
|
-
});
|
|
1072
|
+
}
|
|
626
1073
|
unregisterVisibility(store, object);
|
|
627
1074
|
}
|
|
628
1075
|
function createEvents(store) {
|
|
629
|
-
function calculateDistance(event) {
|
|
1076
|
+
function calculateDistance(event, pointerId) {
|
|
630
1077
|
const { internal } = store.getState();
|
|
631
|
-
const
|
|
632
|
-
|
|
1078
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1079
|
+
if (!pointerState) return 0;
|
|
1080
|
+
const [initialX, initialY] = pointerState.initialClick;
|
|
1081
|
+
const dx = event.offsetX - initialX;
|
|
1082
|
+
const dy = event.offsetY - initialY;
|
|
633
1083
|
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
634
1084
|
}
|
|
635
1085
|
function filterPointerEvents(objects) {
|
|
@@ -665,6 +1115,15 @@ function createEvents(store) {
|
|
|
665
1115
|
return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
|
|
666
1116
|
}
|
|
667
1117
|
let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
|
|
1118
|
+
const aInteractivePriority = a.object.userData?.interactivePriority;
|
|
1119
|
+
const bInteractivePriority = b.object.userData?.interactivePriority;
|
|
1120
|
+
if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
|
|
1121
|
+
if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
|
|
1122
|
+
if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
|
|
1123
|
+
if (aInteractivePriority !== bInteractivePriority) {
|
|
1124
|
+
return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
668
1127
|
const aState = getRootState(a.object);
|
|
669
1128
|
const bState = getRootState(b.object);
|
|
670
1129
|
const aPriority = aState?.events?.priority ?? 1;
|
|
@@ -686,9 +1145,13 @@ function createEvents(store) {
|
|
|
686
1145
|
eventObject = eventObject.parent;
|
|
687
1146
|
}
|
|
688
1147
|
}
|
|
689
|
-
if ("pointerId" in event
|
|
690
|
-
|
|
691
|
-
|
|
1148
|
+
if ("pointerId" in event) {
|
|
1149
|
+
const pointerId = event.pointerId;
|
|
1150
|
+
const pointerState = state.internal.pointerMap.get(pointerId);
|
|
1151
|
+
if (pointerState?.captured.size) {
|
|
1152
|
+
for (const captureData of pointerState.captured.values()) {
|
|
1153
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
1154
|
+
}
|
|
692
1155
|
}
|
|
693
1156
|
}
|
|
694
1157
|
return intersections;
|
|
@@ -701,27 +1164,25 @@ function createEvents(store) {
|
|
|
701
1164
|
if (state) {
|
|
702
1165
|
const { raycaster, pointer, camera, internal } = state;
|
|
703
1166
|
const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
704
|
-
const hasPointerCapture = (id) =>
|
|
1167
|
+
const hasPointerCapture = (id) => {
|
|
1168
|
+
const pointerState = internal.pointerMap.get(id);
|
|
1169
|
+
return pointerState?.captured.has(hit.eventObject) ?? false;
|
|
1170
|
+
};
|
|
705
1171
|
const setPointerCapture = (id) => {
|
|
706
1172
|
const captureData = { intersection: hit, target: event.target };
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
} else {
|
|
710
|
-
internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
|
|
711
|
-
}
|
|
1173
|
+
const pointerState = getPointerState(internal, id);
|
|
1174
|
+
pointerState.captured.set(hit.eventObject, captureData);
|
|
712
1175
|
event.target.setPointerCapture(id);
|
|
713
1176
|
};
|
|
714
1177
|
const releasePointerCapture = (id) => {
|
|
715
|
-
|
|
716
|
-
if (captures) {
|
|
717
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
718
|
-
}
|
|
1178
|
+
releaseInternalPointerCapture(internal, hit.eventObject, id);
|
|
719
1179
|
};
|
|
720
1180
|
const extractEventProps = {};
|
|
721
1181
|
for (const prop in event) {
|
|
722
1182
|
const property = event[prop];
|
|
723
1183
|
if (typeof property !== "function") extractEventProps[prop] = property;
|
|
724
1184
|
}
|
|
1185
|
+
const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
|
|
725
1186
|
const raycastEvent = {
|
|
726
1187
|
...hit,
|
|
727
1188
|
...extractEventProps,
|
|
@@ -732,18 +1193,19 @@ function createEvents(store) {
|
|
|
732
1193
|
unprojectedPoint,
|
|
733
1194
|
ray: raycaster.ray,
|
|
734
1195
|
camera,
|
|
1196
|
+
pointerId: eventPointerId,
|
|
735
1197
|
// Hijack stopPropagation, which just sets a flag
|
|
736
1198
|
stopPropagation() {
|
|
737
|
-
const
|
|
1199
|
+
const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
|
|
738
1200
|
if (
|
|
739
1201
|
// ...if this pointer hasn't been captured
|
|
740
|
-
!
|
|
741
|
-
|
|
1202
|
+
!pointerState?.captured.size || // ... or if the hit object is capturing the pointer
|
|
1203
|
+
pointerState.captured.has(hit.eventObject)
|
|
742
1204
|
) {
|
|
743
1205
|
raycastEvent.stopped = localState.stopped = true;
|
|
744
|
-
if (
|
|
1206
|
+
if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
745
1207
|
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
746
|
-
cancelPointer([...higher, hit]);
|
|
1208
|
+
cancelPointer([...higher, hit], eventPointerId);
|
|
747
1209
|
}
|
|
748
1210
|
}
|
|
749
1211
|
},
|
|
@@ -759,15 +1221,18 @@ function createEvents(store) {
|
|
|
759
1221
|
}
|
|
760
1222
|
return intersections;
|
|
761
1223
|
}
|
|
762
|
-
function cancelPointer(intersections) {
|
|
1224
|
+
function cancelPointer(intersections, pointerId) {
|
|
763
1225
|
const { internal } = store.getState();
|
|
764
|
-
|
|
1226
|
+
const pid = pointerId ?? DEFAULT_POINTER_ID;
|
|
1227
|
+
const pointerState = internal.pointerMap.get(pid);
|
|
1228
|
+
if (!pointerState) return;
|
|
1229
|
+
for (const [hoveredId, hoveredObj] of pointerState.hovered) {
|
|
765
1230
|
if (!intersections.length || !intersections.find(
|
|
766
1231
|
(hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
|
|
767
1232
|
)) {
|
|
768
1233
|
const eventObject = hoveredObj.eventObject;
|
|
769
1234
|
const instance = eventObject.__r3f;
|
|
770
|
-
|
|
1235
|
+
pointerState.hovered.delete(hoveredId);
|
|
771
1236
|
if (instance?.eventCount) {
|
|
772
1237
|
const handlers = instance.handlers;
|
|
773
1238
|
const data = { ...hoveredObj, intersections };
|
|
@@ -796,41 +1261,118 @@ function createEvents(store) {
|
|
|
796
1261
|
instance?.handlers.onDropMissed?.(event);
|
|
797
1262
|
}
|
|
798
1263
|
}
|
|
1264
|
+
function cleanupPointer(pointerId) {
|
|
1265
|
+
const { internal } = store.getState();
|
|
1266
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1267
|
+
if (pointerState) {
|
|
1268
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1269
|
+
const eventObject = hoveredObj.eventObject;
|
|
1270
|
+
const instance = eventObject.__r3f;
|
|
1271
|
+
if (instance?.eventCount) {
|
|
1272
|
+
const handlers = instance.handlers;
|
|
1273
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1274
|
+
handlers.onPointerOut?.(data);
|
|
1275
|
+
handlers.onPointerLeave?.(data);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
internal.pointerMap.delete(pointerId);
|
|
1279
|
+
}
|
|
1280
|
+
internal.pointerDirty.delete(pointerId);
|
|
1281
|
+
}
|
|
1282
|
+
function processDeferredPointer(event, pointerId) {
|
|
1283
|
+
const state = store.getState();
|
|
1284
|
+
const { internal } = state;
|
|
1285
|
+
if (!state.events.enabled) return;
|
|
1286
|
+
const filter = filterPointerEvents;
|
|
1287
|
+
const hits = intersect(event, filter);
|
|
1288
|
+
cancelPointer(hits, pointerId);
|
|
1289
|
+
function onIntersect(data) {
|
|
1290
|
+
const eventObject = data.eventObject;
|
|
1291
|
+
const instance = eventObject.__r3f;
|
|
1292
|
+
if (!instance?.eventCount) return;
|
|
1293
|
+
const handlers = instance.handlers;
|
|
1294
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1295
|
+
const id = makeId(data);
|
|
1296
|
+
const pointerState = getPointerState(internal, pointerId);
|
|
1297
|
+
const hoveredItem = pointerState.hovered.get(id);
|
|
1298
|
+
if (!hoveredItem) {
|
|
1299
|
+
pointerState.hovered.set(id, data);
|
|
1300
|
+
handlers.onPointerOver?.(data);
|
|
1301
|
+
handlers.onPointerEnter?.(data);
|
|
1302
|
+
} else if (hoveredItem.stopped) {
|
|
1303
|
+
data.stopPropagation();
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
handlers.onPointerMove?.(data);
|
|
1307
|
+
}
|
|
1308
|
+
handleIntersects(hits, event, 0, onIntersect);
|
|
1309
|
+
}
|
|
799
1310
|
function handlePointer(name) {
|
|
800
1311
|
switch (name) {
|
|
801
1312
|
case "onPointerLeave":
|
|
802
|
-
case "onPointerCancel":
|
|
803
1313
|
case "onDragLeave":
|
|
804
1314
|
return () => cancelPointer([]);
|
|
1315
|
+
// Global cancel of these events
|
|
1316
|
+
case "onPointerCancel":
|
|
1317
|
+
return (event) => {
|
|
1318
|
+
const pointerId = getPointerId(event);
|
|
1319
|
+
cleanupPointer(pointerId);
|
|
1320
|
+
};
|
|
805
1321
|
case "onLostPointerCapture":
|
|
806
1322
|
return (event) => {
|
|
807
1323
|
const { internal } = store.getState();
|
|
808
|
-
|
|
1324
|
+
const pointerId = getPointerId(event);
|
|
1325
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1326
|
+
if (pointerState?.captured.size) {
|
|
809
1327
|
requestAnimationFrame(() => {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1328
|
+
const pointerState2 = internal.pointerMap.get(pointerId);
|
|
1329
|
+
if (pointerState2?.captured.size) {
|
|
1330
|
+
pointerState2.captured.clear();
|
|
813
1331
|
}
|
|
1332
|
+
cancelPointer([], pointerId);
|
|
814
1333
|
});
|
|
815
1334
|
}
|
|
816
1335
|
};
|
|
817
1336
|
}
|
|
818
1337
|
return function handleEvent(event) {
|
|
819
1338
|
const state = store.getState();
|
|
820
|
-
const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
|
|
1339
|
+
const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
|
|
1340
|
+
const pointerId = getPointerId(event);
|
|
821
1341
|
internal.lastEvent.current = event;
|
|
822
|
-
if (!
|
|
1342
|
+
if (!events.enabled) return;
|
|
823
1343
|
const isPointerMove = name === "onPointerMove";
|
|
824
1344
|
const isDragOver = name === "onDragOver";
|
|
825
1345
|
const isDrop = name === "onDrop";
|
|
826
1346
|
const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
|
|
1347
|
+
const isPointerDown = name === "onPointerDown";
|
|
1348
|
+
const isPointerUp = name === "onPointerUp";
|
|
1349
|
+
const isWheel = name === "onWheel";
|
|
1350
|
+
const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
|
|
1351
|
+
if (isPointerMove && canDeferRaycasts) {
|
|
1352
|
+
events.compute?.(event, state);
|
|
1353
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
|
|
1357
|
+
events.compute?.(event, state);
|
|
1358
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
|
|
1362
|
+
const deferredEvent = internal.pointerDirty.get(pointerId);
|
|
1363
|
+
internal.pointerDirty.delete(pointerId);
|
|
1364
|
+
processDeferredPointer(deferredEvent, pointerId);
|
|
1365
|
+
}
|
|
827
1366
|
const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
|
|
828
1367
|
const hits = intersect(event, filter);
|
|
829
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
830
|
-
if (
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
1368
|
+
const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
|
|
1369
|
+
if (isPointerDown) {
|
|
1370
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1371
|
+
pointerState2.initialClick = [event.offsetX, event.offsetY];
|
|
1372
|
+
pointerState2.initialHits = hits.map((hit) => hit.eventObject);
|
|
1373
|
+
}
|
|
1374
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1375
|
+
const initialHits = pointerState?.initialHits ?? [];
|
|
834
1376
|
if (isClickEvent && !hits.length) {
|
|
835
1377
|
if (delta <= 2) {
|
|
836
1378
|
pointerMissed(event, internal.interaction);
|
|
@@ -845,7 +1387,9 @@ function createEvents(store) {
|
|
|
845
1387
|
dropMissed(event, internal.interaction);
|
|
846
1388
|
if (onDropMissed) onDropMissed(event);
|
|
847
1389
|
}
|
|
848
|
-
if (isPointerMove || isDragOver)
|
|
1390
|
+
if (isPointerMove || isDragOver) {
|
|
1391
|
+
cancelPointer(hits, pointerId);
|
|
1392
|
+
}
|
|
849
1393
|
function onIntersect(data) {
|
|
850
1394
|
const eventObject = data.eventObject;
|
|
851
1395
|
const instance = eventObject.__r3f;
|
|
@@ -854,9 +1398,10 @@ function createEvents(store) {
|
|
|
854
1398
|
if (isPointerMove) {
|
|
855
1399
|
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
856
1400
|
const id = makeId(data);
|
|
857
|
-
const
|
|
1401
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1402
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
858
1403
|
if (!hoveredItem) {
|
|
859
|
-
|
|
1404
|
+
pointerState2.hovered.set(id, data);
|
|
860
1405
|
handlers.onPointerOver?.(data);
|
|
861
1406
|
handlers.onPointerEnter?.(data);
|
|
862
1407
|
} else if (hoveredItem.stopped) {
|
|
@@ -866,9 +1411,10 @@ function createEvents(store) {
|
|
|
866
1411
|
handlers.onPointerMove?.(data);
|
|
867
1412
|
} else if (isDragOver) {
|
|
868
1413
|
const id = makeId(data);
|
|
869
|
-
const
|
|
1414
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1415
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
870
1416
|
if (!hoveredItem) {
|
|
871
|
-
|
|
1417
|
+
pointerState2.hovered.set(id, data);
|
|
872
1418
|
handlers.onDragOverEnter?.(data);
|
|
873
1419
|
} else if (hoveredItem.stopped) {
|
|
874
1420
|
data.stopPropagation();
|
|
@@ -879,18 +1425,18 @@ function createEvents(store) {
|
|
|
879
1425
|
} else {
|
|
880
1426
|
const handler = handlers[name];
|
|
881
1427
|
if (handler) {
|
|
882
|
-
if (!isClickEvent ||
|
|
1428
|
+
if (!isClickEvent || initialHits.includes(eventObject)) {
|
|
883
1429
|
pointerMissed(
|
|
884
1430
|
event,
|
|
885
|
-
internal.interaction.filter((object) => !
|
|
1431
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
886
1432
|
);
|
|
887
1433
|
handler(data);
|
|
888
1434
|
}
|
|
889
1435
|
} else {
|
|
890
|
-
if (isClickEvent &&
|
|
1436
|
+
if (isClickEvent && initialHits.includes(eventObject)) {
|
|
891
1437
|
pointerMissed(
|
|
892
1438
|
event,
|
|
893
|
-
internal.interaction.filter((object) => !
|
|
1439
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
894
1440
|
);
|
|
895
1441
|
}
|
|
896
1442
|
}
|
|
@@ -899,7 +1445,15 @@ function createEvents(store) {
|
|
|
899
1445
|
handleIntersects(hits, event, delta, onIntersect);
|
|
900
1446
|
};
|
|
901
1447
|
}
|
|
902
|
-
|
|
1448
|
+
function flushDeferredPointers() {
|
|
1449
|
+
const { internal, events } = store.getState();
|
|
1450
|
+
if (!events.frameTimedRaycasts) return;
|
|
1451
|
+
for (const [pointerId, event] of internal.pointerDirty) {
|
|
1452
|
+
processDeferredPointer(event, pointerId);
|
|
1453
|
+
}
|
|
1454
|
+
internal.pointerDirty.clear();
|
|
1455
|
+
}
|
|
1456
|
+
return { handlePointer, flushDeferredPointers, processDeferredPointer };
|
|
903
1457
|
}
|
|
904
1458
|
const DOM_EVENTS = {
|
|
905
1459
|
onClick: ["click", false],
|
|
@@ -918,11 +1472,16 @@ const DOM_EVENTS = {
|
|
|
918
1472
|
onLostPointerCapture: ["lostpointercapture", true]
|
|
919
1473
|
};
|
|
920
1474
|
function createPointerEvents(store) {
|
|
921
|
-
const { handlePointer } = createEvents(store);
|
|
1475
|
+
const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
|
|
1476
|
+
let nextXRPointerId = XR_POINTER_ID_START;
|
|
1477
|
+
const xrPointers = /* @__PURE__ */ new Map();
|
|
922
1478
|
return {
|
|
923
1479
|
priority: 1,
|
|
924
1480
|
enabled: true,
|
|
925
|
-
|
|
1481
|
+
frameTimedRaycasts: true,
|
|
1482
|
+
alwaysFireOnScroll: true,
|
|
1483
|
+
updateOnFrame: false,
|
|
1484
|
+
compute(event, state) {
|
|
926
1485
|
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
927
1486
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
928
1487
|
},
|
|
@@ -931,11 +1490,33 @@ function createPointerEvents(store) {
|
|
|
931
1490
|
(acc, key) => ({ ...acc, [key]: handlePointer(key) }),
|
|
932
1491
|
{}
|
|
933
1492
|
),
|
|
934
|
-
update: () => {
|
|
1493
|
+
update: (pointerId) => {
|
|
1494
|
+
const { events, internal } = store.getState();
|
|
1495
|
+
if (!events.handlers) return;
|
|
1496
|
+
if (pointerId !== void 0) {
|
|
1497
|
+
const event = internal.pointerDirty.get(pointerId);
|
|
1498
|
+
if (event) {
|
|
1499
|
+
internal.pointerDirty.delete(pointerId);
|
|
1500
|
+
processDeferredPointer(event, pointerId);
|
|
1501
|
+
} else if (internal.lastEvent?.current) {
|
|
1502
|
+
processDeferredPointer(internal.lastEvent.current, pointerId);
|
|
1503
|
+
}
|
|
1504
|
+
} else {
|
|
1505
|
+
flushDeferredPointers();
|
|
1506
|
+
if (internal.lastEvent?.current) {
|
|
1507
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
flush: () => {
|
|
935
1512
|
const { events, internal } = store.getState();
|
|
936
|
-
|
|
1513
|
+
flushDeferredPointers();
|
|
1514
|
+
if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
|
|
1515
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1516
|
+
}
|
|
937
1517
|
},
|
|
938
1518
|
connect: (target) => {
|
|
1519
|
+
if (!target) return;
|
|
939
1520
|
const { set, events } = store.getState();
|
|
940
1521
|
events.disconnect?.();
|
|
941
1522
|
set((state) => ({ events: { ...state.events, connected: target } }));
|
|
@@ -959,6 +1540,32 @@ function createPointerEvents(store) {
|
|
|
959
1540
|
}
|
|
960
1541
|
set((state) => ({ events: { ...state.events, connected: void 0 } }));
|
|
961
1542
|
}
|
|
1543
|
+
},
|
|
1544
|
+
registerPointer: (config) => {
|
|
1545
|
+
const pointerId = nextXRPointerId++;
|
|
1546
|
+
xrPointers.set(pointerId, config);
|
|
1547
|
+
const { internal } = store.getState();
|
|
1548
|
+
getPointerState(internal, pointerId);
|
|
1549
|
+
return pointerId;
|
|
1550
|
+
},
|
|
1551
|
+
unregisterPointer: (pointerId) => {
|
|
1552
|
+
xrPointers.delete(pointerId);
|
|
1553
|
+
const { internal } = store.getState();
|
|
1554
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1555
|
+
if (pointerState) {
|
|
1556
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1557
|
+
const eventObject = hoveredObj.eventObject;
|
|
1558
|
+
const instance = eventObject.__r3f;
|
|
1559
|
+
if (instance?.eventCount) {
|
|
1560
|
+
const handlers = instance.handlers;
|
|
1561
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1562
|
+
handlers.onPointerOut?.(data);
|
|
1563
|
+
handlers.onPointerLeave?.(data);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
internal.pointerMap.delete(pointerId);
|
|
1567
|
+
}
|
|
1568
|
+
internal.pointerDirty.delete(pointerId);
|
|
962
1569
|
}
|
|
963
1570
|
};
|
|
964
1571
|
}
|
|
@@ -1020,331 +1627,26 @@ function notifyAlpha({ message, link }) {
|
|
|
1020
1627
|
}
|
|
1021
1628
|
}
|
|
1022
1629
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
let performanceTimeout = void 0;
|
|
1046
|
-
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
1047
|
-
const pointer = new Vector2();
|
|
1048
|
-
const rootState = {
|
|
1049
|
-
set,
|
|
1050
|
-
get,
|
|
1051
|
-
// Mock objects that have to be configured
|
|
1052
|
-
gl: null,
|
|
1053
|
-
renderer: null,
|
|
1054
|
-
camera: null,
|
|
1055
|
-
frustum: new Frustum(),
|
|
1056
|
-
autoUpdateFrustum: true,
|
|
1057
|
-
raycaster: null,
|
|
1058
|
-
events: { priority: 1, enabled: true, connected: false },
|
|
1059
|
-
scene: null,
|
|
1060
|
-
rootScene: null,
|
|
1061
|
-
xr: null,
|
|
1062
|
-
inspector: null,
|
|
1063
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
1064
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
1065
|
-
legacy: false,
|
|
1066
|
-
linear: false,
|
|
1067
|
-
flat: false,
|
|
1068
|
-
textureColorSpace: "srgb",
|
|
1069
|
-
isLegacy: false,
|
|
1070
|
-
webGPUSupported: false,
|
|
1071
|
-
isNative: false,
|
|
1072
|
-
controls: null,
|
|
1073
|
-
pointer,
|
|
1074
|
-
mouse: pointer,
|
|
1075
|
-
frameloop: "always",
|
|
1076
|
-
onPointerMissed: void 0,
|
|
1077
|
-
onDragOverMissed: void 0,
|
|
1078
|
-
onDropMissed: void 0,
|
|
1079
|
-
performance: {
|
|
1080
|
-
current: 1,
|
|
1081
|
-
min: 0.5,
|
|
1082
|
-
max: 1,
|
|
1083
|
-
debounce: 200,
|
|
1084
|
-
regress: () => {
|
|
1085
|
-
const state2 = get();
|
|
1086
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
1087
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
1088
|
-
performanceTimeout = setTimeout(
|
|
1089
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
1090
|
-
state2.performance.debounce
|
|
1091
|
-
);
|
|
1092
|
-
}
|
|
1093
|
-
},
|
|
1094
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1095
|
-
viewport: {
|
|
1096
|
-
initialDpr: 0,
|
|
1097
|
-
dpr: 0,
|
|
1098
|
-
width: 0,
|
|
1099
|
-
height: 0,
|
|
1100
|
-
top: 0,
|
|
1101
|
-
left: 0,
|
|
1102
|
-
aspect: 0,
|
|
1103
|
-
distance: 0,
|
|
1104
|
-
factor: 0,
|
|
1105
|
-
getCurrentViewport
|
|
1106
|
-
},
|
|
1107
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
1108
|
-
setSize: (width, height, top, left) => {
|
|
1109
|
-
const state2 = get();
|
|
1110
|
-
if (width === void 0) {
|
|
1111
|
-
set({ _sizeImperative: false });
|
|
1112
|
-
if (state2._sizeProps) {
|
|
1113
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
1114
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
1115
|
-
const currentSize = state2.size;
|
|
1116
|
-
const newSize = {
|
|
1117
|
-
width: propW ?? currentSize.width,
|
|
1118
|
-
height: propH ?? currentSize.height,
|
|
1119
|
-
top: currentSize.top,
|
|
1120
|
-
left: currentSize.left
|
|
1121
|
-
};
|
|
1122
|
-
set((s) => ({
|
|
1123
|
-
size: newSize,
|
|
1124
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
1125
|
-
}));
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
|
-
const w = width;
|
|
1131
|
-
const h = height ?? width;
|
|
1132
|
-
const t = top ?? state2.size.top;
|
|
1133
|
-
const l = left ?? state2.size.left;
|
|
1134
|
-
const size = { width: w, height: h, top: t, left: l };
|
|
1135
|
-
set((s) => ({
|
|
1136
|
-
size,
|
|
1137
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
1138
|
-
_sizeImperative: true
|
|
1139
|
-
}));
|
|
1140
|
-
},
|
|
1141
|
-
setDpr: (dpr) => set((state2) => {
|
|
1142
|
-
const resolved = calculateDpr(dpr);
|
|
1143
|
-
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
1144
|
-
}),
|
|
1145
|
-
setFrameloop: (frameloop = "always") => {
|
|
1146
|
-
set(() => ({ frameloop }));
|
|
1147
|
-
},
|
|
1148
|
-
setError: (error) => set(() => ({ error })),
|
|
1149
|
-
error: null,
|
|
1150
|
-
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
1151
|
-
uniforms: {},
|
|
1152
|
-
nodes: {},
|
|
1153
|
-
textures: /* @__PURE__ */ new Map(),
|
|
1154
|
-
postProcessing: null,
|
|
1155
|
-
passes: {},
|
|
1156
|
-
_hmrVersion: 0,
|
|
1157
|
-
_sizeImperative: false,
|
|
1158
|
-
_sizeProps: null,
|
|
1159
|
-
previousRoot: void 0,
|
|
1160
|
-
internal: {
|
|
1161
|
-
// Events
|
|
1162
|
-
interaction: [],
|
|
1163
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
1164
|
-
subscribers: [],
|
|
1165
|
-
initialClick: [0, 0],
|
|
1166
|
-
initialHits: [],
|
|
1167
|
-
capturedMap: /* @__PURE__ */ new Map(),
|
|
1168
|
-
lastEvent: React.createRef(),
|
|
1169
|
-
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1170
|
-
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1171
|
-
// Occlusion system (WebGPU only)
|
|
1172
|
-
occlusionEnabled: false,
|
|
1173
|
-
occlusionObserver: null,
|
|
1174
|
-
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1175
|
-
helperGroup: null,
|
|
1176
|
-
// Updates
|
|
1177
|
-
active: false,
|
|
1178
|
-
frames: 0,
|
|
1179
|
-
priority: 0,
|
|
1180
|
-
subscribe: (ref, priority, store) => {
|
|
1181
|
-
const internal = get().internal;
|
|
1182
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1183
|
-
internal.subscribers.push({ ref, priority, store });
|
|
1184
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
1185
|
-
return () => {
|
|
1186
|
-
const internal2 = get().internal;
|
|
1187
|
-
if (internal2?.subscribers) {
|
|
1188
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
1189
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
1190
|
-
}
|
|
1191
|
-
};
|
|
1192
|
-
},
|
|
1193
|
-
// Renderer Storage (single source of truth)
|
|
1194
|
-
actualRenderer: null,
|
|
1195
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
1196
|
-
scheduler: null
|
|
1197
|
-
}
|
|
1198
|
-
};
|
|
1199
|
-
return rootState;
|
|
1200
|
-
});
|
|
1201
|
-
const state = rootStore.getState();
|
|
1202
|
-
Object.defineProperty(state, "gl", {
|
|
1203
|
-
get() {
|
|
1204
|
-
const currentState = rootStore.getState();
|
|
1205
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
1206
|
-
const stack = new Error().stack || "";
|
|
1207
|
-
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
1208
|
-
if (!isInternalAccess) {
|
|
1209
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1210
|
-
notifyDepreciated({
|
|
1211
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1212
|
-
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
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
return currentState.internal.actualRenderer;
|
|
1217
|
-
},
|
|
1218
|
-
set(value) {
|
|
1219
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1220
|
-
},
|
|
1221
|
-
enumerable: true,
|
|
1222
|
-
configurable: true
|
|
1223
|
-
});
|
|
1224
|
-
Object.defineProperty(state, "renderer", {
|
|
1225
|
-
get() {
|
|
1226
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1227
|
-
},
|
|
1228
|
-
set(value) {
|
|
1229
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1230
|
-
},
|
|
1231
|
-
enumerable: true,
|
|
1232
|
-
configurable: true
|
|
1233
|
-
});
|
|
1234
|
-
let oldScene = state.scene;
|
|
1235
|
-
rootStore.subscribe(() => {
|
|
1236
|
-
const currentState = rootStore.getState();
|
|
1237
|
-
const { scene, rootScene, set } = currentState;
|
|
1238
|
-
if (scene !== oldScene) {
|
|
1239
|
-
oldScene = scene;
|
|
1240
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1241
|
-
set({ rootScene: scene });
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1245
|
-
let oldSize = state.size;
|
|
1246
|
-
let oldDpr = state.viewport.dpr;
|
|
1247
|
-
let oldCamera = state.camera;
|
|
1248
|
-
rootStore.subscribe(() => {
|
|
1249
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1250
|
-
const actualRenderer = internal.actualRenderer;
|
|
1251
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1252
|
-
oldSize = size;
|
|
1253
|
-
oldDpr = viewport.dpr;
|
|
1254
|
-
updateCamera(camera, size);
|
|
1255
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1256
|
-
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
1257
|
-
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
1258
|
-
}
|
|
1259
|
-
if (camera !== oldCamera) {
|
|
1260
|
-
oldCamera = camera;
|
|
1261
|
-
const { rootScene } = rootStore.getState();
|
|
1262
|
-
if (camera && rootScene && !camera.parent) {
|
|
1263
|
-
rootScene.add(camera);
|
|
1264
|
-
}
|
|
1265
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1266
|
-
const currentState = rootStore.getState();
|
|
1267
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1268
|
-
updateFrustum(camera, currentState.frustum);
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
});
|
|
1272
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1273
|
-
return rootStore;
|
|
1274
|
-
};
|
|
1275
|
-
|
|
1276
|
-
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
1277
|
-
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
1278
|
-
function getLoader(Proto) {
|
|
1279
|
-
if (isConstructor$1(Proto)) {
|
|
1280
|
-
let loader = memoizedLoaders.get(Proto);
|
|
1281
|
-
if (!loader) {
|
|
1282
|
-
loader = new Proto();
|
|
1283
|
-
memoizedLoaders.set(Proto, loader);
|
|
1284
|
-
}
|
|
1285
|
-
return loader;
|
|
1286
|
-
}
|
|
1287
|
-
return Proto;
|
|
1288
|
-
}
|
|
1289
|
-
function loadingFn(extensions, onProgress) {
|
|
1290
|
-
return function(Proto, input) {
|
|
1291
|
-
const loader = getLoader(Proto);
|
|
1292
|
-
if (extensions) extensions(loader);
|
|
1293
|
-
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
1294
|
-
return loader.loadAsync(input, onProgress).then((data) => {
|
|
1295
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1296
|
-
return data;
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
return new Promise(
|
|
1300
|
-
(res, reject) => loader.load(
|
|
1301
|
-
input,
|
|
1302
|
-
(data) => {
|
|
1303
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1304
|
-
res(data);
|
|
1305
|
-
},
|
|
1306
|
-
onProgress,
|
|
1307
|
-
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
1308
|
-
)
|
|
1309
|
-
);
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
function useLoader(loader, input, extensions, onProgress) {
|
|
1313
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1314
|
-
const fn = loadingFn(extensions, onProgress);
|
|
1315
|
-
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
1316
|
-
return Array.isArray(input) ? results : results[0];
|
|
1317
|
-
}
|
|
1318
|
-
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
1319
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1320
|
-
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
1321
|
-
};
|
|
1322
|
-
useLoader.clear = function(loader, input) {
|
|
1323
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1324
|
-
keys.forEach((key) => clear([loader, key]));
|
|
1325
|
-
};
|
|
1326
|
-
useLoader.loader = getLoader;
|
|
1327
|
-
|
|
1328
|
-
var __defProp$1 = Object.defineProperty;
|
|
1329
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1330
|
-
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1331
|
-
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1332
|
-
class PhaseGraph {
|
|
1333
|
-
constructor() {
|
|
1334
|
-
/** Ordered list of phase nodes */
|
|
1335
|
-
__publicField$1(this, "phases", []);
|
|
1336
|
-
/** Quick lookup by name */
|
|
1337
|
-
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1338
|
-
/** Cached ordered names (invalidated on changes) */
|
|
1339
|
-
__publicField$1(this, "orderedNamesCache", null);
|
|
1340
|
-
this.initializeDefaultPhases();
|
|
1341
|
-
}
|
|
1342
|
-
//* Initialization --------------------------------
|
|
1343
|
-
initializeDefaultPhases() {
|
|
1344
|
-
for (const name of DEFAULT_PHASES) {
|
|
1345
|
-
const node = { name, isAutoGenerated: false };
|
|
1346
|
-
this.phases.push(node);
|
|
1347
|
-
this.phaseMap.set(name, node);
|
|
1630
|
+
var __defProp$1 = Object.defineProperty;
|
|
1631
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1632
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1633
|
+
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1634
|
+
class PhaseGraph {
|
|
1635
|
+
constructor() {
|
|
1636
|
+
/** Ordered list of phase nodes */
|
|
1637
|
+
__publicField$1(this, "phases", []);
|
|
1638
|
+
/** Quick lookup by name */
|
|
1639
|
+
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1640
|
+
/** Cached ordered names (invalidated on changes) */
|
|
1641
|
+
__publicField$1(this, "orderedNamesCache", null);
|
|
1642
|
+
this.initializeDefaultPhases();
|
|
1643
|
+
}
|
|
1644
|
+
//* Initialization --------------------------------
|
|
1645
|
+
initializeDefaultPhases() {
|
|
1646
|
+
for (const name of DEFAULT_PHASES) {
|
|
1647
|
+
const node = { name, isAutoGenerated: false };
|
|
1648
|
+
this.phases.push(node);
|
|
1649
|
+
this.phaseMap.set(name, node);
|
|
1348
1650
|
}
|
|
1349
1651
|
this.invalidateCache();
|
|
1350
1652
|
}
|
|
@@ -1577,7 +1879,7 @@ function shouldRun(job, now) {
|
|
|
1577
1879
|
const minInterval = 1e3 / job.fps;
|
|
1578
1880
|
const lastRun = job.lastRun ?? 0;
|
|
1579
1881
|
const elapsed = now - lastRun;
|
|
1580
|
-
if (elapsed < minInterval) return false;
|
|
1882
|
+
if (elapsed < minInterval - 1) return false;
|
|
1581
1883
|
if (job.drop) {
|
|
1582
1884
|
job.lastRun = now;
|
|
1583
1885
|
} else {
|
|
@@ -2246,116 +2548,444 @@ const _Scheduler = class _Scheduler {
|
|
|
2246
2548
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2247
2549
|
root.needsRebuild = false;
|
|
2248
2550
|
}
|
|
2249
|
-
const providedState = root.getState?.() ?? {};
|
|
2250
|
-
const frameState = {
|
|
2251
|
-
...providedState,
|
|
2252
|
-
time: timestamp,
|
|
2253
|
-
delta,
|
|
2254
|
-
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2255
|
-
// Convert ms to seconds
|
|
2256
|
-
frame: this.loopState.frameCount
|
|
2257
|
-
};
|
|
2258
|
-
for (const job of root.sortedJobs) {
|
|
2259
|
-
if (!shouldRun(job, timestamp)) continue;
|
|
2260
|
-
try {
|
|
2261
|
-
job.callback(frameState, delta);
|
|
2262
|
-
} catch (error) {
|
|
2263
|
-
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2264
|
-
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2551
|
+
const providedState = root.getState?.() ?? {};
|
|
2552
|
+
const frameState = {
|
|
2553
|
+
...providedState,
|
|
2554
|
+
time: timestamp,
|
|
2555
|
+
delta,
|
|
2556
|
+
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2557
|
+
// Convert ms to seconds
|
|
2558
|
+
frame: this.loopState.frameCount
|
|
2559
|
+
};
|
|
2560
|
+
for (const job of root.sortedJobs) {
|
|
2561
|
+
if (!shouldRun(job, timestamp)) continue;
|
|
2562
|
+
try {
|
|
2563
|
+
job.callback(frameState, delta);
|
|
2564
|
+
} catch (error) {
|
|
2565
|
+
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2566
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
//* Debug & Inspection Methods ================================
|
|
2571
|
+
/**
|
|
2572
|
+
* Get the total number of registered jobs across all roots.
|
|
2573
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2574
|
+
* @returns {number} Total job count
|
|
2575
|
+
*/
|
|
2576
|
+
getJobCount() {
|
|
2577
|
+
let count = 0;
|
|
2578
|
+
for (const root of this.roots.values()) {
|
|
2579
|
+
count += root.jobs.size;
|
|
2580
|
+
}
|
|
2581
|
+
return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Get all registered job IDs across all roots.
|
|
2585
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2586
|
+
* @returns {string[]} Array of all job IDs
|
|
2587
|
+
*/
|
|
2588
|
+
getJobIds() {
|
|
2589
|
+
const ids = [];
|
|
2590
|
+
for (const root of this.roots.values()) {
|
|
2591
|
+
ids.push(...root.jobs.keys());
|
|
2592
|
+
}
|
|
2593
|
+
ids.push(...this.globalBeforeJobs.keys());
|
|
2594
|
+
ids.push(...this.globalAfterJobs.keys());
|
|
2595
|
+
return ids;
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* Get the number of registered roots (Canvas instances).
|
|
2599
|
+
* @returns {number} Number of registered roots
|
|
2600
|
+
*/
|
|
2601
|
+
getRootCount() {
|
|
2602
|
+
return this.roots.size;
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2606
|
+
* Used by the default render job to know if a user has taken over rendering.
|
|
2607
|
+
*
|
|
2608
|
+
* @param phase The phase to check
|
|
2609
|
+
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2610
|
+
* @returns true if any user jobs exist in the phase
|
|
2611
|
+
*/
|
|
2612
|
+
hasUserJobsInPhase(phase, rootId) {
|
|
2613
|
+
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2614
|
+
return rootsToCheck.some((root) => {
|
|
2615
|
+
if (!root) return false;
|
|
2616
|
+
for (const job of root.jobs.values()) {
|
|
2617
|
+
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2618
|
+
}
|
|
2619
|
+
return false;
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
//* Utility Methods ================================
|
|
2623
|
+
/**
|
|
2624
|
+
* Generate a unique root ID for automatic root registration.
|
|
2625
|
+
* @returns {string} A unique root ID in the format 'root_N'
|
|
2626
|
+
*/
|
|
2627
|
+
generateRootId() {
|
|
2628
|
+
return `root_${this.nextRootIndex++}`;
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Generate a unique job ID.
|
|
2632
|
+
* @returns {string} A unique job ID in the format 'job_N'
|
|
2633
|
+
* @private
|
|
2634
|
+
*/
|
|
2635
|
+
generateJobId() {
|
|
2636
|
+
return `job_${this.nextJobIndex}`;
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Normalize before/after constraints to a Set.
|
|
2640
|
+
* Handles undefined, single string, or array inputs.
|
|
2641
|
+
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2642
|
+
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2643
|
+
* @private
|
|
2644
|
+
*/
|
|
2645
|
+
normalizeConstraints(value) {
|
|
2646
|
+
if (!value) return /* @__PURE__ */ new Set();
|
|
2647
|
+
if (Array.isArray(value)) return new Set(value);
|
|
2648
|
+
return /* @__PURE__ */ new Set([value]);
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2652
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2653
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2654
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2655
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2656
|
+
let Scheduler = _Scheduler;
|
|
2657
|
+
const getScheduler = () => Scheduler.get();
|
|
2658
|
+
if (hmrData) {
|
|
2659
|
+
hmrData.accept?.();
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
2663
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
|
|
2664
|
+
const createStore = (invalidate, advance) => {
|
|
2665
|
+
const rootStore = createWithEqualityFn((set, get) => {
|
|
2666
|
+
const position = new Vector3();
|
|
2667
|
+
const defaultTarget = new Vector3();
|
|
2668
|
+
const tempTarget = new Vector3();
|
|
2669
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
2670
|
+
const { width, height, top, left } = size;
|
|
2671
|
+
const aspect = width / height;
|
|
2672
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
2673
|
+
else tempTarget.set(...target);
|
|
2674
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2675
|
+
if (isOrthographicCamera(camera)) {
|
|
2676
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
2677
|
+
} else {
|
|
2678
|
+
const fov = camera.fov * Math.PI / 180;
|
|
2679
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
2680
|
+
const w = h * (width / height);
|
|
2681
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
let performanceTimeout = void 0;
|
|
2685
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
2686
|
+
const pointer = new Vector2();
|
|
2687
|
+
const rootState = {
|
|
2688
|
+
set,
|
|
2689
|
+
get,
|
|
2690
|
+
// Mock objects that have to be configured
|
|
2691
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
2692
|
+
primaryStore: null,
|
|
2693
|
+
gl: null,
|
|
2694
|
+
renderer: null,
|
|
2695
|
+
camera: null,
|
|
2696
|
+
frustum: new Frustum(),
|
|
2697
|
+
autoUpdateFrustum: true,
|
|
2698
|
+
raycaster: null,
|
|
2699
|
+
events: {
|
|
2700
|
+
priority: 1,
|
|
2701
|
+
enabled: true,
|
|
2702
|
+
connected: false,
|
|
2703
|
+
frameTimedRaycasts: true,
|
|
2704
|
+
alwaysFireOnScroll: true,
|
|
2705
|
+
updateOnFrame: false
|
|
2706
|
+
},
|
|
2707
|
+
scene: null,
|
|
2708
|
+
rootScene: null,
|
|
2709
|
+
xr: null,
|
|
2710
|
+
inspector: null,
|
|
2711
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
2712
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
2713
|
+
textureColorSpace: SRGBColorSpace,
|
|
2714
|
+
isLegacy: false,
|
|
2715
|
+
webGPUSupported: false,
|
|
2716
|
+
isNative: false,
|
|
2717
|
+
controls: null,
|
|
2718
|
+
pointer,
|
|
2719
|
+
mouse: pointer,
|
|
2720
|
+
frameloop: "always",
|
|
2721
|
+
onPointerMissed: void 0,
|
|
2722
|
+
onDragOverMissed: void 0,
|
|
2723
|
+
onDropMissed: void 0,
|
|
2724
|
+
performance: {
|
|
2725
|
+
current: 1,
|
|
2726
|
+
min: 0.5,
|
|
2727
|
+
max: 1,
|
|
2728
|
+
debounce: 200,
|
|
2729
|
+
regress: () => {
|
|
2730
|
+
const state2 = get();
|
|
2731
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
2732
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
2733
|
+
performanceTimeout = setTimeout(
|
|
2734
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
2735
|
+
state2.performance.debounce
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
},
|
|
2739
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2740
|
+
viewport: {
|
|
2741
|
+
initialDpr: 0,
|
|
2742
|
+
dpr: 0,
|
|
2743
|
+
width: 0,
|
|
2744
|
+
height: 0,
|
|
2745
|
+
top: 0,
|
|
2746
|
+
left: 0,
|
|
2747
|
+
aspect: 0,
|
|
2748
|
+
distance: 0,
|
|
2749
|
+
factor: 0,
|
|
2750
|
+
getCurrentViewport
|
|
2751
|
+
},
|
|
2752
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
2753
|
+
setSize: (width, height, top, left) => {
|
|
2754
|
+
const state2 = get();
|
|
2755
|
+
if (width === void 0) {
|
|
2756
|
+
set({ _sizeImperative: false });
|
|
2757
|
+
if (state2._sizeProps) {
|
|
2758
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
2759
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
2760
|
+
const currentSize = state2.size;
|
|
2761
|
+
const newSize = {
|
|
2762
|
+
width: propW ?? currentSize.width,
|
|
2763
|
+
height: propH ?? currentSize.height,
|
|
2764
|
+
top: currentSize.top,
|
|
2765
|
+
left: currentSize.left
|
|
2766
|
+
};
|
|
2767
|
+
set((s) => ({
|
|
2768
|
+
size: newSize,
|
|
2769
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
2770
|
+
}));
|
|
2771
|
+
getScheduler().invalidate();
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
const w = width;
|
|
2777
|
+
const h = height ?? width;
|
|
2778
|
+
const t = top ?? state2.size.top;
|
|
2779
|
+
const l = left ?? state2.size.left;
|
|
2780
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
2781
|
+
set((s) => ({
|
|
2782
|
+
size,
|
|
2783
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
2784
|
+
_sizeImperative: true
|
|
2785
|
+
}));
|
|
2786
|
+
getScheduler().invalidate();
|
|
2787
|
+
},
|
|
2788
|
+
setDpr: (dpr) => set((state2) => {
|
|
2789
|
+
const resolved = calculateDpr(dpr);
|
|
2790
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
2791
|
+
}),
|
|
2792
|
+
setFrameloop: (frameloop = "always") => {
|
|
2793
|
+
set(() => ({ frameloop }));
|
|
2794
|
+
},
|
|
2795
|
+
setError: (error) => set(() => ({ error })),
|
|
2796
|
+
error: null,
|
|
2797
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
|
|
2798
|
+
uniforms: {},
|
|
2799
|
+
nodes: {},
|
|
2800
|
+
buffers: {},
|
|
2801
|
+
gpuStorage: {},
|
|
2802
|
+
textures: /* @__PURE__ */ new Map(),
|
|
2803
|
+
renderPipeline: null,
|
|
2804
|
+
passes: {},
|
|
2805
|
+
_hmrVersion: 0,
|
|
2806
|
+
_sizeImperative: false,
|
|
2807
|
+
_sizeProps: null,
|
|
2808
|
+
previousRoot: void 0,
|
|
2809
|
+
internal: {
|
|
2810
|
+
// Events
|
|
2811
|
+
interaction: [],
|
|
2812
|
+
subscribers: [],
|
|
2813
|
+
// Per-pointer state (new unified structure)
|
|
2814
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
2815
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
2816
|
+
lastEvent: React.createRef(),
|
|
2817
|
+
// Deprecated but kept for backwards compatibility
|
|
2818
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2819
|
+
initialClick: [0, 0],
|
|
2820
|
+
initialHits: [],
|
|
2821
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
2822
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2823
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2824
|
+
// Occlusion system (WebGPU only)
|
|
2825
|
+
occlusionEnabled: false,
|
|
2826
|
+
occlusionObserver: null,
|
|
2827
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
2828
|
+
helperGroup: null,
|
|
2829
|
+
// Updates
|
|
2830
|
+
active: false,
|
|
2831
|
+
frames: 0,
|
|
2832
|
+
priority: 0,
|
|
2833
|
+
subscribe: (ref, priority, store) => {
|
|
2834
|
+
const internal = get().internal;
|
|
2835
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2836
|
+
internal.subscribers.push({ ref, priority, store });
|
|
2837
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
2838
|
+
return () => {
|
|
2839
|
+
const internal2 = get().internal;
|
|
2840
|
+
if (internal2?.subscribers) {
|
|
2841
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
2842
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
},
|
|
2846
|
+
// Renderer Storage (single source of truth)
|
|
2847
|
+
actualRenderer: null,
|
|
2848
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
2849
|
+
scheduler: null
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2852
|
+
return rootState;
|
|
2853
|
+
});
|
|
2854
|
+
const state = rootStore.getState();
|
|
2855
|
+
Object.defineProperty(state, "gl", {
|
|
2856
|
+
get() {
|
|
2857
|
+
const currentState = rootStore.getState();
|
|
2858
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
2859
|
+
const stack = new Error().stack || "";
|
|
2860
|
+
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
2861
|
+
if (!isInternalAccess) {
|
|
2862
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
2863
|
+
notifyDepreciated({
|
|
2864
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
2865
|
+
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
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
return currentState.internal.actualRenderer;
|
|
2870
|
+
},
|
|
2871
|
+
set(value) {
|
|
2872
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2873
|
+
},
|
|
2874
|
+
enumerable: true,
|
|
2875
|
+
configurable: true
|
|
2876
|
+
});
|
|
2877
|
+
Object.defineProperty(state, "renderer", {
|
|
2878
|
+
get() {
|
|
2879
|
+
return rootStore.getState().internal.actualRenderer;
|
|
2880
|
+
},
|
|
2881
|
+
set(value) {
|
|
2882
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2883
|
+
},
|
|
2884
|
+
enumerable: true,
|
|
2885
|
+
configurable: true
|
|
2886
|
+
});
|
|
2887
|
+
let oldScene = state.scene;
|
|
2888
|
+
rootStore.subscribe(() => {
|
|
2889
|
+
const currentState = rootStore.getState();
|
|
2890
|
+
const { scene, rootScene, set } = currentState;
|
|
2891
|
+
if (scene !== oldScene) {
|
|
2892
|
+
oldScene = scene;
|
|
2893
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
2894
|
+
set({ rootScene: scene });
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2898
|
+
let oldSize = state.size;
|
|
2899
|
+
let oldDpr = state.viewport.dpr;
|
|
2900
|
+
let oldCamera = state.camera;
|
|
2901
|
+
rootStore.subscribe(() => {
|
|
2902
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
2903
|
+
const actualRenderer = internal.actualRenderer;
|
|
2904
|
+
const canvasTarget = internal.canvasTarget;
|
|
2905
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
2906
|
+
oldSize = size;
|
|
2907
|
+
oldDpr = viewport.dpr;
|
|
2908
|
+
updateCamera(camera, size);
|
|
2909
|
+
if (internal.isSecondary && canvasTarget) {
|
|
2910
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2911
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2912
|
+
} else {
|
|
2913
|
+
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2914
|
+
actualRenderer.setSize(size.width, size.height, false);
|
|
2915
|
+
if (canvasTarget) {
|
|
2916
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2917
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (camera !== oldCamera) {
|
|
2922
|
+
oldCamera = camera;
|
|
2923
|
+
const { rootScene } = rootStore.getState();
|
|
2924
|
+
if (camera && rootScene && !camera.parent) {
|
|
2925
|
+
rootScene.add(camera);
|
|
2926
|
+
}
|
|
2927
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
2928
|
+
const currentState = rootStore.getState();
|
|
2929
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
2930
|
+
updateFrustum(camera, currentState.frustum);
|
|
2265
2931
|
}
|
|
2266
2932
|
}
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2933
|
+
});
|
|
2934
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
2935
|
+
return rootStore;
|
|
2936
|
+
};
|
|
2937
|
+
|
|
2938
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
2939
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
2940
|
+
function getLoader(Proto) {
|
|
2941
|
+
if (isConstructor$1(Proto)) {
|
|
2942
|
+
let loader = memoizedLoaders.get(Proto);
|
|
2943
|
+
if (!loader) {
|
|
2944
|
+
loader = new Proto();
|
|
2945
|
+
memoizedLoaders.set(Proto, loader);
|
|
2278
2946
|
}
|
|
2279
|
-
return
|
|
2947
|
+
return loader;
|
|
2280
2948
|
}
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2949
|
+
return Proto;
|
|
2950
|
+
}
|
|
2951
|
+
function loadingFn(extensions, onProgress) {
|
|
2952
|
+
return function(Proto, input) {
|
|
2953
|
+
const loader = getLoader(Proto);
|
|
2954
|
+
if (extensions) extensions(loader);
|
|
2955
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
2956
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
2957
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2958
|
+
return data;
|
|
2959
|
+
});
|
|
2290
2960
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
*/
|
|
2310
|
-
hasUserJobsInPhase(phase, rootId) {
|
|
2311
|
-
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2312
|
-
return rootsToCheck.some((root) => {
|
|
2313
|
-
if (!root) return false;
|
|
2314
|
-
for (const job of root.jobs.values()) {
|
|
2315
|
-
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2316
|
-
}
|
|
2317
|
-
return false;
|
|
2318
|
-
});
|
|
2319
|
-
}
|
|
2320
|
-
//* Utility Methods ================================
|
|
2321
|
-
/**
|
|
2322
|
-
* Generate a unique root ID for automatic root registration.
|
|
2323
|
-
* @returns {string} A unique root ID in the format 'root_N'
|
|
2324
|
-
*/
|
|
2325
|
-
generateRootId() {
|
|
2326
|
-
return `root_${this.nextRootIndex++}`;
|
|
2327
|
-
}
|
|
2328
|
-
/**
|
|
2329
|
-
* Generate a unique job ID.
|
|
2330
|
-
* @returns {string} A unique job ID in the format 'job_N'
|
|
2331
|
-
* @private
|
|
2332
|
-
*/
|
|
2333
|
-
generateJobId() {
|
|
2334
|
-
return `job_${this.nextJobIndex}`;
|
|
2335
|
-
}
|
|
2336
|
-
/**
|
|
2337
|
-
* Normalize before/after constraints to a Set.
|
|
2338
|
-
* Handles undefined, single string, or array inputs.
|
|
2339
|
-
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2340
|
-
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2341
|
-
* @private
|
|
2342
|
-
*/
|
|
2343
|
-
normalizeConstraints(value) {
|
|
2344
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
2345
|
-
if (Array.isArray(value)) return new Set(value);
|
|
2346
|
-
return /* @__PURE__ */ new Set([value]);
|
|
2347
|
-
}
|
|
2348
|
-
};
|
|
2349
|
-
//* Static State & Methods (Singleton Usage) ================================
|
|
2350
|
-
//* Cross-Bundle Singleton Key ==============================
|
|
2351
|
-
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2352
|
-
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2353
|
-
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2354
|
-
let Scheduler = _Scheduler;
|
|
2355
|
-
const getScheduler = () => Scheduler.get();
|
|
2356
|
-
if (hmrData) {
|
|
2357
|
-
hmrData.accept?.();
|
|
2961
|
+
return new Promise(
|
|
2962
|
+
(res, reject) => loader.load(
|
|
2963
|
+
input,
|
|
2964
|
+
(data) => {
|
|
2965
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2966
|
+
res(data);
|
|
2967
|
+
},
|
|
2968
|
+
onProgress,
|
|
2969
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
2970
|
+
)
|
|
2971
|
+
);
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
2975
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2976
|
+
const fn = loadingFn(extensions, onProgress);
|
|
2977
|
+
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
2978
|
+
return Array.isArray(input) ? results : results[0];
|
|
2358
2979
|
}
|
|
2980
|
+
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
2981
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2982
|
+
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
2983
|
+
};
|
|
2984
|
+
useLoader.clear = function(loader, input) {
|
|
2985
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2986
|
+
keys.forEach((key) => clear([loader, key]));
|
|
2987
|
+
};
|
|
2988
|
+
useLoader.loader = getLoader;
|
|
2359
2989
|
|
|
2360
2990
|
function useFrame(callback, priorityOrOptions) {
|
|
2361
2991
|
const store = React.useContext(context);
|
|
@@ -2536,6 +3166,9 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2536
3166
|
const textureCache = useThree((state) => state.textures);
|
|
2537
3167
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2538
3168
|
const { onLoad, cache = false } = options;
|
|
3169
|
+
const onLoadRef = useRef(onLoad);
|
|
3170
|
+
onLoadRef.current = onLoad;
|
|
3171
|
+
const onLoadCalledForRef = useRef(null);
|
|
2539
3172
|
const urls = useMemo(() => getUrls(input), [input]);
|
|
2540
3173
|
const cachedResult = useMemo(() => {
|
|
2541
3174
|
if (!cache) return null;
|
|
@@ -2546,9 +3179,13 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2546
3179
|
TextureLoader,
|
|
2547
3180
|
IsObject(input) ? Object.values(input) : input
|
|
2548
3181
|
);
|
|
3182
|
+
const inputKey = urls.join("\0");
|
|
2549
3183
|
useLayoutEffect(() => {
|
|
2550
|
-
if (
|
|
2551
|
-
|
|
3184
|
+
if (cachedResult) return;
|
|
3185
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
3186
|
+
onLoadCalledForRef.current = inputKey;
|
|
3187
|
+
onLoadRef.current?.(loadedTextures);
|
|
3188
|
+
}, [cachedResult, loadedTextures, inputKey]);
|
|
2552
3189
|
useEffect(() => {
|
|
2553
3190
|
if (cachedResult) return;
|
|
2554
3191
|
if ("initTexture" in renderer) {
|
|
@@ -2715,16 +3352,33 @@ function useTextures() {
|
|
|
2715
3352
|
}, [store]);
|
|
2716
3353
|
}
|
|
2717
3354
|
|
|
2718
|
-
function useRenderTarget(
|
|
3355
|
+
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
2719
3356
|
const isLegacy = useThree((s) => s.isLegacy);
|
|
2720
3357
|
const size = useThree((s) => s.size);
|
|
3358
|
+
let width;
|
|
3359
|
+
let height;
|
|
3360
|
+
let opts;
|
|
3361
|
+
if (typeof widthOrOptions === "object") {
|
|
3362
|
+
opts = widthOrOptions;
|
|
3363
|
+
} else if (typeof widthOrOptions === "number") {
|
|
3364
|
+
width = widthOrOptions;
|
|
3365
|
+
if (typeof heightOrOptions === "object") {
|
|
3366
|
+
height = widthOrOptions;
|
|
3367
|
+
opts = heightOrOptions;
|
|
3368
|
+
} else if (typeof heightOrOptions === "number") {
|
|
3369
|
+
height = heightOrOptions;
|
|
3370
|
+
opts = options;
|
|
3371
|
+
} else {
|
|
3372
|
+
height = widthOrOptions;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
2721
3375
|
return useMemo(() => {
|
|
2722
3376
|
const w = width ?? size.width;
|
|
2723
3377
|
const h = height ?? size.height;
|
|
2724
3378
|
{
|
|
2725
|
-
return isLegacy ? new WebGLRenderTarget(w, h,
|
|
3379
|
+
return isLegacy ? new WebGLRenderTarget(w, h, opts) : new RenderTarget(w, h, opts);
|
|
2726
3380
|
}
|
|
2727
|
-
}, [width, height, size.width, size.height,
|
|
3381
|
+
}, [width, height, size.width, size.height, opts, isLegacy]);
|
|
2728
3382
|
}
|
|
2729
3383
|
|
|
2730
3384
|
function useStore() {
|
|
@@ -2774,7 +3428,7 @@ function addTail(callback) {
|
|
|
2774
3428
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
2775
3429
|
getScheduler().invalidate(frames, stackFrames);
|
|
2776
3430
|
}
|
|
2777
|
-
function advance(timestamp
|
|
3431
|
+
function advance(timestamp) {
|
|
2778
3432
|
getScheduler().step(timestamp);
|
|
2779
3433
|
}
|
|
2780
3434
|
|
|
@@ -14228,6 +14882,7 @@ function swapInstances() {
|
|
|
14228
14882
|
instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
|
|
14229
14883
|
instance.object.__r3f = instance;
|
|
14230
14884
|
setFiberRef(fiber, instance.object);
|
|
14885
|
+
delete instance.appliedOnce;
|
|
14231
14886
|
applyProps(instance.object, instance.props);
|
|
14232
14887
|
if (instance.props.attach) {
|
|
14233
14888
|
attach(parent, instance);
|
|
@@ -14301,8 +14956,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
14301
14956
|
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
14302
14957
|
if (isTailSibling) swapInstances();
|
|
14303
14958
|
},
|
|
14304
|
-
finalizeInitialChildren: () =>
|
|
14305
|
-
|
|
14959
|
+
finalizeInitialChildren: (instance) => {
|
|
14960
|
+
for (const prop in instance.props) {
|
|
14961
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
14962
|
+
}
|
|
14963
|
+
return false;
|
|
14964
|
+
},
|
|
14965
|
+
commitMount(instance) {
|
|
14966
|
+
const resolved = {};
|
|
14967
|
+
for (const prop in instance.props) {
|
|
14968
|
+
const value = instance.props[prop];
|
|
14969
|
+
if (isFromRef(value)) {
|
|
14970
|
+
const ref = value[FROM_REF];
|
|
14971
|
+
if (ref.current != null) resolved[prop] = ref.current;
|
|
14972
|
+
}
|
|
14973
|
+
}
|
|
14974
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
14306
14975
|
},
|
|
14307
14976
|
getPublicInstance: (instance) => instance?.object,
|
|
14308
14977
|
prepareForCommit: () => null,
|
|
@@ -14523,6 +15192,9 @@ function createRoot(canvas) {
|
|
|
14523
15192
|
let resolve;
|
|
14524
15193
|
pending = new Promise((_resolve) => resolve = _resolve);
|
|
14525
15194
|
const {
|
|
15195
|
+
id: canvasId,
|
|
15196
|
+
primaryCanvas,
|
|
15197
|
+
scheduler: schedulerConfig,
|
|
14526
15198
|
gl: glConfig,
|
|
14527
15199
|
renderer: rendererConfig,
|
|
14528
15200
|
size: propsSize,
|
|
@@ -14530,10 +15202,6 @@ function createRoot(canvas) {
|
|
|
14530
15202
|
events,
|
|
14531
15203
|
onCreated: onCreatedCallback,
|
|
14532
15204
|
shadows = false,
|
|
14533
|
-
linear = false,
|
|
14534
|
-
flat = false,
|
|
14535
|
-
textureColorSpace = SRGBColorSpace,
|
|
14536
|
-
legacy = false,
|
|
14537
15205
|
orthographic = false,
|
|
14538
15206
|
frameloop = "always",
|
|
14539
15207
|
dpr = [1, 2],
|
|
@@ -14545,8 +15213,10 @@ function createRoot(canvas) {
|
|
|
14545
15213
|
onDropMissed,
|
|
14546
15214
|
autoUpdateFrustum = true,
|
|
14547
15215
|
occlusion = false,
|
|
14548
|
-
_sizeProps
|
|
15216
|
+
_sizeProps,
|
|
15217
|
+
forceEven
|
|
14549
15218
|
} = props;
|
|
15219
|
+
const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
|
|
14550
15220
|
const state = store.getState();
|
|
14551
15221
|
const defaultGLProps = {
|
|
14552
15222
|
canvas,
|
|
@@ -14555,7 +15225,8 @@ function createRoot(canvas) {
|
|
|
14555
15225
|
alpha: true
|
|
14556
15226
|
};
|
|
14557
15227
|
const defaultGPUProps = {
|
|
14558
|
-
canvas
|
|
15228
|
+
canvas,
|
|
15229
|
+
antialias: true
|
|
14559
15230
|
};
|
|
14560
15231
|
const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
|
|
14561
15232
|
if (glConfig && rendererConfig) {
|
|
@@ -14569,19 +15240,61 @@ function createRoot(canvas) {
|
|
|
14569
15240
|
});
|
|
14570
15241
|
}
|
|
14571
15242
|
let renderer = state.internal.actualRenderer;
|
|
15243
|
+
if (primaryCanvas && wantsGL) {
|
|
15244
|
+
throw new Error(
|
|
15245
|
+
"The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
|
|
15246
|
+
);
|
|
15247
|
+
}
|
|
14572
15248
|
if (wantsGL && !state.internal.actualRenderer) {
|
|
14573
15249
|
renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
|
|
14574
15250
|
state.internal.actualRenderer = renderer;
|
|
14575
|
-
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
15251
|
+
state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
|
|
15252
|
+
} else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
|
|
15253
|
+
const primary = await waitForPrimary(primaryCanvas);
|
|
15254
|
+
renderer = primary.renderer;
|
|
15255
|
+
state.internal.actualRenderer = renderer;
|
|
15256
|
+
const canvasTarget = new CanvasTarget(canvas);
|
|
15257
|
+
primary.store.setState((prev) => ({
|
|
15258
|
+
internal: { ...prev.internal, isMultiCanvas: true }
|
|
15259
|
+
}));
|
|
15260
|
+
state.set((prev) => ({
|
|
15261
|
+
webGPUSupported: primary.store.getState().webGPUSupported,
|
|
15262
|
+
renderer,
|
|
15263
|
+
primaryStore: primary.store,
|
|
15264
|
+
internal: {
|
|
15265
|
+
...prev.internal,
|
|
15266
|
+
canvasTarget,
|
|
15267
|
+
isMultiCanvas: true,
|
|
15268
|
+
isSecondary: true,
|
|
15269
|
+
targetId: primaryCanvas
|
|
15270
|
+
}
|
|
15271
|
+
}));
|
|
14576
15272
|
} else if (!wantsGL && !state.internal.actualRenderer) {
|
|
14577
15273
|
renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
|
|
14578
15274
|
if (!renderer.hasInitialized?.()) {
|
|
15275
|
+
const size2 = computeInitialSize(canvas, propsSize);
|
|
15276
|
+
if (size2.width > 0 && size2.height > 0) {
|
|
15277
|
+
const pixelRatio = calculateDpr(dpr);
|
|
15278
|
+
canvas.width = size2.width * pixelRatio;
|
|
15279
|
+
canvas.height = size2.height * pixelRatio;
|
|
15280
|
+
}
|
|
14579
15281
|
await renderer.init();
|
|
14580
15282
|
}
|
|
14581
15283
|
const backend = renderer.backend;
|
|
14582
15284
|
const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
|
|
14583
15285
|
state.internal.actualRenderer = renderer;
|
|
14584
|
-
state.set({ webGPUSupported: isWebGPUBackend, renderer });
|
|
15286
|
+
state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
|
|
15287
|
+
if (canvasId && !state.internal.isSecondary) {
|
|
15288
|
+
const canvasTarget = new CanvasTarget(canvas);
|
|
15289
|
+
const unregisterPrimary = registerPrimary(canvasId, renderer, store);
|
|
15290
|
+
state.set((prev) => ({
|
|
15291
|
+
internal: {
|
|
15292
|
+
...prev.internal,
|
|
15293
|
+
canvasTarget,
|
|
15294
|
+
unregisterPrimary
|
|
15295
|
+
}
|
|
15296
|
+
}));
|
|
15297
|
+
}
|
|
14585
15298
|
}
|
|
14586
15299
|
let raycaster = state.raycaster;
|
|
14587
15300
|
if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
|
|
@@ -14590,6 +15303,7 @@ function createRoot(canvas) {
|
|
|
14590
15303
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
14591
15304
|
applyProps(raycaster, { params: { ...raycaster.params, ...params } });
|
|
14592
15305
|
}
|
|
15306
|
+
let tempCamera = state.camera;
|
|
14593
15307
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
14594
15308
|
lastCamera = cameraOptions;
|
|
14595
15309
|
const isCamera = cameraOptions?.isCamera;
|
|
@@ -14609,6 +15323,7 @@ function createRoot(canvas) {
|
|
|
14609
15323
|
if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
|
|
14610
15324
|
}
|
|
14611
15325
|
state.set({ camera });
|
|
15326
|
+
tempCamera = camera;
|
|
14612
15327
|
raycaster.camera = camera;
|
|
14613
15328
|
}
|
|
14614
15329
|
if (!state.scene) {
|
|
@@ -14626,7 +15341,7 @@ function createRoot(canvas) {
|
|
|
14626
15341
|
rootScene: scene,
|
|
14627
15342
|
internal: { ...prev.internal, container: scene }
|
|
14628
15343
|
}));
|
|
14629
|
-
const camera =
|
|
15344
|
+
const camera = tempCamera;
|
|
14630
15345
|
if (camera && !camera.parent) scene.add(camera);
|
|
14631
15346
|
}
|
|
14632
15347
|
if (events && !state.events.handlers) {
|
|
@@ -14643,6 +15358,9 @@ function createRoot(canvas) {
|
|
|
14643
15358
|
if (_sizeProps !== void 0) {
|
|
14644
15359
|
state.set({ _sizeProps });
|
|
14645
15360
|
}
|
|
15361
|
+
if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
|
|
15362
|
+
state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
|
|
15363
|
+
}
|
|
14646
15364
|
const size = computeInitialSize(canvas, propsSize);
|
|
14647
15365
|
if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
|
|
14648
15366
|
const wasImperative = state._sizeImperative;
|
|
@@ -14669,10 +15387,10 @@ function createRoot(canvas) {
|
|
|
14669
15387
|
lastConfiguredProps.performance = performance;
|
|
14670
15388
|
}
|
|
14671
15389
|
if (!state.xr) {
|
|
14672
|
-
const handleXRFrame = (timestamp,
|
|
15390
|
+
const handleXRFrame = (timestamp, _frame) => {
|
|
14673
15391
|
const state2 = store.getState();
|
|
14674
15392
|
if (state2.frameloop === "never") return;
|
|
14675
|
-
advance(timestamp
|
|
15393
|
+
advance(timestamp);
|
|
14676
15394
|
};
|
|
14677
15395
|
const actualRenderer = state.internal.actualRenderer;
|
|
14678
15396
|
const handleSessionChange = () => {
|
|
@@ -14684,16 +15402,16 @@ function createRoot(canvas) {
|
|
|
14684
15402
|
};
|
|
14685
15403
|
const xr = {
|
|
14686
15404
|
connect() {
|
|
14687
|
-
const { gl, renderer: renderer2
|
|
14688
|
-
const
|
|
14689
|
-
|
|
14690
|
-
|
|
15405
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15406
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15407
|
+
xrManager.addEventListener("sessionstart", handleSessionChange);
|
|
15408
|
+
xrManager.addEventListener("sessionend", handleSessionChange);
|
|
14691
15409
|
},
|
|
14692
15410
|
disconnect() {
|
|
14693
|
-
const { gl, renderer: renderer2
|
|
14694
|
-
const
|
|
14695
|
-
|
|
14696
|
-
|
|
15411
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15412
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15413
|
+
xrManager.removeEventListener("sessionstart", handleSessionChange);
|
|
15414
|
+
xrManager.removeEventListener("sessionend", handleSessionChange);
|
|
14697
15415
|
}
|
|
14698
15416
|
};
|
|
14699
15417
|
if (typeof renderer.xr?.addEventListener === "function") xr.connect();
|
|
@@ -14705,15 +15423,22 @@ function createRoot(canvas) {
|
|
|
14705
15423
|
const oldType = renderer.shadowMap.type;
|
|
14706
15424
|
renderer.shadowMap.enabled = !!shadows;
|
|
14707
15425
|
if (is.boo(shadows)) {
|
|
14708
|
-
renderer.shadowMap.type =
|
|
15426
|
+
renderer.shadowMap.type = PCFShadowMap;
|
|
14709
15427
|
} else if (is.str(shadows)) {
|
|
15428
|
+
if (shadows === "soft") {
|
|
15429
|
+
notifyDepreciated({
|
|
15430
|
+
heading: 'shadows="soft" is deprecated',
|
|
15431
|
+
body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
|
|
15432
|
+
link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
|
|
15433
|
+
});
|
|
15434
|
+
}
|
|
14710
15435
|
const types = {
|
|
14711
15436
|
basic: BasicShadowMap,
|
|
14712
15437
|
percentage: PCFShadowMap,
|
|
14713
|
-
soft:
|
|
15438
|
+
soft: PCFShadowMap,
|
|
14714
15439
|
variance: VSMShadowMap
|
|
14715
15440
|
};
|
|
14716
|
-
renderer.shadowMap.type = types[shadows] ??
|
|
15441
|
+
renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
|
|
14717
15442
|
} else if (is.obj(shadows)) {
|
|
14718
15443
|
Object.assign(renderer.shadowMap, shadows);
|
|
14719
15444
|
}
|
|
@@ -14721,56 +15446,69 @@ function createRoot(canvas) {
|
|
|
14721
15446
|
renderer.shadowMap.needsUpdate = true;
|
|
14722
15447
|
}
|
|
14723
15448
|
}
|
|
14724
|
-
{
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
const flatChanged = flat !== lastConfiguredProps.flat;
|
|
14728
|
-
if (legacyChanged) {
|
|
14729
|
-
if (legacy) {
|
|
14730
|
-
notifyDepreciated({
|
|
14731
|
-
heading: "Legacy Color Management",
|
|
14732
|
-
body: "Legacy color management is deprecated and will be removed in a future version.",
|
|
14733
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
14734
|
-
});
|
|
14735
|
-
}
|
|
14736
|
-
}
|
|
14737
|
-
if (legacyChanged) {
|
|
14738
|
-
ColorManagement.enabled = !legacy;
|
|
14739
|
-
lastConfiguredProps.legacy = legacy;
|
|
14740
|
-
}
|
|
14741
|
-
if (!configured || linearChanged) {
|
|
14742
|
-
renderer.outputColorSpace = linear ? LinearSRGBColorSpace : SRGBColorSpace;
|
|
14743
|
-
lastConfiguredProps.linear = linear;
|
|
14744
|
-
}
|
|
14745
|
-
if (!configured || flatChanged) {
|
|
14746
|
-
renderer.toneMapping = flat ? NoToneMapping : ACESFilmicToneMapping;
|
|
14747
|
-
lastConfiguredProps.flat = flat;
|
|
14748
|
-
}
|
|
14749
|
-
if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
|
|
14750
|
-
if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
|
|
14751
|
-
if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
|
|
15449
|
+
if (!configured) {
|
|
15450
|
+
renderer.outputColorSpace = SRGBColorSpace;
|
|
15451
|
+
renderer.toneMapping = ACESFilmicToneMapping;
|
|
14752
15452
|
}
|
|
14753
15453
|
if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
|
|
14754
15454
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
14755
15455
|
lastConfiguredProps.textureColorSpace = textureColorSpace;
|
|
14756
15456
|
}
|
|
15457
|
+
const r3fProps = ["textureColorSpace"];
|
|
15458
|
+
const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
|
|
15459
|
+
const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
|
|
14757
15460
|
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
|
|
14758
|
-
|
|
15461
|
+
const glProps = {};
|
|
15462
|
+
for (const key in glConfig) {
|
|
15463
|
+
if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
|
|
15464
|
+
}
|
|
15465
|
+
applyProps(renderer, glProps);
|
|
14759
15466
|
}
|
|
14760
15467
|
if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
|
|
14761
15468
|
const currentRenderer = state.renderer;
|
|
14762
15469
|
if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
|
|
14763
|
-
|
|
15470
|
+
const rendererProps = {};
|
|
15471
|
+
for (const key in rendererConfig) {
|
|
15472
|
+
if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
|
|
15473
|
+
}
|
|
15474
|
+
applyProps(currentRenderer, rendererProps);
|
|
14764
15475
|
}
|
|
14765
15476
|
}
|
|
14766
15477
|
const scheduler = getScheduler();
|
|
14767
15478
|
const rootId = state.internal.rootId;
|
|
14768
15479
|
if (!rootId) {
|
|
14769
|
-
const newRootId = scheduler.generateRootId();
|
|
15480
|
+
const newRootId = canvasId || scheduler.generateRootId();
|
|
14770
15481
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14771
15482
|
getState: () => store.getState(),
|
|
14772
15483
|
onError: (err) => store.getState().setError(err)
|
|
14773
15484
|
});
|
|
15485
|
+
const unregisterCanvasTarget = scheduler.register(
|
|
15486
|
+
() => {
|
|
15487
|
+
const state2 = store.getState();
|
|
15488
|
+
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
15489
|
+
const renderer2 = state2.internal.actualRenderer;
|
|
15490
|
+
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
15491
|
+
}
|
|
15492
|
+
},
|
|
15493
|
+
{
|
|
15494
|
+
id: `${newRootId}_canvasTarget`,
|
|
15495
|
+
rootId: newRootId,
|
|
15496
|
+
phase: "start",
|
|
15497
|
+
system: true
|
|
15498
|
+
}
|
|
15499
|
+
);
|
|
15500
|
+
const unregisterEventsFlush = scheduler.register(
|
|
15501
|
+
() => {
|
|
15502
|
+
const state2 = store.getState();
|
|
15503
|
+
state2.events.flush?.();
|
|
15504
|
+
},
|
|
15505
|
+
{
|
|
15506
|
+
id: `${newRootId}_events`,
|
|
15507
|
+
rootId: newRootId,
|
|
15508
|
+
phase: "input",
|
|
15509
|
+
system: true
|
|
15510
|
+
}
|
|
15511
|
+
);
|
|
14774
15512
|
const unregisterFrustum = scheduler.register(
|
|
14775
15513
|
() => {
|
|
14776
15514
|
const state2 = store.getState();
|
|
@@ -14805,18 +15543,22 @@ function createRoot(canvas) {
|
|
|
14805
15543
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14806
15544
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14807
15545
|
try {
|
|
14808
|
-
if (state2.
|
|
15546
|
+
if (state2.renderPipeline?.render) state2.renderPipeline.render();
|
|
14809
15547
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14810
15548
|
} catch (error) {
|
|
14811
15549
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
14812
15550
|
}
|
|
14813
15551
|
},
|
|
14814
15552
|
{
|
|
14815
|
-
|
|
15553
|
+
// Use canvas ID directly as job ID if available, otherwise use generated rootId
|
|
15554
|
+
id: canvasId || `${newRootId}_render`,
|
|
14816
15555
|
rootId: newRootId,
|
|
14817
15556
|
phase: "render",
|
|
14818
|
-
system: true
|
|
15557
|
+
system: true,
|
|
14819
15558
|
// Internal flag: this is a system job, not user-controlled
|
|
15559
|
+
// Apply scheduler config for render ordering and rate limiting
|
|
15560
|
+
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
15561
|
+
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14820
15562
|
}
|
|
14821
15563
|
);
|
|
14822
15564
|
state.set((state2) => ({
|
|
@@ -14825,6 +15567,8 @@ function createRoot(canvas) {
|
|
|
14825
15567
|
rootId: newRootId,
|
|
14826
15568
|
unregisterRoot: () => {
|
|
14827
15569
|
unregisterRoot();
|
|
15570
|
+
unregisterCanvasTarget();
|
|
15571
|
+
unregisterEventsFlush();
|
|
14828
15572
|
unregisterFrustum();
|
|
14829
15573
|
unregisterVisibility();
|
|
14830
15574
|
unregisterRender();
|
|
@@ -14883,15 +15627,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14883
15627
|
const renderer = state.internal.actualRenderer;
|
|
14884
15628
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14885
15629
|
if (unregisterRoot) unregisterRoot();
|
|
15630
|
+
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
15631
|
+
if (unregisterPrimary) unregisterPrimary();
|
|
15632
|
+
const canvasTarget = state.internal.canvasTarget;
|
|
15633
|
+
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14886
15634
|
state.events.disconnect?.();
|
|
14887
15635
|
cleanupHelperGroup(root.store);
|
|
14888
|
-
renderer
|
|
14889
|
-
|
|
14890
|
-
|
|
15636
|
+
if (state.isLegacy && renderer) {
|
|
15637
|
+
;
|
|
15638
|
+
renderer.renderLists?.dispose?.();
|
|
15639
|
+
renderer.forceContextLoss?.();
|
|
15640
|
+
}
|
|
15641
|
+
if (!state.internal.isSecondary) {
|
|
15642
|
+
if (renderer?.xr) state.xr.disconnect();
|
|
15643
|
+
}
|
|
14891
15644
|
dispose(state.scene);
|
|
14892
15645
|
_roots.delete(canvas);
|
|
14893
15646
|
if (callback) callback(canvas);
|
|
14894
|
-
} catch
|
|
15647
|
+
} catch {
|
|
14895
15648
|
}
|
|
14896
15649
|
}, 500);
|
|
14897
15650
|
}
|
|
@@ -14899,36 +15652,34 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14899
15652
|
}
|
|
14900
15653
|
}
|
|
14901
15654
|
function createPortal(children, container, state) {
|
|
14902
|
-
return /* @__PURE__ */ jsx(
|
|
15655
|
+
return /* @__PURE__ */ jsx(Portal, { children, container, state });
|
|
14903
15656
|
}
|
|
14904
|
-
function
|
|
15657
|
+
function Portal({ children, container, state }) {
|
|
14905
15658
|
const isRef = useCallback((obj) => obj && "current" in obj, []);
|
|
14906
|
-
const [resolvedContainer,
|
|
15659
|
+
const [resolvedContainer, _setResolvedContainer] = useState(() => {
|
|
14907
15660
|
if (isRef(container)) return container.current ?? null;
|
|
14908
15661
|
return container;
|
|
14909
15662
|
});
|
|
15663
|
+
const setResolvedContainer = useCallback(
|
|
15664
|
+
(newContainer) => {
|
|
15665
|
+
if (!newContainer || newContainer === resolvedContainer) return;
|
|
15666
|
+
_setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
|
|
15667
|
+
},
|
|
15668
|
+
[resolvedContainer, _setResolvedContainer, isRef]
|
|
15669
|
+
);
|
|
14910
15670
|
useMemo(() => {
|
|
14911
|
-
if (isRef(container)) {
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
const updated = container.current;
|
|
14916
|
-
if (updated && updated !== resolvedContainer) {
|
|
14917
|
-
setResolvedContainer(updated);
|
|
14918
|
-
}
|
|
14919
|
-
});
|
|
14920
|
-
} else if (current !== resolvedContainer) {
|
|
14921
|
-
setResolvedContainer(current);
|
|
14922
|
-
}
|
|
14923
|
-
} else if (container !== resolvedContainer) {
|
|
14924
|
-
setResolvedContainer(container);
|
|
15671
|
+
if (isRef(container) && !container.current) {
|
|
15672
|
+
return queueMicrotask(() => {
|
|
15673
|
+
setResolvedContainer(container.current);
|
|
15674
|
+
});
|
|
14925
15675
|
}
|
|
14926
|
-
|
|
15676
|
+
setResolvedContainer(container);
|
|
15677
|
+
}, [container, isRef, setResolvedContainer]);
|
|
14927
15678
|
if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
|
|
14928
15679
|
const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
|
|
14929
|
-
return /* @__PURE__ */ jsx(
|
|
15680
|
+
return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
|
|
14930
15681
|
}
|
|
14931
|
-
function
|
|
15682
|
+
function PortalInner({ state = {}, children, container }) {
|
|
14932
15683
|
const { events, size, injectScene = true, ...rest } = state;
|
|
14933
15684
|
const previousRoot = useStore();
|
|
14934
15685
|
const [raycaster] = useState(() => new Raycaster());
|
|
@@ -14949,11 +15700,12 @@ function Portal({ state = {}, children, container }) {
|
|
|
14949
15700
|
};
|
|
14950
15701
|
}, [portalScene, container, injectScene]);
|
|
14951
15702
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
15703
|
+
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14952
15704
|
let viewport = void 0;
|
|
14953
|
-
if (injectState.camera && size) {
|
|
15705
|
+
if (injectState.camera && (size || injectState.size)) {
|
|
14954
15706
|
const camera = injectState.camera;
|
|
14955
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(),
|
|
14956
|
-
if (camera !== rootState.camera) updateCamera(camera,
|
|
15707
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
|
|
15708
|
+
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14957
15709
|
}
|
|
14958
15710
|
return {
|
|
14959
15711
|
// The intersect consists of the previous root state
|
|
@@ -14970,7 +15722,7 @@ function Portal({ state = {}, children, container }) {
|
|
|
14970
15722
|
previousRoot,
|
|
14971
15723
|
// Events, size and viewport can be overridden by the inject layer
|
|
14972
15724
|
events: { ...rootState.events, ...injectState.events, ...events },
|
|
14973
|
-
size:
|
|
15725
|
+
size: resolvedSize,
|
|
14974
15726
|
viewport: { ...rootState.viewport, ...viewport },
|
|
14975
15727
|
// Layers are allowed to override events
|
|
14976
15728
|
setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
|
|
@@ -14982,9 +15734,13 @@ function Portal({ state = {}, children, container }) {
|
|
|
14982
15734
|
const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
|
|
14983
15735
|
const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
|
|
14984
15736
|
onMutate(previousRoot.getState());
|
|
14985
|
-
previousRoot.subscribe(onMutate);
|
|
14986
15737
|
return store;
|
|
14987
15738
|
}, [previousRoot, container]);
|
|
15739
|
+
useIsomorphicLayoutEffect(() => {
|
|
15740
|
+
const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
|
|
15741
|
+
const unsubscribe = previousRoot.subscribe(onMutate);
|
|
15742
|
+
return unsubscribe;
|
|
15743
|
+
}, [previousRoot, usePortalStore]);
|
|
14988
15744
|
return (
|
|
14989
15745
|
// @ts-ignore, reconciler types are not maintained
|
|
14990
15746
|
/* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
|
|
@@ -15004,15 +15760,13 @@ function CanvasImpl({
|
|
|
15004
15760
|
fallback,
|
|
15005
15761
|
resize,
|
|
15006
15762
|
style,
|
|
15763
|
+
id,
|
|
15007
15764
|
gl,
|
|
15008
|
-
renderer,
|
|
15765
|
+
renderer: rendererProp,
|
|
15009
15766
|
events = createPointerEvents,
|
|
15010
15767
|
eventSource,
|
|
15011
15768
|
eventPrefix,
|
|
15012
15769
|
shadows,
|
|
15013
|
-
linear,
|
|
15014
|
-
flat,
|
|
15015
|
-
legacy,
|
|
15016
15770
|
orthographic,
|
|
15017
15771
|
frameloop,
|
|
15018
15772
|
dpr,
|
|
@@ -15027,10 +15781,53 @@ function CanvasImpl({
|
|
|
15027
15781
|
hmr,
|
|
15028
15782
|
width,
|
|
15029
15783
|
height,
|
|
15784
|
+
background,
|
|
15785
|
+
forceEven,
|
|
15030
15786
|
...props
|
|
15031
15787
|
}) {
|
|
15788
|
+
const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
|
|
15789
|
+
let primaryCanvas;
|
|
15790
|
+
let scheduler;
|
|
15791
|
+
let renderer;
|
|
15792
|
+
if (isRendererConfig) {
|
|
15793
|
+
const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
|
|
15794
|
+
primaryCanvas = pc;
|
|
15795
|
+
scheduler = sc;
|
|
15796
|
+
renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
|
|
15797
|
+
} else {
|
|
15798
|
+
renderer = rendererProp;
|
|
15799
|
+
}
|
|
15032
15800
|
React.useMemo(() => extend(THREE), []);
|
|
15033
15801
|
const Bridge = useBridge();
|
|
15802
|
+
const backgroundProps = React.useMemo(() => {
|
|
15803
|
+
if (!background) return null;
|
|
15804
|
+
if (typeof background === "object" && !background.isColor) {
|
|
15805
|
+
const { backgroundMap, envMap, files, preset, ...rest } = background;
|
|
15806
|
+
return {
|
|
15807
|
+
...rest,
|
|
15808
|
+
preset,
|
|
15809
|
+
files: envMap || files,
|
|
15810
|
+
backgroundFiles: backgroundMap,
|
|
15811
|
+
background: true
|
|
15812
|
+
};
|
|
15813
|
+
}
|
|
15814
|
+
if (typeof background === "number") {
|
|
15815
|
+
return { color: background, background: true };
|
|
15816
|
+
}
|
|
15817
|
+
if (typeof background === "string") {
|
|
15818
|
+
if (background in presetsObj) {
|
|
15819
|
+
return { preset: background, background: true };
|
|
15820
|
+
}
|
|
15821
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
|
|
15822
|
+
return { files: background, background: true };
|
|
15823
|
+
}
|
|
15824
|
+
return { color: background, background: true };
|
|
15825
|
+
}
|
|
15826
|
+
if (background.isColor) {
|
|
15827
|
+
return { color: background, background: true };
|
|
15828
|
+
}
|
|
15829
|
+
return null;
|
|
15830
|
+
}, [background]);
|
|
15034
15831
|
const hasInitialSizeRef = React.useRef(false);
|
|
15035
15832
|
const measureConfig = React.useMemo(() => {
|
|
15036
15833
|
if (!hasInitialSizeRef.current) {
|
|
@@ -15047,15 +15844,20 @@ function CanvasImpl({
|
|
|
15047
15844
|
};
|
|
15048
15845
|
}, [resize, hasInitialSizeRef.current]);
|
|
15049
15846
|
const [containerRef, containerRect] = useMeasure(measureConfig);
|
|
15050
|
-
const effectiveSize = React.useMemo(
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15847
|
+
const effectiveSize = React.useMemo(() => {
|
|
15848
|
+
let w = width ?? containerRect.width;
|
|
15849
|
+
let h = height ?? containerRect.height;
|
|
15850
|
+
if (forceEven) {
|
|
15851
|
+
w = Math.ceil(w / 2) * 2;
|
|
15852
|
+
h = Math.ceil(h / 2) * 2;
|
|
15853
|
+
}
|
|
15854
|
+
return {
|
|
15855
|
+
width: w,
|
|
15856
|
+
height: h,
|
|
15054
15857
|
top: containerRect.top,
|
|
15055
15858
|
left: containerRect.left
|
|
15056
|
-
}
|
|
15057
|
-
|
|
15058
|
-
);
|
|
15859
|
+
};
|
|
15860
|
+
}, [width, height, containerRect, forceEven]);
|
|
15059
15861
|
if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
|
|
15060
15862
|
hasInitialSizeRef.current = true;
|
|
15061
15863
|
}
|
|
@@ -15095,14 +15897,14 @@ function CanvasImpl({
|
|
|
15095
15897
|
async function run() {
|
|
15096
15898
|
if (!effectActiveRef.current || !root.current) return;
|
|
15097
15899
|
await root.current.configure({
|
|
15900
|
+
id,
|
|
15901
|
+
primaryCanvas,
|
|
15902
|
+
scheduler,
|
|
15098
15903
|
gl,
|
|
15099
15904
|
renderer,
|
|
15100
15905
|
scene,
|
|
15101
15906
|
events,
|
|
15102
15907
|
shadows,
|
|
15103
|
-
linear,
|
|
15104
|
-
flat,
|
|
15105
|
-
legacy,
|
|
15106
15908
|
orthographic,
|
|
15107
15909
|
frameloop,
|
|
15108
15910
|
dpr,
|
|
@@ -15112,6 +15914,7 @@ function CanvasImpl({
|
|
|
15112
15914
|
size: effectiveSize,
|
|
15113
15915
|
// Store size props for reset functionality
|
|
15114
15916
|
_sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
|
|
15917
|
+
forceEven,
|
|
15115
15918
|
// Pass mutable reference to onPointerMissed so it's free to update
|
|
15116
15919
|
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
|
|
15117
15920
|
onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
|
|
@@ -15135,7 +15938,10 @@ function CanvasImpl({
|
|
|
15135
15938
|
});
|
|
15136
15939
|
if (!effectActiveRef.current || !root.current) return;
|
|
15137
15940
|
root.current.render(
|
|
15138
|
-
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */
|
|
15941
|
+
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
|
|
15942
|
+
backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
|
|
15943
|
+
children ?? null
|
|
15944
|
+
] }) }) })
|
|
15139
15945
|
);
|
|
15140
15946
|
}
|
|
15141
15947
|
run();
|
|
@@ -15162,20 +15968,22 @@ function CanvasImpl({
|
|
|
15162
15968
|
const canvas = canvasRef.current;
|
|
15163
15969
|
if (!canvas) return;
|
|
15164
15970
|
const handleHMR = () => {
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
rootEntry
|
|
15168
|
-
nodes
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15971
|
+
queueMicrotask(() => {
|
|
15972
|
+
const rootEntry = _roots.get(canvas);
|
|
15973
|
+
if (rootEntry?.store) {
|
|
15974
|
+
console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
|
|
15975
|
+
rootEntry.store.setState((state) => ({
|
|
15976
|
+
nodes: {},
|
|
15977
|
+
uniforms: {},
|
|
15978
|
+
_hmrVersion: state._hmrVersion + 1
|
|
15979
|
+
}));
|
|
15980
|
+
}
|
|
15981
|
+
});
|
|
15173
15982
|
};
|
|
15174
15983
|
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
15175
15984
|
const hot = import.meta.hot;
|
|
15176
15985
|
hot.on("vite:afterUpdate", handleHMR);
|
|
15177
|
-
return () => hot.
|
|
15178
|
-
});
|
|
15986
|
+
return () => hot.off?.("vite:afterUpdate", handleHMR);
|
|
15179
15987
|
}
|
|
15180
15988
|
if (typeof module !== "undefined" && module.hot) {
|
|
15181
15989
|
const hot = module.hot;
|
|
@@ -15198,7 +16006,16 @@ function CanvasImpl({
|
|
|
15198
16006
|
...style
|
|
15199
16007
|
},
|
|
15200
16008
|
...props,
|
|
15201
|
-
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
16009
|
+
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
16010
|
+
"canvas",
|
|
16011
|
+
{
|
|
16012
|
+
ref: canvasRef,
|
|
16013
|
+
id,
|
|
16014
|
+
className: "r3f-canvas",
|
|
16015
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
16016
|
+
children: fallback
|
|
16017
|
+
}
|
|
16018
|
+
) })
|
|
15202
16019
|
}
|
|
15203
16020
|
);
|
|
15204
16021
|
}
|
|
@@ -15208,4 +16025,4 @@ function Canvas(props) {
|
|
|
15208
16025
|
|
|
15209
16026
|
extend(THREE);
|
|
15210
16027
|
|
|
15211
|
-
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 };
|
|
16028
|
+
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 };
|