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