@react-three/fiber 10.0.0-alpha.2 → 10.0.0-canary.1b98c17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/dist/index.cjs +1454 -618
- package/dist/index.d.cts +2130 -1285
- package/dist/index.d.mts +2130 -1285
- package/dist/index.d.ts +2130 -1285
- package/dist/index.mjs +1439 -622
- package/dist/legacy.cjs +1420 -608
- package/dist/legacy.d.cts +2131 -1286
- package/dist/legacy.d.mts +2131 -1286
- package/dist/legacy.d.ts +2131 -1286
- package/dist/legacy.mjs +1405 -612
- package/dist/webgpu/index.cjs +1841 -577
- package/dist/webgpu/index.d.cts +2310 -1312
- package/dist/webgpu/index.d.mts +2310 -1312
- package/dist/webgpu/index.d.ts +2310 -1312
- package/dist/webgpu/index.mjs +1821 -580
- package/package.json +3 -1
- package/readme.md +244 -318
package/dist/legacy.cjs
CHANGED
|
@@ -5,6 +5,12 @@ const jsxRuntime = require('react/jsx-runtime');
|
|
|
5
5
|
const React = require('react');
|
|
6
6
|
const useMeasure = require('react-use-measure');
|
|
7
7
|
const itsFine = require('its-fine');
|
|
8
|
+
const fiber = require('@react-three/fiber');
|
|
9
|
+
const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
|
|
10
|
+
const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
|
|
11
|
+
const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
|
|
12
|
+
const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
|
|
13
|
+
const gainmapJs = require('@monogrid/gainmap-js');
|
|
8
14
|
const Tb = require('scheduler');
|
|
9
15
|
const traditional = require('zustand/traditional');
|
|
10
16
|
const suspendReact = require('suspend-react');
|
|
@@ -66,6 +72,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
66
72
|
WebGPURenderer: WebGPURenderer
|
|
67
73
|
}, [three__namespace]);
|
|
68
74
|
|
|
75
|
+
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
76
|
+
const pendingSubscribers = /* @__PURE__ */ new Map();
|
|
77
|
+
function registerPrimary(id, renderer, store) {
|
|
78
|
+
if (primaryRegistry.has(id)) {
|
|
79
|
+
console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
|
|
80
|
+
}
|
|
81
|
+
const entry = { renderer, store };
|
|
82
|
+
primaryRegistry.set(id, entry);
|
|
83
|
+
const subscribers = pendingSubscribers.get(id);
|
|
84
|
+
if (subscribers) {
|
|
85
|
+
subscribers.forEach((callback) => callback(entry));
|
|
86
|
+
pendingSubscribers.delete(id);
|
|
87
|
+
}
|
|
88
|
+
return () => {
|
|
89
|
+
const currentEntry = primaryRegistry.get(id);
|
|
90
|
+
if (currentEntry?.renderer === renderer) {
|
|
91
|
+
primaryRegistry.delete(id);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function getPrimary(id) {
|
|
96
|
+
return primaryRegistry.get(id);
|
|
97
|
+
}
|
|
98
|
+
function waitForPrimary(id, timeout = 5e3) {
|
|
99
|
+
const existing = primaryRegistry.get(id);
|
|
100
|
+
if (existing) {
|
|
101
|
+
return Promise.resolve(existing);
|
|
102
|
+
}
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const timeoutId = setTimeout(() => {
|
|
105
|
+
const subscribers = pendingSubscribers.get(id);
|
|
106
|
+
if (subscribers) {
|
|
107
|
+
const index = subscribers.indexOf(callback);
|
|
108
|
+
if (index !== -1) subscribers.splice(index, 1);
|
|
109
|
+
if (subscribers.length === 0) pendingSubscribers.delete(id);
|
|
110
|
+
}
|
|
111
|
+
reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
|
|
112
|
+
}, timeout);
|
|
113
|
+
const callback = (entry) => {
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
resolve(entry);
|
|
116
|
+
};
|
|
117
|
+
if (!pendingSubscribers.has(id)) {
|
|
118
|
+
pendingSubscribers.set(id, []);
|
|
119
|
+
}
|
|
120
|
+
pendingSubscribers.get(id).push(callback);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function hasPrimary(id) {
|
|
124
|
+
return primaryRegistry.has(id);
|
|
125
|
+
}
|
|
126
|
+
function unregisterPrimary(id) {
|
|
127
|
+
primaryRegistry.delete(id);
|
|
128
|
+
}
|
|
129
|
+
function getPrimaryIds() {
|
|
130
|
+
return Array.from(primaryRegistry.keys());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const presetsObj = {
|
|
134
|
+
apartment: "lebombo_1k.hdr",
|
|
135
|
+
city: "potsdamer_platz_1k.hdr",
|
|
136
|
+
dawn: "kiara_1_dawn_1k.hdr",
|
|
137
|
+
forest: "forest_slope_1k.hdr",
|
|
138
|
+
lobby: "st_fagans_interior_1k.hdr",
|
|
139
|
+
night: "dikhololo_night_1k.hdr",
|
|
140
|
+
park: "rooitou_park_1k.hdr",
|
|
141
|
+
studio: "studio_small_03_1k.hdr",
|
|
142
|
+
sunset: "venice_sunset_1k.hdr",
|
|
143
|
+
warehouse: "empty_warehouse_01_1k.hdr"
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
|
|
147
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
148
|
+
const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
|
|
149
|
+
function useEnvironment({
|
|
150
|
+
files = defaultFiles,
|
|
151
|
+
path = "",
|
|
152
|
+
preset = void 0,
|
|
153
|
+
colorSpace = void 0,
|
|
154
|
+
extensions
|
|
155
|
+
} = {}) {
|
|
156
|
+
if (preset) {
|
|
157
|
+
validatePreset(preset);
|
|
158
|
+
files = presetsObj[preset];
|
|
159
|
+
path = CUBEMAP_ROOT;
|
|
160
|
+
}
|
|
161
|
+
const multiFile = isArray(files);
|
|
162
|
+
const { extension, isCubemap } = getExtension(files);
|
|
163
|
+
const loader = getLoader$1(extension);
|
|
164
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
165
|
+
const renderer = fiber.useThree((state) => state.renderer);
|
|
166
|
+
React.useLayoutEffect(() => {
|
|
167
|
+
if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
|
|
168
|
+
function clearGainmapTexture() {
|
|
169
|
+
fiber.useLoader.clear(loader, multiFile ? [files] : files);
|
|
170
|
+
}
|
|
171
|
+
renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
|
|
172
|
+
}, [extension, files, loader, multiFile, renderer.domElement]);
|
|
173
|
+
const loaderResult = fiber.useLoader(
|
|
174
|
+
loader,
|
|
175
|
+
multiFile ? [files] : files,
|
|
176
|
+
(loader2) => {
|
|
177
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
178
|
+
loader2.setRenderer?.(renderer);
|
|
179
|
+
}
|
|
180
|
+
loader2.setPath?.(path);
|
|
181
|
+
if (extensions) extensions(loader2);
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
let texture = multiFile ? (
|
|
185
|
+
// @ts-ignore
|
|
186
|
+
loaderResult[0]
|
|
187
|
+
) : loaderResult;
|
|
188
|
+
if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
|
|
189
|
+
texture = texture.renderTarget?.texture;
|
|
190
|
+
}
|
|
191
|
+
texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
|
|
192
|
+
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
193
|
+
return texture;
|
|
194
|
+
}
|
|
195
|
+
const preloadDefaultOptions = {
|
|
196
|
+
files: defaultFiles,
|
|
197
|
+
path: "",
|
|
198
|
+
preset: void 0,
|
|
199
|
+
extensions: void 0
|
|
200
|
+
};
|
|
201
|
+
useEnvironment.preload = (preloadOptions) => {
|
|
202
|
+
const options = { ...preloadDefaultOptions, ...preloadOptions };
|
|
203
|
+
let { files, path = "" } = options;
|
|
204
|
+
const { preset, extensions } = options;
|
|
205
|
+
if (preset) {
|
|
206
|
+
validatePreset(preset);
|
|
207
|
+
files = presetsObj[preset];
|
|
208
|
+
path = CUBEMAP_ROOT;
|
|
209
|
+
}
|
|
210
|
+
const { extension } = getExtension(files);
|
|
211
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
212
|
+
throw new Error("useEnvironment: Preloading gainmaps is not supported");
|
|
213
|
+
}
|
|
214
|
+
const loader = getLoader$1(extension);
|
|
215
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
216
|
+
fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
|
|
217
|
+
loader2.setPath?.(path);
|
|
218
|
+
if (extensions) extensions(loader2);
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
const clearDefaultOptins = {
|
|
222
|
+
files: defaultFiles,
|
|
223
|
+
preset: void 0
|
|
224
|
+
};
|
|
225
|
+
useEnvironment.clear = (clearOptions) => {
|
|
226
|
+
const options = { ...clearDefaultOptins, ...clearOptions };
|
|
227
|
+
let { files } = options;
|
|
228
|
+
const { preset } = options;
|
|
229
|
+
if (preset) {
|
|
230
|
+
validatePreset(preset);
|
|
231
|
+
files = presetsObj[preset];
|
|
232
|
+
}
|
|
233
|
+
const { extension } = getExtension(files);
|
|
234
|
+
const loader = getLoader$1(extension);
|
|
235
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
236
|
+
fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
|
|
237
|
+
};
|
|
238
|
+
function validatePreset(preset) {
|
|
239
|
+
if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
|
|
240
|
+
}
|
|
241
|
+
function getExtension(files) {
|
|
242
|
+
const isCubemap = isArray(files) && files.length === 6;
|
|
243
|
+
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
244
|
+
const firstEntry = isArray(files) ? files[0] : files;
|
|
245
|
+
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();
|
|
246
|
+
return { extension, isCubemap, isGainmap };
|
|
247
|
+
}
|
|
248
|
+
function getLoader$1(extension) {
|
|
249
|
+
const loader = extension === "cube" ? three.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
|
|
250
|
+
return loader;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const isRef$1 = (obj) => obj.current && obj.current.isScene;
|
|
254
|
+
const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
|
|
255
|
+
function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
|
|
256
|
+
sceneProps = {
|
|
257
|
+
backgroundBlurriness: 0,
|
|
258
|
+
backgroundIntensity: 1,
|
|
259
|
+
backgroundRotation: [0, 0, 0],
|
|
260
|
+
environmentIntensity: 1,
|
|
261
|
+
environmentRotation: [0, 0, 0],
|
|
262
|
+
...sceneProps
|
|
263
|
+
};
|
|
264
|
+
const target = resolveScene(scene || defaultScene);
|
|
265
|
+
const oldbg = target.background;
|
|
266
|
+
const oldenv = target.environment;
|
|
267
|
+
const oldSceneProps = {
|
|
268
|
+
// @ts-ignore
|
|
269
|
+
backgroundBlurriness: target.backgroundBlurriness,
|
|
270
|
+
// @ts-ignore
|
|
271
|
+
backgroundIntensity: target.backgroundIntensity,
|
|
272
|
+
// @ts-ignore
|
|
273
|
+
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
274
|
+
// @ts-ignore
|
|
275
|
+
environmentIntensity: target.environmentIntensity,
|
|
276
|
+
// @ts-ignore
|
|
277
|
+
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
278
|
+
};
|
|
279
|
+
if (background !== "only") target.environment = texture;
|
|
280
|
+
if (background) target.background = texture;
|
|
281
|
+
fiber.applyProps(target, sceneProps);
|
|
282
|
+
return () => {
|
|
283
|
+
if (background !== "only") target.environment = oldenv;
|
|
284
|
+
if (background) target.background = oldbg;
|
|
285
|
+
fiber.applyProps(target, oldSceneProps);
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function EnvironmentMap({ scene, background = false, map, ...config }) {
|
|
289
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
290
|
+
React__namespace.useLayoutEffect(() => {
|
|
291
|
+
if (map) return setEnvProps(background, scene, defaultScene, map, config);
|
|
292
|
+
});
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
function EnvironmentCube({
|
|
296
|
+
background = false,
|
|
297
|
+
scene,
|
|
298
|
+
blur,
|
|
299
|
+
backgroundBlurriness,
|
|
300
|
+
backgroundIntensity,
|
|
301
|
+
backgroundRotation,
|
|
302
|
+
environmentIntensity,
|
|
303
|
+
environmentRotation,
|
|
304
|
+
...rest
|
|
305
|
+
}) {
|
|
306
|
+
const texture = useEnvironment(rest);
|
|
307
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
308
|
+
React__namespace.useLayoutEffect(() => {
|
|
309
|
+
return setEnvProps(background, scene, defaultScene, texture, {
|
|
310
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
311
|
+
backgroundIntensity,
|
|
312
|
+
backgroundRotation,
|
|
313
|
+
environmentIntensity,
|
|
314
|
+
environmentRotation
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
React__namespace.useEffect(() => {
|
|
318
|
+
return () => {
|
|
319
|
+
texture.dispose();
|
|
320
|
+
};
|
|
321
|
+
}, [texture]);
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
function EnvironmentPortal({
|
|
325
|
+
children,
|
|
326
|
+
near = 0.1,
|
|
327
|
+
far = 1e3,
|
|
328
|
+
resolution = 256,
|
|
329
|
+
frames = 1,
|
|
330
|
+
map,
|
|
331
|
+
background = false,
|
|
332
|
+
blur,
|
|
333
|
+
backgroundBlurriness,
|
|
334
|
+
backgroundIntensity,
|
|
335
|
+
backgroundRotation,
|
|
336
|
+
environmentIntensity,
|
|
337
|
+
environmentRotation,
|
|
338
|
+
scene,
|
|
339
|
+
files,
|
|
340
|
+
path,
|
|
341
|
+
preset = void 0,
|
|
342
|
+
extensions
|
|
343
|
+
}) {
|
|
344
|
+
const gl = fiber.useThree((state) => state.gl);
|
|
345
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
346
|
+
const camera = React__namespace.useRef(null);
|
|
347
|
+
const [virtualScene] = React__namespace.useState(() => new three.Scene());
|
|
348
|
+
const fbo = React__namespace.useMemo(() => {
|
|
349
|
+
const fbo2 = new three.WebGLCubeRenderTarget(resolution);
|
|
350
|
+
fbo2.texture.type = three.HalfFloatType;
|
|
351
|
+
return fbo2;
|
|
352
|
+
}, [resolution]);
|
|
353
|
+
React__namespace.useEffect(() => {
|
|
354
|
+
return () => {
|
|
355
|
+
fbo.dispose();
|
|
356
|
+
};
|
|
357
|
+
}, [fbo]);
|
|
358
|
+
React__namespace.useLayoutEffect(() => {
|
|
359
|
+
if (frames === 1) {
|
|
360
|
+
const autoClear = gl.autoClear;
|
|
361
|
+
gl.autoClear = true;
|
|
362
|
+
camera.current.update(gl, virtualScene);
|
|
363
|
+
gl.autoClear = autoClear;
|
|
364
|
+
}
|
|
365
|
+
return setEnvProps(background, scene, defaultScene, fbo.texture, {
|
|
366
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
367
|
+
backgroundIntensity,
|
|
368
|
+
backgroundRotation,
|
|
369
|
+
environmentIntensity,
|
|
370
|
+
environmentRotation
|
|
371
|
+
});
|
|
372
|
+
}, [
|
|
373
|
+
children,
|
|
374
|
+
virtualScene,
|
|
375
|
+
fbo.texture,
|
|
376
|
+
scene,
|
|
377
|
+
defaultScene,
|
|
378
|
+
background,
|
|
379
|
+
frames,
|
|
380
|
+
gl,
|
|
381
|
+
blur,
|
|
382
|
+
backgroundBlurriness,
|
|
383
|
+
backgroundIntensity,
|
|
384
|
+
backgroundRotation,
|
|
385
|
+
environmentIntensity,
|
|
386
|
+
environmentRotation
|
|
387
|
+
]);
|
|
388
|
+
let count = 1;
|
|
389
|
+
fiber.useFrame(() => {
|
|
390
|
+
if (frames === Infinity || count < frames) {
|
|
391
|
+
const autoClear = gl.autoClear;
|
|
392
|
+
gl.autoClear = true;
|
|
393
|
+
camera.current.update(gl, virtualScene);
|
|
394
|
+
gl.autoClear = autoClear;
|
|
395
|
+
count++;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
|
|
399
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
400
|
+
children,
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
|
|
402
|
+
files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
|
|
403
|
+
] }),
|
|
404
|
+
virtualScene
|
|
405
|
+
) });
|
|
406
|
+
}
|
|
407
|
+
function EnvironmentGround(props) {
|
|
408
|
+
const textureDefault = useEnvironment(props);
|
|
409
|
+
const texture = props.map || textureDefault;
|
|
410
|
+
React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
|
|
411
|
+
React__namespace.useEffect(() => {
|
|
412
|
+
return () => {
|
|
413
|
+
textureDefault.dispose();
|
|
414
|
+
};
|
|
415
|
+
}, [textureDefault]);
|
|
416
|
+
const height = props.ground?.height ?? 15;
|
|
417
|
+
const radius = props.ground?.radius ?? 60;
|
|
418
|
+
const scale = props.ground?.scale ?? 1e3;
|
|
419
|
+
const args = React__namespace.useMemo(
|
|
420
|
+
() => [texture, height, radius],
|
|
421
|
+
[texture, height, radius]
|
|
422
|
+
);
|
|
423
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
424
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
|
|
425
|
+
/* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
|
|
426
|
+
] });
|
|
427
|
+
}
|
|
428
|
+
function EnvironmentColor({ color, scene }) {
|
|
429
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
430
|
+
React__namespace.useLayoutEffect(() => {
|
|
431
|
+
if (color === void 0) return;
|
|
432
|
+
const target = resolveScene(scene || defaultScene);
|
|
433
|
+
const oldBg = target.background;
|
|
434
|
+
target.background = new three.Color(color);
|
|
435
|
+
return () => {
|
|
436
|
+
target.background = oldBg;
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
function EnvironmentDualSource(props) {
|
|
442
|
+
const { backgroundFiles, ...envProps } = props;
|
|
443
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
444
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
|
|
445
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
|
|
446
|
+
] });
|
|
447
|
+
}
|
|
448
|
+
function Environment(props) {
|
|
449
|
+
if (props.color && !props.files && !props.preset && !props.map) {
|
|
450
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
|
|
451
|
+
}
|
|
452
|
+
if (props.backgroundFiles && props.backgroundFiles !== props.files) {
|
|
453
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
|
|
454
|
+
}
|
|
455
|
+
return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
|
|
456
|
+
}
|
|
457
|
+
|
|
69
458
|
var __defProp$2 = Object.defineProperty;
|
|
70
459
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
71
460
|
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -245,7 +634,8 @@ function prepare(target, root, type, props) {
|
|
|
245
634
|
object,
|
|
246
635
|
eventCount: 0,
|
|
247
636
|
handlers: {},
|
|
248
|
-
isHidden: false
|
|
637
|
+
isHidden: false,
|
|
638
|
+
deferredRefs: []
|
|
249
639
|
};
|
|
250
640
|
if (object) object.__r3f = instance;
|
|
251
641
|
}
|
|
@@ -294,7 +684,7 @@ function createOcclusionObserverNode(store, uniform) {
|
|
|
294
684
|
let occlusionSetupPromise = null;
|
|
295
685
|
function enableOcclusion(store) {
|
|
296
686
|
const state = store.getState();
|
|
297
|
-
const { internal, renderer
|
|
687
|
+
const { internal, renderer } = state;
|
|
298
688
|
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
299
689
|
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
300
690
|
if (!hasOcclusionSupport) {
|
|
@@ -457,6 +847,22 @@ function hasVisibilityHandlers(handlers) {
|
|
|
457
847
|
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
458
848
|
}
|
|
459
849
|
|
|
850
|
+
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
851
|
+
function fromRef(ref) {
|
|
852
|
+
return { [FROM_REF]: ref };
|
|
853
|
+
}
|
|
854
|
+
function isFromRef(value) {
|
|
855
|
+
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const ONCE = Symbol.for("@react-three/fiber.once");
|
|
859
|
+
function once(...args) {
|
|
860
|
+
return { [ONCE]: args.length ? args : true };
|
|
861
|
+
}
|
|
862
|
+
function isOnce(value) {
|
|
863
|
+
return value !== null && typeof value === "object" && ONCE in value;
|
|
864
|
+
}
|
|
865
|
+
|
|
460
866
|
const RESERVED_PROPS = [
|
|
461
867
|
"children",
|
|
462
868
|
"key",
|
|
@@ -527,7 +933,7 @@ function getMemoizedPrototype(root) {
|
|
|
527
933
|
ctor = new root.constructor();
|
|
528
934
|
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
529
935
|
}
|
|
530
|
-
} catch
|
|
936
|
+
} catch {
|
|
531
937
|
}
|
|
532
938
|
return ctor;
|
|
533
939
|
}
|
|
@@ -573,6 +979,25 @@ function applyProps(object, props) {
|
|
|
573
979
|
continue;
|
|
574
980
|
}
|
|
575
981
|
if (value === void 0) continue;
|
|
982
|
+
if (isFromRef(value)) {
|
|
983
|
+
instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (isOnce(value)) {
|
|
987
|
+
if (instance?.appliedOnce?.has(prop)) continue;
|
|
988
|
+
if (instance) {
|
|
989
|
+
instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
|
|
990
|
+
instance.appliedOnce.add(prop);
|
|
991
|
+
}
|
|
992
|
+
const { root: targetRoot, key: targetKey } = resolve(object, prop);
|
|
993
|
+
const args = value[ONCE];
|
|
994
|
+
if (typeof targetRoot[targetKey] === "function") {
|
|
995
|
+
targetRoot[targetKey](...args === true ? [] : args);
|
|
996
|
+
} else if (args !== true && args.length > 0) {
|
|
997
|
+
targetRoot[targetKey] = args[0];
|
|
998
|
+
}
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
576
1001
|
let { root, key, target } = resolve(object, prop);
|
|
577
1002
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
578
1003
|
throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
|
|
@@ -595,7 +1020,10 @@ function applyProps(object, props) {
|
|
|
595
1020
|
else target.set(value);
|
|
596
1021
|
} else {
|
|
597
1022
|
root[key] = value;
|
|
598
|
-
if (
|
|
1023
|
+
if (key.endsWith("Node") && root.isMaterial) {
|
|
1024
|
+
root.needsUpdate = true;
|
|
1025
|
+
}
|
|
1026
|
+
if (rootState && rootState.renderer?.outputColorSpace === three.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
599
1027
|
root[key].format === three.RGBAFormat && root[key].type === three.UnsignedByteType) {
|
|
600
1028
|
root[key].colorSpace = rootState.textureColorSpace;
|
|
601
1029
|
}
|
|
@@ -628,38 +1056,60 @@ function applyProps(object, props) {
|
|
|
628
1056
|
return object;
|
|
629
1057
|
}
|
|
630
1058
|
|
|
1059
|
+
const DEFAULT_POINTER_ID = 0;
|
|
1060
|
+
const XR_POINTER_ID_START = 1e3;
|
|
1061
|
+
function getPointerState(internal, pointerId) {
|
|
1062
|
+
let state = internal.pointerMap.get(pointerId);
|
|
1063
|
+
if (!state) {
|
|
1064
|
+
state = {
|
|
1065
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
1066
|
+
captured: /* @__PURE__ */ new Map(),
|
|
1067
|
+
initialClick: [0, 0],
|
|
1068
|
+
initialHits: []
|
|
1069
|
+
};
|
|
1070
|
+
internal.pointerMap.set(pointerId, state);
|
|
1071
|
+
}
|
|
1072
|
+
return state;
|
|
1073
|
+
}
|
|
1074
|
+
function getPointerId(event) {
|
|
1075
|
+
return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
|
|
1076
|
+
}
|
|
631
1077
|
function makeId(event) {
|
|
632
1078
|
return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
|
|
633
1079
|
}
|
|
634
|
-
function releaseInternalPointerCapture(
|
|
635
|
-
const
|
|
1080
|
+
function releaseInternalPointerCapture(internal, obj, pointerId) {
|
|
1081
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1082
|
+
if (!pointerState) return;
|
|
1083
|
+
const captureData = pointerState.captured.get(obj);
|
|
636
1084
|
if (captureData) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
capturedMap.delete(pointerId);
|
|
640
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
641
|
-
}
|
|
1085
|
+
pointerState.captured.delete(obj);
|
|
1086
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
642
1087
|
}
|
|
643
1088
|
}
|
|
644
1089
|
function removeInteractivity(store, object) {
|
|
645
1090
|
const { internal } = store.getState();
|
|
646
1091
|
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1092
|
+
for (const [pointerId, pointerState] of internal.pointerMap) {
|
|
1093
|
+
pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
|
|
1094
|
+
pointerState.hovered.forEach((value, key) => {
|
|
1095
|
+
if (value.eventObject === object || value.object === object) {
|
|
1096
|
+
pointerState.hovered.delete(key);
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
if (pointerState.captured.has(object)) {
|
|
1100
|
+
releaseInternalPointerCapture(internal, object, pointerId);
|
|
651
1101
|
}
|
|
652
|
-
}
|
|
653
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
654
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
655
|
-
});
|
|
1102
|
+
}
|
|
656
1103
|
unregisterVisibility(store, object);
|
|
657
1104
|
}
|
|
658
1105
|
function createEvents(store) {
|
|
659
|
-
function calculateDistance(event) {
|
|
1106
|
+
function calculateDistance(event, pointerId) {
|
|
660
1107
|
const { internal } = store.getState();
|
|
661
|
-
const
|
|
662
|
-
|
|
1108
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1109
|
+
if (!pointerState) return 0;
|
|
1110
|
+
const [initialX, initialY] = pointerState.initialClick;
|
|
1111
|
+
const dx = event.offsetX - initialX;
|
|
1112
|
+
const dy = event.offsetY - initialY;
|
|
663
1113
|
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
664
1114
|
}
|
|
665
1115
|
function filterPointerEvents(objects) {
|
|
@@ -695,6 +1145,15 @@ function createEvents(store) {
|
|
|
695
1145
|
return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
|
|
696
1146
|
}
|
|
697
1147
|
let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
|
|
1148
|
+
const aInteractivePriority = a.object.userData?.interactivePriority;
|
|
1149
|
+
const bInteractivePriority = b.object.userData?.interactivePriority;
|
|
1150
|
+
if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
|
|
1151
|
+
if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
|
|
1152
|
+
if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
|
|
1153
|
+
if (aInteractivePriority !== bInteractivePriority) {
|
|
1154
|
+
return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
698
1157
|
const aState = getRootState(a.object);
|
|
699
1158
|
const bState = getRootState(b.object);
|
|
700
1159
|
const aPriority = aState?.events?.priority ?? 1;
|
|
@@ -716,9 +1175,13 @@ function createEvents(store) {
|
|
|
716
1175
|
eventObject = eventObject.parent;
|
|
717
1176
|
}
|
|
718
1177
|
}
|
|
719
|
-
if ("pointerId" in event
|
|
720
|
-
|
|
721
|
-
|
|
1178
|
+
if ("pointerId" in event) {
|
|
1179
|
+
const pointerId = event.pointerId;
|
|
1180
|
+
const pointerState = state.internal.pointerMap.get(pointerId);
|
|
1181
|
+
if (pointerState?.captured.size) {
|
|
1182
|
+
for (const captureData of pointerState.captured.values()) {
|
|
1183
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
1184
|
+
}
|
|
722
1185
|
}
|
|
723
1186
|
}
|
|
724
1187
|
return intersections;
|
|
@@ -731,27 +1194,25 @@ function createEvents(store) {
|
|
|
731
1194
|
if (state) {
|
|
732
1195
|
const { raycaster, pointer, camera, internal } = state;
|
|
733
1196
|
const unprojectedPoint = new three.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
734
|
-
const hasPointerCapture = (id) =>
|
|
1197
|
+
const hasPointerCapture = (id) => {
|
|
1198
|
+
const pointerState = internal.pointerMap.get(id);
|
|
1199
|
+
return pointerState?.captured.has(hit.eventObject) ?? false;
|
|
1200
|
+
};
|
|
735
1201
|
const setPointerCapture = (id) => {
|
|
736
1202
|
const captureData = { intersection: hit, target: event.target };
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
} else {
|
|
740
|
-
internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
|
|
741
|
-
}
|
|
1203
|
+
const pointerState = getPointerState(internal, id);
|
|
1204
|
+
pointerState.captured.set(hit.eventObject, captureData);
|
|
742
1205
|
event.target.setPointerCapture(id);
|
|
743
1206
|
};
|
|
744
1207
|
const releasePointerCapture = (id) => {
|
|
745
|
-
|
|
746
|
-
if (captures) {
|
|
747
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
748
|
-
}
|
|
1208
|
+
releaseInternalPointerCapture(internal, hit.eventObject, id);
|
|
749
1209
|
};
|
|
750
1210
|
const extractEventProps = {};
|
|
751
1211
|
for (const prop in event) {
|
|
752
1212
|
const property = event[prop];
|
|
753
1213
|
if (typeof property !== "function") extractEventProps[prop] = property;
|
|
754
1214
|
}
|
|
1215
|
+
const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
|
|
755
1216
|
const raycastEvent = {
|
|
756
1217
|
...hit,
|
|
757
1218
|
...extractEventProps,
|
|
@@ -762,18 +1223,19 @@ function createEvents(store) {
|
|
|
762
1223
|
unprojectedPoint,
|
|
763
1224
|
ray: raycaster.ray,
|
|
764
1225
|
camera,
|
|
1226
|
+
pointerId: eventPointerId,
|
|
765
1227
|
// Hijack stopPropagation, which just sets a flag
|
|
766
1228
|
stopPropagation() {
|
|
767
|
-
const
|
|
1229
|
+
const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
|
|
768
1230
|
if (
|
|
769
1231
|
// ...if this pointer hasn't been captured
|
|
770
|
-
!
|
|
771
|
-
|
|
1232
|
+
!pointerState?.captured.size || // ... or if the hit object is capturing the pointer
|
|
1233
|
+
pointerState.captured.has(hit.eventObject)
|
|
772
1234
|
) {
|
|
773
1235
|
raycastEvent.stopped = localState.stopped = true;
|
|
774
|
-
if (
|
|
1236
|
+
if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
775
1237
|
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
776
|
-
cancelPointer([...higher, hit]);
|
|
1238
|
+
cancelPointer([...higher, hit], eventPointerId);
|
|
777
1239
|
}
|
|
778
1240
|
}
|
|
779
1241
|
},
|
|
@@ -789,15 +1251,18 @@ function createEvents(store) {
|
|
|
789
1251
|
}
|
|
790
1252
|
return intersections;
|
|
791
1253
|
}
|
|
792
|
-
function cancelPointer(intersections) {
|
|
1254
|
+
function cancelPointer(intersections, pointerId) {
|
|
793
1255
|
const { internal } = store.getState();
|
|
794
|
-
|
|
1256
|
+
const pid = pointerId ?? DEFAULT_POINTER_ID;
|
|
1257
|
+
const pointerState = internal.pointerMap.get(pid);
|
|
1258
|
+
if (!pointerState) return;
|
|
1259
|
+
for (const [hoveredId, hoveredObj] of pointerState.hovered) {
|
|
795
1260
|
if (!intersections.length || !intersections.find(
|
|
796
1261
|
(hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
|
|
797
1262
|
)) {
|
|
798
1263
|
const eventObject = hoveredObj.eventObject;
|
|
799
1264
|
const instance = eventObject.__r3f;
|
|
800
|
-
|
|
1265
|
+
pointerState.hovered.delete(hoveredId);
|
|
801
1266
|
if (instance?.eventCount) {
|
|
802
1267
|
const handlers = instance.handlers;
|
|
803
1268
|
const data = { ...hoveredObj, intersections };
|
|
@@ -826,41 +1291,118 @@ function createEvents(store) {
|
|
|
826
1291
|
instance?.handlers.onDropMissed?.(event);
|
|
827
1292
|
}
|
|
828
1293
|
}
|
|
1294
|
+
function cleanupPointer(pointerId) {
|
|
1295
|
+
const { internal } = store.getState();
|
|
1296
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1297
|
+
if (pointerState) {
|
|
1298
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1299
|
+
const eventObject = hoveredObj.eventObject;
|
|
1300
|
+
const instance = eventObject.__r3f;
|
|
1301
|
+
if (instance?.eventCount) {
|
|
1302
|
+
const handlers = instance.handlers;
|
|
1303
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1304
|
+
handlers.onPointerOut?.(data);
|
|
1305
|
+
handlers.onPointerLeave?.(data);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
internal.pointerMap.delete(pointerId);
|
|
1309
|
+
}
|
|
1310
|
+
internal.pointerDirty.delete(pointerId);
|
|
1311
|
+
}
|
|
1312
|
+
function processDeferredPointer(event, pointerId) {
|
|
1313
|
+
const state = store.getState();
|
|
1314
|
+
const { internal } = state;
|
|
1315
|
+
if (!state.events.enabled) return;
|
|
1316
|
+
const filter = filterPointerEvents;
|
|
1317
|
+
const hits = intersect(event, filter);
|
|
1318
|
+
cancelPointer(hits, pointerId);
|
|
1319
|
+
function onIntersect(data) {
|
|
1320
|
+
const eventObject = data.eventObject;
|
|
1321
|
+
const instance = eventObject.__r3f;
|
|
1322
|
+
if (!instance?.eventCount) return;
|
|
1323
|
+
const handlers = instance.handlers;
|
|
1324
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1325
|
+
const id = makeId(data);
|
|
1326
|
+
const pointerState = getPointerState(internal, pointerId);
|
|
1327
|
+
const hoveredItem = pointerState.hovered.get(id);
|
|
1328
|
+
if (!hoveredItem) {
|
|
1329
|
+
pointerState.hovered.set(id, data);
|
|
1330
|
+
handlers.onPointerOver?.(data);
|
|
1331
|
+
handlers.onPointerEnter?.(data);
|
|
1332
|
+
} else if (hoveredItem.stopped) {
|
|
1333
|
+
data.stopPropagation();
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
handlers.onPointerMove?.(data);
|
|
1337
|
+
}
|
|
1338
|
+
handleIntersects(hits, event, 0, onIntersect);
|
|
1339
|
+
}
|
|
829
1340
|
function handlePointer(name) {
|
|
830
1341
|
switch (name) {
|
|
831
1342
|
case "onPointerLeave":
|
|
832
|
-
case "onPointerCancel":
|
|
833
1343
|
case "onDragLeave":
|
|
834
1344
|
return () => cancelPointer([]);
|
|
1345
|
+
// Global cancel of these events
|
|
1346
|
+
case "onPointerCancel":
|
|
1347
|
+
return (event) => {
|
|
1348
|
+
const pointerId = getPointerId(event);
|
|
1349
|
+
cleanupPointer(pointerId);
|
|
1350
|
+
};
|
|
835
1351
|
case "onLostPointerCapture":
|
|
836
1352
|
return (event) => {
|
|
837
1353
|
const { internal } = store.getState();
|
|
838
|
-
|
|
1354
|
+
const pointerId = getPointerId(event);
|
|
1355
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1356
|
+
if (pointerState?.captured.size) {
|
|
839
1357
|
requestAnimationFrame(() => {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1358
|
+
const pointerState2 = internal.pointerMap.get(pointerId);
|
|
1359
|
+
if (pointerState2?.captured.size) {
|
|
1360
|
+
pointerState2.captured.clear();
|
|
843
1361
|
}
|
|
1362
|
+
cancelPointer([], pointerId);
|
|
844
1363
|
});
|
|
845
1364
|
}
|
|
846
1365
|
};
|
|
847
1366
|
}
|
|
848
1367
|
return function handleEvent(event) {
|
|
849
1368
|
const state = store.getState();
|
|
850
|
-
const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
|
|
1369
|
+
const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
|
|
1370
|
+
const pointerId = getPointerId(event);
|
|
851
1371
|
internal.lastEvent.current = event;
|
|
852
|
-
if (!
|
|
1372
|
+
if (!events.enabled) return;
|
|
853
1373
|
const isPointerMove = name === "onPointerMove";
|
|
854
1374
|
const isDragOver = name === "onDragOver";
|
|
855
1375
|
const isDrop = name === "onDrop";
|
|
856
1376
|
const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
|
|
1377
|
+
const isPointerDown = name === "onPointerDown";
|
|
1378
|
+
const isPointerUp = name === "onPointerUp";
|
|
1379
|
+
const isWheel = name === "onWheel";
|
|
1380
|
+
const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
|
|
1381
|
+
if (isPointerMove && canDeferRaycasts) {
|
|
1382
|
+
events.compute?.(event, state);
|
|
1383
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
|
|
1387
|
+
events.compute?.(event, state);
|
|
1388
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
|
|
1392
|
+
const deferredEvent = internal.pointerDirty.get(pointerId);
|
|
1393
|
+
internal.pointerDirty.delete(pointerId);
|
|
1394
|
+
processDeferredPointer(deferredEvent, pointerId);
|
|
1395
|
+
}
|
|
857
1396
|
const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
|
|
858
1397
|
const hits = intersect(event, filter);
|
|
859
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
860
|
-
if (
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1398
|
+
const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
|
|
1399
|
+
if (isPointerDown) {
|
|
1400
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1401
|
+
pointerState2.initialClick = [event.offsetX, event.offsetY];
|
|
1402
|
+
pointerState2.initialHits = hits.map((hit) => hit.eventObject);
|
|
1403
|
+
}
|
|
1404
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1405
|
+
const initialHits = pointerState?.initialHits ?? [];
|
|
864
1406
|
if (isClickEvent && !hits.length) {
|
|
865
1407
|
if (delta <= 2) {
|
|
866
1408
|
pointerMissed(event, internal.interaction);
|
|
@@ -875,7 +1417,9 @@ function createEvents(store) {
|
|
|
875
1417
|
dropMissed(event, internal.interaction);
|
|
876
1418
|
if (onDropMissed) onDropMissed(event);
|
|
877
1419
|
}
|
|
878
|
-
if (isPointerMove || isDragOver)
|
|
1420
|
+
if (isPointerMove || isDragOver) {
|
|
1421
|
+
cancelPointer(hits, pointerId);
|
|
1422
|
+
}
|
|
879
1423
|
function onIntersect(data) {
|
|
880
1424
|
const eventObject = data.eventObject;
|
|
881
1425
|
const instance = eventObject.__r3f;
|
|
@@ -884,9 +1428,10 @@ function createEvents(store) {
|
|
|
884
1428
|
if (isPointerMove) {
|
|
885
1429
|
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
886
1430
|
const id = makeId(data);
|
|
887
|
-
const
|
|
1431
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1432
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
888
1433
|
if (!hoveredItem) {
|
|
889
|
-
|
|
1434
|
+
pointerState2.hovered.set(id, data);
|
|
890
1435
|
handlers.onPointerOver?.(data);
|
|
891
1436
|
handlers.onPointerEnter?.(data);
|
|
892
1437
|
} else if (hoveredItem.stopped) {
|
|
@@ -896,9 +1441,10 @@ function createEvents(store) {
|
|
|
896
1441
|
handlers.onPointerMove?.(data);
|
|
897
1442
|
} else if (isDragOver) {
|
|
898
1443
|
const id = makeId(data);
|
|
899
|
-
const
|
|
1444
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1445
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
900
1446
|
if (!hoveredItem) {
|
|
901
|
-
|
|
1447
|
+
pointerState2.hovered.set(id, data);
|
|
902
1448
|
handlers.onDragOverEnter?.(data);
|
|
903
1449
|
} else if (hoveredItem.stopped) {
|
|
904
1450
|
data.stopPropagation();
|
|
@@ -909,18 +1455,18 @@ function createEvents(store) {
|
|
|
909
1455
|
} else {
|
|
910
1456
|
const handler = handlers[name];
|
|
911
1457
|
if (handler) {
|
|
912
|
-
if (!isClickEvent ||
|
|
1458
|
+
if (!isClickEvent || initialHits.includes(eventObject)) {
|
|
913
1459
|
pointerMissed(
|
|
914
1460
|
event,
|
|
915
|
-
internal.interaction.filter((object) => !
|
|
1461
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
916
1462
|
);
|
|
917
1463
|
handler(data);
|
|
918
1464
|
}
|
|
919
1465
|
} else {
|
|
920
|
-
if (isClickEvent &&
|
|
1466
|
+
if (isClickEvent && initialHits.includes(eventObject)) {
|
|
921
1467
|
pointerMissed(
|
|
922
1468
|
event,
|
|
923
|
-
internal.interaction.filter((object) => !
|
|
1469
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
924
1470
|
);
|
|
925
1471
|
}
|
|
926
1472
|
}
|
|
@@ -929,7 +1475,15 @@ function createEvents(store) {
|
|
|
929
1475
|
handleIntersects(hits, event, delta, onIntersect);
|
|
930
1476
|
};
|
|
931
1477
|
}
|
|
932
|
-
|
|
1478
|
+
function flushDeferredPointers() {
|
|
1479
|
+
const { internal, events } = store.getState();
|
|
1480
|
+
if (!events.frameTimedRaycasts) return;
|
|
1481
|
+
for (const [pointerId, event] of internal.pointerDirty) {
|
|
1482
|
+
processDeferredPointer(event, pointerId);
|
|
1483
|
+
}
|
|
1484
|
+
internal.pointerDirty.clear();
|
|
1485
|
+
}
|
|
1486
|
+
return { handlePointer, flushDeferredPointers, processDeferredPointer };
|
|
933
1487
|
}
|
|
934
1488
|
const DOM_EVENTS = {
|
|
935
1489
|
onClick: ["click", false],
|
|
@@ -948,11 +1502,16 @@ const DOM_EVENTS = {
|
|
|
948
1502
|
onLostPointerCapture: ["lostpointercapture", true]
|
|
949
1503
|
};
|
|
950
1504
|
function createPointerEvents(store) {
|
|
951
|
-
const { handlePointer } = createEvents(store);
|
|
1505
|
+
const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
|
|
1506
|
+
let nextXRPointerId = XR_POINTER_ID_START;
|
|
1507
|
+
const xrPointers = /* @__PURE__ */ new Map();
|
|
952
1508
|
return {
|
|
953
1509
|
priority: 1,
|
|
954
1510
|
enabled: true,
|
|
955
|
-
|
|
1511
|
+
frameTimedRaycasts: true,
|
|
1512
|
+
alwaysFireOnScroll: true,
|
|
1513
|
+
updateOnFrame: false,
|
|
1514
|
+
compute(event, state) {
|
|
956
1515
|
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
957
1516
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
958
1517
|
},
|
|
@@ -961,11 +1520,33 @@ function createPointerEvents(store) {
|
|
|
961
1520
|
(acc, key) => ({ ...acc, [key]: handlePointer(key) }),
|
|
962
1521
|
{}
|
|
963
1522
|
),
|
|
964
|
-
update: () => {
|
|
1523
|
+
update: (pointerId) => {
|
|
1524
|
+
const { events, internal } = store.getState();
|
|
1525
|
+
if (!events.handlers) return;
|
|
1526
|
+
if (pointerId !== void 0) {
|
|
1527
|
+
const event = internal.pointerDirty.get(pointerId);
|
|
1528
|
+
if (event) {
|
|
1529
|
+
internal.pointerDirty.delete(pointerId);
|
|
1530
|
+
processDeferredPointer(event, pointerId);
|
|
1531
|
+
} else if (internal.lastEvent?.current) {
|
|
1532
|
+
processDeferredPointer(internal.lastEvent.current, pointerId);
|
|
1533
|
+
}
|
|
1534
|
+
} else {
|
|
1535
|
+
flushDeferredPointers();
|
|
1536
|
+
if (internal.lastEvent?.current) {
|
|
1537
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
flush: () => {
|
|
965
1542
|
const { events, internal } = store.getState();
|
|
966
|
-
|
|
1543
|
+
flushDeferredPointers();
|
|
1544
|
+
if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
|
|
1545
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1546
|
+
}
|
|
967
1547
|
},
|
|
968
1548
|
connect: (target) => {
|
|
1549
|
+
if (!target) return;
|
|
969
1550
|
const { set, events } = store.getState();
|
|
970
1551
|
events.disconnect?.();
|
|
971
1552
|
set((state) => ({ events: { ...state.events, connected: target } }));
|
|
@@ -989,6 +1570,32 @@ function createPointerEvents(store) {
|
|
|
989
1570
|
}
|
|
990
1571
|
set((state) => ({ events: { ...state.events, connected: void 0 } }));
|
|
991
1572
|
}
|
|
1573
|
+
},
|
|
1574
|
+
registerPointer: (config) => {
|
|
1575
|
+
const pointerId = nextXRPointerId++;
|
|
1576
|
+
xrPointers.set(pointerId, config);
|
|
1577
|
+
const { internal } = store.getState();
|
|
1578
|
+
getPointerState(internal, pointerId);
|
|
1579
|
+
return pointerId;
|
|
1580
|
+
},
|
|
1581
|
+
unregisterPointer: (pointerId) => {
|
|
1582
|
+
xrPointers.delete(pointerId);
|
|
1583
|
+
const { internal } = store.getState();
|
|
1584
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1585
|
+
if (pointerState) {
|
|
1586
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1587
|
+
const eventObject = hoveredObj.eventObject;
|
|
1588
|
+
const instance = eventObject.__r3f;
|
|
1589
|
+
if (instance?.eventCount) {
|
|
1590
|
+
const handlers = instance.handlers;
|
|
1591
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1592
|
+
handlers.onPointerOut?.(data);
|
|
1593
|
+
handlers.onPointerLeave?.(data);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
internal.pointerMap.delete(pointerId);
|
|
1597
|
+
}
|
|
1598
|
+
internal.pointerDirty.delete(pointerId);
|
|
992
1599
|
}
|
|
993
1600
|
};
|
|
994
1601
|
}
|
|
@@ -1050,331 +1657,26 @@ function notifyAlpha({ message, link }) {
|
|
|
1050
1657
|
}
|
|
1051
1658
|
}
|
|
1052
1659
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
let performanceTimeout = void 0;
|
|
1076
|
-
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
1077
|
-
const pointer = new three.Vector2();
|
|
1078
|
-
const rootState = {
|
|
1079
|
-
set,
|
|
1080
|
-
get,
|
|
1081
|
-
// Mock objects that have to be configured
|
|
1082
|
-
gl: null,
|
|
1083
|
-
renderer: null,
|
|
1084
|
-
camera: null,
|
|
1085
|
-
frustum: new three.Frustum(),
|
|
1086
|
-
autoUpdateFrustum: true,
|
|
1087
|
-
raycaster: null,
|
|
1088
|
-
events: { priority: 1, enabled: true, connected: false },
|
|
1089
|
-
scene: null,
|
|
1090
|
-
rootScene: null,
|
|
1091
|
-
xr: null,
|
|
1092
|
-
inspector: null,
|
|
1093
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
1094
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
1095
|
-
legacy: false,
|
|
1096
|
-
linear: false,
|
|
1097
|
-
flat: false,
|
|
1098
|
-
textureColorSpace: "srgb",
|
|
1099
|
-
isLegacy: false,
|
|
1100
|
-
webGPUSupported: false,
|
|
1101
|
-
isNative: false,
|
|
1102
|
-
controls: null,
|
|
1103
|
-
pointer,
|
|
1104
|
-
mouse: pointer,
|
|
1105
|
-
frameloop: "always",
|
|
1106
|
-
onPointerMissed: void 0,
|
|
1107
|
-
onDragOverMissed: void 0,
|
|
1108
|
-
onDropMissed: void 0,
|
|
1109
|
-
performance: {
|
|
1110
|
-
current: 1,
|
|
1111
|
-
min: 0.5,
|
|
1112
|
-
max: 1,
|
|
1113
|
-
debounce: 200,
|
|
1114
|
-
regress: () => {
|
|
1115
|
-
const state2 = get();
|
|
1116
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
1117
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
1118
|
-
performanceTimeout = setTimeout(
|
|
1119
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
1120
|
-
state2.performance.debounce
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
},
|
|
1124
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1125
|
-
viewport: {
|
|
1126
|
-
initialDpr: 0,
|
|
1127
|
-
dpr: 0,
|
|
1128
|
-
width: 0,
|
|
1129
|
-
height: 0,
|
|
1130
|
-
top: 0,
|
|
1131
|
-
left: 0,
|
|
1132
|
-
aspect: 0,
|
|
1133
|
-
distance: 0,
|
|
1134
|
-
factor: 0,
|
|
1135
|
-
getCurrentViewport
|
|
1136
|
-
},
|
|
1137
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
1138
|
-
setSize: (width, height, top, left) => {
|
|
1139
|
-
const state2 = get();
|
|
1140
|
-
if (width === void 0) {
|
|
1141
|
-
set({ _sizeImperative: false });
|
|
1142
|
-
if (state2._sizeProps) {
|
|
1143
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
1144
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
1145
|
-
const currentSize = state2.size;
|
|
1146
|
-
const newSize = {
|
|
1147
|
-
width: propW ?? currentSize.width,
|
|
1148
|
-
height: propH ?? currentSize.height,
|
|
1149
|
-
top: currentSize.top,
|
|
1150
|
-
left: currentSize.left
|
|
1151
|
-
};
|
|
1152
|
-
set((s) => ({
|
|
1153
|
-
size: newSize,
|
|
1154
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
1155
|
-
}));
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
const w = width;
|
|
1161
|
-
const h = height ?? width;
|
|
1162
|
-
const t = top ?? state2.size.top;
|
|
1163
|
-
const l = left ?? state2.size.left;
|
|
1164
|
-
const size = { width: w, height: h, top: t, left: l };
|
|
1165
|
-
set((s) => ({
|
|
1166
|
-
size,
|
|
1167
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
1168
|
-
_sizeImperative: true
|
|
1169
|
-
}));
|
|
1170
|
-
},
|
|
1171
|
-
setDpr: (dpr) => set((state2) => {
|
|
1172
|
-
const resolved = calculateDpr(dpr);
|
|
1173
|
-
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
1174
|
-
}),
|
|
1175
|
-
setFrameloop: (frameloop = "always") => {
|
|
1176
|
-
set(() => ({ frameloop }));
|
|
1177
|
-
},
|
|
1178
|
-
setError: (error) => set(() => ({ error })),
|
|
1179
|
-
error: null,
|
|
1180
|
-
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
1181
|
-
uniforms: {},
|
|
1182
|
-
nodes: {},
|
|
1183
|
-
textures: /* @__PURE__ */ new Map(),
|
|
1184
|
-
postProcessing: null,
|
|
1185
|
-
passes: {},
|
|
1186
|
-
_hmrVersion: 0,
|
|
1187
|
-
_sizeImperative: false,
|
|
1188
|
-
_sizeProps: null,
|
|
1189
|
-
previousRoot: void 0,
|
|
1190
|
-
internal: {
|
|
1191
|
-
// Events
|
|
1192
|
-
interaction: [],
|
|
1193
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
1194
|
-
subscribers: [],
|
|
1195
|
-
initialClick: [0, 0],
|
|
1196
|
-
initialHits: [],
|
|
1197
|
-
capturedMap: /* @__PURE__ */ new Map(),
|
|
1198
|
-
lastEvent: React__namespace.createRef(),
|
|
1199
|
-
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1200
|
-
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1201
|
-
// Occlusion system (WebGPU only)
|
|
1202
|
-
occlusionEnabled: false,
|
|
1203
|
-
occlusionObserver: null,
|
|
1204
|
-
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1205
|
-
helperGroup: null,
|
|
1206
|
-
// Updates
|
|
1207
|
-
active: false,
|
|
1208
|
-
frames: 0,
|
|
1209
|
-
priority: 0,
|
|
1210
|
-
subscribe: (ref, priority, store) => {
|
|
1211
|
-
const internal = get().internal;
|
|
1212
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1213
|
-
internal.subscribers.push({ ref, priority, store });
|
|
1214
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
1215
|
-
return () => {
|
|
1216
|
-
const internal2 = get().internal;
|
|
1217
|
-
if (internal2?.subscribers) {
|
|
1218
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
1219
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
1220
|
-
}
|
|
1221
|
-
};
|
|
1222
|
-
},
|
|
1223
|
-
// Renderer Storage (single source of truth)
|
|
1224
|
-
actualRenderer: null,
|
|
1225
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
1226
|
-
scheduler: null
|
|
1227
|
-
}
|
|
1228
|
-
};
|
|
1229
|
-
return rootState;
|
|
1230
|
-
});
|
|
1231
|
-
const state = rootStore.getState();
|
|
1232
|
-
Object.defineProperty(state, "gl", {
|
|
1233
|
-
get() {
|
|
1234
|
-
const currentState = rootStore.getState();
|
|
1235
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
1236
|
-
const stack = new Error().stack || "";
|
|
1237
|
-
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
1238
|
-
if (!isInternalAccess) {
|
|
1239
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1240
|
-
notifyDepreciated({
|
|
1241
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1242
|
-
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
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
return currentState.internal.actualRenderer;
|
|
1247
|
-
},
|
|
1248
|
-
set(value) {
|
|
1249
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1250
|
-
},
|
|
1251
|
-
enumerable: true,
|
|
1252
|
-
configurable: true
|
|
1253
|
-
});
|
|
1254
|
-
Object.defineProperty(state, "renderer", {
|
|
1255
|
-
get() {
|
|
1256
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1257
|
-
},
|
|
1258
|
-
set(value) {
|
|
1259
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1260
|
-
},
|
|
1261
|
-
enumerable: true,
|
|
1262
|
-
configurable: true
|
|
1263
|
-
});
|
|
1264
|
-
let oldScene = state.scene;
|
|
1265
|
-
rootStore.subscribe(() => {
|
|
1266
|
-
const currentState = rootStore.getState();
|
|
1267
|
-
const { scene, rootScene, set } = currentState;
|
|
1268
|
-
if (scene !== oldScene) {
|
|
1269
|
-
oldScene = scene;
|
|
1270
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1271
|
-
set({ rootScene: scene });
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
});
|
|
1275
|
-
let oldSize = state.size;
|
|
1276
|
-
let oldDpr = state.viewport.dpr;
|
|
1277
|
-
let oldCamera = state.camera;
|
|
1278
|
-
rootStore.subscribe(() => {
|
|
1279
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1280
|
-
const actualRenderer = internal.actualRenderer;
|
|
1281
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1282
|
-
oldSize = size;
|
|
1283
|
-
oldDpr = viewport.dpr;
|
|
1284
|
-
updateCamera(camera, size);
|
|
1285
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1286
|
-
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
1287
|
-
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
1288
|
-
}
|
|
1289
|
-
if (camera !== oldCamera) {
|
|
1290
|
-
oldCamera = camera;
|
|
1291
|
-
const { rootScene } = rootStore.getState();
|
|
1292
|
-
if (camera && rootScene && !camera.parent) {
|
|
1293
|
-
rootScene.add(camera);
|
|
1294
|
-
}
|
|
1295
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1296
|
-
const currentState = rootStore.getState();
|
|
1297
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1298
|
-
updateFrustum(camera, currentState.frustum);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
});
|
|
1302
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1303
|
-
return rootStore;
|
|
1304
|
-
};
|
|
1305
|
-
|
|
1306
|
-
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
1307
|
-
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
1308
|
-
function getLoader(Proto) {
|
|
1309
|
-
if (isConstructor$1(Proto)) {
|
|
1310
|
-
let loader = memoizedLoaders.get(Proto);
|
|
1311
|
-
if (!loader) {
|
|
1312
|
-
loader = new Proto();
|
|
1313
|
-
memoizedLoaders.set(Proto, loader);
|
|
1314
|
-
}
|
|
1315
|
-
return loader;
|
|
1316
|
-
}
|
|
1317
|
-
return Proto;
|
|
1318
|
-
}
|
|
1319
|
-
function loadingFn(extensions, onProgress) {
|
|
1320
|
-
return function(Proto, input) {
|
|
1321
|
-
const loader = getLoader(Proto);
|
|
1322
|
-
if (extensions) extensions(loader);
|
|
1323
|
-
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
1324
|
-
return loader.loadAsync(input, onProgress).then((data) => {
|
|
1325
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1326
|
-
return data;
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
return new Promise(
|
|
1330
|
-
(res, reject) => loader.load(
|
|
1331
|
-
input,
|
|
1332
|
-
(data) => {
|
|
1333
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1334
|
-
res(data);
|
|
1335
|
-
},
|
|
1336
|
-
onProgress,
|
|
1337
|
-
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
1338
|
-
)
|
|
1339
|
-
);
|
|
1340
|
-
};
|
|
1341
|
-
}
|
|
1342
|
-
function useLoader(loader, input, extensions, onProgress) {
|
|
1343
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1344
|
-
const fn = loadingFn(extensions, onProgress);
|
|
1345
|
-
const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
|
|
1346
|
-
return Array.isArray(input) ? results : results[0];
|
|
1347
|
-
}
|
|
1348
|
-
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
1349
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1350
|
-
keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
1351
|
-
};
|
|
1352
|
-
useLoader.clear = function(loader, input) {
|
|
1353
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1354
|
-
keys.forEach((key) => suspendReact.clear([loader, key]));
|
|
1355
|
-
};
|
|
1356
|
-
useLoader.loader = getLoader;
|
|
1357
|
-
|
|
1358
|
-
var __defProp$1 = Object.defineProperty;
|
|
1359
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1360
|
-
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1361
|
-
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1362
|
-
class PhaseGraph {
|
|
1363
|
-
constructor() {
|
|
1364
|
-
/** Ordered list of phase nodes */
|
|
1365
|
-
__publicField$1(this, "phases", []);
|
|
1366
|
-
/** Quick lookup by name */
|
|
1367
|
-
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1368
|
-
/** Cached ordered names (invalidated on changes) */
|
|
1369
|
-
__publicField$1(this, "orderedNamesCache", null);
|
|
1370
|
-
this.initializeDefaultPhases();
|
|
1371
|
-
}
|
|
1372
|
-
//* Initialization --------------------------------
|
|
1373
|
-
initializeDefaultPhases() {
|
|
1374
|
-
for (const name of DEFAULT_PHASES) {
|
|
1375
|
-
const node = { name, isAutoGenerated: false };
|
|
1376
|
-
this.phases.push(node);
|
|
1377
|
-
this.phaseMap.set(name, node);
|
|
1660
|
+
var __defProp$1 = Object.defineProperty;
|
|
1661
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1662
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1663
|
+
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1664
|
+
class PhaseGraph {
|
|
1665
|
+
constructor() {
|
|
1666
|
+
/** Ordered list of phase nodes */
|
|
1667
|
+
__publicField$1(this, "phases", []);
|
|
1668
|
+
/** Quick lookup by name */
|
|
1669
|
+
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1670
|
+
/** Cached ordered names (invalidated on changes) */
|
|
1671
|
+
__publicField$1(this, "orderedNamesCache", null);
|
|
1672
|
+
this.initializeDefaultPhases();
|
|
1673
|
+
}
|
|
1674
|
+
//* Initialization --------------------------------
|
|
1675
|
+
initializeDefaultPhases() {
|
|
1676
|
+
for (const name of DEFAULT_PHASES) {
|
|
1677
|
+
const node = { name, isAutoGenerated: false };
|
|
1678
|
+
this.phases.push(node);
|
|
1679
|
+
this.phaseMap.set(name, node);
|
|
1378
1680
|
}
|
|
1379
1681
|
this.invalidateCache();
|
|
1380
1682
|
}
|
|
@@ -1607,7 +1909,7 @@ function shouldRun(job, now) {
|
|
|
1607
1909
|
const minInterval = 1e3 / job.fps;
|
|
1608
1910
|
const lastRun = job.lastRun ?? 0;
|
|
1609
1911
|
const elapsed = now - lastRun;
|
|
1610
|
-
if (elapsed < minInterval) return false;
|
|
1912
|
+
if (elapsed < minInterval - 1) return false;
|
|
1611
1913
|
if (job.drop) {
|
|
1612
1914
|
job.lastRun = now;
|
|
1613
1915
|
} else {
|
|
@@ -2276,116 +2578,444 @@ const _Scheduler = class _Scheduler {
|
|
|
2276
2578
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2277
2579
|
root.needsRebuild = false;
|
|
2278
2580
|
}
|
|
2279
|
-
const providedState = root.getState?.() ?? {};
|
|
2280
|
-
const frameState = {
|
|
2281
|
-
...providedState,
|
|
2282
|
-
time: timestamp,
|
|
2283
|
-
delta,
|
|
2284
|
-
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2285
|
-
// Convert ms to seconds
|
|
2286
|
-
frame: this.loopState.frameCount
|
|
2287
|
-
};
|
|
2288
|
-
for (const job of root.sortedJobs) {
|
|
2289
|
-
if (!shouldRun(job, timestamp)) continue;
|
|
2290
|
-
try {
|
|
2291
|
-
job.callback(frameState, delta);
|
|
2292
|
-
} catch (error) {
|
|
2293
|
-
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2294
|
-
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2581
|
+
const providedState = root.getState?.() ?? {};
|
|
2582
|
+
const frameState = {
|
|
2583
|
+
...providedState,
|
|
2584
|
+
time: timestamp,
|
|
2585
|
+
delta,
|
|
2586
|
+
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2587
|
+
// Convert ms to seconds
|
|
2588
|
+
frame: this.loopState.frameCount
|
|
2589
|
+
};
|
|
2590
|
+
for (const job of root.sortedJobs) {
|
|
2591
|
+
if (!shouldRun(job, timestamp)) continue;
|
|
2592
|
+
try {
|
|
2593
|
+
job.callback(frameState, delta);
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2596
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
//* Debug & Inspection Methods ================================
|
|
2601
|
+
/**
|
|
2602
|
+
* Get the total number of registered jobs across all roots.
|
|
2603
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2604
|
+
* @returns {number} Total job count
|
|
2605
|
+
*/
|
|
2606
|
+
getJobCount() {
|
|
2607
|
+
let count = 0;
|
|
2608
|
+
for (const root of this.roots.values()) {
|
|
2609
|
+
count += root.jobs.size;
|
|
2610
|
+
}
|
|
2611
|
+
return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Get all registered job IDs across all roots.
|
|
2615
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2616
|
+
* @returns {string[]} Array of all job IDs
|
|
2617
|
+
*/
|
|
2618
|
+
getJobIds() {
|
|
2619
|
+
const ids = [];
|
|
2620
|
+
for (const root of this.roots.values()) {
|
|
2621
|
+
ids.push(...root.jobs.keys());
|
|
2622
|
+
}
|
|
2623
|
+
ids.push(...this.globalBeforeJobs.keys());
|
|
2624
|
+
ids.push(...this.globalAfterJobs.keys());
|
|
2625
|
+
return ids;
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* Get the number of registered roots (Canvas instances).
|
|
2629
|
+
* @returns {number} Number of registered roots
|
|
2630
|
+
*/
|
|
2631
|
+
getRootCount() {
|
|
2632
|
+
return this.roots.size;
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2636
|
+
* Used by the default render job to know if a user has taken over rendering.
|
|
2637
|
+
*
|
|
2638
|
+
* @param phase The phase to check
|
|
2639
|
+
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2640
|
+
* @returns true if any user jobs exist in the phase
|
|
2641
|
+
*/
|
|
2642
|
+
hasUserJobsInPhase(phase, rootId) {
|
|
2643
|
+
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2644
|
+
return rootsToCheck.some((root) => {
|
|
2645
|
+
if (!root) return false;
|
|
2646
|
+
for (const job of root.jobs.values()) {
|
|
2647
|
+
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2648
|
+
}
|
|
2649
|
+
return false;
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
//* Utility Methods ================================
|
|
2653
|
+
/**
|
|
2654
|
+
* Generate a unique root ID for automatic root registration.
|
|
2655
|
+
* @returns {string} A unique root ID in the format 'root_N'
|
|
2656
|
+
*/
|
|
2657
|
+
generateRootId() {
|
|
2658
|
+
return `root_${this.nextRootIndex++}`;
|
|
2659
|
+
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Generate a unique job ID.
|
|
2662
|
+
* @returns {string} A unique job ID in the format 'job_N'
|
|
2663
|
+
* @private
|
|
2664
|
+
*/
|
|
2665
|
+
generateJobId() {
|
|
2666
|
+
return `job_${this.nextJobIndex}`;
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Normalize before/after constraints to a Set.
|
|
2670
|
+
* Handles undefined, single string, or array inputs.
|
|
2671
|
+
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2672
|
+
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2673
|
+
* @private
|
|
2674
|
+
*/
|
|
2675
|
+
normalizeConstraints(value) {
|
|
2676
|
+
if (!value) return /* @__PURE__ */ new Set();
|
|
2677
|
+
if (Array.isArray(value)) return new Set(value);
|
|
2678
|
+
return /* @__PURE__ */ new Set([value]);
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2682
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2683
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2684
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2685
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2686
|
+
let Scheduler = _Scheduler;
|
|
2687
|
+
const getScheduler = () => Scheduler.get();
|
|
2688
|
+
if (hmrData) {
|
|
2689
|
+
hmrData.accept?.();
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
2693
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
|
|
2694
|
+
const createStore = (invalidate, advance) => {
|
|
2695
|
+
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
2696
|
+
const position = new three.Vector3();
|
|
2697
|
+
const defaultTarget = new three.Vector3();
|
|
2698
|
+
const tempTarget = new three.Vector3();
|
|
2699
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
2700
|
+
const { width, height, top, left } = size;
|
|
2701
|
+
const aspect = width / height;
|
|
2702
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
2703
|
+
else tempTarget.set(...target);
|
|
2704
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2705
|
+
if (isOrthographicCamera(camera)) {
|
|
2706
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
2707
|
+
} else {
|
|
2708
|
+
const fov = camera.fov * Math.PI / 180;
|
|
2709
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
2710
|
+
const w = h * (width / height);
|
|
2711
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
let performanceTimeout = void 0;
|
|
2715
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
2716
|
+
const pointer = new three.Vector2();
|
|
2717
|
+
const rootState = {
|
|
2718
|
+
set,
|
|
2719
|
+
get,
|
|
2720
|
+
// Mock objects that have to be configured
|
|
2721
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
2722
|
+
primaryStore: null,
|
|
2723
|
+
gl: null,
|
|
2724
|
+
renderer: null,
|
|
2725
|
+
camera: null,
|
|
2726
|
+
frustum: new three.Frustum(),
|
|
2727
|
+
autoUpdateFrustum: true,
|
|
2728
|
+
raycaster: null,
|
|
2729
|
+
events: {
|
|
2730
|
+
priority: 1,
|
|
2731
|
+
enabled: true,
|
|
2732
|
+
connected: false,
|
|
2733
|
+
frameTimedRaycasts: true,
|
|
2734
|
+
alwaysFireOnScroll: true,
|
|
2735
|
+
updateOnFrame: false
|
|
2736
|
+
},
|
|
2737
|
+
scene: null,
|
|
2738
|
+
rootScene: null,
|
|
2739
|
+
xr: null,
|
|
2740
|
+
inspector: null,
|
|
2741
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
2742
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
2743
|
+
textureColorSpace: three.SRGBColorSpace,
|
|
2744
|
+
isLegacy: false,
|
|
2745
|
+
webGPUSupported: false,
|
|
2746
|
+
isNative: false,
|
|
2747
|
+
controls: null,
|
|
2748
|
+
pointer,
|
|
2749
|
+
mouse: pointer,
|
|
2750
|
+
frameloop: "always",
|
|
2751
|
+
onPointerMissed: void 0,
|
|
2752
|
+
onDragOverMissed: void 0,
|
|
2753
|
+
onDropMissed: void 0,
|
|
2754
|
+
performance: {
|
|
2755
|
+
current: 1,
|
|
2756
|
+
min: 0.5,
|
|
2757
|
+
max: 1,
|
|
2758
|
+
debounce: 200,
|
|
2759
|
+
regress: () => {
|
|
2760
|
+
const state2 = get();
|
|
2761
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
2762
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
2763
|
+
performanceTimeout = setTimeout(
|
|
2764
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
2765
|
+
state2.performance.debounce
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
},
|
|
2769
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2770
|
+
viewport: {
|
|
2771
|
+
initialDpr: 0,
|
|
2772
|
+
dpr: 0,
|
|
2773
|
+
width: 0,
|
|
2774
|
+
height: 0,
|
|
2775
|
+
top: 0,
|
|
2776
|
+
left: 0,
|
|
2777
|
+
aspect: 0,
|
|
2778
|
+
distance: 0,
|
|
2779
|
+
factor: 0,
|
|
2780
|
+
getCurrentViewport
|
|
2781
|
+
},
|
|
2782
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
2783
|
+
setSize: (width, height, top, left) => {
|
|
2784
|
+
const state2 = get();
|
|
2785
|
+
if (width === void 0) {
|
|
2786
|
+
set({ _sizeImperative: false });
|
|
2787
|
+
if (state2._sizeProps) {
|
|
2788
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
2789
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
2790
|
+
const currentSize = state2.size;
|
|
2791
|
+
const newSize = {
|
|
2792
|
+
width: propW ?? currentSize.width,
|
|
2793
|
+
height: propH ?? currentSize.height,
|
|
2794
|
+
top: currentSize.top,
|
|
2795
|
+
left: currentSize.left
|
|
2796
|
+
};
|
|
2797
|
+
set((s) => ({
|
|
2798
|
+
size: newSize,
|
|
2799
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
2800
|
+
}));
|
|
2801
|
+
getScheduler().invalidate();
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
const w = width;
|
|
2807
|
+
const h = height ?? width;
|
|
2808
|
+
const t = top ?? state2.size.top;
|
|
2809
|
+
const l = left ?? state2.size.left;
|
|
2810
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
2811
|
+
set((s) => ({
|
|
2812
|
+
size,
|
|
2813
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
2814
|
+
_sizeImperative: true
|
|
2815
|
+
}));
|
|
2816
|
+
getScheduler().invalidate();
|
|
2817
|
+
},
|
|
2818
|
+
setDpr: (dpr) => set((state2) => {
|
|
2819
|
+
const resolved = calculateDpr(dpr);
|
|
2820
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
2821
|
+
}),
|
|
2822
|
+
setFrameloop: (frameloop = "always") => {
|
|
2823
|
+
set(() => ({ frameloop }));
|
|
2824
|
+
},
|
|
2825
|
+
setError: (error) => set(() => ({ error })),
|
|
2826
|
+
error: null,
|
|
2827
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
|
|
2828
|
+
uniforms: {},
|
|
2829
|
+
nodes: {},
|
|
2830
|
+
buffers: {},
|
|
2831
|
+
gpuStorage: {},
|
|
2832
|
+
textures: /* @__PURE__ */ new Map(),
|
|
2833
|
+
renderPipeline: null,
|
|
2834
|
+
passes: {},
|
|
2835
|
+
_hmrVersion: 0,
|
|
2836
|
+
_sizeImperative: false,
|
|
2837
|
+
_sizeProps: null,
|
|
2838
|
+
previousRoot: void 0,
|
|
2839
|
+
internal: {
|
|
2840
|
+
// Events
|
|
2841
|
+
interaction: [],
|
|
2842
|
+
subscribers: [],
|
|
2843
|
+
// Per-pointer state (new unified structure)
|
|
2844
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
2845
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
2846
|
+
lastEvent: React__namespace.createRef(),
|
|
2847
|
+
// Deprecated but kept for backwards compatibility
|
|
2848
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2849
|
+
initialClick: [0, 0],
|
|
2850
|
+
initialHits: [],
|
|
2851
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
2852
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2853
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2854
|
+
// Occlusion system (WebGPU only)
|
|
2855
|
+
occlusionEnabled: false,
|
|
2856
|
+
occlusionObserver: null,
|
|
2857
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
2858
|
+
helperGroup: null,
|
|
2859
|
+
// Updates
|
|
2860
|
+
active: false,
|
|
2861
|
+
frames: 0,
|
|
2862
|
+
priority: 0,
|
|
2863
|
+
subscribe: (ref, priority, store) => {
|
|
2864
|
+
const internal = get().internal;
|
|
2865
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2866
|
+
internal.subscribers.push({ ref, priority, store });
|
|
2867
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
2868
|
+
return () => {
|
|
2869
|
+
const internal2 = get().internal;
|
|
2870
|
+
if (internal2?.subscribers) {
|
|
2871
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
2872
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
2873
|
+
}
|
|
2874
|
+
};
|
|
2875
|
+
},
|
|
2876
|
+
// Renderer Storage (single source of truth)
|
|
2877
|
+
actualRenderer: null,
|
|
2878
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
2879
|
+
scheduler: null
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
return rootState;
|
|
2883
|
+
});
|
|
2884
|
+
const state = rootStore.getState();
|
|
2885
|
+
Object.defineProperty(state, "gl", {
|
|
2886
|
+
get() {
|
|
2887
|
+
const currentState = rootStore.getState();
|
|
2888
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
2889
|
+
const stack = new Error().stack || "";
|
|
2890
|
+
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
2891
|
+
if (!isInternalAccess) {
|
|
2892
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
2893
|
+
notifyDepreciated({
|
|
2894
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
2895
|
+
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
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return currentState.internal.actualRenderer;
|
|
2900
|
+
},
|
|
2901
|
+
set(value) {
|
|
2902
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2903
|
+
},
|
|
2904
|
+
enumerable: true,
|
|
2905
|
+
configurable: true
|
|
2906
|
+
});
|
|
2907
|
+
Object.defineProperty(state, "renderer", {
|
|
2908
|
+
get() {
|
|
2909
|
+
return rootStore.getState().internal.actualRenderer;
|
|
2910
|
+
},
|
|
2911
|
+
set(value) {
|
|
2912
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2913
|
+
},
|
|
2914
|
+
enumerable: true,
|
|
2915
|
+
configurable: true
|
|
2916
|
+
});
|
|
2917
|
+
let oldScene = state.scene;
|
|
2918
|
+
rootStore.subscribe(() => {
|
|
2919
|
+
const currentState = rootStore.getState();
|
|
2920
|
+
const { scene, rootScene, set } = currentState;
|
|
2921
|
+
if (scene !== oldScene) {
|
|
2922
|
+
oldScene = scene;
|
|
2923
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
2924
|
+
set({ rootScene: scene });
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
});
|
|
2928
|
+
let oldSize = state.size;
|
|
2929
|
+
let oldDpr = state.viewport.dpr;
|
|
2930
|
+
let oldCamera = state.camera;
|
|
2931
|
+
rootStore.subscribe(() => {
|
|
2932
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
2933
|
+
const actualRenderer = internal.actualRenderer;
|
|
2934
|
+
const canvasTarget = internal.canvasTarget;
|
|
2935
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
2936
|
+
oldSize = size;
|
|
2937
|
+
oldDpr = viewport.dpr;
|
|
2938
|
+
updateCamera(camera, size);
|
|
2939
|
+
if (internal.isSecondary && canvasTarget) {
|
|
2940
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2941
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2942
|
+
} else {
|
|
2943
|
+
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2944
|
+
actualRenderer.setSize(size.width, size.height, false);
|
|
2945
|
+
if (canvasTarget) {
|
|
2946
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2947
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2948
|
+
}
|
|
2295
2949
|
}
|
|
2296
2950
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2951
|
+
if (camera !== oldCamera) {
|
|
2952
|
+
oldCamera = camera;
|
|
2953
|
+
const { rootScene } = rootStore.getState();
|
|
2954
|
+
if (camera && rootScene && !camera.parent) {
|
|
2955
|
+
rootScene.add(camera);
|
|
2956
|
+
}
|
|
2957
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
2958
|
+
const currentState = rootStore.getState();
|
|
2959
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
2960
|
+
updateFrustum(camera, currentState.frustum);
|
|
2961
|
+
}
|
|
2308
2962
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2963
|
+
});
|
|
2964
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
2965
|
+
return rootStore;
|
|
2966
|
+
};
|
|
2967
|
+
|
|
2968
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
2969
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
2970
|
+
function getLoader(Proto) {
|
|
2971
|
+
if (isConstructor$1(Proto)) {
|
|
2972
|
+
let loader = memoizedLoaders.get(Proto);
|
|
2973
|
+
if (!loader) {
|
|
2974
|
+
loader = new Proto();
|
|
2975
|
+
memoizedLoaders.set(Proto, loader);
|
|
2320
2976
|
}
|
|
2321
|
-
|
|
2322
|
-
ids.push(...this.globalAfterJobs.keys());
|
|
2323
|
-
return ids;
|
|
2324
|
-
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Get the number of registered roots (Canvas instances).
|
|
2327
|
-
* @returns {number} Number of registered roots
|
|
2328
|
-
*/
|
|
2329
|
-
getRootCount() {
|
|
2330
|
-
return this.roots.size;
|
|
2331
|
-
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2334
|
-
* Used by the default render job to know if a user has taken over rendering.
|
|
2335
|
-
*
|
|
2336
|
-
* @param phase The phase to check
|
|
2337
|
-
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2338
|
-
* @returns true if any user jobs exist in the phase
|
|
2339
|
-
*/
|
|
2340
|
-
hasUserJobsInPhase(phase, rootId) {
|
|
2341
|
-
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2342
|
-
return rootsToCheck.some((root) => {
|
|
2343
|
-
if (!root) return false;
|
|
2344
|
-
for (const job of root.jobs.values()) {
|
|
2345
|
-
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2346
|
-
}
|
|
2347
|
-
return false;
|
|
2348
|
-
});
|
|
2349
|
-
}
|
|
2350
|
-
//* Utility Methods ================================
|
|
2351
|
-
/**
|
|
2352
|
-
* Generate a unique root ID for automatic root registration.
|
|
2353
|
-
* @returns {string} A unique root ID in the format 'root_N'
|
|
2354
|
-
*/
|
|
2355
|
-
generateRootId() {
|
|
2356
|
-
return `root_${this.nextRootIndex++}`;
|
|
2357
|
-
}
|
|
2358
|
-
/**
|
|
2359
|
-
* Generate a unique job ID.
|
|
2360
|
-
* @returns {string} A unique job ID in the format 'job_N'
|
|
2361
|
-
* @private
|
|
2362
|
-
*/
|
|
2363
|
-
generateJobId() {
|
|
2364
|
-
return `job_${this.nextJobIndex}`;
|
|
2365
|
-
}
|
|
2366
|
-
/**
|
|
2367
|
-
* Normalize before/after constraints to a Set.
|
|
2368
|
-
* Handles undefined, single string, or array inputs.
|
|
2369
|
-
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2370
|
-
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2371
|
-
* @private
|
|
2372
|
-
*/
|
|
2373
|
-
normalizeConstraints(value) {
|
|
2374
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
2375
|
-
if (Array.isArray(value)) return new Set(value);
|
|
2376
|
-
return /* @__PURE__ */ new Set([value]);
|
|
2977
|
+
return loader;
|
|
2377
2978
|
}
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
if (
|
|
2387
|
-
|
|
2979
|
+
return Proto;
|
|
2980
|
+
}
|
|
2981
|
+
function loadingFn(extensions, onProgress) {
|
|
2982
|
+
return function(Proto, input) {
|
|
2983
|
+
const loader = getLoader(Proto);
|
|
2984
|
+
if (extensions) extensions(loader);
|
|
2985
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
2986
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
2987
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2988
|
+
return data;
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
return new Promise(
|
|
2992
|
+
(res, reject) => loader.load(
|
|
2993
|
+
input,
|
|
2994
|
+
(data) => {
|
|
2995
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2996
|
+
res(data);
|
|
2997
|
+
},
|
|
2998
|
+
onProgress,
|
|
2999
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
3000
|
+
)
|
|
3001
|
+
);
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
3005
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
3006
|
+
const fn = loadingFn(extensions, onProgress);
|
|
3007
|
+
const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
|
|
3008
|
+
return Array.isArray(input) ? results : results[0];
|
|
2388
3009
|
}
|
|
3010
|
+
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
3011
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
3012
|
+
keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
3013
|
+
};
|
|
3014
|
+
useLoader.clear = function(loader, input) {
|
|
3015
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
3016
|
+
keys.forEach((key) => suspendReact.clear([loader, key]));
|
|
3017
|
+
};
|
|
3018
|
+
useLoader.loader = getLoader;
|
|
2389
3019
|
|
|
2390
3020
|
function useFrame(callback, priorityOrOptions) {
|
|
2391
3021
|
const store = React__namespace.useContext(context);
|
|
@@ -2566,6 +3196,9 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2566
3196
|
const textureCache = useThree((state) => state.textures);
|
|
2567
3197
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2568
3198
|
const { onLoad, cache = false } = options;
|
|
3199
|
+
const onLoadRef = React.useRef(onLoad);
|
|
3200
|
+
onLoadRef.current = onLoad;
|
|
3201
|
+
const onLoadCalledForRef = React.useRef(null);
|
|
2569
3202
|
const urls = React.useMemo(() => getUrls(input), [input]);
|
|
2570
3203
|
const cachedResult = React.useMemo(() => {
|
|
2571
3204
|
if (!cache) return null;
|
|
@@ -2576,9 +3209,13 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2576
3209
|
three.TextureLoader,
|
|
2577
3210
|
IsObject(input) ? Object.values(input) : input
|
|
2578
3211
|
);
|
|
3212
|
+
const inputKey = urls.join("\0");
|
|
2579
3213
|
React.useLayoutEffect(() => {
|
|
2580
|
-
if (
|
|
2581
|
-
|
|
3214
|
+
if (cachedResult) return;
|
|
3215
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
3216
|
+
onLoadCalledForRef.current = inputKey;
|
|
3217
|
+
onLoadRef.current?.(loadedTextures);
|
|
3218
|
+
}, [cachedResult, loadedTextures, inputKey]);
|
|
2582
3219
|
React.useEffect(() => {
|
|
2583
3220
|
if (cachedResult) return;
|
|
2584
3221
|
if ("initTexture" in renderer) {
|
|
@@ -2745,14 +3382,31 @@ function useTextures() {
|
|
|
2745
3382
|
}, [store]);
|
|
2746
3383
|
}
|
|
2747
3384
|
|
|
2748
|
-
function useRenderTarget(
|
|
3385
|
+
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
2749
3386
|
const isLegacy = useThree((s) => s.isLegacy);
|
|
2750
3387
|
const size = useThree((s) => s.size);
|
|
3388
|
+
let width;
|
|
3389
|
+
let height;
|
|
3390
|
+
let opts;
|
|
3391
|
+
if (typeof widthOrOptions === "object") {
|
|
3392
|
+
opts = widthOrOptions;
|
|
3393
|
+
} else if (typeof widthOrOptions === "number") {
|
|
3394
|
+
width = widthOrOptions;
|
|
3395
|
+
if (typeof heightOrOptions === "object") {
|
|
3396
|
+
height = widthOrOptions;
|
|
3397
|
+
opts = heightOrOptions;
|
|
3398
|
+
} else if (typeof heightOrOptions === "number") {
|
|
3399
|
+
height = heightOrOptions;
|
|
3400
|
+
opts = options;
|
|
3401
|
+
} else {
|
|
3402
|
+
height = widthOrOptions;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
2751
3405
|
return React.useMemo(() => {
|
|
2752
3406
|
const w = width ?? size.width;
|
|
2753
3407
|
const h = height ?? size.height;
|
|
2754
|
-
return new three.WebGLRenderTarget(w, h,
|
|
2755
|
-
}, [width, height, size.width, size.height,
|
|
3408
|
+
return new three.WebGLRenderTarget(w, h, opts);
|
|
3409
|
+
}, [width, height, size.width, size.height, opts, isLegacy]);
|
|
2756
3410
|
}
|
|
2757
3411
|
|
|
2758
3412
|
function useStore() {
|
|
@@ -2802,7 +3456,7 @@ function addTail(callback) {
|
|
|
2802
3456
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
2803
3457
|
getScheduler().invalidate(frames, stackFrames);
|
|
2804
3458
|
}
|
|
2805
|
-
function advance(timestamp
|
|
3459
|
+
function advance(timestamp) {
|
|
2806
3460
|
getScheduler().step(timestamp);
|
|
2807
3461
|
}
|
|
2808
3462
|
|
|
@@ -14256,6 +14910,7 @@ function swapInstances() {
|
|
|
14256
14910
|
instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
|
|
14257
14911
|
instance.object.__r3f = instance;
|
|
14258
14912
|
setFiberRef(fiber, instance.object);
|
|
14913
|
+
delete instance.appliedOnce;
|
|
14259
14914
|
applyProps(instance.object, instance.props);
|
|
14260
14915
|
if (instance.props.attach) {
|
|
14261
14916
|
attach(parent, instance);
|
|
@@ -14329,8 +14984,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
14329
14984
|
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
14330
14985
|
if (isTailSibling) swapInstances();
|
|
14331
14986
|
},
|
|
14332
|
-
finalizeInitialChildren: () =>
|
|
14333
|
-
|
|
14987
|
+
finalizeInitialChildren: (instance) => {
|
|
14988
|
+
for (const prop in instance.props) {
|
|
14989
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
14990
|
+
}
|
|
14991
|
+
return false;
|
|
14992
|
+
},
|
|
14993
|
+
commitMount(instance) {
|
|
14994
|
+
const resolved = {};
|
|
14995
|
+
for (const prop in instance.props) {
|
|
14996
|
+
const value = instance.props[prop];
|
|
14997
|
+
if (isFromRef(value)) {
|
|
14998
|
+
const ref = value[FROM_REF];
|
|
14999
|
+
if (ref.current != null) resolved[prop] = ref.current;
|
|
15000
|
+
}
|
|
15001
|
+
}
|
|
15002
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
14334
15003
|
},
|
|
14335
15004
|
getPublicInstance: (instance) => instance?.object,
|
|
14336
15005
|
prepareForCommit: () => null,
|
|
@@ -14551,6 +15220,9 @@ function createRoot(canvas) {
|
|
|
14551
15220
|
let resolve;
|
|
14552
15221
|
pending = new Promise((_resolve) => resolve = _resolve);
|
|
14553
15222
|
const {
|
|
15223
|
+
id: canvasId,
|
|
15224
|
+
primaryCanvas,
|
|
15225
|
+
scheduler: schedulerConfig,
|
|
14554
15226
|
gl: glConfig,
|
|
14555
15227
|
renderer: rendererConfig,
|
|
14556
15228
|
size: propsSize,
|
|
@@ -14558,10 +15230,6 @@ function createRoot(canvas) {
|
|
|
14558
15230
|
events,
|
|
14559
15231
|
onCreated: onCreatedCallback,
|
|
14560
15232
|
shadows = false,
|
|
14561
|
-
linear = false,
|
|
14562
|
-
flat = false,
|
|
14563
|
-
textureColorSpace = three.SRGBColorSpace,
|
|
14564
|
-
legacy = false,
|
|
14565
15233
|
orthographic = false,
|
|
14566
15234
|
frameloop = "always",
|
|
14567
15235
|
dpr = [1, 2],
|
|
@@ -14573,8 +15241,10 @@ function createRoot(canvas) {
|
|
|
14573
15241
|
onDropMissed,
|
|
14574
15242
|
autoUpdateFrustum = true,
|
|
14575
15243
|
occlusion = false,
|
|
14576
|
-
_sizeProps
|
|
15244
|
+
_sizeProps,
|
|
15245
|
+
forceEven
|
|
14577
15246
|
} = props;
|
|
15247
|
+
const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || three.SRGBColorSpace;
|
|
14578
15248
|
const state = store.getState();
|
|
14579
15249
|
const defaultGLProps = {
|
|
14580
15250
|
canvas,
|
|
@@ -14587,15 +15257,25 @@ function createRoot(canvas) {
|
|
|
14587
15257
|
"WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
|
|
14588
15258
|
);
|
|
14589
15259
|
}
|
|
14590
|
-
(state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
15260
|
+
const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
|
|
14591
15261
|
if (glConfig && rendererConfig) {
|
|
14592
15262
|
throw new Error("Cannot use both gl and renderer props at the same time");
|
|
14593
15263
|
}
|
|
14594
15264
|
let renderer = state.internal.actualRenderer;
|
|
15265
|
+
if (primaryCanvas && !R3F_BUILD_WEBGPU) {
|
|
15266
|
+
throw new Error(
|
|
15267
|
+
"The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
|
|
15268
|
+
);
|
|
15269
|
+
}
|
|
15270
|
+
if (primaryCanvas && wantsGL) {
|
|
15271
|
+
throw new Error(
|
|
15272
|
+
"The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
|
|
15273
|
+
);
|
|
15274
|
+
}
|
|
14595
15275
|
if (!state.internal.actualRenderer) {
|
|
14596
15276
|
renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
|
|
14597
15277
|
state.internal.actualRenderer = renderer;
|
|
14598
|
-
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
15278
|
+
state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
|
|
14599
15279
|
}
|
|
14600
15280
|
let raycaster = state.raycaster;
|
|
14601
15281
|
if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
|
|
@@ -14604,6 +15284,7 @@ function createRoot(canvas) {
|
|
|
14604
15284
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
14605
15285
|
applyProps(raycaster, { params: { ...raycaster.params, ...params } });
|
|
14606
15286
|
}
|
|
15287
|
+
let tempCamera = state.camera;
|
|
14607
15288
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
14608
15289
|
lastCamera = cameraOptions;
|
|
14609
15290
|
const isCamera = cameraOptions?.isCamera;
|
|
@@ -14623,6 +15304,7 @@ function createRoot(canvas) {
|
|
|
14623
15304
|
if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
|
|
14624
15305
|
}
|
|
14625
15306
|
state.set({ camera });
|
|
15307
|
+
tempCamera = camera;
|
|
14626
15308
|
raycaster.camera = camera;
|
|
14627
15309
|
}
|
|
14628
15310
|
if (!state.scene) {
|
|
@@ -14640,7 +15322,7 @@ function createRoot(canvas) {
|
|
|
14640
15322
|
rootScene: scene,
|
|
14641
15323
|
internal: { ...prev.internal, container: scene }
|
|
14642
15324
|
}));
|
|
14643
|
-
const camera =
|
|
15325
|
+
const camera = tempCamera;
|
|
14644
15326
|
if (camera && !camera.parent) scene.add(camera);
|
|
14645
15327
|
}
|
|
14646
15328
|
if (events && !state.events.handlers) {
|
|
@@ -14657,6 +15339,9 @@ function createRoot(canvas) {
|
|
|
14657
15339
|
if (_sizeProps !== void 0) {
|
|
14658
15340
|
state.set({ _sizeProps });
|
|
14659
15341
|
}
|
|
15342
|
+
if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
|
|
15343
|
+
state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
|
|
15344
|
+
}
|
|
14660
15345
|
const size = computeInitialSize(canvas, propsSize);
|
|
14661
15346
|
if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
|
|
14662
15347
|
const wasImperative = state._sizeImperative;
|
|
@@ -14683,10 +15368,10 @@ function createRoot(canvas) {
|
|
|
14683
15368
|
lastConfiguredProps.performance = performance;
|
|
14684
15369
|
}
|
|
14685
15370
|
if (!state.xr) {
|
|
14686
|
-
const handleXRFrame = (timestamp,
|
|
15371
|
+
const handleXRFrame = (timestamp, _frame) => {
|
|
14687
15372
|
const state2 = store.getState();
|
|
14688
15373
|
if (state2.frameloop === "never") return;
|
|
14689
|
-
advance(timestamp
|
|
15374
|
+
advance(timestamp);
|
|
14690
15375
|
};
|
|
14691
15376
|
const actualRenderer = state.internal.actualRenderer;
|
|
14692
15377
|
const handleSessionChange = () => {
|
|
@@ -14698,16 +15383,16 @@ function createRoot(canvas) {
|
|
|
14698
15383
|
};
|
|
14699
15384
|
const xr = {
|
|
14700
15385
|
connect() {
|
|
14701
|
-
const { gl, renderer: renderer2
|
|
14702
|
-
const
|
|
14703
|
-
|
|
14704
|
-
|
|
15386
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15387
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15388
|
+
xrManager.addEventListener("sessionstart", handleSessionChange);
|
|
15389
|
+
xrManager.addEventListener("sessionend", handleSessionChange);
|
|
14705
15390
|
},
|
|
14706
15391
|
disconnect() {
|
|
14707
|
-
const { gl, renderer: renderer2
|
|
14708
|
-
const
|
|
14709
|
-
|
|
14710
|
-
|
|
15392
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15393
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15394
|
+
xrManager.removeEventListener("sessionstart", handleSessionChange);
|
|
15395
|
+
xrManager.removeEventListener("sessionend", handleSessionChange);
|
|
14711
15396
|
}
|
|
14712
15397
|
};
|
|
14713
15398
|
if (typeof renderer.xr?.addEventListener === "function") xr.connect();
|
|
@@ -14719,15 +15404,22 @@ function createRoot(canvas) {
|
|
|
14719
15404
|
const oldType = renderer.shadowMap.type;
|
|
14720
15405
|
renderer.shadowMap.enabled = !!shadows;
|
|
14721
15406
|
if (is.boo(shadows)) {
|
|
14722
|
-
renderer.shadowMap.type = three.
|
|
15407
|
+
renderer.shadowMap.type = three.PCFShadowMap;
|
|
14723
15408
|
} else if (is.str(shadows)) {
|
|
15409
|
+
if (shadows === "soft") {
|
|
15410
|
+
notifyDepreciated({
|
|
15411
|
+
heading: 'shadows="soft" is deprecated',
|
|
15412
|
+
body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
|
|
15413
|
+
link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
|
|
15414
|
+
});
|
|
15415
|
+
}
|
|
14724
15416
|
const types = {
|
|
14725
15417
|
basic: three.BasicShadowMap,
|
|
14726
15418
|
percentage: three.PCFShadowMap,
|
|
14727
|
-
soft: three.
|
|
15419
|
+
soft: three.PCFShadowMap,
|
|
14728
15420
|
variance: three.VSMShadowMap
|
|
14729
15421
|
};
|
|
14730
|
-
renderer.shadowMap.type = types[shadows] ?? three.
|
|
15422
|
+
renderer.shadowMap.type = types[shadows] ?? three.PCFShadowMap;
|
|
14731
15423
|
} else if (is.obj(shadows)) {
|
|
14732
15424
|
Object.assign(renderer.shadowMap, shadows);
|
|
14733
15425
|
}
|
|
@@ -14735,47 +15427,69 @@ function createRoot(canvas) {
|
|
|
14735
15427
|
renderer.shadowMap.needsUpdate = true;
|
|
14736
15428
|
}
|
|
14737
15429
|
}
|
|
14738
|
-
{
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
const flatChanged = flat !== lastConfiguredProps.flat;
|
|
14742
|
-
if (legacyChanged) {
|
|
14743
|
-
three.ColorManagement.enabled = !legacy;
|
|
14744
|
-
lastConfiguredProps.legacy = legacy;
|
|
14745
|
-
}
|
|
14746
|
-
if (!configured || linearChanged) {
|
|
14747
|
-
renderer.outputColorSpace = linear ? three.LinearSRGBColorSpace : three.SRGBColorSpace;
|
|
14748
|
-
lastConfiguredProps.linear = linear;
|
|
14749
|
-
}
|
|
14750
|
-
if (!configured || flatChanged) {
|
|
14751
|
-
renderer.toneMapping = flat ? three.NoToneMapping : three.ACESFilmicToneMapping;
|
|
14752
|
-
lastConfiguredProps.flat = flat;
|
|
14753
|
-
}
|
|
14754
|
-
if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
|
|
14755
|
-
if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
|
|
14756
|
-
if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
|
|
15430
|
+
if (!configured) {
|
|
15431
|
+
renderer.outputColorSpace = three.SRGBColorSpace;
|
|
15432
|
+
renderer.toneMapping = three.ACESFilmicToneMapping;
|
|
14757
15433
|
}
|
|
14758
15434
|
if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
|
|
14759
15435
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
14760
15436
|
lastConfiguredProps.textureColorSpace = textureColorSpace;
|
|
14761
15437
|
}
|
|
15438
|
+
const r3fProps = ["textureColorSpace"];
|
|
15439
|
+
const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
|
|
15440
|
+
const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
|
|
14762
15441
|
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
|
|
14763
|
-
|
|
15442
|
+
const glProps = {};
|
|
15443
|
+
for (const key in glConfig) {
|
|
15444
|
+
if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
|
|
15445
|
+
}
|
|
15446
|
+
applyProps(renderer, glProps);
|
|
14764
15447
|
}
|
|
14765
15448
|
if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
|
|
14766
15449
|
const currentRenderer = state.renderer;
|
|
14767
15450
|
if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
|
|
14768
|
-
|
|
15451
|
+
const rendererProps = {};
|
|
15452
|
+
for (const key in rendererConfig) {
|
|
15453
|
+
if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
|
|
15454
|
+
}
|
|
15455
|
+
applyProps(currentRenderer, rendererProps);
|
|
14769
15456
|
}
|
|
14770
15457
|
}
|
|
14771
15458
|
const scheduler = getScheduler();
|
|
14772
15459
|
const rootId = state.internal.rootId;
|
|
14773
15460
|
if (!rootId) {
|
|
14774
|
-
const newRootId = scheduler.generateRootId();
|
|
15461
|
+
const newRootId = canvasId || scheduler.generateRootId();
|
|
14775
15462
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14776
15463
|
getState: () => store.getState(),
|
|
14777
15464
|
onError: (err) => store.getState().setError(err)
|
|
14778
15465
|
});
|
|
15466
|
+
const unregisterCanvasTarget = scheduler.register(
|
|
15467
|
+
() => {
|
|
15468
|
+
const state2 = store.getState();
|
|
15469
|
+
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
15470
|
+
const renderer2 = state2.internal.actualRenderer;
|
|
15471
|
+
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
15472
|
+
}
|
|
15473
|
+
},
|
|
15474
|
+
{
|
|
15475
|
+
id: `${newRootId}_canvasTarget`,
|
|
15476
|
+
rootId: newRootId,
|
|
15477
|
+
phase: "start",
|
|
15478
|
+
system: true
|
|
15479
|
+
}
|
|
15480
|
+
);
|
|
15481
|
+
const unregisterEventsFlush = scheduler.register(
|
|
15482
|
+
() => {
|
|
15483
|
+
const state2 = store.getState();
|
|
15484
|
+
state2.events.flush?.();
|
|
15485
|
+
},
|
|
15486
|
+
{
|
|
15487
|
+
id: `${newRootId}_events`,
|
|
15488
|
+
rootId: newRootId,
|
|
15489
|
+
phase: "input",
|
|
15490
|
+
system: true
|
|
15491
|
+
}
|
|
15492
|
+
);
|
|
14779
15493
|
const unregisterFrustum = scheduler.register(
|
|
14780
15494
|
() => {
|
|
14781
15495
|
const state2 = store.getState();
|
|
@@ -14810,18 +15524,22 @@ function createRoot(canvas) {
|
|
|
14810
15524
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14811
15525
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14812
15526
|
try {
|
|
14813
|
-
if (state2.
|
|
15527
|
+
if (state2.renderPipeline?.render) state2.renderPipeline.render();
|
|
14814
15528
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14815
15529
|
} catch (error) {
|
|
14816
15530
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
14817
15531
|
}
|
|
14818
15532
|
},
|
|
14819
15533
|
{
|
|
14820
|
-
|
|
15534
|
+
// Use canvas ID directly as job ID if available, otherwise use generated rootId
|
|
15535
|
+
id: canvasId || `${newRootId}_render`,
|
|
14821
15536
|
rootId: newRootId,
|
|
14822
15537
|
phase: "render",
|
|
14823
|
-
system: true
|
|
15538
|
+
system: true,
|
|
14824
15539
|
// Internal flag: this is a system job, not user-controlled
|
|
15540
|
+
// Apply scheduler config for render ordering and rate limiting
|
|
15541
|
+
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
15542
|
+
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14825
15543
|
}
|
|
14826
15544
|
);
|
|
14827
15545
|
state.set((state2) => ({
|
|
@@ -14830,6 +15548,8 @@ function createRoot(canvas) {
|
|
|
14830
15548
|
rootId: newRootId,
|
|
14831
15549
|
unregisterRoot: () => {
|
|
14832
15550
|
unregisterRoot();
|
|
15551
|
+
unregisterCanvasTarget();
|
|
15552
|
+
unregisterEventsFlush();
|
|
14833
15553
|
unregisterFrustum();
|
|
14834
15554
|
unregisterVisibility();
|
|
14835
15555
|
unregisterRender();
|
|
@@ -14888,15 +15608,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14888
15608
|
const renderer = state.internal.actualRenderer;
|
|
14889
15609
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14890
15610
|
if (unregisterRoot) unregisterRoot();
|
|
15611
|
+
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
15612
|
+
if (unregisterPrimary) unregisterPrimary();
|
|
15613
|
+
const canvasTarget = state.internal.canvasTarget;
|
|
15614
|
+
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14891
15615
|
state.events.disconnect?.();
|
|
14892
15616
|
cleanupHelperGroup(root.store);
|
|
14893
|
-
renderer
|
|
14894
|
-
|
|
14895
|
-
|
|
15617
|
+
if (state.isLegacy && renderer) {
|
|
15618
|
+
;
|
|
15619
|
+
renderer.renderLists?.dispose?.();
|
|
15620
|
+
renderer.forceContextLoss?.();
|
|
15621
|
+
}
|
|
15622
|
+
if (!state.internal.isSecondary) {
|
|
15623
|
+
if (renderer?.xr) state.xr.disconnect();
|
|
15624
|
+
}
|
|
14896
15625
|
dispose(state.scene);
|
|
14897
15626
|
_roots.delete(canvas);
|
|
14898
15627
|
if (callback) callback(canvas);
|
|
14899
|
-
} catch
|
|
15628
|
+
} catch {
|
|
14900
15629
|
}
|
|
14901
15630
|
}, 500);
|
|
14902
15631
|
}
|
|
@@ -14904,36 +15633,34 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14904
15633
|
}
|
|
14905
15634
|
}
|
|
14906
15635
|
function createPortal(children, container, state) {
|
|
14907
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15636
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
|
|
14908
15637
|
}
|
|
14909
|
-
function
|
|
15638
|
+
function Portal({ children, container, state }) {
|
|
14910
15639
|
const isRef = React.useCallback((obj) => obj && "current" in obj, []);
|
|
14911
|
-
const [resolvedContainer,
|
|
15640
|
+
const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
|
|
14912
15641
|
if (isRef(container)) return container.current ?? null;
|
|
14913
15642
|
return container;
|
|
14914
15643
|
});
|
|
15644
|
+
const setResolvedContainer = React.useCallback(
|
|
15645
|
+
(newContainer) => {
|
|
15646
|
+
if (!newContainer || newContainer === resolvedContainer) return;
|
|
15647
|
+
_setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
|
|
15648
|
+
},
|
|
15649
|
+
[resolvedContainer, _setResolvedContainer, isRef]
|
|
15650
|
+
);
|
|
14915
15651
|
React.useMemo(() => {
|
|
14916
|
-
if (isRef(container)) {
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
const updated = container.current;
|
|
14921
|
-
if (updated && updated !== resolvedContainer) {
|
|
14922
|
-
setResolvedContainer(updated);
|
|
14923
|
-
}
|
|
14924
|
-
});
|
|
14925
|
-
} else if (current !== resolvedContainer) {
|
|
14926
|
-
setResolvedContainer(current);
|
|
14927
|
-
}
|
|
14928
|
-
} else if (container !== resolvedContainer) {
|
|
14929
|
-
setResolvedContainer(container);
|
|
15652
|
+
if (isRef(container) && !container.current) {
|
|
15653
|
+
return queueMicrotask(() => {
|
|
15654
|
+
setResolvedContainer(container.current);
|
|
15655
|
+
});
|
|
14930
15656
|
}
|
|
14931
|
-
|
|
15657
|
+
setResolvedContainer(container);
|
|
15658
|
+
}, [container, isRef, setResolvedContainer]);
|
|
14932
15659
|
if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
14933
15660
|
const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
|
|
14934
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15661
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
|
|
14935
15662
|
}
|
|
14936
|
-
function
|
|
15663
|
+
function PortalInner({ state = {}, children, container }) {
|
|
14937
15664
|
const { events, size, injectScene = true, ...rest } = state;
|
|
14938
15665
|
const previousRoot = useStore();
|
|
14939
15666
|
const [raycaster] = React.useState(() => new three.Raycaster());
|
|
@@ -14954,11 +15681,12 @@ function Portal({ state = {}, children, container }) {
|
|
|
14954
15681
|
};
|
|
14955
15682
|
}, [portalScene, container, injectScene]);
|
|
14956
15683
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
15684
|
+
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14957
15685
|
let viewport = void 0;
|
|
14958
|
-
if (injectState.camera && size) {
|
|
15686
|
+
if (injectState.camera && (size || injectState.size)) {
|
|
14959
15687
|
const camera = injectState.camera;
|
|
14960
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(),
|
|
14961
|
-
if (camera !== rootState.camera) updateCamera(camera,
|
|
15688
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), resolvedSize);
|
|
15689
|
+
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14962
15690
|
}
|
|
14963
15691
|
return {
|
|
14964
15692
|
// The intersect consists of the previous root state
|
|
@@ -14975,7 +15703,7 @@ function Portal({ state = {}, children, container }) {
|
|
|
14975
15703
|
previousRoot,
|
|
14976
15704
|
// Events, size and viewport can be overridden by the inject layer
|
|
14977
15705
|
events: { ...rootState.events, ...injectState.events, ...events },
|
|
14978
|
-
size:
|
|
15706
|
+
size: resolvedSize,
|
|
14979
15707
|
viewport: { ...rootState.viewport, ...viewport },
|
|
14980
15708
|
// Layers are allowed to override events
|
|
14981
15709
|
setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
|
|
@@ -14987,9 +15715,13 @@ function Portal({ state = {}, children, container }) {
|
|
|
14987
15715
|
const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
|
|
14988
15716
|
const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
|
|
14989
15717
|
onMutate(previousRoot.getState());
|
|
14990
|
-
previousRoot.subscribe(onMutate);
|
|
14991
15718
|
return store;
|
|
14992
15719
|
}, [previousRoot, container]);
|
|
15720
|
+
useIsomorphicLayoutEffect(() => {
|
|
15721
|
+
const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
|
|
15722
|
+
const unsubscribe = previousRoot.subscribe(onMutate);
|
|
15723
|
+
return unsubscribe;
|
|
15724
|
+
}, [previousRoot, usePortalStore]);
|
|
14993
15725
|
return (
|
|
14994
15726
|
// @ts-ignore, reconciler types are not maintained
|
|
14995
15727
|
/* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
|
|
@@ -15009,15 +15741,13 @@ function CanvasImpl({
|
|
|
15009
15741
|
fallback,
|
|
15010
15742
|
resize,
|
|
15011
15743
|
style,
|
|
15744
|
+
id,
|
|
15012
15745
|
gl,
|
|
15013
|
-
renderer,
|
|
15746
|
+
renderer: rendererProp,
|
|
15014
15747
|
events = createPointerEvents,
|
|
15015
15748
|
eventSource,
|
|
15016
15749
|
eventPrefix,
|
|
15017
15750
|
shadows,
|
|
15018
|
-
linear,
|
|
15019
|
-
flat,
|
|
15020
|
-
legacy,
|
|
15021
15751
|
orthographic,
|
|
15022
15752
|
frameloop,
|
|
15023
15753
|
dpr,
|
|
@@ -15032,10 +15762,53 @@ function CanvasImpl({
|
|
|
15032
15762
|
hmr,
|
|
15033
15763
|
width,
|
|
15034
15764
|
height,
|
|
15765
|
+
background,
|
|
15766
|
+
forceEven,
|
|
15035
15767
|
...props
|
|
15036
15768
|
}) {
|
|
15769
|
+
const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
|
|
15770
|
+
let primaryCanvas;
|
|
15771
|
+
let scheduler;
|
|
15772
|
+
let renderer;
|
|
15773
|
+
if (isRendererConfig) {
|
|
15774
|
+
const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
|
|
15775
|
+
primaryCanvas = pc;
|
|
15776
|
+
scheduler = sc;
|
|
15777
|
+
renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
|
|
15778
|
+
} else {
|
|
15779
|
+
renderer = rendererProp;
|
|
15780
|
+
}
|
|
15037
15781
|
React__namespace.useMemo(() => extend(THREE), []);
|
|
15038
15782
|
const Bridge = useBridge();
|
|
15783
|
+
const backgroundProps = React__namespace.useMemo(() => {
|
|
15784
|
+
if (!background) return null;
|
|
15785
|
+
if (typeof background === "object" && !background.isColor) {
|
|
15786
|
+
const { backgroundMap, envMap, files, preset, ...rest } = background;
|
|
15787
|
+
return {
|
|
15788
|
+
...rest,
|
|
15789
|
+
preset,
|
|
15790
|
+
files: envMap || files,
|
|
15791
|
+
backgroundFiles: backgroundMap,
|
|
15792
|
+
background: true
|
|
15793
|
+
};
|
|
15794
|
+
}
|
|
15795
|
+
if (typeof background === "number") {
|
|
15796
|
+
return { color: background, background: true };
|
|
15797
|
+
}
|
|
15798
|
+
if (typeof background === "string") {
|
|
15799
|
+
if (background in presetsObj) {
|
|
15800
|
+
return { preset: background, background: true };
|
|
15801
|
+
}
|
|
15802
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
|
|
15803
|
+
return { files: background, background: true };
|
|
15804
|
+
}
|
|
15805
|
+
return { color: background, background: true };
|
|
15806
|
+
}
|
|
15807
|
+
if (background.isColor) {
|
|
15808
|
+
return { color: background, background: true };
|
|
15809
|
+
}
|
|
15810
|
+
return null;
|
|
15811
|
+
}, [background]);
|
|
15039
15812
|
const hasInitialSizeRef = React__namespace.useRef(false);
|
|
15040
15813
|
const measureConfig = React__namespace.useMemo(() => {
|
|
15041
15814
|
if (!hasInitialSizeRef.current) {
|
|
@@ -15052,15 +15825,20 @@ function CanvasImpl({
|
|
|
15052
15825
|
};
|
|
15053
15826
|
}, [resize, hasInitialSizeRef.current]);
|
|
15054
15827
|
const [containerRef, containerRect] = useMeasure__default(measureConfig);
|
|
15055
|
-
const effectiveSize = React__namespace.useMemo(
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15828
|
+
const effectiveSize = React__namespace.useMemo(() => {
|
|
15829
|
+
let w = width ?? containerRect.width;
|
|
15830
|
+
let h = height ?? containerRect.height;
|
|
15831
|
+
if (forceEven) {
|
|
15832
|
+
w = Math.ceil(w / 2) * 2;
|
|
15833
|
+
h = Math.ceil(h / 2) * 2;
|
|
15834
|
+
}
|
|
15835
|
+
return {
|
|
15836
|
+
width: w,
|
|
15837
|
+
height: h,
|
|
15059
15838
|
top: containerRect.top,
|
|
15060
15839
|
left: containerRect.left
|
|
15061
|
-
}
|
|
15062
|
-
|
|
15063
|
-
);
|
|
15840
|
+
};
|
|
15841
|
+
}, [width, height, containerRect, forceEven]);
|
|
15064
15842
|
if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
|
|
15065
15843
|
hasInitialSizeRef.current = true;
|
|
15066
15844
|
}
|
|
@@ -15100,14 +15878,14 @@ function CanvasImpl({
|
|
|
15100
15878
|
async function run() {
|
|
15101
15879
|
if (!effectActiveRef.current || !root.current) return;
|
|
15102
15880
|
await root.current.configure({
|
|
15881
|
+
id,
|
|
15882
|
+
primaryCanvas,
|
|
15883
|
+
scheduler,
|
|
15103
15884
|
gl,
|
|
15104
15885
|
renderer,
|
|
15105
15886
|
scene,
|
|
15106
15887
|
events,
|
|
15107
15888
|
shadows,
|
|
15108
|
-
linear,
|
|
15109
|
-
flat,
|
|
15110
|
-
legacy,
|
|
15111
15889
|
orthographic,
|
|
15112
15890
|
frameloop,
|
|
15113
15891
|
dpr,
|
|
@@ -15117,6 +15895,7 @@ function CanvasImpl({
|
|
|
15117
15895
|
size: effectiveSize,
|
|
15118
15896
|
// Store size props for reset functionality
|
|
15119
15897
|
_sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
|
|
15898
|
+
forceEven,
|
|
15120
15899
|
// Pass mutable reference to onPointerMissed so it's free to update
|
|
15121
15900
|
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
|
|
15122
15901
|
onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
|
|
@@ -15140,7 +15919,10 @@ function CanvasImpl({
|
|
|
15140
15919
|
});
|
|
15141
15920
|
if (!effectActiveRef.current || !root.current) return;
|
|
15142
15921
|
root.current.render(
|
|
15143
|
-
/* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.
|
|
15922
|
+
/* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
|
|
15923
|
+
backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
|
|
15924
|
+
children ?? null
|
|
15925
|
+
] }) }) })
|
|
15144
15926
|
);
|
|
15145
15927
|
}
|
|
15146
15928
|
run();
|
|
@@ -15167,20 +15949,22 @@ function CanvasImpl({
|
|
|
15167
15949
|
const canvas = canvasRef.current;
|
|
15168
15950
|
if (!canvas) return;
|
|
15169
15951
|
const handleHMR = () => {
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
rootEntry
|
|
15173
|
-
nodes
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15952
|
+
queueMicrotask(() => {
|
|
15953
|
+
const rootEntry = _roots.get(canvas);
|
|
15954
|
+
if (rootEntry?.store) {
|
|
15955
|
+
console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
|
|
15956
|
+
rootEntry.store.setState((state) => ({
|
|
15957
|
+
nodes: {},
|
|
15958
|
+
uniforms: {},
|
|
15959
|
+
_hmrVersion: state._hmrVersion + 1
|
|
15960
|
+
}));
|
|
15961
|
+
}
|
|
15962
|
+
});
|
|
15178
15963
|
};
|
|
15179
15964
|
if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('legacy.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
|
|
15180
15965
|
const hot = undefined;
|
|
15181
15966
|
hot.on("vite:afterUpdate", handleHMR);
|
|
15182
|
-
return () => hot.
|
|
15183
|
-
});
|
|
15967
|
+
return () => hot.off?.("vite:afterUpdate", handleHMR);
|
|
15184
15968
|
}
|
|
15185
15969
|
if (typeof module !== "undefined" && module.hot) {
|
|
15186
15970
|
const hot = module.hot;
|
|
@@ -15203,7 +15987,16 @@ function CanvasImpl({
|
|
|
15203
15987
|
...style
|
|
15204
15988
|
},
|
|
15205
15989
|
...props,
|
|
15206
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15990
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15991
|
+
"canvas",
|
|
15992
|
+
{
|
|
15993
|
+
ref: canvasRef,
|
|
15994
|
+
id,
|
|
15995
|
+
className: "r3f-canvas",
|
|
15996
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
15997
|
+
children: fallback
|
|
15998
|
+
}
|
|
15999
|
+
) })
|
|
15207
16000
|
}
|
|
15208
16001
|
);
|
|
15209
16002
|
}
|
|
@@ -15215,8 +16008,15 @@ extend(THREE);
|
|
|
15215
16008
|
|
|
15216
16009
|
exports.Block = Block;
|
|
15217
16010
|
exports.Canvas = Canvas;
|
|
16011
|
+
exports.Environment = Environment;
|
|
16012
|
+
exports.EnvironmentCube = EnvironmentCube;
|
|
16013
|
+
exports.EnvironmentMap = EnvironmentMap;
|
|
16014
|
+
exports.EnvironmentPortal = EnvironmentPortal;
|
|
15218
16015
|
exports.ErrorBoundary = ErrorBoundary;
|
|
16016
|
+
exports.FROM_REF = FROM_REF;
|
|
15219
16017
|
exports.IsObject = IsObject;
|
|
16018
|
+
exports.ONCE = ONCE;
|
|
16019
|
+
exports.Portal = Portal;
|
|
15220
16020
|
exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
|
|
15221
16021
|
exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
|
|
15222
16022
|
exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
|
|
@@ -15246,30 +16046,41 @@ exports.events = createPointerEvents;
|
|
|
15246
16046
|
exports.extend = extend;
|
|
15247
16047
|
exports.findInitialRoot = findInitialRoot;
|
|
15248
16048
|
exports.flushSync = flushSync;
|
|
16049
|
+
exports.fromRef = fromRef;
|
|
15249
16050
|
exports.getInstanceProps = getInstanceProps;
|
|
16051
|
+
exports.getPrimary = getPrimary;
|
|
16052
|
+
exports.getPrimaryIds = getPrimaryIds;
|
|
15250
16053
|
exports.getRootState = getRootState;
|
|
15251
16054
|
exports.getScheduler = getScheduler;
|
|
15252
16055
|
exports.getUuidPrefix = getUuidPrefix;
|
|
15253
16056
|
exports.hasConstructor = hasConstructor;
|
|
16057
|
+
exports.hasPrimary = hasPrimary;
|
|
15254
16058
|
exports.invalidate = invalidate;
|
|
15255
16059
|
exports.invalidateInstance = invalidateInstance;
|
|
15256
16060
|
exports.is = is;
|
|
15257
16061
|
exports.isColorRepresentation = isColorRepresentation;
|
|
15258
16062
|
exports.isCopyable = isCopyable;
|
|
16063
|
+
exports.isFromRef = isFromRef;
|
|
15259
16064
|
exports.isObject3D = isObject3D;
|
|
16065
|
+
exports.isOnce = isOnce;
|
|
15260
16066
|
exports.isOrthographicCamera = isOrthographicCamera;
|
|
15261
16067
|
exports.isRef = isRef;
|
|
15262
16068
|
exports.isRenderer = isRenderer;
|
|
15263
16069
|
exports.isTexture = isTexture;
|
|
15264
16070
|
exports.isVectorLike = isVectorLike;
|
|
16071
|
+
exports.once = once;
|
|
15265
16072
|
exports.prepare = prepare;
|
|
16073
|
+
exports.presetsObj = presetsObj;
|
|
15266
16074
|
exports.reconciler = reconciler;
|
|
16075
|
+
exports.registerPrimary = registerPrimary;
|
|
15267
16076
|
exports.removeInteractivity = removeInteractivity;
|
|
15268
16077
|
exports.resolve = resolve;
|
|
15269
16078
|
exports.unmountComponentAtNode = unmountComponentAtNode;
|
|
16079
|
+
exports.unregisterPrimary = unregisterPrimary;
|
|
15270
16080
|
exports.updateCamera = updateCamera;
|
|
15271
16081
|
exports.updateFrustum = updateFrustum;
|
|
15272
16082
|
exports.useBridge = useBridge;
|
|
16083
|
+
exports.useEnvironment = useEnvironment;
|
|
15273
16084
|
exports.useFrame = useFrame;
|
|
15274
16085
|
exports.useGraph = useGraph;
|
|
15275
16086
|
exports.useInstanceHandle = useInstanceHandle;
|
|
@@ -15281,3 +16092,4 @@ exports.useStore = useStore;
|
|
|
15281
16092
|
exports.useTexture = useTexture;
|
|
15282
16093
|
exports.useTextures = useTextures;
|
|
15283
16094
|
exports.useThree = useThree;
|
|
16095
|
+
exports.waitForPrimary = waitForPrimary;
|