@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/legacy.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import * as three from 'three';
|
|
2
|
-
import { WebGLRenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1,
|
|
3
|
-
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { WebGLRenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGLRenderer } from 'three';
|
|
3
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import React__default, {
|
|
5
|
+
import React__default, { useLayoutEffect, useRef, useMemo, useEffect, useContext, useImperativeHandle, useCallback, useState } from 'react';
|
|
6
6
|
import useMeasure from 'react-use-measure';
|
|
7
7
|
import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
|
|
8
|
+
import { useThree as useThree$1, useLoader as useLoader$1, useFrame as useFrame$1, createPortal as createPortal$1, applyProps as applyProps$1, extend as extend$1 } from '@react-three/fiber';
|
|
9
|
+
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
10
|
+
import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
|
|
11
|
+
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
|
|
12
|
+
import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
|
|
13
|
+
import { GainMapLoader } from '@monogrid/gainmap-js';
|
|
8
14
|
import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
|
|
9
15
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
10
16
|
import { suspend, preload, clear } from 'suspend-react';
|
|
@@ -45,6 +51,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
45
51
|
WebGPURenderer: WebGPURenderer
|
|
46
52
|
}, [three]);
|
|
47
53
|
|
|
54
|
+
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
55
|
+
const pendingSubscribers = /* @__PURE__ */ new Map();
|
|
56
|
+
function registerPrimary(id, renderer, store) {
|
|
57
|
+
if (primaryRegistry.has(id)) {
|
|
58
|
+
console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
|
|
59
|
+
}
|
|
60
|
+
const entry = { renderer, store };
|
|
61
|
+
primaryRegistry.set(id, entry);
|
|
62
|
+
const subscribers = pendingSubscribers.get(id);
|
|
63
|
+
if (subscribers) {
|
|
64
|
+
subscribers.forEach((callback) => callback(entry));
|
|
65
|
+
pendingSubscribers.delete(id);
|
|
66
|
+
}
|
|
67
|
+
return () => {
|
|
68
|
+
const currentEntry = primaryRegistry.get(id);
|
|
69
|
+
if (currentEntry?.renderer === renderer) {
|
|
70
|
+
primaryRegistry.delete(id);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function getPrimary(id) {
|
|
75
|
+
return primaryRegistry.get(id);
|
|
76
|
+
}
|
|
77
|
+
function waitForPrimary(id, timeout = 5e3) {
|
|
78
|
+
const existing = primaryRegistry.get(id);
|
|
79
|
+
if (existing) {
|
|
80
|
+
return Promise.resolve(existing);
|
|
81
|
+
}
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const timeoutId = setTimeout(() => {
|
|
84
|
+
const subscribers = pendingSubscribers.get(id);
|
|
85
|
+
if (subscribers) {
|
|
86
|
+
const index = subscribers.indexOf(callback);
|
|
87
|
+
if (index !== -1) subscribers.splice(index, 1);
|
|
88
|
+
if (subscribers.length === 0) pendingSubscribers.delete(id);
|
|
89
|
+
}
|
|
90
|
+
reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
|
|
91
|
+
}, timeout);
|
|
92
|
+
const callback = (entry) => {
|
|
93
|
+
clearTimeout(timeoutId);
|
|
94
|
+
resolve(entry);
|
|
95
|
+
};
|
|
96
|
+
if (!pendingSubscribers.has(id)) {
|
|
97
|
+
pendingSubscribers.set(id, []);
|
|
98
|
+
}
|
|
99
|
+
pendingSubscribers.get(id).push(callback);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function hasPrimary(id) {
|
|
103
|
+
return primaryRegistry.has(id);
|
|
104
|
+
}
|
|
105
|
+
function unregisterPrimary(id) {
|
|
106
|
+
primaryRegistry.delete(id);
|
|
107
|
+
}
|
|
108
|
+
function getPrimaryIds() {
|
|
109
|
+
return Array.from(primaryRegistry.keys());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const presetsObj = {
|
|
113
|
+
apartment: "lebombo_1k.hdr",
|
|
114
|
+
city: "potsdamer_platz_1k.hdr",
|
|
115
|
+
dawn: "kiara_1_dawn_1k.hdr",
|
|
116
|
+
forest: "forest_slope_1k.hdr",
|
|
117
|
+
lobby: "st_fagans_interior_1k.hdr",
|
|
118
|
+
night: "dikhololo_night_1k.hdr",
|
|
119
|
+
park: "rooitou_park_1k.hdr",
|
|
120
|
+
studio: "studio_small_03_1k.hdr",
|
|
121
|
+
sunset: "venice_sunset_1k.hdr",
|
|
122
|
+
warehouse: "empty_warehouse_01_1k.hdr"
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
|
|
126
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
127
|
+
const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
|
|
128
|
+
function useEnvironment({
|
|
129
|
+
files = defaultFiles,
|
|
130
|
+
path = "",
|
|
131
|
+
preset = void 0,
|
|
132
|
+
colorSpace = void 0,
|
|
133
|
+
extensions
|
|
134
|
+
} = {}) {
|
|
135
|
+
if (preset) {
|
|
136
|
+
validatePreset(preset);
|
|
137
|
+
files = presetsObj[preset];
|
|
138
|
+
path = CUBEMAP_ROOT;
|
|
139
|
+
}
|
|
140
|
+
const multiFile = isArray(files);
|
|
141
|
+
const { extension, isCubemap } = getExtension(files);
|
|
142
|
+
const loader = getLoader$1(extension);
|
|
143
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
144
|
+
const renderer = useThree$1((state) => state.renderer);
|
|
145
|
+
useLayoutEffect(() => {
|
|
146
|
+
if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
|
|
147
|
+
function clearGainmapTexture() {
|
|
148
|
+
useLoader$1.clear(loader, multiFile ? [files] : files);
|
|
149
|
+
}
|
|
150
|
+
renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
|
|
151
|
+
}, [extension, files, loader, multiFile, renderer.domElement]);
|
|
152
|
+
const loaderResult = useLoader$1(
|
|
153
|
+
loader,
|
|
154
|
+
multiFile ? [files] : files,
|
|
155
|
+
(loader2) => {
|
|
156
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
157
|
+
loader2.setRenderer?.(renderer);
|
|
158
|
+
}
|
|
159
|
+
loader2.setPath?.(path);
|
|
160
|
+
if (extensions) extensions(loader2);
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
let texture = multiFile ? (
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
loaderResult[0]
|
|
166
|
+
) : loaderResult;
|
|
167
|
+
if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
|
|
168
|
+
texture = texture.renderTarget?.texture;
|
|
169
|
+
}
|
|
170
|
+
texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
|
|
171
|
+
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
172
|
+
return texture;
|
|
173
|
+
}
|
|
174
|
+
const preloadDefaultOptions = {
|
|
175
|
+
files: defaultFiles,
|
|
176
|
+
path: "",
|
|
177
|
+
preset: void 0,
|
|
178
|
+
extensions: void 0
|
|
179
|
+
};
|
|
180
|
+
useEnvironment.preload = (preloadOptions) => {
|
|
181
|
+
const options = { ...preloadDefaultOptions, ...preloadOptions };
|
|
182
|
+
let { files, path = "" } = options;
|
|
183
|
+
const { preset, extensions } = options;
|
|
184
|
+
if (preset) {
|
|
185
|
+
validatePreset(preset);
|
|
186
|
+
files = presetsObj[preset];
|
|
187
|
+
path = CUBEMAP_ROOT;
|
|
188
|
+
}
|
|
189
|
+
const { extension } = getExtension(files);
|
|
190
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
191
|
+
throw new Error("useEnvironment: Preloading gainmaps is not supported");
|
|
192
|
+
}
|
|
193
|
+
const loader = getLoader$1(extension);
|
|
194
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
195
|
+
useLoader$1.preload(loader, isArray(files) ? [files] : files, (loader2) => {
|
|
196
|
+
loader2.setPath?.(path);
|
|
197
|
+
if (extensions) extensions(loader2);
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const clearDefaultOptins = {
|
|
201
|
+
files: defaultFiles,
|
|
202
|
+
preset: void 0
|
|
203
|
+
};
|
|
204
|
+
useEnvironment.clear = (clearOptions) => {
|
|
205
|
+
const options = { ...clearDefaultOptins, ...clearOptions };
|
|
206
|
+
let { files } = options;
|
|
207
|
+
const { preset } = options;
|
|
208
|
+
if (preset) {
|
|
209
|
+
validatePreset(preset);
|
|
210
|
+
files = presetsObj[preset];
|
|
211
|
+
}
|
|
212
|
+
const { extension } = getExtension(files);
|
|
213
|
+
const loader = getLoader$1(extension);
|
|
214
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
215
|
+
useLoader$1.clear(loader, isArray(files) ? [files] : files);
|
|
216
|
+
};
|
|
217
|
+
function validatePreset(preset) {
|
|
218
|
+
if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
|
|
219
|
+
}
|
|
220
|
+
function getExtension(files) {
|
|
221
|
+
const isCubemap = isArray(files) && files.length === 6;
|
|
222
|
+
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
223
|
+
const firstEntry = isArray(files) ? files[0] : files;
|
|
224
|
+
const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
|
|
225
|
+
return { extension, isCubemap, isGainmap };
|
|
226
|
+
}
|
|
227
|
+
function getLoader$1(extension) {
|
|
228
|
+
const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
|
|
229
|
+
return loader;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const isRef$1 = (obj) => obj.current && obj.current.isScene;
|
|
233
|
+
const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
|
|
234
|
+
function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
|
|
235
|
+
sceneProps = {
|
|
236
|
+
backgroundBlurriness: 0,
|
|
237
|
+
backgroundIntensity: 1,
|
|
238
|
+
backgroundRotation: [0, 0, 0],
|
|
239
|
+
environmentIntensity: 1,
|
|
240
|
+
environmentRotation: [0, 0, 0],
|
|
241
|
+
...sceneProps
|
|
242
|
+
};
|
|
243
|
+
const target = resolveScene(scene || defaultScene);
|
|
244
|
+
const oldbg = target.background;
|
|
245
|
+
const oldenv = target.environment;
|
|
246
|
+
const oldSceneProps = {
|
|
247
|
+
// @ts-ignore
|
|
248
|
+
backgroundBlurriness: target.backgroundBlurriness,
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
backgroundIntensity: target.backgroundIntensity,
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
253
|
+
// @ts-ignore
|
|
254
|
+
environmentIntensity: target.environmentIntensity,
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
257
|
+
};
|
|
258
|
+
if (background !== "only") target.environment = texture;
|
|
259
|
+
if (background) target.background = texture;
|
|
260
|
+
applyProps$1(target, sceneProps);
|
|
261
|
+
return () => {
|
|
262
|
+
if (background !== "only") target.environment = oldenv;
|
|
263
|
+
if (background) target.background = oldbg;
|
|
264
|
+
applyProps$1(target, oldSceneProps);
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function EnvironmentMap({ scene, background = false, map, ...config }) {
|
|
268
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
269
|
+
React.useLayoutEffect(() => {
|
|
270
|
+
if (map) return setEnvProps(background, scene, defaultScene, map, config);
|
|
271
|
+
});
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
function EnvironmentCube({
|
|
275
|
+
background = false,
|
|
276
|
+
scene,
|
|
277
|
+
blur,
|
|
278
|
+
backgroundBlurriness,
|
|
279
|
+
backgroundIntensity,
|
|
280
|
+
backgroundRotation,
|
|
281
|
+
environmentIntensity,
|
|
282
|
+
environmentRotation,
|
|
283
|
+
...rest
|
|
284
|
+
}) {
|
|
285
|
+
const texture = useEnvironment(rest);
|
|
286
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
287
|
+
React.useLayoutEffect(() => {
|
|
288
|
+
return setEnvProps(background, scene, defaultScene, texture, {
|
|
289
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
290
|
+
backgroundIntensity,
|
|
291
|
+
backgroundRotation,
|
|
292
|
+
environmentIntensity,
|
|
293
|
+
environmentRotation
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
React.useEffect(() => {
|
|
297
|
+
return () => {
|
|
298
|
+
texture.dispose();
|
|
299
|
+
};
|
|
300
|
+
}, [texture]);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
function EnvironmentPortal({
|
|
304
|
+
children,
|
|
305
|
+
near = 0.1,
|
|
306
|
+
far = 1e3,
|
|
307
|
+
resolution = 256,
|
|
308
|
+
frames = 1,
|
|
309
|
+
map,
|
|
310
|
+
background = false,
|
|
311
|
+
blur,
|
|
312
|
+
backgroundBlurriness,
|
|
313
|
+
backgroundIntensity,
|
|
314
|
+
backgroundRotation,
|
|
315
|
+
environmentIntensity,
|
|
316
|
+
environmentRotation,
|
|
317
|
+
scene,
|
|
318
|
+
files,
|
|
319
|
+
path,
|
|
320
|
+
preset = void 0,
|
|
321
|
+
extensions
|
|
322
|
+
}) {
|
|
323
|
+
const gl = useThree$1((state) => state.gl);
|
|
324
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
325
|
+
const camera = React.useRef(null);
|
|
326
|
+
const [virtualScene] = React.useState(() => new Scene());
|
|
327
|
+
const fbo = React.useMemo(() => {
|
|
328
|
+
const fbo2 = new WebGLCubeRenderTarget(resolution);
|
|
329
|
+
fbo2.texture.type = HalfFloatType;
|
|
330
|
+
return fbo2;
|
|
331
|
+
}, [resolution]);
|
|
332
|
+
React.useEffect(() => {
|
|
333
|
+
return () => {
|
|
334
|
+
fbo.dispose();
|
|
335
|
+
};
|
|
336
|
+
}, [fbo]);
|
|
337
|
+
React.useLayoutEffect(() => {
|
|
338
|
+
if (frames === 1) {
|
|
339
|
+
const autoClear = gl.autoClear;
|
|
340
|
+
gl.autoClear = true;
|
|
341
|
+
camera.current.update(gl, virtualScene);
|
|
342
|
+
gl.autoClear = autoClear;
|
|
343
|
+
}
|
|
344
|
+
return setEnvProps(background, scene, defaultScene, fbo.texture, {
|
|
345
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
346
|
+
backgroundIntensity,
|
|
347
|
+
backgroundRotation,
|
|
348
|
+
environmentIntensity,
|
|
349
|
+
environmentRotation
|
|
350
|
+
});
|
|
351
|
+
}, [
|
|
352
|
+
children,
|
|
353
|
+
virtualScene,
|
|
354
|
+
fbo.texture,
|
|
355
|
+
scene,
|
|
356
|
+
defaultScene,
|
|
357
|
+
background,
|
|
358
|
+
frames,
|
|
359
|
+
gl,
|
|
360
|
+
blur,
|
|
361
|
+
backgroundBlurriness,
|
|
362
|
+
backgroundIntensity,
|
|
363
|
+
backgroundRotation,
|
|
364
|
+
environmentIntensity,
|
|
365
|
+
environmentRotation
|
|
366
|
+
]);
|
|
367
|
+
let count = 1;
|
|
368
|
+
useFrame$1(() => {
|
|
369
|
+
if (frames === Infinity || count < frames) {
|
|
370
|
+
const autoClear = gl.autoClear;
|
|
371
|
+
gl.autoClear = true;
|
|
372
|
+
camera.current.update(gl, virtualScene);
|
|
373
|
+
gl.autoClear = autoClear;
|
|
374
|
+
count++;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
return /* @__PURE__ */ jsx(Fragment, { children: createPortal$1(
|
|
378
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
379
|
+
children,
|
|
380
|
+
/* @__PURE__ */ jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
|
|
381
|
+
files || preset ? /* @__PURE__ */ jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsx(EnvironmentMap, { background: true, map, extensions }) : null
|
|
382
|
+
] }),
|
|
383
|
+
virtualScene
|
|
384
|
+
) });
|
|
385
|
+
}
|
|
386
|
+
function EnvironmentGround(props) {
|
|
387
|
+
const textureDefault = useEnvironment(props);
|
|
388
|
+
const texture = props.map || textureDefault;
|
|
389
|
+
React.useMemo(() => extend$1({ GroundProjectedEnvImpl: GroundedSkybox }), []);
|
|
390
|
+
React.useEffect(() => {
|
|
391
|
+
return () => {
|
|
392
|
+
textureDefault.dispose();
|
|
393
|
+
};
|
|
394
|
+
}, [textureDefault]);
|
|
395
|
+
const height = props.ground?.height ?? 15;
|
|
396
|
+
const radius = props.ground?.radius ?? 60;
|
|
397
|
+
const scale = props.ground?.scale ?? 1e3;
|
|
398
|
+
const args = React.useMemo(
|
|
399
|
+
() => [texture, height, radius],
|
|
400
|
+
[texture, height, radius]
|
|
401
|
+
);
|
|
402
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
403
|
+
/* @__PURE__ */ jsx(EnvironmentMap, { ...props, map: texture }),
|
|
404
|
+
/* @__PURE__ */ jsx("groundProjectedEnvImpl", { args, scale })
|
|
405
|
+
] });
|
|
406
|
+
}
|
|
407
|
+
function EnvironmentColor({ color, scene }) {
|
|
408
|
+
const defaultScene = useThree$1((state) => state.scene);
|
|
409
|
+
React.useLayoutEffect(() => {
|
|
410
|
+
if (color === void 0) return;
|
|
411
|
+
const target = resolveScene(scene || defaultScene);
|
|
412
|
+
const oldBg = target.background;
|
|
413
|
+
target.background = new Color(color);
|
|
414
|
+
return () => {
|
|
415
|
+
target.background = oldBg;
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
function EnvironmentDualSource(props) {
|
|
421
|
+
const { backgroundFiles, ...envProps } = props;
|
|
422
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
423
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...envProps, background: false }),
|
|
424
|
+
/* @__PURE__ */ jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
|
|
425
|
+
] });
|
|
426
|
+
}
|
|
427
|
+
function Environment(props) {
|
|
428
|
+
if (props.color && !props.files && !props.preset && !props.map) {
|
|
429
|
+
return /* @__PURE__ */ jsx(EnvironmentColor, { ...props });
|
|
430
|
+
}
|
|
431
|
+
if (props.backgroundFiles && props.backgroundFiles !== props.files) {
|
|
432
|
+
return /* @__PURE__ */ jsx(EnvironmentDualSource, { ...props });
|
|
433
|
+
}
|
|
434
|
+
return props.ground ? /* @__PURE__ */ jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsx(EnvironmentCube, { ...props });
|
|
435
|
+
}
|
|
436
|
+
|
|
48
437
|
var __defProp$2 = Object.defineProperty;
|
|
49
438
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
50
439
|
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -224,7 +613,8 @@ function prepare(target, root, type, props) {
|
|
|
224
613
|
object,
|
|
225
614
|
eventCount: 0,
|
|
226
615
|
handlers: {},
|
|
227
|
-
isHidden: false
|
|
616
|
+
isHidden: false,
|
|
617
|
+
deferredRefs: []
|
|
228
618
|
};
|
|
229
619
|
if (object) object.__r3f = instance;
|
|
230
620
|
}
|
|
@@ -273,7 +663,7 @@ function createOcclusionObserverNode(store, uniform) {
|
|
|
273
663
|
let occlusionSetupPromise = null;
|
|
274
664
|
function enableOcclusion(store) {
|
|
275
665
|
const state = store.getState();
|
|
276
|
-
const { internal, renderer
|
|
666
|
+
const { internal, renderer } = state;
|
|
277
667
|
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
278
668
|
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
279
669
|
if (!hasOcclusionSupport) {
|
|
@@ -436,6 +826,22 @@ function hasVisibilityHandlers(handlers) {
|
|
|
436
826
|
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
437
827
|
}
|
|
438
828
|
|
|
829
|
+
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
830
|
+
function fromRef(ref) {
|
|
831
|
+
return { [FROM_REF]: ref };
|
|
832
|
+
}
|
|
833
|
+
function isFromRef(value) {
|
|
834
|
+
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const ONCE = Symbol.for("@react-three/fiber.once");
|
|
838
|
+
function once(...args) {
|
|
839
|
+
return { [ONCE]: args.length ? args : true };
|
|
840
|
+
}
|
|
841
|
+
function isOnce(value) {
|
|
842
|
+
return value !== null && typeof value === "object" && ONCE in value;
|
|
843
|
+
}
|
|
844
|
+
|
|
439
845
|
const RESERVED_PROPS = [
|
|
440
846
|
"children",
|
|
441
847
|
"key",
|
|
@@ -506,7 +912,7 @@ function getMemoizedPrototype(root) {
|
|
|
506
912
|
ctor = new root.constructor();
|
|
507
913
|
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
508
914
|
}
|
|
509
|
-
} catch
|
|
915
|
+
} catch {
|
|
510
916
|
}
|
|
511
917
|
return ctor;
|
|
512
918
|
}
|
|
@@ -552,6 +958,25 @@ function applyProps(object, props) {
|
|
|
552
958
|
continue;
|
|
553
959
|
}
|
|
554
960
|
if (value === void 0) continue;
|
|
961
|
+
if (isFromRef(value)) {
|
|
962
|
+
instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (isOnce(value)) {
|
|
966
|
+
if (instance?.appliedOnce?.has(prop)) continue;
|
|
967
|
+
if (instance) {
|
|
968
|
+
instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
|
|
969
|
+
instance.appliedOnce.add(prop);
|
|
970
|
+
}
|
|
971
|
+
const { root: targetRoot, key: targetKey } = resolve(object, prop);
|
|
972
|
+
const args = value[ONCE];
|
|
973
|
+
if (typeof targetRoot[targetKey] === "function") {
|
|
974
|
+
targetRoot[targetKey](...args === true ? [] : args);
|
|
975
|
+
} else if (args !== true && args.length > 0) {
|
|
976
|
+
targetRoot[targetKey] = args[0];
|
|
977
|
+
}
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
555
980
|
let { root, key, target } = resolve(object, prop);
|
|
556
981
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
557
982
|
throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
|
|
@@ -574,7 +999,10 @@ function applyProps(object, props) {
|
|
|
574
999
|
else target.set(value);
|
|
575
1000
|
} else {
|
|
576
1001
|
root[key] = value;
|
|
577
|
-
if (
|
|
1002
|
+
if (key.endsWith("Node") && root.isMaterial) {
|
|
1003
|
+
root.needsUpdate = true;
|
|
1004
|
+
}
|
|
1005
|
+
if (rootState && rootState.renderer?.outputColorSpace === SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
578
1006
|
root[key].format === RGBAFormat && root[key].type === UnsignedByteType) {
|
|
579
1007
|
root[key].colorSpace = rootState.textureColorSpace;
|
|
580
1008
|
}
|
|
@@ -607,38 +1035,60 @@ function applyProps(object, props) {
|
|
|
607
1035
|
return object;
|
|
608
1036
|
}
|
|
609
1037
|
|
|
1038
|
+
const DEFAULT_POINTER_ID = 0;
|
|
1039
|
+
const XR_POINTER_ID_START = 1e3;
|
|
1040
|
+
function getPointerState(internal, pointerId) {
|
|
1041
|
+
let state = internal.pointerMap.get(pointerId);
|
|
1042
|
+
if (!state) {
|
|
1043
|
+
state = {
|
|
1044
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
1045
|
+
captured: /* @__PURE__ */ new Map(),
|
|
1046
|
+
initialClick: [0, 0],
|
|
1047
|
+
initialHits: []
|
|
1048
|
+
};
|
|
1049
|
+
internal.pointerMap.set(pointerId, state);
|
|
1050
|
+
}
|
|
1051
|
+
return state;
|
|
1052
|
+
}
|
|
1053
|
+
function getPointerId(event) {
|
|
1054
|
+
return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
|
|
1055
|
+
}
|
|
610
1056
|
function makeId(event) {
|
|
611
1057
|
return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
|
|
612
1058
|
}
|
|
613
|
-
function releaseInternalPointerCapture(
|
|
614
|
-
const
|
|
1059
|
+
function releaseInternalPointerCapture(internal, obj, pointerId) {
|
|
1060
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1061
|
+
if (!pointerState) return;
|
|
1062
|
+
const captureData = pointerState.captured.get(obj);
|
|
615
1063
|
if (captureData) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
capturedMap.delete(pointerId);
|
|
619
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
620
|
-
}
|
|
1064
|
+
pointerState.captured.delete(obj);
|
|
1065
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
621
1066
|
}
|
|
622
1067
|
}
|
|
623
1068
|
function removeInteractivity(store, object) {
|
|
624
1069
|
const { internal } = store.getState();
|
|
625
1070
|
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1071
|
+
for (const [pointerId, pointerState] of internal.pointerMap) {
|
|
1072
|
+
pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
|
|
1073
|
+
pointerState.hovered.forEach((value, key) => {
|
|
1074
|
+
if (value.eventObject === object || value.object === object) {
|
|
1075
|
+
pointerState.hovered.delete(key);
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
if (pointerState.captured.has(object)) {
|
|
1079
|
+
releaseInternalPointerCapture(internal, object, pointerId);
|
|
630
1080
|
}
|
|
631
|
-
}
|
|
632
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
633
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
634
|
-
});
|
|
1081
|
+
}
|
|
635
1082
|
unregisterVisibility(store, object);
|
|
636
1083
|
}
|
|
637
1084
|
function createEvents(store) {
|
|
638
|
-
function calculateDistance(event) {
|
|
1085
|
+
function calculateDistance(event, pointerId) {
|
|
639
1086
|
const { internal } = store.getState();
|
|
640
|
-
const
|
|
641
|
-
|
|
1087
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1088
|
+
if (!pointerState) return 0;
|
|
1089
|
+
const [initialX, initialY] = pointerState.initialClick;
|
|
1090
|
+
const dx = event.offsetX - initialX;
|
|
1091
|
+
const dy = event.offsetY - initialY;
|
|
642
1092
|
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
643
1093
|
}
|
|
644
1094
|
function filterPointerEvents(objects) {
|
|
@@ -674,6 +1124,15 @@ function createEvents(store) {
|
|
|
674
1124
|
return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
|
|
675
1125
|
}
|
|
676
1126
|
let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
|
|
1127
|
+
const aInteractivePriority = a.object.userData?.interactivePriority;
|
|
1128
|
+
const bInteractivePriority = b.object.userData?.interactivePriority;
|
|
1129
|
+
if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
|
|
1130
|
+
if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
|
|
1131
|
+
if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
|
|
1132
|
+
if (aInteractivePriority !== bInteractivePriority) {
|
|
1133
|
+
return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
677
1136
|
const aState = getRootState(a.object);
|
|
678
1137
|
const bState = getRootState(b.object);
|
|
679
1138
|
const aPriority = aState?.events?.priority ?? 1;
|
|
@@ -695,9 +1154,13 @@ function createEvents(store) {
|
|
|
695
1154
|
eventObject = eventObject.parent;
|
|
696
1155
|
}
|
|
697
1156
|
}
|
|
698
|
-
if ("pointerId" in event
|
|
699
|
-
|
|
700
|
-
|
|
1157
|
+
if ("pointerId" in event) {
|
|
1158
|
+
const pointerId = event.pointerId;
|
|
1159
|
+
const pointerState = state.internal.pointerMap.get(pointerId);
|
|
1160
|
+
if (pointerState?.captured.size) {
|
|
1161
|
+
for (const captureData of pointerState.captured.values()) {
|
|
1162
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
1163
|
+
}
|
|
701
1164
|
}
|
|
702
1165
|
}
|
|
703
1166
|
return intersections;
|
|
@@ -710,27 +1173,25 @@ function createEvents(store) {
|
|
|
710
1173
|
if (state) {
|
|
711
1174
|
const { raycaster, pointer, camera, internal } = state;
|
|
712
1175
|
const unprojectedPoint = new Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
713
|
-
const hasPointerCapture = (id) =>
|
|
1176
|
+
const hasPointerCapture = (id) => {
|
|
1177
|
+
const pointerState = internal.pointerMap.get(id);
|
|
1178
|
+
return pointerState?.captured.has(hit.eventObject) ?? false;
|
|
1179
|
+
};
|
|
714
1180
|
const setPointerCapture = (id) => {
|
|
715
1181
|
const captureData = { intersection: hit, target: event.target };
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
} else {
|
|
719
|
-
internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
|
|
720
|
-
}
|
|
1182
|
+
const pointerState = getPointerState(internal, id);
|
|
1183
|
+
pointerState.captured.set(hit.eventObject, captureData);
|
|
721
1184
|
event.target.setPointerCapture(id);
|
|
722
1185
|
};
|
|
723
1186
|
const releasePointerCapture = (id) => {
|
|
724
|
-
|
|
725
|
-
if (captures) {
|
|
726
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
727
|
-
}
|
|
1187
|
+
releaseInternalPointerCapture(internal, hit.eventObject, id);
|
|
728
1188
|
};
|
|
729
1189
|
const extractEventProps = {};
|
|
730
1190
|
for (const prop in event) {
|
|
731
1191
|
const property = event[prop];
|
|
732
1192
|
if (typeof property !== "function") extractEventProps[prop] = property;
|
|
733
1193
|
}
|
|
1194
|
+
const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
|
|
734
1195
|
const raycastEvent = {
|
|
735
1196
|
...hit,
|
|
736
1197
|
...extractEventProps,
|
|
@@ -741,18 +1202,19 @@ function createEvents(store) {
|
|
|
741
1202
|
unprojectedPoint,
|
|
742
1203
|
ray: raycaster.ray,
|
|
743
1204
|
camera,
|
|
1205
|
+
pointerId: eventPointerId,
|
|
744
1206
|
// Hijack stopPropagation, which just sets a flag
|
|
745
1207
|
stopPropagation() {
|
|
746
|
-
const
|
|
1208
|
+
const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
|
|
747
1209
|
if (
|
|
748
1210
|
// ...if this pointer hasn't been captured
|
|
749
|
-
!
|
|
750
|
-
|
|
1211
|
+
!pointerState?.captured.size || // ... or if the hit object is capturing the pointer
|
|
1212
|
+
pointerState.captured.has(hit.eventObject)
|
|
751
1213
|
) {
|
|
752
1214
|
raycastEvent.stopped = localState.stopped = true;
|
|
753
|
-
if (
|
|
1215
|
+
if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
754
1216
|
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
755
|
-
cancelPointer([...higher, hit]);
|
|
1217
|
+
cancelPointer([...higher, hit], eventPointerId);
|
|
756
1218
|
}
|
|
757
1219
|
}
|
|
758
1220
|
},
|
|
@@ -768,15 +1230,18 @@ function createEvents(store) {
|
|
|
768
1230
|
}
|
|
769
1231
|
return intersections;
|
|
770
1232
|
}
|
|
771
|
-
function cancelPointer(intersections) {
|
|
1233
|
+
function cancelPointer(intersections, pointerId) {
|
|
772
1234
|
const { internal } = store.getState();
|
|
773
|
-
|
|
1235
|
+
const pid = pointerId ?? DEFAULT_POINTER_ID;
|
|
1236
|
+
const pointerState = internal.pointerMap.get(pid);
|
|
1237
|
+
if (!pointerState) return;
|
|
1238
|
+
for (const [hoveredId, hoveredObj] of pointerState.hovered) {
|
|
774
1239
|
if (!intersections.length || !intersections.find(
|
|
775
1240
|
(hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
|
|
776
1241
|
)) {
|
|
777
1242
|
const eventObject = hoveredObj.eventObject;
|
|
778
1243
|
const instance = eventObject.__r3f;
|
|
779
|
-
|
|
1244
|
+
pointerState.hovered.delete(hoveredId);
|
|
780
1245
|
if (instance?.eventCount) {
|
|
781
1246
|
const handlers = instance.handlers;
|
|
782
1247
|
const data = { ...hoveredObj, intersections };
|
|
@@ -805,41 +1270,118 @@ function createEvents(store) {
|
|
|
805
1270
|
instance?.handlers.onDropMissed?.(event);
|
|
806
1271
|
}
|
|
807
1272
|
}
|
|
1273
|
+
function cleanupPointer(pointerId) {
|
|
1274
|
+
const { internal } = store.getState();
|
|
1275
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1276
|
+
if (pointerState) {
|
|
1277
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1278
|
+
const eventObject = hoveredObj.eventObject;
|
|
1279
|
+
const instance = eventObject.__r3f;
|
|
1280
|
+
if (instance?.eventCount) {
|
|
1281
|
+
const handlers = instance.handlers;
|
|
1282
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1283
|
+
handlers.onPointerOut?.(data);
|
|
1284
|
+
handlers.onPointerLeave?.(data);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
internal.pointerMap.delete(pointerId);
|
|
1288
|
+
}
|
|
1289
|
+
internal.pointerDirty.delete(pointerId);
|
|
1290
|
+
}
|
|
1291
|
+
function processDeferredPointer(event, pointerId) {
|
|
1292
|
+
const state = store.getState();
|
|
1293
|
+
const { internal } = state;
|
|
1294
|
+
if (!state.events.enabled) return;
|
|
1295
|
+
const filter = filterPointerEvents;
|
|
1296
|
+
const hits = intersect(event, filter);
|
|
1297
|
+
cancelPointer(hits, pointerId);
|
|
1298
|
+
function onIntersect(data) {
|
|
1299
|
+
const eventObject = data.eventObject;
|
|
1300
|
+
const instance = eventObject.__r3f;
|
|
1301
|
+
if (!instance?.eventCount) return;
|
|
1302
|
+
const handlers = instance.handlers;
|
|
1303
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1304
|
+
const id = makeId(data);
|
|
1305
|
+
const pointerState = getPointerState(internal, pointerId);
|
|
1306
|
+
const hoveredItem = pointerState.hovered.get(id);
|
|
1307
|
+
if (!hoveredItem) {
|
|
1308
|
+
pointerState.hovered.set(id, data);
|
|
1309
|
+
handlers.onPointerOver?.(data);
|
|
1310
|
+
handlers.onPointerEnter?.(data);
|
|
1311
|
+
} else if (hoveredItem.stopped) {
|
|
1312
|
+
data.stopPropagation();
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
handlers.onPointerMove?.(data);
|
|
1316
|
+
}
|
|
1317
|
+
handleIntersects(hits, event, 0, onIntersect);
|
|
1318
|
+
}
|
|
808
1319
|
function handlePointer(name) {
|
|
809
1320
|
switch (name) {
|
|
810
1321
|
case "onPointerLeave":
|
|
811
|
-
case "onPointerCancel":
|
|
812
1322
|
case "onDragLeave":
|
|
813
1323
|
return () => cancelPointer([]);
|
|
1324
|
+
// Global cancel of these events
|
|
1325
|
+
case "onPointerCancel":
|
|
1326
|
+
return (event) => {
|
|
1327
|
+
const pointerId = getPointerId(event);
|
|
1328
|
+
cleanupPointer(pointerId);
|
|
1329
|
+
};
|
|
814
1330
|
case "onLostPointerCapture":
|
|
815
1331
|
return (event) => {
|
|
816
1332
|
const { internal } = store.getState();
|
|
817
|
-
|
|
1333
|
+
const pointerId = getPointerId(event);
|
|
1334
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1335
|
+
if (pointerState?.captured.size) {
|
|
818
1336
|
requestAnimationFrame(() => {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1337
|
+
const pointerState2 = internal.pointerMap.get(pointerId);
|
|
1338
|
+
if (pointerState2?.captured.size) {
|
|
1339
|
+
pointerState2.captured.clear();
|
|
822
1340
|
}
|
|
1341
|
+
cancelPointer([], pointerId);
|
|
823
1342
|
});
|
|
824
1343
|
}
|
|
825
1344
|
};
|
|
826
1345
|
}
|
|
827
1346
|
return function handleEvent(event) {
|
|
828
1347
|
const state = store.getState();
|
|
829
|
-
const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
|
|
1348
|
+
const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
|
|
1349
|
+
const pointerId = getPointerId(event);
|
|
830
1350
|
internal.lastEvent.current = event;
|
|
831
|
-
if (!
|
|
1351
|
+
if (!events.enabled) return;
|
|
832
1352
|
const isPointerMove = name === "onPointerMove";
|
|
833
1353
|
const isDragOver = name === "onDragOver";
|
|
834
1354
|
const isDrop = name === "onDrop";
|
|
835
1355
|
const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
|
|
1356
|
+
const isPointerDown = name === "onPointerDown";
|
|
1357
|
+
const isPointerUp = name === "onPointerUp";
|
|
1358
|
+
const isWheel = name === "onWheel";
|
|
1359
|
+
const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
|
|
1360
|
+
if (isPointerMove && canDeferRaycasts) {
|
|
1361
|
+
events.compute?.(event, state);
|
|
1362
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
|
|
1366
|
+
events.compute?.(event, state);
|
|
1367
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
|
|
1371
|
+
const deferredEvent = internal.pointerDirty.get(pointerId);
|
|
1372
|
+
internal.pointerDirty.delete(pointerId);
|
|
1373
|
+
processDeferredPointer(deferredEvent, pointerId);
|
|
1374
|
+
}
|
|
836
1375
|
const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
|
|
837
1376
|
const hits = intersect(event, filter);
|
|
838
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
839
|
-
if (
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1377
|
+
const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
|
|
1378
|
+
if (isPointerDown) {
|
|
1379
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1380
|
+
pointerState2.initialClick = [event.offsetX, event.offsetY];
|
|
1381
|
+
pointerState2.initialHits = hits.map((hit) => hit.eventObject);
|
|
1382
|
+
}
|
|
1383
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1384
|
+
const initialHits = pointerState?.initialHits ?? [];
|
|
843
1385
|
if (isClickEvent && !hits.length) {
|
|
844
1386
|
if (delta <= 2) {
|
|
845
1387
|
pointerMissed(event, internal.interaction);
|
|
@@ -854,7 +1396,9 @@ function createEvents(store) {
|
|
|
854
1396
|
dropMissed(event, internal.interaction);
|
|
855
1397
|
if (onDropMissed) onDropMissed(event);
|
|
856
1398
|
}
|
|
857
|
-
if (isPointerMove || isDragOver)
|
|
1399
|
+
if (isPointerMove || isDragOver) {
|
|
1400
|
+
cancelPointer(hits, pointerId);
|
|
1401
|
+
}
|
|
858
1402
|
function onIntersect(data) {
|
|
859
1403
|
const eventObject = data.eventObject;
|
|
860
1404
|
const instance = eventObject.__r3f;
|
|
@@ -863,9 +1407,10 @@ function createEvents(store) {
|
|
|
863
1407
|
if (isPointerMove) {
|
|
864
1408
|
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
865
1409
|
const id = makeId(data);
|
|
866
|
-
const
|
|
1410
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1411
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
867
1412
|
if (!hoveredItem) {
|
|
868
|
-
|
|
1413
|
+
pointerState2.hovered.set(id, data);
|
|
869
1414
|
handlers.onPointerOver?.(data);
|
|
870
1415
|
handlers.onPointerEnter?.(data);
|
|
871
1416
|
} else if (hoveredItem.stopped) {
|
|
@@ -875,9 +1420,10 @@ function createEvents(store) {
|
|
|
875
1420
|
handlers.onPointerMove?.(data);
|
|
876
1421
|
} else if (isDragOver) {
|
|
877
1422
|
const id = makeId(data);
|
|
878
|
-
const
|
|
1423
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1424
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
879
1425
|
if (!hoveredItem) {
|
|
880
|
-
|
|
1426
|
+
pointerState2.hovered.set(id, data);
|
|
881
1427
|
handlers.onDragOverEnter?.(data);
|
|
882
1428
|
} else if (hoveredItem.stopped) {
|
|
883
1429
|
data.stopPropagation();
|
|
@@ -888,18 +1434,18 @@ function createEvents(store) {
|
|
|
888
1434
|
} else {
|
|
889
1435
|
const handler = handlers[name];
|
|
890
1436
|
if (handler) {
|
|
891
|
-
if (!isClickEvent ||
|
|
1437
|
+
if (!isClickEvent || initialHits.includes(eventObject)) {
|
|
892
1438
|
pointerMissed(
|
|
893
1439
|
event,
|
|
894
|
-
internal.interaction.filter((object) => !
|
|
1440
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
895
1441
|
);
|
|
896
1442
|
handler(data);
|
|
897
1443
|
}
|
|
898
1444
|
} else {
|
|
899
|
-
if (isClickEvent &&
|
|
1445
|
+
if (isClickEvent && initialHits.includes(eventObject)) {
|
|
900
1446
|
pointerMissed(
|
|
901
1447
|
event,
|
|
902
|
-
internal.interaction.filter((object) => !
|
|
1448
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
903
1449
|
);
|
|
904
1450
|
}
|
|
905
1451
|
}
|
|
@@ -908,7 +1454,15 @@ function createEvents(store) {
|
|
|
908
1454
|
handleIntersects(hits, event, delta, onIntersect);
|
|
909
1455
|
};
|
|
910
1456
|
}
|
|
911
|
-
|
|
1457
|
+
function flushDeferredPointers() {
|
|
1458
|
+
const { internal, events } = store.getState();
|
|
1459
|
+
if (!events.frameTimedRaycasts) return;
|
|
1460
|
+
for (const [pointerId, event] of internal.pointerDirty) {
|
|
1461
|
+
processDeferredPointer(event, pointerId);
|
|
1462
|
+
}
|
|
1463
|
+
internal.pointerDirty.clear();
|
|
1464
|
+
}
|
|
1465
|
+
return { handlePointer, flushDeferredPointers, processDeferredPointer };
|
|
912
1466
|
}
|
|
913
1467
|
const DOM_EVENTS = {
|
|
914
1468
|
onClick: ["click", false],
|
|
@@ -927,11 +1481,16 @@ const DOM_EVENTS = {
|
|
|
927
1481
|
onLostPointerCapture: ["lostpointercapture", true]
|
|
928
1482
|
};
|
|
929
1483
|
function createPointerEvents(store) {
|
|
930
|
-
const { handlePointer } = createEvents(store);
|
|
1484
|
+
const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
|
|
1485
|
+
let nextXRPointerId = XR_POINTER_ID_START;
|
|
1486
|
+
const xrPointers = /* @__PURE__ */ new Map();
|
|
931
1487
|
return {
|
|
932
1488
|
priority: 1,
|
|
933
1489
|
enabled: true,
|
|
934
|
-
|
|
1490
|
+
frameTimedRaycasts: true,
|
|
1491
|
+
alwaysFireOnScroll: true,
|
|
1492
|
+
updateOnFrame: false,
|
|
1493
|
+
compute(event, state) {
|
|
935
1494
|
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
936
1495
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
937
1496
|
},
|
|
@@ -940,11 +1499,33 @@ function createPointerEvents(store) {
|
|
|
940
1499
|
(acc, key) => ({ ...acc, [key]: handlePointer(key) }),
|
|
941
1500
|
{}
|
|
942
1501
|
),
|
|
943
|
-
update: () => {
|
|
1502
|
+
update: (pointerId) => {
|
|
1503
|
+
const { events, internal } = store.getState();
|
|
1504
|
+
if (!events.handlers) return;
|
|
1505
|
+
if (pointerId !== void 0) {
|
|
1506
|
+
const event = internal.pointerDirty.get(pointerId);
|
|
1507
|
+
if (event) {
|
|
1508
|
+
internal.pointerDirty.delete(pointerId);
|
|
1509
|
+
processDeferredPointer(event, pointerId);
|
|
1510
|
+
} else if (internal.lastEvent?.current) {
|
|
1511
|
+
processDeferredPointer(internal.lastEvent.current, pointerId);
|
|
1512
|
+
}
|
|
1513
|
+
} else {
|
|
1514
|
+
flushDeferredPointers();
|
|
1515
|
+
if (internal.lastEvent?.current) {
|
|
1516
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
},
|
|
1520
|
+
flush: () => {
|
|
944
1521
|
const { events, internal } = store.getState();
|
|
945
|
-
|
|
1522
|
+
flushDeferredPointers();
|
|
1523
|
+
if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
|
|
1524
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1525
|
+
}
|
|
946
1526
|
},
|
|
947
1527
|
connect: (target) => {
|
|
1528
|
+
if (!target) return;
|
|
948
1529
|
const { set, events } = store.getState();
|
|
949
1530
|
events.disconnect?.();
|
|
950
1531
|
set((state) => ({ events: { ...state.events, connected: target } }));
|
|
@@ -968,6 +1549,32 @@ function createPointerEvents(store) {
|
|
|
968
1549
|
}
|
|
969
1550
|
set((state) => ({ events: { ...state.events, connected: void 0 } }));
|
|
970
1551
|
}
|
|
1552
|
+
},
|
|
1553
|
+
registerPointer: (config) => {
|
|
1554
|
+
const pointerId = nextXRPointerId++;
|
|
1555
|
+
xrPointers.set(pointerId, config);
|
|
1556
|
+
const { internal } = store.getState();
|
|
1557
|
+
getPointerState(internal, pointerId);
|
|
1558
|
+
return pointerId;
|
|
1559
|
+
},
|
|
1560
|
+
unregisterPointer: (pointerId) => {
|
|
1561
|
+
xrPointers.delete(pointerId);
|
|
1562
|
+
const { internal } = store.getState();
|
|
1563
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1564
|
+
if (pointerState) {
|
|
1565
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1566
|
+
const eventObject = hoveredObj.eventObject;
|
|
1567
|
+
const instance = eventObject.__r3f;
|
|
1568
|
+
if (instance?.eventCount) {
|
|
1569
|
+
const handlers = instance.handlers;
|
|
1570
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1571
|
+
handlers.onPointerOut?.(data);
|
|
1572
|
+
handlers.onPointerLeave?.(data);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
internal.pointerMap.delete(pointerId);
|
|
1576
|
+
}
|
|
1577
|
+
internal.pointerDirty.delete(pointerId);
|
|
971
1578
|
}
|
|
972
1579
|
};
|
|
973
1580
|
}
|
|
@@ -1029,331 +1636,26 @@ function notifyAlpha({ message, link }) {
|
|
|
1029
1636
|
}
|
|
1030
1637
|
}
|
|
1031
1638
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
let performanceTimeout = void 0;
|
|
1055
|
-
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
1056
|
-
const pointer = new Vector2();
|
|
1057
|
-
const rootState = {
|
|
1058
|
-
set,
|
|
1059
|
-
get,
|
|
1060
|
-
// Mock objects that have to be configured
|
|
1061
|
-
gl: null,
|
|
1062
|
-
renderer: null,
|
|
1063
|
-
camera: null,
|
|
1064
|
-
frustum: new Frustum(),
|
|
1065
|
-
autoUpdateFrustum: true,
|
|
1066
|
-
raycaster: null,
|
|
1067
|
-
events: { priority: 1, enabled: true, connected: false },
|
|
1068
|
-
scene: null,
|
|
1069
|
-
rootScene: null,
|
|
1070
|
-
xr: null,
|
|
1071
|
-
inspector: null,
|
|
1072
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
1073
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
1074
|
-
legacy: false,
|
|
1075
|
-
linear: false,
|
|
1076
|
-
flat: false,
|
|
1077
|
-
textureColorSpace: "srgb",
|
|
1078
|
-
isLegacy: false,
|
|
1079
|
-
webGPUSupported: false,
|
|
1080
|
-
isNative: false,
|
|
1081
|
-
controls: null,
|
|
1082
|
-
pointer,
|
|
1083
|
-
mouse: pointer,
|
|
1084
|
-
frameloop: "always",
|
|
1085
|
-
onPointerMissed: void 0,
|
|
1086
|
-
onDragOverMissed: void 0,
|
|
1087
|
-
onDropMissed: void 0,
|
|
1088
|
-
performance: {
|
|
1089
|
-
current: 1,
|
|
1090
|
-
min: 0.5,
|
|
1091
|
-
max: 1,
|
|
1092
|
-
debounce: 200,
|
|
1093
|
-
regress: () => {
|
|
1094
|
-
const state2 = get();
|
|
1095
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
1096
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
1097
|
-
performanceTimeout = setTimeout(
|
|
1098
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
1099
|
-
state2.performance.debounce
|
|
1100
|
-
);
|
|
1101
|
-
}
|
|
1102
|
-
},
|
|
1103
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1104
|
-
viewport: {
|
|
1105
|
-
initialDpr: 0,
|
|
1106
|
-
dpr: 0,
|
|
1107
|
-
width: 0,
|
|
1108
|
-
height: 0,
|
|
1109
|
-
top: 0,
|
|
1110
|
-
left: 0,
|
|
1111
|
-
aspect: 0,
|
|
1112
|
-
distance: 0,
|
|
1113
|
-
factor: 0,
|
|
1114
|
-
getCurrentViewport
|
|
1115
|
-
},
|
|
1116
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
1117
|
-
setSize: (width, height, top, left) => {
|
|
1118
|
-
const state2 = get();
|
|
1119
|
-
if (width === void 0) {
|
|
1120
|
-
set({ _sizeImperative: false });
|
|
1121
|
-
if (state2._sizeProps) {
|
|
1122
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
1123
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
1124
|
-
const currentSize = state2.size;
|
|
1125
|
-
const newSize = {
|
|
1126
|
-
width: propW ?? currentSize.width,
|
|
1127
|
-
height: propH ?? currentSize.height,
|
|
1128
|
-
top: currentSize.top,
|
|
1129
|
-
left: currentSize.left
|
|
1130
|
-
};
|
|
1131
|
-
set((s) => ({
|
|
1132
|
-
size: newSize,
|
|
1133
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
1134
|
-
}));
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return;
|
|
1138
|
-
}
|
|
1139
|
-
const w = width;
|
|
1140
|
-
const h = height ?? width;
|
|
1141
|
-
const t = top ?? state2.size.top;
|
|
1142
|
-
const l = left ?? state2.size.left;
|
|
1143
|
-
const size = { width: w, height: h, top: t, left: l };
|
|
1144
|
-
set((s) => ({
|
|
1145
|
-
size,
|
|
1146
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
1147
|
-
_sizeImperative: true
|
|
1148
|
-
}));
|
|
1149
|
-
},
|
|
1150
|
-
setDpr: (dpr) => set((state2) => {
|
|
1151
|
-
const resolved = calculateDpr(dpr);
|
|
1152
|
-
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
1153
|
-
}),
|
|
1154
|
-
setFrameloop: (frameloop = "always") => {
|
|
1155
|
-
set(() => ({ frameloop }));
|
|
1156
|
-
},
|
|
1157
|
-
setError: (error) => set(() => ({ error })),
|
|
1158
|
-
error: null,
|
|
1159
|
-
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
1160
|
-
uniforms: {},
|
|
1161
|
-
nodes: {},
|
|
1162
|
-
textures: /* @__PURE__ */ new Map(),
|
|
1163
|
-
postProcessing: null,
|
|
1164
|
-
passes: {},
|
|
1165
|
-
_hmrVersion: 0,
|
|
1166
|
-
_sizeImperative: false,
|
|
1167
|
-
_sizeProps: null,
|
|
1168
|
-
previousRoot: void 0,
|
|
1169
|
-
internal: {
|
|
1170
|
-
// Events
|
|
1171
|
-
interaction: [],
|
|
1172
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
1173
|
-
subscribers: [],
|
|
1174
|
-
initialClick: [0, 0],
|
|
1175
|
-
initialHits: [],
|
|
1176
|
-
capturedMap: /* @__PURE__ */ new Map(),
|
|
1177
|
-
lastEvent: React.createRef(),
|
|
1178
|
-
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1179
|
-
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1180
|
-
// Occlusion system (WebGPU only)
|
|
1181
|
-
occlusionEnabled: false,
|
|
1182
|
-
occlusionObserver: null,
|
|
1183
|
-
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1184
|
-
helperGroup: null,
|
|
1185
|
-
// Updates
|
|
1186
|
-
active: false,
|
|
1187
|
-
frames: 0,
|
|
1188
|
-
priority: 0,
|
|
1189
|
-
subscribe: (ref, priority, store) => {
|
|
1190
|
-
const internal = get().internal;
|
|
1191
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1192
|
-
internal.subscribers.push({ ref, priority, store });
|
|
1193
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
1194
|
-
return () => {
|
|
1195
|
-
const internal2 = get().internal;
|
|
1196
|
-
if (internal2?.subscribers) {
|
|
1197
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
1198
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
1199
|
-
}
|
|
1200
|
-
};
|
|
1201
|
-
},
|
|
1202
|
-
// Renderer Storage (single source of truth)
|
|
1203
|
-
actualRenderer: null,
|
|
1204
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
1205
|
-
scheduler: null
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
return rootState;
|
|
1209
|
-
});
|
|
1210
|
-
const state = rootStore.getState();
|
|
1211
|
-
Object.defineProperty(state, "gl", {
|
|
1212
|
-
get() {
|
|
1213
|
-
const currentState = rootStore.getState();
|
|
1214
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
1215
|
-
const stack = new Error().stack || "";
|
|
1216
|
-
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
1217
|
-
if (!isInternalAccess) {
|
|
1218
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1219
|
-
notifyDepreciated({
|
|
1220
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1221
|
-
body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
|
|
1222
|
-
});
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
return currentState.internal.actualRenderer;
|
|
1226
|
-
},
|
|
1227
|
-
set(value) {
|
|
1228
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1229
|
-
},
|
|
1230
|
-
enumerable: true,
|
|
1231
|
-
configurable: true
|
|
1232
|
-
});
|
|
1233
|
-
Object.defineProperty(state, "renderer", {
|
|
1234
|
-
get() {
|
|
1235
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1236
|
-
},
|
|
1237
|
-
set(value) {
|
|
1238
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1239
|
-
},
|
|
1240
|
-
enumerable: true,
|
|
1241
|
-
configurable: true
|
|
1242
|
-
});
|
|
1243
|
-
let oldScene = state.scene;
|
|
1244
|
-
rootStore.subscribe(() => {
|
|
1245
|
-
const currentState = rootStore.getState();
|
|
1246
|
-
const { scene, rootScene, set } = currentState;
|
|
1247
|
-
if (scene !== oldScene) {
|
|
1248
|
-
oldScene = scene;
|
|
1249
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1250
|
-
set({ rootScene: scene });
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
let oldSize = state.size;
|
|
1255
|
-
let oldDpr = state.viewport.dpr;
|
|
1256
|
-
let oldCamera = state.camera;
|
|
1257
|
-
rootStore.subscribe(() => {
|
|
1258
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1259
|
-
const actualRenderer = internal.actualRenderer;
|
|
1260
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1261
|
-
oldSize = size;
|
|
1262
|
-
oldDpr = viewport.dpr;
|
|
1263
|
-
updateCamera(camera, size);
|
|
1264
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1265
|
-
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
1266
|
-
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
1267
|
-
}
|
|
1268
|
-
if (camera !== oldCamera) {
|
|
1269
|
-
oldCamera = camera;
|
|
1270
|
-
const { rootScene } = rootStore.getState();
|
|
1271
|
-
if (camera && rootScene && !camera.parent) {
|
|
1272
|
-
rootScene.add(camera);
|
|
1273
|
-
}
|
|
1274
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1275
|
-
const currentState = rootStore.getState();
|
|
1276
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1277
|
-
updateFrustum(camera, currentState.frustum);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1282
|
-
return rootStore;
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
1286
|
-
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
1287
|
-
function getLoader(Proto) {
|
|
1288
|
-
if (isConstructor$1(Proto)) {
|
|
1289
|
-
let loader = memoizedLoaders.get(Proto);
|
|
1290
|
-
if (!loader) {
|
|
1291
|
-
loader = new Proto();
|
|
1292
|
-
memoizedLoaders.set(Proto, loader);
|
|
1293
|
-
}
|
|
1294
|
-
return loader;
|
|
1295
|
-
}
|
|
1296
|
-
return Proto;
|
|
1297
|
-
}
|
|
1298
|
-
function loadingFn(extensions, onProgress) {
|
|
1299
|
-
return function(Proto, input) {
|
|
1300
|
-
const loader = getLoader(Proto);
|
|
1301
|
-
if (extensions) extensions(loader);
|
|
1302
|
-
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
1303
|
-
return loader.loadAsync(input, onProgress).then((data) => {
|
|
1304
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1305
|
-
return data;
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
return new Promise(
|
|
1309
|
-
(res, reject) => loader.load(
|
|
1310
|
-
input,
|
|
1311
|
-
(data) => {
|
|
1312
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1313
|
-
res(data);
|
|
1314
|
-
},
|
|
1315
|
-
onProgress,
|
|
1316
|
-
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
1317
|
-
)
|
|
1318
|
-
);
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
function useLoader(loader, input, extensions, onProgress) {
|
|
1322
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1323
|
-
const fn = loadingFn(extensions, onProgress);
|
|
1324
|
-
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
1325
|
-
return Array.isArray(input) ? results : results[0];
|
|
1326
|
-
}
|
|
1327
|
-
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
1328
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1329
|
-
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
1330
|
-
};
|
|
1331
|
-
useLoader.clear = function(loader, input) {
|
|
1332
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1333
|
-
keys.forEach((key) => clear([loader, key]));
|
|
1334
|
-
};
|
|
1335
|
-
useLoader.loader = getLoader;
|
|
1336
|
-
|
|
1337
|
-
var __defProp$1 = Object.defineProperty;
|
|
1338
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1339
|
-
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1340
|
-
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1341
|
-
class PhaseGraph {
|
|
1342
|
-
constructor() {
|
|
1343
|
-
/** Ordered list of phase nodes */
|
|
1344
|
-
__publicField$1(this, "phases", []);
|
|
1345
|
-
/** Quick lookup by name */
|
|
1346
|
-
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1347
|
-
/** Cached ordered names (invalidated on changes) */
|
|
1348
|
-
__publicField$1(this, "orderedNamesCache", null);
|
|
1349
|
-
this.initializeDefaultPhases();
|
|
1350
|
-
}
|
|
1351
|
-
//* Initialization --------------------------------
|
|
1352
|
-
initializeDefaultPhases() {
|
|
1353
|
-
for (const name of DEFAULT_PHASES) {
|
|
1354
|
-
const node = { name, isAutoGenerated: false };
|
|
1355
|
-
this.phases.push(node);
|
|
1356
|
-
this.phaseMap.set(name, node);
|
|
1639
|
+
var __defProp$1 = Object.defineProperty;
|
|
1640
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1641
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1642
|
+
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1643
|
+
class PhaseGraph {
|
|
1644
|
+
constructor() {
|
|
1645
|
+
/** Ordered list of phase nodes */
|
|
1646
|
+
__publicField$1(this, "phases", []);
|
|
1647
|
+
/** Quick lookup by name */
|
|
1648
|
+
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1649
|
+
/** Cached ordered names (invalidated on changes) */
|
|
1650
|
+
__publicField$1(this, "orderedNamesCache", null);
|
|
1651
|
+
this.initializeDefaultPhases();
|
|
1652
|
+
}
|
|
1653
|
+
//* Initialization --------------------------------
|
|
1654
|
+
initializeDefaultPhases() {
|
|
1655
|
+
for (const name of DEFAULT_PHASES) {
|
|
1656
|
+
const node = { name, isAutoGenerated: false };
|
|
1657
|
+
this.phases.push(node);
|
|
1658
|
+
this.phaseMap.set(name, node);
|
|
1357
1659
|
}
|
|
1358
1660
|
this.invalidateCache();
|
|
1359
1661
|
}
|
|
@@ -1586,7 +1888,7 @@ function shouldRun(job, now) {
|
|
|
1586
1888
|
const minInterval = 1e3 / job.fps;
|
|
1587
1889
|
const lastRun = job.lastRun ?? 0;
|
|
1588
1890
|
const elapsed = now - lastRun;
|
|
1589
|
-
if (elapsed < minInterval) return false;
|
|
1891
|
+
if (elapsed < minInterval - 1) return false;
|
|
1590
1892
|
if (job.drop) {
|
|
1591
1893
|
job.lastRun = now;
|
|
1592
1894
|
} else {
|
|
@@ -2255,116 +2557,444 @@ const _Scheduler = class _Scheduler {
|
|
|
2255
2557
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2256
2558
|
root.needsRebuild = false;
|
|
2257
2559
|
}
|
|
2258
|
-
const providedState = root.getState?.() ?? {};
|
|
2259
|
-
const frameState = {
|
|
2260
|
-
...providedState,
|
|
2261
|
-
time: timestamp,
|
|
2262
|
-
delta,
|
|
2263
|
-
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2264
|
-
// Convert ms to seconds
|
|
2265
|
-
frame: this.loopState.frameCount
|
|
2266
|
-
};
|
|
2267
|
-
for (const job of root.sortedJobs) {
|
|
2268
|
-
if (!shouldRun(job, timestamp)) continue;
|
|
2269
|
-
try {
|
|
2270
|
-
job.callback(frameState, delta);
|
|
2271
|
-
} catch (error) {
|
|
2272
|
-
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2273
|
-
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2560
|
+
const providedState = root.getState?.() ?? {};
|
|
2561
|
+
const frameState = {
|
|
2562
|
+
...providedState,
|
|
2563
|
+
time: timestamp,
|
|
2564
|
+
delta,
|
|
2565
|
+
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2566
|
+
// Convert ms to seconds
|
|
2567
|
+
frame: this.loopState.frameCount
|
|
2568
|
+
};
|
|
2569
|
+
for (const job of root.sortedJobs) {
|
|
2570
|
+
if (!shouldRun(job, timestamp)) continue;
|
|
2571
|
+
try {
|
|
2572
|
+
job.callback(frameState, delta);
|
|
2573
|
+
} catch (error) {
|
|
2574
|
+
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2575
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
//* Debug & Inspection Methods ================================
|
|
2580
|
+
/**
|
|
2581
|
+
* Get the total number of registered jobs across all roots.
|
|
2582
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2583
|
+
* @returns {number} Total job count
|
|
2584
|
+
*/
|
|
2585
|
+
getJobCount() {
|
|
2586
|
+
let count = 0;
|
|
2587
|
+
for (const root of this.roots.values()) {
|
|
2588
|
+
count += root.jobs.size;
|
|
2589
|
+
}
|
|
2590
|
+
return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Get all registered job IDs across all roots.
|
|
2594
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2595
|
+
* @returns {string[]} Array of all job IDs
|
|
2596
|
+
*/
|
|
2597
|
+
getJobIds() {
|
|
2598
|
+
const ids = [];
|
|
2599
|
+
for (const root of this.roots.values()) {
|
|
2600
|
+
ids.push(...root.jobs.keys());
|
|
2601
|
+
}
|
|
2602
|
+
ids.push(...this.globalBeforeJobs.keys());
|
|
2603
|
+
ids.push(...this.globalAfterJobs.keys());
|
|
2604
|
+
return ids;
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Get the number of registered roots (Canvas instances).
|
|
2608
|
+
* @returns {number} Number of registered roots
|
|
2609
|
+
*/
|
|
2610
|
+
getRootCount() {
|
|
2611
|
+
return this.roots.size;
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2615
|
+
* Used by the default render job to know if a user has taken over rendering.
|
|
2616
|
+
*
|
|
2617
|
+
* @param phase The phase to check
|
|
2618
|
+
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2619
|
+
* @returns true if any user jobs exist in the phase
|
|
2620
|
+
*/
|
|
2621
|
+
hasUserJobsInPhase(phase, rootId) {
|
|
2622
|
+
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2623
|
+
return rootsToCheck.some((root) => {
|
|
2624
|
+
if (!root) return false;
|
|
2625
|
+
for (const job of root.jobs.values()) {
|
|
2626
|
+
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2627
|
+
}
|
|
2628
|
+
return false;
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
//* Utility Methods ================================
|
|
2632
|
+
/**
|
|
2633
|
+
* Generate a unique root ID for automatic root registration.
|
|
2634
|
+
* @returns {string} A unique root ID in the format 'root_N'
|
|
2635
|
+
*/
|
|
2636
|
+
generateRootId() {
|
|
2637
|
+
return `root_${this.nextRootIndex++}`;
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Generate a unique job ID.
|
|
2641
|
+
* @returns {string} A unique job ID in the format 'job_N'
|
|
2642
|
+
* @private
|
|
2643
|
+
*/
|
|
2644
|
+
generateJobId() {
|
|
2645
|
+
return `job_${this.nextJobIndex}`;
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Normalize before/after constraints to a Set.
|
|
2649
|
+
* Handles undefined, single string, or array inputs.
|
|
2650
|
+
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2651
|
+
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2652
|
+
* @private
|
|
2653
|
+
*/
|
|
2654
|
+
normalizeConstraints(value) {
|
|
2655
|
+
if (!value) return /* @__PURE__ */ new Set();
|
|
2656
|
+
if (Array.isArray(value)) return new Set(value);
|
|
2657
|
+
return /* @__PURE__ */ new Set([value]);
|
|
2658
|
+
}
|
|
2659
|
+
};
|
|
2660
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2661
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2662
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2663
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2664
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2665
|
+
let Scheduler = _Scheduler;
|
|
2666
|
+
const getScheduler = () => Scheduler.get();
|
|
2667
|
+
if (hmrData) {
|
|
2668
|
+
hmrData.accept?.();
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
2672
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
|
|
2673
|
+
const createStore = (invalidate, advance) => {
|
|
2674
|
+
const rootStore = createWithEqualityFn((set, get) => {
|
|
2675
|
+
const position = new Vector3();
|
|
2676
|
+
const defaultTarget = new Vector3();
|
|
2677
|
+
const tempTarget = new Vector3();
|
|
2678
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
2679
|
+
const { width, height, top, left } = size;
|
|
2680
|
+
const aspect = width / height;
|
|
2681
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
2682
|
+
else tempTarget.set(...target);
|
|
2683
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2684
|
+
if (isOrthographicCamera(camera)) {
|
|
2685
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
2686
|
+
} else {
|
|
2687
|
+
const fov = camera.fov * Math.PI / 180;
|
|
2688
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
2689
|
+
const w = h * (width / height);
|
|
2690
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
let performanceTimeout = void 0;
|
|
2694
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
2695
|
+
const pointer = new Vector2();
|
|
2696
|
+
const rootState = {
|
|
2697
|
+
set,
|
|
2698
|
+
get,
|
|
2699
|
+
// Mock objects that have to be configured
|
|
2700
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
2701
|
+
primaryStore: null,
|
|
2702
|
+
gl: null,
|
|
2703
|
+
renderer: null,
|
|
2704
|
+
camera: null,
|
|
2705
|
+
frustum: new Frustum(),
|
|
2706
|
+
autoUpdateFrustum: true,
|
|
2707
|
+
raycaster: null,
|
|
2708
|
+
events: {
|
|
2709
|
+
priority: 1,
|
|
2710
|
+
enabled: true,
|
|
2711
|
+
connected: false,
|
|
2712
|
+
frameTimedRaycasts: true,
|
|
2713
|
+
alwaysFireOnScroll: true,
|
|
2714
|
+
updateOnFrame: false
|
|
2715
|
+
},
|
|
2716
|
+
scene: null,
|
|
2717
|
+
rootScene: null,
|
|
2718
|
+
xr: null,
|
|
2719
|
+
inspector: null,
|
|
2720
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
2721
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
2722
|
+
textureColorSpace: SRGBColorSpace,
|
|
2723
|
+
isLegacy: false,
|
|
2724
|
+
webGPUSupported: false,
|
|
2725
|
+
isNative: false,
|
|
2726
|
+
controls: null,
|
|
2727
|
+
pointer,
|
|
2728
|
+
mouse: pointer,
|
|
2729
|
+
frameloop: "always",
|
|
2730
|
+
onPointerMissed: void 0,
|
|
2731
|
+
onDragOverMissed: void 0,
|
|
2732
|
+
onDropMissed: void 0,
|
|
2733
|
+
performance: {
|
|
2734
|
+
current: 1,
|
|
2735
|
+
min: 0.5,
|
|
2736
|
+
max: 1,
|
|
2737
|
+
debounce: 200,
|
|
2738
|
+
regress: () => {
|
|
2739
|
+
const state2 = get();
|
|
2740
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
2741
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
2742
|
+
performanceTimeout = setTimeout(
|
|
2743
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
2744
|
+
state2.performance.debounce
|
|
2745
|
+
);
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2748
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2749
|
+
viewport: {
|
|
2750
|
+
initialDpr: 0,
|
|
2751
|
+
dpr: 0,
|
|
2752
|
+
width: 0,
|
|
2753
|
+
height: 0,
|
|
2754
|
+
top: 0,
|
|
2755
|
+
left: 0,
|
|
2756
|
+
aspect: 0,
|
|
2757
|
+
distance: 0,
|
|
2758
|
+
factor: 0,
|
|
2759
|
+
getCurrentViewport
|
|
2760
|
+
},
|
|
2761
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
2762
|
+
setSize: (width, height, top, left) => {
|
|
2763
|
+
const state2 = get();
|
|
2764
|
+
if (width === void 0) {
|
|
2765
|
+
set({ _sizeImperative: false });
|
|
2766
|
+
if (state2._sizeProps) {
|
|
2767
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
2768
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
2769
|
+
const currentSize = state2.size;
|
|
2770
|
+
const newSize = {
|
|
2771
|
+
width: propW ?? currentSize.width,
|
|
2772
|
+
height: propH ?? currentSize.height,
|
|
2773
|
+
top: currentSize.top,
|
|
2774
|
+
left: currentSize.left
|
|
2775
|
+
};
|
|
2776
|
+
set((s) => ({
|
|
2777
|
+
size: newSize,
|
|
2778
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
2779
|
+
}));
|
|
2780
|
+
getScheduler().invalidate();
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
const w = width;
|
|
2786
|
+
const h = height ?? width;
|
|
2787
|
+
const t = top ?? state2.size.top;
|
|
2788
|
+
const l = left ?? state2.size.left;
|
|
2789
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
2790
|
+
set((s) => ({
|
|
2791
|
+
size,
|
|
2792
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
2793
|
+
_sizeImperative: true
|
|
2794
|
+
}));
|
|
2795
|
+
getScheduler().invalidate();
|
|
2796
|
+
},
|
|
2797
|
+
setDpr: (dpr) => set((state2) => {
|
|
2798
|
+
const resolved = calculateDpr(dpr);
|
|
2799
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
2800
|
+
}),
|
|
2801
|
+
setFrameloop: (frameloop = "always") => {
|
|
2802
|
+
set(() => ({ frameloop }));
|
|
2803
|
+
},
|
|
2804
|
+
setError: (error) => set(() => ({ error })),
|
|
2805
|
+
error: null,
|
|
2806
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
|
|
2807
|
+
uniforms: {},
|
|
2808
|
+
nodes: {},
|
|
2809
|
+
buffers: {},
|
|
2810
|
+
gpuStorage: {},
|
|
2811
|
+
textures: /* @__PURE__ */ new Map(),
|
|
2812
|
+
renderPipeline: null,
|
|
2813
|
+
passes: {},
|
|
2814
|
+
_hmrVersion: 0,
|
|
2815
|
+
_sizeImperative: false,
|
|
2816
|
+
_sizeProps: null,
|
|
2817
|
+
previousRoot: void 0,
|
|
2818
|
+
internal: {
|
|
2819
|
+
// Events
|
|
2820
|
+
interaction: [],
|
|
2821
|
+
subscribers: [],
|
|
2822
|
+
// Per-pointer state (new unified structure)
|
|
2823
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
2824
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
2825
|
+
lastEvent: React.createRef(),
|
|
2826
|
+
// Deprecated but kept for backwards compatibility
|
|
2827
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2828
|
+
initialClick: [0, 0],
|
|
2829
|
+
initialHits: [],
|
|
2830
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
2831
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2832
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2833
|
+
// Occlusion system (WebGPU only)
|
|
2834
|
+
occlusionEnabled: false,
|
|
2835
|
+
occlusionObserver: null,
|
|
2836
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
2837
|
+
helperGroup: null,
|
|
2838
|
+
// Updates
|
|
2839
|
+
active: false,
|
|
2840
|
+
frames: 0,
|
|
2841
|
+
priority: 0,
|
|
2842
|
+
subscribe: (ref, priority, store) => {
|
|
2843
|
+
const internal = get().internal;
|
|
2844
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2845
|
+
internal.subscribers.push({ ref, priority, store });
|
|
2846
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
2847
|
+
return () => {
|
|
2848
|
+
const internal2 = get().internal;
|
|
2849
|
+
if (internal2?.subscribers) {
|
|
2850
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
2851
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
},
|
|
2855
|
+
// Renderer Storage (single source of truth)
|
|
2856
|
+
actualRenderer: null,
|
|
2857
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
2858
|
+
scheduler: null
|
|
2859
|
+
}
|
|
2860
|
+
};
|
|
2861
|
+
return rootState;
|
|
2862
|
+
});
|
|
2863
|
+
const state = rootStore.getState();
|
|
2864
|
+
Object.defineProperty(state, "gl", {
|
|
2865
|
+
get() {
|
|
2866
|
+
const currentState = rootStore.getState();
|
|
2867
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
2868
|
+
const stack = new Error().stack || "";
|
|
2869
|
+
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
2870
|
+
if (!isInternalAccess) {
|
|
2871
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
2872
|
+
notifyDepreciated({
|
|
2873
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
2874
|
+
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
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return currentState.internal.actualRenderer;
|
|
2879
|
+
},
|
|
2880
|
+
set(value) {
|
|
2881
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2882
|
+
},
|
|
2883
|
+
enumerable: true,
|
|
2884
|
+
configurable: true
|
|
2885
|
+
});
|
|
2886
|
+
Object.defineProperty(state, "renderer", {
|
|
2887
|
+
get() {
|
|
2888
|
+
return rootStore.getState().internal.actualRenderer;
|
|
2889
|
+
},
|
|
2890
|
+
set(value) {
|
|
2891
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2892
|
+
},
|
|
2893
|
+
enumerable: true,
|
|
2894
|
+
configurable: true
|
|
2895
|
+
});
|
|
2896
|
+
let oldScene = state.scene;
|
|
2897
|
+
rootStore.subscribe(() => {
|
|
2898
|
+
const currentState = rootStore.getState();
|
|
2899
|
+
const { scene, rootScene, set } = currentState;
|
|
2900
|
+
if (scene !== oldScene) {
|
|
2901
|
+
oldScene = scene;
|
|
2902
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
2903
|
+
set({ rootScene: scene });
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
let oldSize = state.size;
|
|
2908
|
+
let oldDpr = state.viewport.dpr;
|
|
2909
|
+
let oldCamera = state.camera;
|
|
2910
|
+
rootStore.subscribe(() => {
|
|
2911
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
2912
|
+
const actualRenderer = internal.actualRenderer;
|
|
2913
|
+
const canvasTarget = internal.canvasTarget;
|
|
2914
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
2915
|
+
oldSize = size;
|
|
2916
|
+
oldDpr = viewport.dpr;
|
|
2917
|
+
updateCamera(camera, size);
|
|
2918
|
+
if (internal.isSecondary && canvasTarget) {
|
|
2919
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2920
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2921
|
+
} else {
|
|
2922
|
+
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2923
|
+
actualRenderer.setSize(size.width, size.height, false);
|
|
2924
|
+
if (canvasTarget) {
|
|
2925
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2926
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2927
|
+
}
|
|
2274
2928
|
}
|
|
2275
2929
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2930
|
+
if (camera !== oldCamera) {
|
|
2931
|
+
oldCamera = camera;
|
|
2932
|
+
const { rootScene } = rootStore.getState();
|
|
2933
|
+
if (camera && rootScene && !camera.parent) {
|
|
2934
|
+
rootScene.add(camera);
|
|
2935
|
+
}
|
|
2936
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
2937
|
+
const currentState = rootStore.getState();
|
|
2938
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
2939
|
+
updateFrustum(camera, currentState.frustum);
|
|
2940
|
+
}
|
|
2287
2941
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2942
|
+
});
|
|
2943
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
2944
|
+
return rootStore;
|
|
2945
|
+
};
|
|
2946
|
+
|
|
2947
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
2948
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
2949
|
+
function getLoader(Proto) {
|
|
2950
|
+
if (isConstructor$1(Proto)) {
|
|
2951
|
+
let loader = memoizedLoaders.get(Proto);
|
|
2952
|
+
if (!loader) {
|
|
2953
|
+
loader = new Proto();
|
|
2954
|
+
memoizedLoaders.set(Proto, loader);
|
|
2299
2955
|
}
|
|
2300
|
-
|
|
2301
|
-
ids.push(...this.globalAfterJobs.keys());
|
|
2302
|
-
return ids;
|
|
2303
|
-
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Get the number of registered roots (Canvas instances).
|
|
2306
|
-
* @returns {number} Number of registered roots
|
|
2307
|
-
*/
|
|
2308
|
-
getRootCount() {
|
|
2309
|
-
return this.roots.size;
|
|
2310
|
-
}
|
|
2311
|
-
/**
|
|
2312
|
-
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2313
|
-
* Used by the default render job to know if a user has taken over rendering.
|
|
2314
|
-
*
|
|
2315
|
-
* @param phase The phase to check
|
|
2316
|
-
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2317
|
-
* @returns true if any user jobs exist in the phase
|
|
2318
|
-
*/
|
|
2319
|
-
hasUserJobsInPhase(phase, rootId) {
|
|
2320
|
-
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2321
|
-
return rootsToCheck.some((root) => {
|
|
2322
|
-
if (!root) return false;
|
|
2323
|
-
for (const job of root.jobs.values()) {
|
|
2324
|
-
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2325
|
-
}
|
|
2326
|
-
return false;
|
|
2327
|
-
});
|
|
2328
|
-
}
|
|
2329
|
-
//* Utility Methods ================================
|
|
2330
|
-
/**
|
|
2331
|
-
* Generate a unique root ID for automatic root registration.
|
|
2332
|
-
* @returns {string} A unique root ID in the format 'root_N'
|
|
2333
|
-
*/
|
|
2334
|
-
generateRootId() {
|
|
2335
|
-
return `root_${this.nextRootIndex++}`;
|
|
2336
|
-
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Generate a unique job ID.
|
|
2339
|
-
* @returns {string} A unique job ID in the format 'job_N'
|
|
2340
|
-
* @private
|
|
2341
|
-
*/
|
|
2342
|
-
generateJobId() {
|
|
2343
|
-
return `job_${this.nextJobIndex}`;
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Normalize before/after constraints to a Set.
|
|
2347
|
-
* Handles undefined, single string, or array inputs.
|
|
2348
|
-
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2349
|
-
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2350
|
-
* @private
|
|
2351
|
-
*/
|
|
2352
|
-
normalizeConstraints(value) {
|
|
2353
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
2354
|
-
if (Array.isArray(value)) return new Set(value);
|
|
2355
|
-
return /* @__PURE__ */ new Set([value]);
|
|
2956
|
+
return loader;
|
|
2356
2957
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
if (
|
|
2366
|
-
|
|
2958
|
+
return Proto;
|
|
2959
|
+
}
|
|
2960
|
+
function loadingFn(extensions, onProgress) {
|
|
2961
|
+
return function(Proto, input) {
|
|
2962
|
+
const loader = getLoader(Proto);
|
|
2963
|
+
if (extensions) extensions(loader);
|
|
2964
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
2965
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
2966
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2967
|
+
return data;
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
return new Promise(
|
|
2971
|
+
(res, reject) => loader.load(
|
|
2972
|
+
input,
|
|
2973
|
+
(data) => {
|
|
2974
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2975
|
+
res(data);
|
|
2976
|
+
},
|
|
2977
|
+
onProgress,
|
|
2978
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
2979
|
+
)
|
|
2980
|
+
);
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
2984
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2985
|
+
const fn = loadingFn(extensions, onProgress);
|
|
2986
|
+
const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
|
|
2987
|
+
return Array.isArray(input) ? results : results[0];
|
|
2367
2988
|
}
|
|
2989
|
+
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
2990
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2991
|
+
keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
2992
|
+
};
|
|
2993
|
+
useLoader.clear = function(loader, input) {
|
|
2994
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2995
|
+
keys.forEach((key) => clear([loader, key]));
|
|
2996
|
+
};
|
|
2997
|
+
useLoader.loader = getLoader;
|
|
2368
2998
|
|
|
2369
2999
|
function useFrame(callback, priorityOrOptions) {
|
|
2370
3000
|
const store = React.useContext(context);
|
|
@@ -2545,6 +3175,9 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2545
3175
|
const textureCache = useThree((state) => state.textures);
|
|
2546
3176
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2547
3177
|
const { onLoad, cache = false } = options;
|
|
3178
|
+
const onLoadRef = useRef(onLoad);
|
|
3179
|
+
onLoadRef.current = onLoad;
|
|
3180
|
+
const onLoadCalledForRef = useRef(null);
|
|
2548
3181
|
const urls = useMemo(() => getUrls(input), [input]);
|
|
2549
3182
|
const cachedResult = useMemo(() => {
|
|
2550
3183
|
if (!cache) return null;
|
|
@@ -2555,9 +3188,13 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2555
3188
|
TextureLoader,
|
|
2556
3189
|
IsObject(input) ? Object.values(input) : input
|
|
2557
3190
|
);
|
|
3191
|
+
const inputKey = urls.join("\0");
|
|
2558
3192
|
useLayoutEffect(() => {
|
|
2559
|
-
if (
|
|
2560
|
-
|
|
3193
|
+
if (cachedResult) return;
|
|
3194
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
3195
|
+
onLoadCalledForRef.current = inputKey;
|
|
3196
|
+
onLoadRef.current?.(loadedTextures);
|
|
3197
|
+
}, [cachedResult, loadedTextures, inputKey]);
|
|
2561
3198
|
useEffect(() => {
|
|
2562
3199
|
if (cachedResult) return;
|
|
2563
3200
|
if ("initTexture" in renderer) {
|
|
@@ -2724,14 +3361,31 @@ function useTextures() {
|
|
|
2724
3361
|
}, [store]);
|
|
2725
3362
|
}
|
|
2726
3363
|
|
|
2727
|
-
function useRenderTarget(
|
|
3364
|
+
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
2728
3365
|
const isLegacy = useThree((s) => s.isLegacy);
|
|
2729
3366
|
const size = useThree((s) => s.size);
|
|
3367
|
+
let width;
|
|
3368
|
+
let height;
|
|
3369
|
+
let opts;
|
|
3370
|
+
if (typeof widthOrOptions === "object") {
|
|
3371
|
+
opts = widthOrOptions;
|
|
3372
|
+
} else if (typeof widthOrOptions === "number") {
|
|
3373
|
+
width = widthOrOptions;
|
|
3374
|
+
if (typeof heightOrOptions === "object") {
|
|
3375
|
+
height = widthOrOptions;
|
|
3376
|
+
opts = heightOrOptions;
|
|
3377
|
+
} else if (typeof heightOrOptions === "number") {
|
|
3378
|
+
height = heightOrOptions;
|
|
3379
|
+
opts = options;
|
|
3380
|
+
} else {
|
|
3381
|
+
height = widthOrOptions;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
2730
3384
|
return useMemo(() => {
|
|
2731
3385
|
const w = width ?? size.width;
|
|
2732
3386
|
const h = height ?? size.height;
|
|
2733
|
-
return new WebGLRenderTarget(w, h,
|
|
2734
|
-
}, [width, height, size.width, size.height,
|
|
3387
|
+
return new WebGLRenderTarget(w, h, opts);
|
|
3388
|
+
}, [width, height, size.width, size.height, opts, isLegacy]);
|
|
2735
3389
|
}
|
|
2736
3390
|
|
|
2737
3391
|
function useStore() {
|
|
@@ -2781,7 +3435,7 @@ function addTail(callback) {
|
|
|
2781
3435
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
2782
3436
|
getScheduler().invalidate(frames, stackFrames);
|
|
2783
3437
|
}
|
|
2784
|
-
function advance(timestamp
|
|
3438
|
+
function advance(timestamp) {
|
|
2785
3439
|
getScheduler().step(timestamp);
|
|
2786
3440
|
}
|
|
2787
3441
|
|
|
@@ -14235,6 +14889,7 @@ function swapInstances() {
|
|
|
14235
14889
|
instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
|
|
14236
14890
|
instance.object.__r3f = instance;
|
|
14237
14891
|
setFiberRef(fiber, instance.object);
|
|
14892
|
+
delete instance.appliedOnce;
|
|
14238
14893
|
applyProps(instance.object, instance.props);
|
|
14239
14894
|
if (instance.props.attach) {
|
|
14240
14895
|
attach(parent, instance);
|
|
@@ -14308,8 +14963,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
14308
14963
|
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
14309
14964
|
if (isTailSibling) swapInstances();
|
|
14310
14965
|
},
|
|
14311
|
-
finalizeInitialChildren: () =>
|
|
14312
|
-
|
|
14966
|
+
finalizeInitialChildren: (instance) => {
|
|
14967
|
+
for (const prop in instance.props) {
|
|
14968
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
14969
|
+
}
|
|
14970
|
+
return false;
|
|
14971
|
+
},
|
|
14972
|
+
commitMount(instance) {
|
|
14973
|
+
const resolved = {};
|
|
14974
|
+
for (const prop in instance.props) {
|
|
14975
|
+
const value = instance.props[prop];
|
|
14976
|
+
if (isFromRef(value)) {
|
|
14977
|
+
const ref = value[FROM_REF];
|
|
14978
|
+
if (ref.current != null) resolved[prop] = ref.current;
|
|
14979
|
+
}
|
|
14980
|
+
}
|
|
14981
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
14313
14982
|
},
|
|
14314
14983
|
getPublicInstance: (instance) => instance?.object,
|
|
14315
14984
|
prepareForCommit: () => null,
|
|
@@ -14530,6 +15199,9 @@ function createRoot(canvas) {
|
|
|
14530
15199
|
let resolve;
|
|
14531
15200
|
pending = new Promise((_resolve) => resolve = _resolve);
|
|
14532
15201
|
const {
|
|
15202
|
+
id: canvasId,
|
|
15203
|
+
primaryCanvas,
|
|
15204
|
+
scheduler: schedulerConfig,
|
|
14533
15205
|
gl: glConfig,
|
|
14534
15206
|
renderer: rendererConfig,
|
|
14535
15207
|
size: propsSize,
|
|
@@ -14537,10 +15209,6 @@ function createRoot(canvas) {
|
|
|
14537
15209
|
events,
|
|
14538
15210
|
onCreated: onCreatedCallback,
|
|
14539
15211
|
shadows = false,
|
|
14540
|
-
linear = false,
|
|
14541
|
-
flat = false,
|
|
14542
|
-
textureColorSpace = SRGBColorSpace,
|
|
14543
|
-
legacy = false,
|
|
14544
15212
|
orthographic = false,
|
|
14545
15213
|
frameloop = "always",
|
|
14546
15214
|
dpr = [1, 2],
|
|
@@ -14552,8 +15220,10 @@ function createRoot(canvas) {
|
|
|
14552
15220
|
onDropMissed,
|
|
14553
15221
|
autoUpdateFrustum = true,
|
|
14554
15222
|
occlusion = false,
|
|
14555
|
-
_sizeProps
|
|
15223
|
+
_sizeProps,
|
|
15224
|
+
forceEven
|
|
14556
15225
|
} = props;
|
|
15226
|
+
const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
|
|
14557
15227
|
const state = store.getState();
|
|
14558
15228
|
const defaultGLProps = {
|
|
14559
15229
|
canvas,
|
|
@@ -14566,15 +15236,25 @@ function createRoot(canvas) {
|
|
|
14566
15236
|
"WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
|
|
14567
15237
|
);
|
|
14568
15238
|
}
|
|
14569
|
-
(state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
15239
|
+
const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
14570
15240
|
if (glConfig && rendererConfig) {
|
|
14571
15241
|
throw new Error("Cannot use both gl and renderer props at the same time");
|
|
14572
15242
|
}
|
|
14573
15243
|
let renderer = state.internal.actualRenderer;
|
|
15244
|
+
if (primaryCanvas && !R3F_BUILD_WEBGPU) {
|
|
15245
|
+
throw new Error(
|
|
15246
|
+
"The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
|
|
15247
|
+
);
|
|
15248
|
+
}
|
|
15249
|
+
if (primaryCanvas && wantsGL) {
|
|
15250
|
+
throw new Error(
|
|
15251
|
+
"The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
|
|
15252
|
+
);
|
|
15253
|
+
}
|
|
14574
15254
|
if (!state.internal.actualRenderer) {
|
|
14575
15255
|
renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
|
|
14576
15256
|
state.internal.actualRenderer = renderer;
|
|
14577
|
-
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
15257
|
+
state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
|
|
14578
15258
|
}
|
|
14579
15259
|
let raycaster = state.raycaster;
|
|
14580
15260
|
if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
|
|
@@ -14583,6 +15263,7 @@ function createRoot(canvas) {
|
|
|
14583
15263
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
14584
15264
|
applyProps(raycaster, { params: { ...raycaster.params, ...params } });
|
|
14585
15265
|
}
|
|
15266
|
+
let tempCamera = state.camera;
|
|
14586
15267
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
14587
15268
|
lastCamera = cameraOptions;
|
|
14588
15269
|
const isCamera = cameraOptions?.isCamera;
|
|
@@ -14602,6 +15283,7 @@ function createRoot(canvas) {
|
|
|
14602
15283
|
if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
|
|
14603
15284
|
}
|
|
14604
15285
|
state.set({ camera });
|
|
15286
|
+
tempCamera = camera;
|
|
14605
15287
|
raycaster.camera = camera;
|
|
14606
15288
|
}
|
|
14607
15289
|
if (!state.scene) {
|
|
@@ -14619,7 +15301,7 @@ function createRoot(canvas) {
|
|
|
14619
15301
|
rootScene: scene,
|
|
14620
15302
|
internal: { ...prev.internal, container: scene }
|
|
14621
15303
|
}));
|
|
14622
|
-
const camera =
|
|
15304
|
+
const camera = tempCamera;
|
|
14623
15305
|
if (camera && !camera.parent) scene.add(camera);
|
|
14624
15306
|
}
|
|
14625
15307
|
if (events && !state.events.handlers) {
|
|
@@ -14636,6 +15318,9 @@ function createRoot(canvas) {
|
|
|
14636
15318
|
if (_sizeProps !== void 0) {
|
|
14637
15319
|
state.set({ _sizeProps });
|
|
14638
15320
|
}
|
|
15321
|
+
if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
|
|
15322
|
+
state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
|
|
15323
|
+
}
|
|
14639
15324
|
const size = computeInitialSize(canvas, propsSize);
|
|
14640
15325
|
if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
|
|
14641
15326
|
const wasImperative = state._sizeImperative;
|
|
@@ -14662,10 +15347,10 @@ function createRoot(canvas) {
|
|
|
14662
15347
|
lastConfiguredProps.performance = performance;
|
|
14663
15348
|
}
|
|
14664
15349
|
if (!state.xr) {
|
|
14665
|
-
const handleXRFrame = (timestamp,
|
|
15350
|
+
const handleXRFrame = (timestamp, _frame) => {
|
|
14666
15351
|
const state2 = store.getState();
|
|
14667
15352
|
if (state2.frameloop === "never") return;
|
|
14668
|
-
advance(timestamp
|
|
15353
|
+
advance(timestamp);
|
|
14669
15354
|
};
|
|
14670
15355
|
const actualRenderer = state.internal.actualRenderer;
|
|
14671
15356
|
const handleSessionChange = () => {
|
|
@@ -14677,16 +15362,16 @@ function createRoot(canvas) {
|
|
|
14677
15362
|
};
|
|
14678
15363
|
const xr = {
|
|
14679
15364
|
connect() {
|
|
14680
|
-
const { gl, renderer: renderer2
|
|
14681
|
-
const
|
|
14682
|
-
|
|
14683
|
-
|
|
15365
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15366
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15367
|
+
xrManager.addEventListener("sessionstart", handleSessionChange);
|
|
15368
|
+
xrManager.addEventListener("sessionend", handleSessionChange);
|
|
14684
15369
|
},
|
|
14685
15370
|
disconnect() {
|
|
14686
|
-
const { gl, renderer: renderer2
|
|
14687
|
-
const
|
|
14688
|
-
|
|
14689
|
-
|
|
15371
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15372
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15373
|
+
xrManager.removeEventListener("sessionstart", handleSessionChange);
|
|
15374
|
+
xrManager.removeEventListener("sessionend", handleSessionChange);
|
|
14690
15375
|
}
|
|
14691
15376
|
};
|
|
14692
15377
|
if (typeof renderer.xr?.addEventListener === "function") xr.connect();
|
|
@@ -14698,15 +15383,22 @@ function createRoot(canvas) {
|
|
|
14698
15383
|
const oldType = renderer.shadowMap.type;
|
|
14699
15384
|
renderer.shadowMap.enabled = !!shadows;
|
|
14700
15385
|
if (is.boo(shadows)) {
|
|
14701
|
-
renderer.shadowMap.type =
|
|
15386
|
+
renderer.shadowMap.type = PCFShadowMap;
|
|
14702
15387
|
} else if (is.str(shadows)) {
|
|
15388
|
+
if (shadows === "soft") {
|
|
15389
|
+
notifyDepreciated({
|
|
15390
|
+
heading: 'shadows="soft" is deprecated',
|
|
15391
|
+
body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
|
|
15392
|
+
link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
|
|
15393
|
+
});
|
|
15394
|
+
}
|
|
14703
15395
|
const types = {
|
|
14704
15396
|
basic: BasicShadowMap,
|
|
14705
15397
|
percentage: PCFShadowMap,
|
|
14706
|
-
soft:
|
|
15398
|
+
soft: PCFShadowMap,
|
|
14707
15399
|
variance: VSMShadowMap
|
|
14708
15400
|
};
|
|
14709
|
-
renderer.shadowMap.type = types[shadows] ??
|
|
15401
|
+
renderer.shadowMap.type = types[shadows] ?? PCFShadowMap;
|
|
14710
15402
|
} else if (is.obj(shadows)) {
|
|
14711
15403
|
Object.assign(renderer.shadowMap, shadows);
|
|
14712
15404
|
}
|
|
@@ -14714,47 +15406,69 @@ function createRoot(canvas) {
|
|
|
14714
15406
|
renderer.shadowMap.needsUpdate = true;
|
|
14715
15407
|
}
|
|
14716
15408
|
}
|
|
14717
|
-
{
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
-
const flatChanged = flat !== lastConfiguredProps.flat;
|
|
14721
|
-
if (legacyChanged) {
|
|
14722
|
-
ColorManagement.enabled = !legacy;
|
|
14723
|
-
lastConfiguredProps.legacy = legacy;
|
|
14724
|
-
}
|
|
14725
|
-
if (!configured || linearChanged) {
|
|
14726
|
-
renderer.outputColorSpace = linear ? LinearSRGBColorSpace : SRGBColorSpace;
|
|
14727
|
-
lastConfiguredProps.linear = linear;
|
|
14728
|
-
}
|
|
14729
|
-
if (!configured || flatChanged) {
|
|
14730
|
-
renderer.toneMapping = flat ? NoToneMapping : ACESFilmicToneMapping;
|
|
14731
|
-
lastConfiguredProps.flat = flat;
|
|
14732
|
-
}
|
|
14733
|
-
if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
|
|
14734
|
-
if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
|
|
14735
|
-
if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
|
|
15409
|
+
if (!configured) {
|
|
15410
|
+
renderer.outputColorSpace = SRGBColorSpace;
|
|
15411
|
+
renderer.toneMapping = ACESFilmicToneMapping;
|
|
14736
15412
|
}
|
|
14737
15413
|
if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
|
|
14738
15414
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
14739
15415
|
lastConfiguredProps.textureColorSpace = textureColorSpace;
|
|
14740
15416
|
}
|
|
15417
|
+
const r3fProps = ["textureColorSpace"];
|
|
15418
|
+
const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
|
|
15419
|
+
const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
|
|
14741
15420
|
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
|
|
14742
|
-
|
|
15421
|
+
const glProps = {};
|
|
15422
|
+
for (const key in glConfig) {
|
|
15423
|
+
if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
|
|
15424
|
+
}
|
|
15425
|
+
applyProps(renderer, glProps);
|
|
14743
15426
|
}
|
|
14744
15427
|
if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
|
|
14745
15428
|
const currentRenderer = state.renderer;
|
|
14746
15429
|
if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
|
|
14747
|
-
|
|
15430
|
+
const rendererProps = {};
|
|
15431
|
+
for (const key in rendererConfig) {
|
|
15432
|
+
if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
|
|
15433
|
+
}
|
|
15434
|
+
applyProps(currentRenderer, rendererProps);
|
|
14748
15435
|
}
|
|
14749
15436
|
}
|
|
14750
15437
|
const scheduler = getScheduler();
|
|
14751
15438
|
const rootId = state.internal.rootId;
|
|
14752
15439
|
if (!rootId) {
|
|
14753
|
-
const newRootId = scheduler.generateRootId();
|
|
15440
|
+
const newRootId = canvasId || scheduler.generateRootId();
|
|
14754
15441
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14755
15442
|
getState: () => store.getState(),
|
|
14756
15443
|
onError: (err) => store.getState().setError(err)
|
|
14757
15444
|
});
|
|
15445
|
+
const unregisterCanvasTarget = scheduler.register(
|
|
15446
|
+
() => {
|
|
15447
|
+
const state2 = store.getState();
|
|
15448
|
+
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
15449
|
+
const renderer2 = state2.internal.actualRenderer;
|
|
15450
|
+
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
15451
|
+
}
|
|
15452
|
+
},
|
|
15453
|
+
{
|
|
15454
|
+
id: `${newRootId}_canvasTarget`,
|
|
15455
|
+
rootId: newRootId,
|
|
15456
|
+
phase: "start",
|
|
15457
|
+
system: true
|
|
15458
|
+
}
|
|
15459
|
+
);
|
|
15460
|
+
const unregisterEventsFlush = scheduler.register(
|
|
15461
|
+
() => {
|
|
15462
|
+
const state2 = store.getState();
|
|
15463
|
+
state2.events.flush?.();
|
|
15464
|
+
},
|
|
15465
|
+
{
|
|
15466
|
+
id: `${newRootId}_events`,
|
|
15467
|
+
rootId: newRootId,
|
|
15468
|
+
phase: "input",
|
|
15469
|
+
system: true
|
|
15470
|
+
}
|
|
15471
|
+
);
|
|
14758
15472
|
const unregisterFrustum = scheduler.register(
|
|
14759
15473
|
() => {
|
|
14760
15474
|
const state2 = store.getState();
|
|
@@ -14789,18 +15503,22 @@ function createRoot(canvas) {
|
|
|
14789
15503
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14790
15504
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14791
15505
|
try {
|
|
14792
|
-
if (state2.
|
|
15506
|
+
if (state2.renderPipeline?.render) state2.renderPipeline.render();
|
|
14793
15507
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14794
15508
|
} catch (error) {
|
|
14795
15509
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
14796
15510
|
}
|
|
14797
15511
|
},
|
|
14798
15512
|
{
|
|
14799
|
-
|
|
15513
|
+
// Use canvas ID directly as job ID if available, otherwise use generated rootId
|
|
15514
|
+
id: canvasId || `${newRootId}_render`,
|
|
14800
15515
|
rootId: newRootId,
|
|
14801
15516
|
phase: "render",
|
|
14802
|
-
system: true
|
|
15517
|
+
system: true,
|
|
14803
15518
|
// Internal flag: this is a system job, not user-controlled
|
|
15519
|
+
// Apply scheduler config for render ordering and rate limiting
|
|
15520
|
+
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
15521
|
+
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14804
15522
|
}
|
|
14805
15523
|
);
|
|
14806
15524
|
state.set((state2) => ({
|
|
@@ -14809,6 +15527,8 @@ function createRoot(canvas) {
|
|
|
14809
15527
|
rootId: newRootId,
|
|
14810
15528
|
unregisterRoot: () => {
|
|
14811
15529
|
unregisterRoot();
|
|
15530
|
+
unregisterCanvasTarget();
|
|
15531
|
+
unregisterEventsFlush();
|
|
14812
15532
|
unregisterFrustum();
|
|
14813
15533
|
unregisterVisibility();
|
|
14814
15534
|
unregisterRender();
|
|
@@ -14867,15 +15587,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14867
15587
|
const renderer = state.internal.actualRenderer;
|
|
14868
15588
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14869
15589
|
if (unregisterRoot) unregisterRoot();
|
|
15590
|
+
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
15591
|
+
if (unregisterPrimary) unregisterPrimary();
|
|
15592
|
+
const canvasTarget = state.internal.canvasTarget;
|
|
15593
|
+
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14870
15594
|
state.events.disconnect?.();
|
|
14871
15595
|
cleanupHelperGroup(root.store);
|
|
14872
|
-
renderer
|
|
14873
|
-
|
|
14874
|
-
|
|
15596
|
+
if (state.isLegacy && renderer) {
|
|
15597
|
+
;
|
|
15598
|
+
renderer.renderLists?.dispose?.();
|
|
15599
|
+
renderer.forceContextLoss?.();
|
|
15600
|
+
}
|
|
15601
|
+
if (!state.internal.isSecondary) {
|
|
15602
|
+
if (renderer?.xr) state.xr.disconnect();
|
|
15603
|
+
}
|
|
14875
15604
|
dispose(state.scene);
|
|
14876
15605
|
_roots.delete(canvas);
|
|
14877
15606
|
if (callback) callback(canvas);
|
|
14878
|
-
} catch
|
|
15607
|
+
} catch {
|
|
14879
15608
|
}
|
|
14880
15609
|
}, 500);
|
|
14881
15610
|
}
|
|
@@ -14883,36 +15612,34 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14883
15612
|
}
|
|
14884
15613
|
}
|
|
14885
15614
|
function createPortal(children, container, state) {
|
|
14886
|
-
return /* @__PURE__ */ jsx(
|
|
15615
|
+
return /* @__PURE__ */ jsx(Portal, { children, container, state });
|
|
14887
15616
|
}
|
|
14888
|
-
function
|
|
15617
|
+
function Portal({ children, container, state }) {
|
|
14889
15618
|
const isRef = useCallback((obj) => obj && "current" in obj, []);
|
|
14890
|
-
const [resolvedContainer,
|
|
15619
|
+
const [resolvedContainer, _setResolvedContainer] = useState(() => {
|
|
14891
15620
|
if (isRef(container)) return container.current ?? null;
|
|
14892
15621
|
return container;
|
|
14893
15622
|
});
|
|
15623
|
+
const setResolvedContainer = useCallback(
|
|
15624
|
+
(newContainer) => {
|
|
15625
|
+
if (!newContainer || newContainer === resolvedContainer) return;
|
|
15626
|
+
_setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
|
|
15627
|
+
},
|
|
15628
|
+
[resolvedContainer, _setResolvedContainer, isRef]
|
|
15629
|
+
);
|
|
14894
15630
|
useMemo(() => {
|
|
14895
|
-
if (isRef(container)) {
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
const updated = container.current;
|
|
14900
|
-
if (updated && updated !== resolvedContainer) {
|
|
14901
|
-
setResolvedContainer(updated);
|
|
14902
|
-
}
|
|
14903
|
-
});
|
|
14904
|
-
} else if (current !== resolvedContainer) {
|
|
14905
|
-
setResolvedContainer(current);
|
|
14906
|
-
}
|
|
14907
|
-
} else if (container !== resolvedContainer) {
|
|
14908
|
-
setResolvedContainer(container);
|
|
15631
|
+
if (isRef(container) && !container.current) {
|
|
15632
|
+
return queueMicrotask(() => {
|
|
15633
|
+
setResolvedContainer(container.current);
|
|
15634
|
+
});
|
|
14909
15635
|
}
|
|
14910
|
-
|
|
15636
|
+
setResolvedContainer(container);
|
|
15637
|
+
}, [container, isRef, setResolvedContainer]);
|
|
14911
15638
|
if (!resolvedContainer) return /* @__PURE__ */ jsx(Fragment, {});
|
|
14912
15639
|
const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
|
|
14913
|
-
return /* @__PURE__ */ jsx(
|
|
15640
|
+
return /* @__PURE__ */ jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
|
|
14914
15641
|
}
|
|
14915
|
-
function
|
|
15642
|
+
function PortalInner({ state = {}, children, container }) {
|
|
14916
15643
|
const { events, size, injectScene = true, ...rest } = state;
|
|
14917
15644
|
const previousRoot = useStore();
|
|
14918
15645
|
const [raycaster] = useState(() => new Raycaster());
|
|
@@ -14933,11 +15660,12 @@ function Portal({ state = {}, children, container }) {
|
|
|
14933
15660
|
};
|
|
14934
15661
|
}, [portalScene, container, injectScene]);
|
|
14935
15662
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
15663
|
+
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14936
15664
|
let viewport = void 0;
|
|
14937
|
-
if (injectState.camera && size) {
|
|
15665
|
+
if (injectState.camera && (size || injectState.size)) {
|
|
14938
15666
|
const camera = injectState.camera;
|
|
14939
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(),
|
|
14940
|
-
if (camera !== rootState.camera) updateCamera(camera,
|
|
15667
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
|
|
15668
|
+
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14941
15669
|
}
|
|
14942
15670
|
return {
|
|
14943
15671
|
// The intersect consists of the previous root state
|
|
@@ -14954,7 +15682,7 @@ function Portal({ state = {}, children, container }) {
|
|
|
14954
15682
|
previousRoot,
|
|
14955
15683
|
// Events, size and viewport can be overridden by the inject layer
|
|
14956
15684
|
events: { ...rootState.events, ...injectState.events, ...events },
|
|
14957
|
-
size:
|
|
15685
|
+
size: resolvedSize,
|
|
14958
15686
|
viewport: { ...rootState.viewport, ...viewport },
|
|
14959
15687
|
// Layers are allowed to override events
|
|
14960
15688
|
setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
|
|
@@ -14966,9 +15694,13 @@ function Portal({ state = {}, children, container }) {
|
|
|
14966
15694
|
const store = createWithEqualityFn((set, get) => ({ ...rest, set, get }));
|
|
14967
15695
|
const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
|
|
14968
15696
|
onMutate(previousRoot.getState());
|
|
14969
|
-
previousRoot.subscribe(onMutate);
|
|
14970
15697
|
return store;
|
|
14971
15698
|
}, [previousRoot, container]);
|
|
15699
|
+
useIsomorphicLayoutEffect(() => {
|
|
15700
|
+
const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
|
|
15701
|
+
const unsubscribe = previousRoot.subscribe(onMutate);
|
|
15702
|
+
return unsubscribe;
|
|
15703
|
+
}, [previousRoot, usePortalStore]);
|
|
14972
15704
|
return (
|
|
14973
15705
|
// @ts-ignore, reconciler types are not maintained
|
|
14974
15706
|
/* @__PURE__ */ jsx(Fragment, { children: reconciler.createPortal(
|
|
@@ -14988,15 +15720,13 @@ function CanvasImpl({
|
|
|
14988
15720
|
fallback,
|
|
14989
15721
|
resize,
|
|
14990
15722
|
style,
|
|
15723
|
+
id,
|
|
14991
15724
|
gl,
|
|
14992
|
-
renderer,
|
|
15725
|
+
renderer: rendererProp,
|
|
14993
15726
|
events = createPointerEvents,
|
|
14994
15727
|
eventSource,
|
|
14995
15728
|
eventPrefix,
|
|
14996
15729
|
shadows,
|
|
14997
|
-
linear,
|
|
14998
|
-
flat,
|
|
14999
|
-
legacy,
|
|
15000
15730
|
orthographic,
|
|
15001
15731
|
frameloop,
|
|
15002
15732
|
dpr,
|
|
@@ -15011,10 +15741,53 @@ function CanvasImpl({
|
|
|
15011
15741
|
hmr,
|
|
15012
15742
|
width,
|
|
15013
15743
|
height,
|
|
15744
|
+
background,
|
|
15745
|
+
forceEven,
|
|
15014
15746
|
...props
|
|
15015
15747
|
}) {
|
|
15748
|
+
const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
|
|
15749
|
+
let primaryCanvas;
|
|
15750
|
+
let scheduler;
|
|
15751
|
+
let renderer;
|
|
15752
|
+
if (isRendererConfig) {
|
|
15753
|
+
const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
|
|
15754
|
+
primaryCanvas = pc;
|
|
15755
|
+
scheduler = sc;
|
|
15756
|
+
renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
|
|
15757
|
+
} else {
|
|
15758
|
+
renderer = rendererProp;
|
|
15759
|
+
}
|
|
15016
15760
|
React.useMemo(() => extend(THREE), []);
|
|
15017
15761
|
const Bridge = useBridge();
|
|
15762
|
+
const backgroundProps = React.useMemo(() => {
|
|
15763
|
+
if (!background) return null;
|
|
15764
|
+
if (typeof background === "object" && !background.isColor) {
|
|
15765
|
+
const { backgroundMap, envMap, files, preset, ...rest } = background;
|
|
15766
|
+
return {
|
|
15767
|
+
...rest,
|
|
15768
|
+
preset,
|
|
15769
|
+
files: envMap || files,
|
|
15770
|
+
backgroundFiles: backgroundMap,
|
|
15771
|
+
background: true
|
|
15772
|
+
};
|
|
15773
|
+
}
|
|
15774
|
+
if (typeof background === "number") {
|
|
15775
|
+
return { color: background, background: true };
|
|
15776
|
+
}
|
|
15777
|
+
if (typeof background === "string") {
|
|
15778
|
+
if (background in presetsObj) {
|
|
15779
|
+
return { preset: background, background: true };
|
|
15780
|
+
}
|
|
15781
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
|
|
15782
|
+
return { files: background, background: true };
|
|
15783
|
+
}
|
|
15784
|
+
return { color: background, background: true };
|
|
15785
|
+
}
|
|
15786
|
+
if (background.isColor) {
|
|
15787
|
+
return { color: background, background: true };
|
|
15788
|
+
}
|
|
15789
|
+
return null;
|
|
15790
|
+
}, [background]);
|
|
15018
15791
|
const hasInitialSizeRef = React.useRef(false);
|
|
15019
15792
|
const measureConfig = React.useMemo(() => {
|
|
15020
15793
|
if (!hasInitialSizeRef.current) {
|
|
@@ -15031,15 +15804,20 @@ function CanvasImpl({
|
|
|
15031
15804
|
};
|
|
15032
15805
|
}, [resize, hasInitialSizeRef.current]);
|
|
15033
15806
|
const [containerRef, containerRect] = useMeasure(measureConfig);
|
|
15034
|
-
const effectiveSize = React.useMemo(
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15807
|
+
const effectiveSize = React.useMemo(() => {
|
|
15808
|
+
let w = width ?? containerRect.width;
|
|
15809
|
+
let h = height ?? containerRect.height;
|
|
15810
|
+
if (forceEven) {
|
|
15811
|
+
w = Math.ceil(w / 2) * 2;
|
|
15812
|
+
h = Math.ceil(h / 2) * 2;
|
|
15813
|
+
}
|
|
15814
|
+
return {
|
|
15815
|
+
width: w,
|
|
15816
|
+
height: h,
|
|
15038
15817
|
top: containerRect.top,
|
|
15039
15818
|
left: containerRect.left
|
|
15040
|
-
}
|
|
15041
|
-
|
|
15042
|
-
);
|
|
15819
|
+
};
|
|
15820
|
+
}, [width, height, containerRect, forceEven]);
|
|
15043
15821
|
if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
|
|
15044
15822
|
hasInitialSizeRef.current = true;
|
|
15045
15823
|
}
|
|
@@ -15079,14 +15857,14 @@ function CanvasImpl({
|
|
|
15079
15857
|
async function run() {
|
|
15080
15858
|
if (!effectActiveRef.current || !root.current) return;
|
|
15081
15859
|
await root.current.configure({
|
|
15860
|
+
id,
|
|
15861
|
+
primaryCanvas,
|
|
15862
|
+
scheduler,
|
|
15082
15863
|
gl,
|
|
15083
15864
|
renderer,
|
|
15084
15865
|
scene,
|
|
15085
15866
|
events,
|
|
15086
15867
|
shadows,
|
|
15087
|
-
linear,
|
|
15088
|
-
flat,
|
|
15089
|
-
legacy,
|
|
15090
15868
|
orthographic,
|
|
15091
15869
|
frameloop,
|
|
15092
15870
|
dpr,
|
|
@@ -15096,6 +15874,7 @@ function CanvasImpl({
|
|
|
15096
15874
|
size: effectiveSize,
|
|
15097
15875
|
// Store size props for reset functionality
|
|
15098
15876
|
_sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
|
|
15877
|
+
forceEven,
|
|
15099
15878
|
// Pass mutable reference to onPointerMissed so it's free to update
|
|
15100
15879
|
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
|
|
15101
15880
|
onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
|
|
@@ -15119,7 +15898,10 @@ function CanvasImpl({
|
|
|
15119
15898
|
});
|
|
15120
15899
|
if (!effectActiveRef.current || !root.current) return;
|
|
15121
15900
|
root.current.render(
|
|
15122
|
-
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */
|
|
15901
|
+
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
|
|
15902
|
+
backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
|
|
15903
|
+
children ?? null
|
|
15904
|
+
] }) }) })
|
|
15123
15905
|
);
|
|
15124
15906
|
}
|
|
15125
15907
|
run();
|
|
@@ -15146,20 +15928,22 @@ function CanvasImpl({
|
|
|
15146
15928
|
const canvas = canvasRef.current;
|
|
15147
15929
|
if (!canvas) return;
|
|
15148
15930
|
const handleHMR = () => {
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
rootEntry
|
|
15152
|
-
nodes
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15931
|
+
queueMicrotask(() => {
|
|
15932
|
+
const rootEntry = _roots.get(canvas);
|
|
15933
|
+
if (rootEntry?.store) {
|
|
15934
|
+
console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
|
|
15935
|
+
rootEntry.store.setState((state) => ({
|
|
15936
|
+
nodes: {},
|
|
15937
|
+
uniforms: {},
|
|
15938
|
+
_hmrVersion: state._hmrVersion + 1
|
|
15939
|
+
}));
|
|
15940
|
+
}
|
|
15941
|
+
});
|
|
15157
15942
|
};
|
|
15158
15943
|
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
15159
15944
|
const hot = import.meta.hot;
|
|
15160
15945
|
hot.on("vite:afterUpdate", handleHMR);
|
|
15161
|
-
return () => hot.
|
|
15162
|
-
});
|
|
15946
|
+
return () => hot.off?.("vite:afterUpdate", handleHMR);
|
|
15163
15947
|
}
|
|
15164
15948
|
if (typeof module !== "undefined" && module.hot) {
|
|
15165
15949
|
const hot = module.hot;
|
|
@@ -15182,7 +15966,16 @@ function CanvasImpl({
|
|
|
15182
15966
|
...style
|
|
15183
15967
|
},
|
|
15184
15968
|
...props,
|
|
15185
|
-
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
15969
|
+
children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
15970
|
+
"canvas",
|
|
15971
|
+
{
|
|
15972
|
+
ref: canvasRef,
|
|
15973
|
+
id,
|
|
15974
|
+
className: "r3f-canvas",
|
|
15975
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
15976
|
+
children: fallback
|
|
15977
|
+
}
|
|
15978
|
+
) })
|
|
15186
15979
|
}
|
|
15187
15980
|
);
|
|
15188
15981
|
}
|
|
@@ -15192,4 +15985,4 @@ function Canvas(props) {
|
|
|
15192
15985
|
|
|
15193
15986
|
extend(THREE);
|
|
15194
15987
|
|
|
15195
|
-
export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree };
|
|
15988
|
+
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 };
|