@react-three/fiber 10.0.0-alpha.2 → 10.0.0-canary.1b98c17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/dist/index.cjs +1454 -618
- package/dist/index.d.cts +2130 -1285
- package/dist/index.d.mts +2130 -1285
- package/dist/index.d.ts +2130 -1285
- package/dist/index.mjs +1439 -622
- package/dist/legacy.cjs +1420 -608
- package/dist/legacy.d.cts +2131 -1286
- package/dist/legacy.d.mts +2131 -1286
- package/dist/legacy.d.ts +2131 -1286
- package/dist/legacy.mjs +1405 -612
- package/dist/webgpu/index.cjs +1841 -577
- package/dist/webgpu/index.d.cts +2310 -1312
- package/dist/webgpu/index.d.mts +2310 -1312
- package/dist/webgpu/index.d.ts +2310 -1312
- package/dist/webgpu/index.mjs +1821 -580
- package/package.json +3 -1
- package/readme.md +244 -318
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,12 @@ const jsxRuntime = require('react/jsx-runtime');
|
|
|
7
7
|
const React = require('react');
|
|
8
8
|
const useMeasure = require('react-use-measure');
|
|
9
9
|
const itsFine = require('its-fine');
|
|
10
|
+
const fiber = require('@react-three/fiber');
|
|
11
|
+
const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
|
|
12
|
+
const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
|
|
13
|
+
const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
|
|
14
|
+
const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
|
|
15
|
+
const gainmapJs = require('@monogrid/gainmap-js');
|
|
10
16
|
const Tb = require('scheduler');
|
|
11
17
|
const traditional = require('zustand/traditional');
|
|
12
18
|
const suspendReact = require('suspend-react');
|
|
@@ -57,6 +63,389 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
57
63
|
WebGLRenderer: three.WebGLRenderer
|
|
58
64
|
}, [webgpu__namespace]);
|
|
59
65
|
|
|
66
|
+
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
67
|
+
const pendingSubscribers = /* @__PURE__ */ new Map();
|
|
68
|
+
function registerPrimary(id, renderer, store) {
|
|
69
|
+
if (primaryRegistry.has(id)) {
|
|
70
|
+
console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
|
|
71
|
+
}
|
|
72
|
+
const entry = { renderer, store };
|
|
73
|
+
primaryRegistry.set(id, entry);
|
|
74
|
+
const subscribers = pendingSubscribers.get(id);
|
|
75
|
+
if (subscribers) {
|
|
76
|
+
subscribers.forEach((callback) => callback(entry));
|
|
77
|
+
pendingSubscribers.delete(id);
|
|
78
|
+
}
|
|
79
|
+
return () => {
|
|
80
|
+
const currentEntry = primaryRegistry.get(id);
|
|
81
|
+
if (currentEntry?.renderer === renderer) {
|
|
82
|
+
primaryRegistry.delete(id);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function getPrimary(id) {
|
|
87
|
+
return primaryRegistry.get(id);
|
|
88
|
+
}
|
|
89
|
+
function waitForPrimary(id, timeout = 5e3) {
|
|
90
|
+
const existing = primaryRegistry.get(id);
|
|
91
|
+
if (existing) {
|
|
92
|
+
return Promise.resolve(existing);
|
|
93
|
+
}
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
const timeoutId = setTimeout(() => {
|
|
96
|
+
const subscribers = pendingSubscribers.get(id);
|
|
97
|
+
if (subscribers) {
|
|
98
|
+
const index = subscribers.indexOf(callback);
|
|
99
|
+
if (index !== -1) subscribers.splice(index, 1);
|
|
100
|
+
if (subscribers.length === 0) pendingSubscribers.delete(id);
|
|
101
|
+
}
|
|
102
|
+
reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
|
|
103
|
+
}, timeout);
|
|
104
|
+
const callback = (entry) => {
|
|
105
|
+
clearTimeout(timeoutId);
|
|
106
|
+
resolve(entry);
|
|
107
|
+
};
|
|
108
|
+
if (!pendingSubscribers.has(id)) {
|
|
109
|
+
pendingSubscribers.set(id, []);
|
|
110
|
+
}
|
|
111
|
+
pendingSubscribers.get(id).push(callback);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function hasPrimary(id) {
|
|
115
|
+
return primaryRegistry.has(id);
|
|
116
|
+
}
|
|
117
|
+
function unregisterPrimary(id) {
|
|
118
|
+
primaryRegistry.delete(id);
|
|
119
|
+
}
|
|
120
|
+
function getPrimaryIds() {
|
|
121
|
+
return Array.from(primaryRegistry.keys());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const presetsObj = {
|
|
125
|
+
apartment: "lebombo_1k.hdr",
|
|
126
|
+
city: "potsdamer_platz_1k.hdr",
|
|
127
|
+
dawn: "kiara_1_dawn_1k.hdr",
|
|
128
|
+
forest: "forest_slope_1k.hdr",
|
|
129
|
+
lobby: "st_fagans_interior_1k.hdr",
|
|
130
|
+
night: "dikhololo_night_1k.hdr",
|
|
131
|
+
park: "rooitou_park_1k.hdr",
|
|
132
|
+
studio: "studio_small_03_1k.hdr",
|
|
133
|
+
sunset: "venice_sunset_1k.hdr",
|
|
134
|
+
warehouse: "empty_warehouse_01_1k.hdr"
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
|
|
138
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
139
|
+
const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
|
|
140
|
+
function useEnvironment({
|
|
141
|
+
files = defaultFiles,
|
|
142
|
+
path = "",
|
|
143
|
+
preset = void 0,
|
|
144
|
+
colorSpace = void 0,
|
|
145
|
+
extensions
|
|
146
|
+
} = {}) {
|
|
147
|
+
if (preset) {
|
|
148
|
+
validatePreset(preset);
|
|
149
|
+
files = presetsObj[preset];
|
|
150
|
+
path = CUBEMAP_ROOT;
|
|
151
|
+
}
|
|
152
|
+
const multiFile = isArray(files);
|
|
153
|
+
const { extension, isCubemap } = getExtension(files);
|
|
154
|
+
const loader = getLoader$1(extension);
|
|
155
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
156
|
+
const renderer = fiber.useThree((state) => state.renderer);
|
|
157
|
+
React.useLayoutEffect(() => {
|
|
158
|
+
if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
|
|
159
|
+
function clearGainmapTexture() {
|
|
160
|
+
fiber.useLoader.clear(loader, multiFile ? [files] : files);
|
|
161
|
+
}
|
|
162
|
+
renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
|
|
163
|
+
}, [extension, files, loader, multiFile, renderer.domElement]);
|
|
164
|
+
const loaderResult = fiber.useLoader(
|
|
165
|
+
loader,
|
|
166
|
+
multiFile ? [files] : files,
|
|
167
|
+
(loader2) => {
|
|
168
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
169
|
+
loader2.setRenderer?.(renderer);
|
|
170
|
+
}
|
|
171
|
+
loader2.setPath?.(path);
|
|
172
|
+
if (extensions) extensions(loader2);
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
let texture = multiFile ? (
|
|
176
|
+
// @ts-ignore
|
|
177
|
+
loaderResult[0]
|
|
178
|
+
) : loaderResult;
|
|
179
|
+
if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
|
|
180
|
+
texture = texture.renderTarget?.texture;
|
|
181
|
+
}
|
|
182
|
+
texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
|
|
183
|
+
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
184
|
+
return texture;
|
|
185
|
+
}
|
|
186
|
+
const preloadDefaultOptions = {
|
|
187
|
+
files: defaultFiles,
|
|
188
|
+
path: "",
|
|
189
|
+
preset: void 0,
|
|
190
|
+
extensions: void 0
|
|
191
|
+
};
|
|
192
|
+
useEnvironment.preload = (preloadOptions) => {
|
|
193
|
+
const options = { ...preloadDefaultOptions, ...preloadOptions };
|
|
194
|
+
let { files, path = "" } = options;
|
|
195
|
+
const { preset, extensions } = options;
|
|
196
|
+
if (preset) {
|
|
197
|
+
validatePreset(preset);
|
|
198
|
+
files = presetsObj[preset];
|
|
199
|
+
path = CUBEMAP_ROOT;
|
|
200
|
+
}
|
|
201
|
+
const { extension } = getExtension(files);
|
|
202
|
+
if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
|
|
203
|
+
throw new Error("useEnvironment: Preloading gainmaps is not supported");
|
|
204
|
+
}
|
|
205
|
+
const loader = getLoader$1(extension);
|
|
206
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
207
|
+
fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
|
|
208
|
+
loader2.setPath?.(path);
|
|
209
|
+
if (extensions) extensions(loader2);
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
const clearDefaultOptins = {
|
|
213
|
+
files: defaultFiles,
|
|
214
|
+
preset: void 0
|
|
215
|
+
};
|
|
216
|
+
useEnvironment.clear = (clearOptions) => {
|
|
217
|
+
const options = { ...clearDefaultOptins, ...clearOptions };
|
|
218
|
+
let { files } = options;
|
|
219
|
+
const { preset } = options;
|
|
220
|
+
if (preset) {
|
|
221
|
+
validatePreset(preset);
|
|
222
|
+
files = presetsObj[preset];
|
|
223
|
+
}
|
|
224
|
+
const { extension } = getExtension(files);
|
|
225
|
+
const loader = getLoader$1(extension);
|
|
226
|
+
if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
|
|
227
|
+
fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
|
|
228
|
+
};
|
|
229
|
+
function validatePreset(preset) {
|
|
230
|
+
if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
|
|
231
|
+
}
|
|
232
|
+
function getExtension(files) {
|
|
233
|
+
const isCubemap = isArray(files) && files.length === 6;
|
|
234
|
+
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
235
|
+
const firstEntry = isArray(files) ? files[0] : files;
|
|
236
|
+
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();
|
|
237
|
+
return { extension, isCubemap, isGainmap };
|
|
238
|
+
}
|
|
239
|
+
function getLoader$1(extension) {
|
|
240
|
+
const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
|
|
241
|
+
return loader;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const isRef$1 = (obj) => obj.current && obj.current.isScene;
|
|
245
|
+
const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
|
|
246
|
+
function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
|
|
247
|
+
sceneProps = {
|
|
248
|
+
backgroundBlurriness: 0,
|
|
249
|
+
backgroundIntensity: 1,
|
|
250
|
+
backgroundRotation: [0, 0, 0],
|
|
251
|
+
environmentIntensity: 1,
|
|
252
|
+
environmentRotation: [0, 0, 0],
|
|
253
|
+
...sceneProps
|
|
254
|
+
};
|
|
255
|
+
const target = resolveScene(scene || defaultScene);
|
|
256
|
+
const oldbg = target.background;
|
|
257
|
+
const oldenv = target.environment;
|
|
258
|
+
const oldSceneProps = {
|
|
259
|
+
// @ts-ignore
|
|
260
|
+
backgroundBlurriness: target.backgroundBlurriness,
|
|
261
|
+
// @ts-ignore
|
|
262
|
+
backgroundIntensity: target.backgroundIntensity,
|
|
263
|
+
// @ts-ignore
|
|
264
|
+
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
environmentIntensity: target.environmentIntensity,
|
|
267
|
+
// @ts-ignore
|
|
268
|
+
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
269
|
+
};
|
|
270
|
+
if (background !== "only") target.environment = texture;
|
|
271
|
+
if (background) target.background = texture;
|
|
272
|
+
fiber.applyProps(target, sceneProps);
|
|
273
|
+
return () => {
|
|
274
|
+
if (background !== "only") target.environment = oldenv;
|
|
275
|
+
if (background) target.background = oldbg;
|
|
276
|
+
fiber.applyProps(target, oldSceneProps);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function EnvironmentMap({ scene, background = false, map, ...config }) {
|
|
280
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
281
|
+
React__namespace.useLayoutEffect(() => {
|
|
282
|
+
if (map) return setEnvProps(background, scene, defaultScene, map, config);
|
|
283
|
+
});
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
function EnvironmentCube({
|
|
287
|
+
background = false,
|
|
288
|
+
scene,
|
|
289
|
+
blur,
|
|
290
|
+
backgroundBlurriness,
|
|
291
|
+
backgroundIntensity,
|
|
292
|
+
backgroundRotation,
|
|
293
|
+
environmentIntensity,
|
|
294
|
+
environmentRotation,
|
|
295
|
+
...rest
|
|
296
|
+
}) {
|
|
297
|
+
const texture = useEnvironment(rest);
|
|
298
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
299
|
+
React__namespace.useLayoutEffect(() => {
|
|
300
|
+
return setEnvProps(background, scene, defaultScene, texture, {
|
|
301
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
302
|
+
backgroundIntensity,
|
|
303
|
+
backgroundRotation,
|
|
304
|
+
environmentIntensity,
|
|
305
|
+
environmentRotation
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
React__namespace.useEffect(() => {
|
|
309
|
+
return () => {
|
|
310
|
+
texture.dispose();
|
|
311
|
+
};
|
|
312
|
+
}, [texture]);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
function EnvironmentPortal({
|
|
316
|
+
children,
|
|
317
|
+
near = 0.1,
|
|
318
|
+
far = 1e3,
|
|
319
|
+
resolution = 256,
|
|
320
|
+
frames = 1,
|
|
321
|
+
map,
|
|
322
|
+
background = false,
|
|
323
|
+
blur,
|
|
324
|
+
backgroundBlurriness,
|
|
325
|
+
backgroundIntensity,
|
|
326
|
+
backgroundRotation,
|
|
327
|
+
environmentIntensity,
|
|
328
|
+
environmentRotation,
|
|
329
|
+
scene,
|
|
330
|
+
files,
|
|
331
|
+
path,
|
|
332
|
+
preset = void 0,
|
|
333
|
+
extensions
|
|
334
|
+
}) {
|
|
335
|
+
const gl = fiber.useThree((state) => state.gl);
|
|
336
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
337
|
+
const camera = React__namespace.useRef(null);
|
|
338
|
+
const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
|
|
339
|
+
const fbo = React__namespace.useMemo(() => {
|
|
340
|
+
const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
|
|
341
|
+
fbo2.texture.type = webgpu.HalfFloatType;
|
|
342
|
+
return fbo2;
|
|
343
|
+
}, [resolution]);
|
|
344
|
+
React__namespace.useEffect(() => {
|
|
345
|
+
return () => {
|
|
346
|
+
fbo.dispose();
|
|
347
|
+
};
|
|
348
|
+
}, [fbo]);
|
|
349
|
+
React__namespace.useLayoutEffect(() => {
|
|
350
|
+
if (frames === 1) {
|
|
351
|
+
const autoClear = gl.autoClear;
|
|
352
|
+
gl.autoClear = true;
|
|
353
|
+
camera.current.update(gl, virtualScene);
|
|
354
|
+
gl.autoClear = autoClear;
|
|
355
|
+
}
|
|
356
|
+
return setEnvProps(background, scene, defaultScene, fbo.texture, {
|
|
357
|
+
backgroundBlurriness: blur ?? backgroundBlurriness,
|
|
358
|
+
backgroundIntensity,
|
|
359
|
+
backgroundRotation,
|
|
360
|
+
environmentIntensity,
|
|
361
|
+
environmentRotation
|
|
362
|
+
});
|
|
363
|
+
}, [
|
|
364
|
+
children,
|
|
365
|
+
virtualScene,
|
|
366
|
+
fbo.texture,
|
|
367
|
+
scene,
|
|
368
|
+
defaultScene,
|
|
369
|
+
background,
|
|
370
|
+
frames,
|
|
371
|
+
gl,
|
|
372
|
+
blur,
|
|
373
|
+
backgroundBlurriness,
|
|
374
|
+
backgroundIntensity,
|
|
375
|
+
backgroundRotation,
|
|
376
|
+
environmentIntensity,
|
|
377
|
+
environmentRotation
|
|
378
|
+
]);
|
|
379
|
+
let count = 1;
|
|
380
|
+
fiber.useFrame(() => {
|
|
381
|
+
if (frames === Infinity || count < frames) {
|
|
382
|
+
const autoClear = gl.autoClear;
|
|
383
|
+
gl.autoClear = true;
|
|
384
|
+
camera.current.update(gl, virtualScene);
|
|
385
|
+
gl.autoClear = autoClear;
|
|
386
|
+
count++;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
|
|
390
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
391
|
+
children,
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
|
|
393
|
+
files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
|
|
394
|
+
] }),
|
|
395
|
+
virtualScene
|
|
396
|
+
) });
|
|
397
|
+
}
|
|
398
|
+
function EnvironmentGround(props) {
|
|
399
|
+
const textureDefault = useEnvironment(props);
|
|
400
|
+
const texture = props.map || textureDefault;
|
|
401
|
+
React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
|
|
402
|
+
React__namespace.useEffect(() => {
|
|
403
|
+
return () => {
|
|
404
|
+
textureDefault.dispose();
|
|
405
|
+
};
|
|
406
|
+
}, [textureDefault]);
|
|
407
|
+
const height = props.ground?.height ?? 15;
|
|
408
|
+
const radius = props.ground?.radius ?? 60;
|
|
409
|
+
const scale = props.ground?.scale ?? 1e3;
|
|
410
|
+
const args = React__namespace.useMemo(
|
|
411
|
+
() => [texture, height, radius],
|
|
412
|
+
[texture, height, radius]
|
|
413
|
+
);
|
|
414
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
|
|
416
|
+
/* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
|
|
417
|
+
] });
|
|
418
|
+
}
|
|
419
|
+
function EnvironmentColor({ color, scene }) {
|
|
420
|
+
const defaultScene = fiber.useThree((state) => state.scene);
|
|
421
|
+
React__namespace.useLayoutEffect(() => {
|
|
422
|
+
if (color === void 0) return;
|
|
423
|
+
const target = resolveScene(scene || defaultScene);
|
|
424
|
+
const oldBg = target.background;
|
|
425
|
+
target.background = new webgpu.Color(color);
|
|
426
|
+
return () => {
|
|
427
|
+
target.background = oldBg;
|
|
428
|
+
};
|
|
429
|
+
});
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function EnvironmentDualSource(props) {
|
|
433
|
+
const { backgroundFiles, ...envProps } = props;
|
|
434
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
435
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
|
|
436
|
+
/* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
|
|
437
|
+
] });
|
|
438
|
+
}
|
|
439
|
+
function Environment(props) {
|
|
440
|
+
if (props.color && !props.files && !props.preset && !props.map) {
|
|
441
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
|
|
442
|
+
}
|
|
443
|
+
if (props.backgroundFiles && props.backgroundFiles !== props.files) {
|
|
444
|
+
return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
|
|
445
|
+
}
|
|
446
|
+
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 });
|
|
447
|
+
}
|
|
448
|
+
|
|
60
449
|
var __defProp$2 = Object.defineProperty;
|
|
61
450
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
62
451
|
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -236,7 +625,8 @@ function prepare(target, root, type, props) {
|
|
|
236
625
|
object,
|
|
237
626
|
eventCount: 0,
|
|
238
627
|
handlers: {},
|
|
239
|
-
isHidden: false
|
|
628
|
+
isHidden: false,
|
|
629
|
+
deferredRefs: []
|
|
240
630
|
};
|
|
241
631
|
if (object) object.__r3f = instance;
|
|
242
632
|
}
|
|
@@ -285,7 +675,7 @@ function createOcclusionObserverNode(store, uniform) {
|
|
|
285
675
|
let occlusionSetupPromise = null;
|
|
286
676
|
function enableOcclusion(store) {
|
|
287
677
|
const state = store.getState();
|
|
288
|
-
const { internal, renderer
|
|
678
|
+
const { internal, renderer } = state;
|
|
289
679
|
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
290
680
|
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
291
681
|
if (!hasOcclusionSupport) {
|
|
@@ -448,6 +838,22 @@ function hasVisibilityHandlers(handlers) {
|
|
|
448
838
|
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
449
839
|
}
|
|
450
840
|
|
|
841
|
+
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
842
|
+
function fromRef(ref) {
|
|
843
|
+
return { [FROM_REF]: ref };
|
|
844
|
+
}
|
|
845
|
+
function isFromRef(value) {
|
|
846
|
+
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const ONCE = Symbol.for("@react-three/fiber.once");
|
|
850
|
+
function once(...args) {
|
|
851
|
+
return { [ONCE]: args.length ? args : true };
|
|
852
|
+
}
|
|
853
|
+
function isOnce(value) {
|
|
854
|
+
return value !== null && typeof value === "object" && ONCE in value;
|
|
855
|
+
}
|
|
856
|
+
|
|
451
857
|
const RESERVED_PROPS = [
|
|
452
858
|
"children",
|
|
453
859
|
"key",
|
|
@@ -518,7 +924,7 @@ function getMemoizedPrototype(root) {
|
|
|
518
924
|
ctor = new root.constructor();
|
|
519
925
|
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
520
926
|
}
|
|
521
|
-
} catch
|
|
927
|
+
} catch {
|
|
522
928
|
}
|
|
523
929
|
return ctor;
|
|
524
930
|
}
|
|
@@ -564,6 +970,25 @@ function applyProps(object, props) {
|
|
|
564
970
|
continue;
|
|
565
971
|
}
|
|
566
972
|
if (value === void 0) continue;
|
|
973
|
+
if (isFromRef(value)) {
|
|
974
|
+
instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (isOnce(value)) {
|
|
978
|
+
if (instance?.appliedOnce?.has(prop)) continue;
|
|
979
|
+
if (instance) {
|
|
980
|
+
instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
|
|
981
|
+
instance.appliedOnce.add(prop);
|
|
982
|
+
}
|
|
983
|
+
const { root: targetRoot, key: targetKey } = resolve(object, prop);
|
|
984
|
+
const args = value[ONCE];
|
|
985
|
+
if (typeof targetRoot[targetKey] === "function") {
|
|
986
|
+
targetRoot[targetKey](...args === true ? [] : args);
|
|
987
|
+
} else if (args !== true && args.length > 0) {
|
|
988
|
+
targetRoot[targetKey] = args[0];
|
|
989
|
+
}
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
567
992
|
let { root, key, target } = resolve(object, prop);
|
|
568
993
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
569
994
|
throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
|
|
@@ -586,7 +1011,10 @@ function applyProps(object, props) {
|
|
|
586
1011
|
else target.set(value);
|
|
587
1012
|
} else {
|
|
588
1013
|
root[key] = value;
|
|
589
|
-
if (
|
|
1014
|
+
if (key.endsWith("Node") && root.isMaterial) {
|
|
1015
|
+
root.needsUpdate = true;
|
|
1016
|
+
}
|
|
1017
|
+
if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
590
1018
|
root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
|
|
591
1019
|
root[key].colorSpace = rootState.textureColorSpace;
|
|
592
1020
|
}
|
|
@@ -619,38 +1047,60 @@ function applyProps(object, props) {
|
|
|
619
1047
|
return object;
|
|
620
1048
|
}
|
|
621
1049
|
|
|
1050
|
+
const DEFAULT_POINTER_ID = 0;
|
|
1051
|
+
const XR_POINTER_ID_START = 1e3;
|
|
1052
|
+
function getPointerState(internal, pointerId) {
|
|
1053
|
+
let state = internal.pointerMap.get(pointerId);
|
|
1054
|
+
if (!state) {
|
|
1055
|
+
state = {
|
|
1056
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
1057
|
+
captured: /* @__PURE__ */ new Map(),
|
|
1058
|
+
initialClick: [0, 0],
|
|
1059
|
+
initialHits: []
|
|
1060
|
+
};
|
|
1061
|
+
internal.pointerMap.set(pointerId, state);
|
|
1062
|
+
}
|
|
1063
|
+
return state;
|
|
1064
|
+
}
|
|
1065
|
+
function getPointerId(event) {
|
|
1066
|
+
return "pointerId" in event ? event.pointerId : DEFAULT_POINTER_ID;
|
|
1067
|
+
}
|
|
622
1068
|
function makeId(event) {
|
|
623
1069
|
return (event.eventObject || event.object).uuid + "/" + event.index + event.instanceId;
|
|
624
1070
|
}
|
|
625
|
-
function releaseInternalPointerCapture(
|
|
626
|
-
const
|
|
1071
|
+
function releaseInternalPointerCapture(internal, obj, pointerId) {
|
|
1072
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1073
|
+
if (!pointerState) return;
|
|
1074
|
+
const captureData = pointerState.captured.get(obj);
|
|
627
1075
|
if (captureData) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
capturedMap.delete(pointerId);
|
|
631
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
632
|
-
}
|
|
1076
|
+
pointerState.captured.delete(obj);
|
|
1077
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
633
1078
|
}
|
|
634
1079
|
}
|
|
635
1080
|
function removeInteractivity(store, object) {
|
|
636
1081
|
const { internal } = store.getState();
|
|
637
1082
|
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
1083
|
+
for (const [pointerId, pointerState] of internal.pointerMap) {
|
|
1084
|
+
pointerState.initialHits = pointerState.initialHits.filter((o) => o !== object);
|
|
1085
|
+
pointerState.hovered.forEach((value, key) => {
|
|
1086
|
+
if (value.eventObject === object || value.object === object) {
|
|
1087
|
+
pointerState.hovered.delete(key);
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
if (pointerState.captured.has(object)) {
|
|
1091
|
+
releaseInternalPointerCapture(internal, object, pointerId);
|
|
642
1092
|
}
|
|
643
|
-
}
|
|
644
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
645
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
646
|
-
});
|
|
1093
|
+
}
|
|
647
1094
|
unregisterVisibility(store, object);
|
|
648
1095
|
}
|
|
649
1096
|
function createEvents(store) {
|
|
650
|
-
function calculateDistance(event) {
|
|
1097
|
+
function calculateDistance(event, pointerId) {
|
|
651
1098
|
const { internal } = store.getState();
|
|
652
|
-
const
|
|
653
|
-
|
|
1099
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1100
|
+
if (!pointerState) return 0;
|
|
1101
|
+
const [initialX, initialY] = pointerState.initialClick;
|
|
1102
|
+
const dx = event.offsetX - initialX;
|
|
1103
|
+
const dy = event.offsetY - initialY;
|
|
654
1104
|
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
655
1105
|
}
|
|
656
1106
|
function filterPointerEvents(objects) {
|
|
@@ -686,6 +1136,15 @@ function createEvents(store) {
|
|
|
686
1136
|
return state2.raycaster.camera ? state2.raycaster.intersectObject(obj, true) : [];
|
|
687
1137
|
}
|
|
688
1138
|
let hits = eventsObjects.flatMap(handleRaycast).sort((a, b) => {
|
|
1139
|
+
const aInteractivePriority = a.object.userData?.interactivePriority;
|
|
1140
|
+
const bInteractivePriority = b.object.userData?.interactivePriority;
|
|
1141
|
+
if (aInteractivePriority !== void 0 || bInteractivePriority !== void 0) {
|
|
1142
|
+
if (aInteractivePriority !== void 0 && bInteractivePriority === void 0) return -1;
|
|
1143
|
+
if (bInteractivePriority !== void 0 && aInteractivePriority === void 0) return 1;
|
|
1144
|
+
if (aInteractivePriority !== bInteractivePriority) {
|
|
1145
|
+
return (bInteractivePriority ?? 0) - (aInteractivePriority ?? 0);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
689
1148
|
const aState = getRootState(a.object);
|
|
690
1149
|
const bState = getRootState(b.object);
|
|
691
1150
|
const aPriority = aState?.events?.priority ?? 1;
|
|
@@ -707,9 +1166,13 @@ function createEvents(store) {
|
|
|
707
1166
|
eventObject = eventObject.parent;
|
|
708
1167
|
}
|
|
709
1168
|
}
|
|
710
|
-
if ("pointerId" in event
|
|
711
|
-
|
|
712
|
-
|
|
1169
|
+
if ("pointerId" in event) {
|
|
1170
|
+
const pointerId = event.pointerId;
|
|
1171
|
+
const pointerState = state.internal.pointerMap.get(pointerId);
|
|
1172
|
+
if (pointerState?.captured.size) {
|
|
1173
|
+
for (const captureData of pointerState.captured.values()) {
|
|
1174
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
1175
|
+
}
|
|
713
1176
|
}
|
|
714
1177
|
}
|
|
715
1178
|
return intersections;
|
|
@@ -722,27 +1185,25 @@ function createEvents(store) {
|
|
|
722
1185
|
if (state) {
|
|
723
1186
|
const { raycaster, pointer, camera, internal } = state;
|
|
724
1187
|
const unprojectedPoint = new webgpu.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
725
|
-
const hasPointerCapture = (id) =>
|
|
1188
|
+
const hasPointerCapture = (id) => {
|
|
1189
|
+
const pointerState = internal.pointerMap.get(id);
|
|
1190
|
+
return pointerState?.captured.has(hit.eventObject) ?? false;
|
|
1191
|
+
};
|
|
726
1192
|
const setPointerCapture = (id) => {
|
|
727
1193
|
const captureData = { intersection: hit, target: event.target };
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
} else {
|
|
731
|
-
internal.capturedMap.set(id, /* @__PURE__ */ new Map([[hit.eventObject, captureData]]));
|
|
732
|
-
}
|
|
1194
|
+
const pointerState = getPointerState(internal, id);
|
|
1195
|
+
pointerState.captured.set(hit.eventObject, captureData);
|
|
733
1196
|
event.target.setPointerCapture(id);
|
|
734
1197
|
};
|
|
735
1198
|
const releasePointerCapture = (id) => {
|
|
736
|
-
|
|
737
|
-
if (captures) {
|
|
738
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
739
|
-
}
|
|
1199
|
+
releaseInternalPointerCapture(internal, hit.eventObject, id);
|
|
740
1200
|
};
|
|
741
1201
|
const extractEventProps = {};
|
|
742
1202
|
for (const prop in event) {
|
|
743
1203
|
const property = event[prop];
|
|
744
1204
|
if (typeof property !== "function") extractEventProps[prop] = property;
|
|
745
1205
|
}
|
|
1206
|
+
const eventPointerId = "pointerId" in event ? event.pointerId : void 0;
|
|
746
1207
|
const raycastEvent = {
|
|
747
1208
|
...hit,
|
|
748
1209
|
...extractEventProps,
|
|
@@ -753,18 +1214,19 @@ function createEvents(store) {
|
|
|
753
1214
|
unprojectedPoint,
|
|
754
1215
|
ray: raycaster.ray,
|
|
755
1216
|
camera,
|
|
1217
|
+
pointerId: eventPointerId,
|
|
756
1218
|
// Hijack stopPropagation, which just sets a flag
|
|
757
1219
|
stopPropagation() {
|
|
758
|
-
const
|
|
1220
|
+
const pointerState = eventPointerId !== void 0 ? internal.pointerMap.get(eventPointerId) : void 0;
|
|
759
1221
|
if (
|
|
760
1222
|
// ...if this pointer hasn't been captured
|
|
761
|
-
!
|
|
762
|
-
|
|
1223
|
+
!pointerState?.captured.size || // ... or if the hit object is capturing the pointer
|
|
1224
|
+
pointerState.captured.has(hit.eventObject)
|
|
763
1225
|
) {
|
|
764
1226
|
raycastEvent.stopped = localState.stopped = true;
|
|
765
|
-
if (
|
|
1227
|
+
if (pointerState?.hovered.size && Array.from(pointerState.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
766
1228
|
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
767
|
-
cancelPointer([...higher, hit]);
|
|
1229
|
+
cancelPointer([...higher, hit], eventPointerId);
|
|
768
1230
|
}
|
|
769
1231
|
}
|
|
770
1232
|
},
|
|
@@ -780,15 +1242,18 @@ function createEvents(store) {
|
|
|
780
1242
|
}
|
|
781
1243
|
return intersections;
|
|
782
1244
|
}
|
|
783
|
-
function cancelPointer(intersections) {
|
|
1245
|
+
function cancelPointer(intersections, pointerId) {
|
|
784
1246
|
const { internal } = store.getState();
|
|
785
|
-
|
|
1247
|
+
const pid = pointerId ?? DEFAULT_POINTER_ID;
|
|
1248
|
+
const pointerState = internal.pointerMap.get(pid);
|
|
1249
|
+
if (!pointerState) return;
|
|
1250
|
+
for (const [hoveredId, hoveredObj] of pointerState.hovered) {
|
|
786
1251
|
if (!intersections.length || !intersections.find(
|
|
787
1252
|
(hit) => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId
|
|
788
1253
|
)) {
|
|
789
1254
|
const eventObject = hoveredObj.eventObject;
|
|
790
1255
|
const instance = eventObject.__r3f;
|
|
791
|
-
|
|
1256
|
+
pointerState.hovered.delete(hoveredId);
|
|
792
1257
|
if (instance?.eventCount) {
|
|
793
1258
|
const handlers = instance.handlers;
|
|
794
1259
|
const data = { ...hoveredObj, intersections };
|
|
@@ -817,41 +1282,118 @@ function createEvents(store) {
|
|
|
817
1282
|
instance?.handlers.onDropMissed?.(event);
|
|
818
1283
|
}
|
|
819
1284
|
}
|
|
1285
|
+
function cleanupPointer(pointerId) {
|
|
1286
|
+
const { internal } = store.getState();
|
|
1287
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1288
|
+
if (pointerState) {
|
|
1289
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1290
|
+
const eventObject = hoveredObj.eventObject;
|
|
1291
|
+
const instance = eventObject.__r3f;
|
|
1292
|
+
if (instance?.eventCount) {
|
|
1293
|
+
const handlers = instance.handlers;
|
|
1294
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1295
|
+
handlers.onPointerOut?.(data);
|
|
1296
|
+
handlers.onPointerLeave?.(data);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
internal.pointerMap.delete(pointerId);
|
|
1300
|
+
}
|
|
1301
|
+
internal.pointerDirty.delete(pointerId);
|
|
1302
|
+
}
|
|
1303
|
+
function processDeferredPointer(event, pointerId) {
|
|
1304
|
+
const state = store.getState();
|
|
1305
|
+
const { internal } = state;
|
|
1306
|
+
if (!state.events.enabled) return;
|
|
1307
|
+
const filter = filterPointerEvents;
|
|
1308
|
+
const hits = intersect(event, filter);
|
|
1309
|
+
cancelPointer(hits, pointerId);
|
|
1310
|
+
function onIntersect(data) {
|
|
1311
|
+
const eventObject = data.eventObject;
|
|
1312
|
+
const instance = eventObject.__r3f;
|
|
1313
|
+
if (!instance?.eventCount) return;
|
|
1314
|
+
const handlers = instance.handlers;
|
|
1315
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1316
|
+
const id = makeId(data);
|
|
1317
|
+
const pointerState = getPointerState(internal, pointerId);
|
|
1318
|
+
const hoveredItem = pointerState.hovered.get(id);
|
|
1319
|
+
if (!hoveredItem) {
|
|
1320
|
+
pointerState.hovered.set(id, data);
|
|
1321
|
+
handlers.onPointerOver?.(data);
|
|
1322
|
+
handlers.onPointerEnter?.(data);
|
|
1323
|
+
} else if (hoveredItem.stopped) {
|
|
1324
|
+
data.stopPropagation();
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
handlers.onPointerMove?.(data);
|
|
1328
|
+
}
|
|
1329
|
+
handleIntersects(hits, event, 0, onIntersect);
|
|
1330
|
+
}
|
|
820
1331
|
function handlePointer(name) {
|
|
821
1332
|
switch (name) {
|
|
822
1333
|
case "onPointerLeave":
|
|
823
|
-
case "onPointerCancel":
|
|
824
1334
|
case "onDragLeave":
|
|
825
1335
|
return () => cancelPointer([]);
|
|
1336
|
+
// Global cancel of these events
|
|
1337
|
+
case "onPointerCancel":
|
|
1338
|
+
return (event) => {
|
|
1339
|
+
const pointerId = getPointerId(event);
|
|
1340
|
+
cleanupPointer(pointerId);
|
|
1341
|
+
};
|
|
826
1342
|
case "onLostPointerCapture":
|
|
827
1343
|
return (event) => {
|
|
828
1344
|
const { internal } = store.getState();
|
|
829
|
-
|
|
1345
|
+
const pointerId = getPointerId(event);
|
|
1346
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1347
|
+
if (pointerState?.captured.size) {
|
|
830
1348
|
requestAnimationFrame(() => {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
1349
|
+
const pointerState2 = internal.pointerMap.get(pointerId);
|
|
1350
|
+
if (pointerState2?.captured.size) {
|
|
1351
|
+
pointerState2.captured.clear();
|
|
834
1352
|
}
|
|
1353
|
+
cancelPointer([], pointerId);
|
|
835
1354
|
});
|
|
836
1355
|
}
|
|
837
1356
|
};
|
|
838
1357
|
}
|
|
839
1358
|
return function handleEvent(event) {
|
|
840
1359
|
const state = store.getState();
|
|
841
|
-
const { onPointerMissed, onDragOverMissed, onDropMissed, internal } = state;
|
|
1360
|
+
const { onPointerMissed, onDragOverMissed, onDropMissed, internal, events } = state;
|
|
1361
|
+
const pointerId = getPointerId(event);
|
|
842
1362
|
internal.lastEvent.current = event;
|
|
843
|
-
if (!
|
|
1363
|
+
if (!events.enabled) return;
|
|
844
1364
|
const isPointerMove = name === "onPointerMove";
|
|
845
1365
|
const isDragOver = name === "onDragOver";
|
|
846
1366
|
const isDrop = name === "onDrop";
|
|
847
1367
|
const isClickEvent = name === "onClick" || name === "onContextMenu" || name === "onDoubleClick";
|
|
1368
|
+
const isPointerDown = name === "onPointerDown";
|
|
1369
|
+
const isPointerUp = name === "onPointerUp";
|
|
1370
|
+
const isWheel = name === "onWheel";
|
|
1371
|
+
const canDeferRaycasts = events.frameTimedRaycasts && state.frameloop === "always";
|
|
1372
|
+
if (isPointerMove && canDeferRaycasts) {
|
|
1373
|
+
events.compute?.(event, state);
|
|
1374
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (isWheel && canDeferRaycasts && !events.alwaysFireOnScroll) {
|
|
1378
|
+
events.compute?.(event, state);
|
|
1379
|
+
internal.pointerDirty.set(pointerId, event);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
if ((isClickEvent || isPointerDown || isPointerUp) && internal.pointerDirty.has(pointerId)) {
|
|
1383
|
+
const deferredEvent = internal.pointerDirty.get(pointerId);
|
|
1384
|
+
internal.pointerDirty.delete(pointerId);
|
|
1385
|
+
processDeferredPointer(deferredEvent, pointerId);
|
|
1386
|
+
}
|
|
848
1387
|
const filter = isPointerMove || isDragOver || isDrop ? filterPointerEvents : void 0;
|
|
849
1388
|
const hits = intersect(event, filter);
|
|
850
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1389
|
+
const delta = isClickEvent ? calculateDistance(event, pointerId) : 0;
|
|
1390
|
+
if (isPointerDown) {
|
|
1391
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1392
|
+
pointerState2.initialClick = [event.offsetX, event.offsetY];
|
|
1393
|
+
pointerState2.initialHits = hits.map((hit) => hit.eventObject);
|
|
1394
|
+
}
|
|
1395
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1396
|
+
const initialHits = pointerState?.initialHits ?? [];
|
|
855
1397
|
if (isClickEvent && !hits.length) {
|
|
856
1398
|
if (delta <= 2) {
|
|
857
1399
|
pointerMissed(event, internal.interaction);
|
|
@@ -866,7 +1408,9 @@ function createEvents(store) {
|
|
|
866
1408
|
dropMissed(event, internal.interaction);
|
|
867
1409
|
if (onDropMissed) onDropMissed(event);
|
|
868
1410
|
}
|
|
869
|
-
if (isPointerMove || isDragOver)
|
|
1411
|
+
if (isPointerMove || isDragOver) {
|
|
1412
|
+
cancelPointer(hits, pointerId);
|
|
1413
|
+
}
|
|
870
1414
|
function onIntersect(data) {
|
|
871
1415
|
const eventObject = data.eventObject;
|
|
872
1416
|
const instance = eventObject.__r3f;
|
|
@@ -875,9 +1419,10 @@ function createEvents(store) {
|
|
|
875
1419
|
if (isPointerMove) {
|
|
876
1420
|
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
877
1421
|
const id = makeId(data);
|
|
878
|
-
const
|
|
1422
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1423
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
879
1424
|
if (!hoveredItem) {
|
|
880
|
-
|
|
1425
|
+
pointerState2.hovered.set(id, data);
|
|
881
1426
|
handlers.onPointerOver?.(data);
|
|
882
1427
|
handlers.onPointerEnter?.(data);
|
|
883
1428
|
} else if (hoveredItem.stopped) {
|
|
@@ -887,9 +1432,10 @@ function createEvents(store) {
|
|
|
887
1432
|
handlers.onPointerMove?.(data);
|
|
888
1433
|
} else if (isDragOver) {
|
|
889
1434
|
const id = makeId(data);
|
|
890
|
-
const
|
|
1435
|
+
const pointerState2 = getPointerState(internal, pointerId);
|
|
1436
|
+
const hoveredItem = pointerState2.hovered.get(id);
|
|
891
1437
|
if (!hoveredItem) {
|
|
892
|
-
|
|
1438
|
+
pointerState2.hovered.set(id, data);
|
|
893
1439
|
handlers.onDragOverEnter?.(data);
|
|
894
1440
|
} else if (hoveredItem.stopped) {
|
|
895
1441
|
data.stopPropagation();
|
|
@@ -900,18 +1446,18 @@ function createEvents(store) {
|
|
|
900
1446
|
} else {
|
|
901
1447
|
const handler = handlers[name];
|
|
902
1448
|
if (handler) {
|
|
903
|
-
if (!isClickEvent ||
|
|
1449
|
+
if (!isClickEvent || initialHits.includes(eventObject)) {
|
|
904
1450
|
pointerMissed(
|
|
905
1451
|
event,
|
|
906
|
-
internal.interaction.filter((object) => !
|
|
1452
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
907
1453
|
);
|
|
908
1454
|
handler(data);
|
|
909
1455
|
}
|
|
910
1456
|
} else {
|
|
911
|
-
if (isClickEvent &&
|
|
1457
|
+
if (isClickEvent && initialHits.includes(eventObject)) {
|
|
912
1458
|
pointerMissed(
|
|
913
1459
|
event,
|
|
914
|
-
internal.interaction.filter((object) => !
|
|
1460
|
+
internal.interaction.filter((object) => !initialHits.includes(object))
|
|
915
1461
|
);
|
|
916
1462
|
}
|
|
917
1463
|
}
|
|
@@ -920,7 +1466,15 @@ function createEvents(store) {
|
|
|
920
1466
|
handleIntersects(hits, event, delta, onIntersect);
|
|
921
1467
|
};
|
|
922
1468
|
}
|
|
923
|
-
|
|
1469
|
+
function flushDeferredPointers() {
|
|
1470
|
+
const { internal, events } = store.getState();
|
|
1471
|
+
if (!events.frameTimedRaycasts) return;
|
|
1472
|
+
for (const [pointerId, event] of internal.pointerDirty) {
|
|
1473
|
+
processDeferredPointer(event, pointerId);
|
|
1474
|
+
}
|
|
1475
|
+
internal.pointerDirty.clear();
|
|
1476
|
+
}
|
|
1477
|
+
return { handlePointer, flushDeferredPointers, processDeferredPointer };
|
|
924
1478
|
}
|
|
925
1479
|
const DOM_EVENTS = {
|
|
926
1480
|
onClick: ["click", false],
|
|
@@ -939,11 +1493,16 @@ const DOM_EVENTS = {
|
|
|
939
1493
|
onLostPointerCapture: ["lostpointercapture", true]
|
|
940
1494
|
};
|
|
941
1495
|
function createPointerEvents(store) {
|
|
942
|
-
const { handlePointer } = createEvents(store);
|
|
1496
|
+
const { handlePointer, flushDeferredPointers, processDeferredPointer } = createEvents(store);
|
|
1497
|
+
let nextXRPointerId = XR_POINTER_ID_START;
|
|
1498
|
+
const xrPointers = /* @__PURE__ */ new Map();
|
|
943
1499
|
return {
|
|
944
1500
|
priority: 1,
|
|
945
1501
|
enabled: true,
|
|
946
|
-
|
|
1502
|
+
frameTimedRaycasts: true,
|
|
1503
|
+
alwaysFireOnScroll: true,
|
|
1504
|
+
updateOnFrame: false,
|
|
1505
|
+
compute(event, state) {
|
|
947
1506
|
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
948
1507
|
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
949
1508
|
},
|
|
@@ -952,11 +1511,33 @@ function createPointerEvents(store) {
|
|
|
952
1511
|
(acc, key) => ({ ...acc, [key]: handlePointer(key) }),
|
|
953
1512
|
{}
|
|
954
1513
|
),
|
|
955
|
-
update: () => {
|
|
1514
|
+
update: (pointerId) => {
|
|
1515
|
+
const { events, internal } = store.getState();
|
|
1516
|
+
if (!events.handlers) return;
|
|
1517
|
+
if (pointerId !== void 0) {
|
|
1518
|
+
const event = internal.pointerDirty.get(pointerId);
|
|
1519
|
+
if (event) {
|
|
1520
|
+
internal.pointerDirty.delete(pointerId);
|
|
1521
|
+
processDeferredPointer(event, pointerId);
|
|
1522
|
+
} else if (internal.lastEvent?.current) {
|
|
1523
|
+
processDeferredPointer(internal.lastEvent.current, pointerId);
|
|
1524
|
+
}
|
|
1525
|
+
} else {
|
|
1526
|
+
flushDeferredPointers();
|
|
1527
|
+
if (internal.lastEvent?.current) {
|
|
1528
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
},
|
|
1532
|
+
flush: () => {
|
|
956
1533
|
const { events, internal } = store.getState();
|
|
957
|
-
|
|
1534
|
+
flushDeferredPointers();
|
|
1535
|
+
if (events.updateOnFrame && internal.lastEvent?.current && events.handlers) {
|
|
1536
|
+
events.handlers.onPointerMove(internal.lastEvent.current);
|
|
1537
|
+
}
|
|
958
1538
|
},
|
|
959
1539
|
connect: (target) => {
|
|
1540
|
+
if (!target) return;
|
|
960
1541
|
const { set, events } = store.getState();
|
|
961
1542
|
events.disconnect?.();
|
|
962
1543
|
set((state) => ({ events: { ...state.events, connected: target } }));
|
|
@@ -980,6 +1561,32 @@ function createPointerEvents(store) {
|
|
|
980
1561
|
}
|
|
981
1562
|
set((state) => ({ events: { ...state.events, connected: void 0 } }));
|
|
982
1563
|
}
|
|
1564
|
+
},
|
|
1565
|
+
registerPointer: (config) => {
|
|
1566
|
+
const pointerId = nextXRPointerId++;
|
|
1567
|
+
xrPointers.set(pointerId, config);
|
|
1568
|
+
const { internal } = store.getState();
|
|
1569
|
+
getPointerState(internal, pointerId);
|
|
1570
|
+
return pointerId;
|
|
1571
|
+
},
|
|
1572
|
+
unregisterPointer: (pointerId) => {
|
|
1573
|
+
xrPointers.delete(pointerId);
|
|
1574
|
+
const { internal } = store.getState();
|
|
1575
|
+
const pointerState = internal.pointerMap.get(pointerId);
|
|
1576
|
+
if (pointerState) {
|
|
1577
|
+
for (const [, hoveredObj] of pointerState.hovered) {
|
|
1578
|
+
const eventObject = hoveredObj.eventObject;
|
|
1579
|
+
const instance = eventObject.__r3f;
|
|
1580
|
+
if (instance?.eventCount) {
|
|
1581
|
+
const handlers = instance.handlers;
|
|
1582
|
+
const data = { ...hoveredObj, intersections: [] };
|
|
1583
|
+
handlers.onPointerOut?.(data);
|
|
1584
|
+
handlers.onPointerLeave?.(data);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
internal.pointerMap.delete(pointerId);
|
|
1588
|
+
}
|
|
1589
|
+
internal.pointerDirty.delete(pointerId);
|
|
983
1590
|
}
|
|
984
1591
|
};
|
|
985
1592
|
}
|
|
@@ -1041,331 +1648,26 @@ function notifyAlpha({ message, link }) {
|
|
|
1041
1648
|
}
|
|
1042
1649
|
}
|
|
1043
1650
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
let performanceTimeout = void 0;
|
|
1067
|
-
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
1068
|
-
const pointer = new webgpu.Vector2();
|
|
1069
|
-
const rootState = {
|
|
1070
|
-
set,
|
|
1071
|
-
get,
|
|
1072
|
-
// Mock objects that have to be configured
|
|
1073
|
-
gl: null,
|
|
1074
|
-
renderer: null,
|
|
1075
|
-
camera: null,
|
|
1076
|
-
frustum: new webgpu.Frustum(),
|
|
1077
|
-
autoUpdateFrustum: true,
|
|
1078
|
-
raycaster: null,
|
|
1079
|
-
events: { priority: 1, enabled: true, connected: false },
|
|
1080
|
-
scene: null,
|
|
1081
|
-
rootScene: null,
|
|
1082
|
-
xr: null,
|
|
1083
|
-
inspector: null,
|
|
1084
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
1085
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
1086
|
-
legacy: false,
|
|
1087
|
-
linear: false,
|
|
1088
|
-
flat: false,
|
|
1089
|
-
textureColorSpace: "srgb",
|
|
1090
|
-
isLegacy: false,
|
|
1091
|
-
webGPUSupported: false,
|
|
1092
|
-
isNative: false,
|
|
1093
|
-
controls: null,
|
|
1094
|
-
pointer,
|
|
1095
|
-
mouse: pointer,
|
|
1096
|
-
frameloop: "always",
|
|
1097
|
-
onPointerMissed: void 0,
|
|
1098
|
-
onDragOverMissed: void 0,
|
|
1099
|
-
onDropMissed: void 0,
|
|
1100
|
-
performance: {
|
|
1101
|
-
current: 1,
|
|
1102
|
-
min: 0.5,
|
|
1103
|
-
max: 1,
|
|
1104
|
-
debounce: 200,
|
|
1105
|
-
regress: () => {
|
|
1106
|
-
const state2 = get();
|
|
1107
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
1108
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
1109
|
-
performanceTimeout = setTimeout(
|
|
1110
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
1111
|
-
state2.performance.debounce
|
|
1112
|
-
);
|
|
1113
|
-
}
|
|
1114
|
-
},
|
|
1115
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1116
|
-
viewport: {
|
|
1117
|
-
initialDpr: 0,
|
|
1118
|
-
dpr: 0,
|
|
1119
|
-
width: 0,
|
|
1120
|
-
height: 0,
|
|
1121
|
-
top: 0,
|
|
1122
|
-
left: 0,
|
|
1123
|
-
aspect: 0,
|
|
1124
|
-
distance: 0,
|
|
1125
|
-
factor: 0,
|
|
1126
|
-
getCurrentViewport
|
|
1127
|
-
},
|
|
1128
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
1129
|
-
setSize: (width, height, top, left) => {
|
|
1130
|
-
const state2 = get();
|
|
1131
|
-
if (width === void 0) {
|
|
1132
|
-
set({ _sizeImperative: false });
|
|
1133
|
-
if (state2._sizeProps) {
|
|
1134
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
1135
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
1136
|
-
const currentSize = state2.size;
|
|
1137
|
-
const newSize = {
|
|
1138
|
-
width: propW ?? currentSize.width,
|
|
1139
|
-
height: propH ?? currentSize.height,
|
|
1140
|
-
top: currentSize.top,
|
|
1141
|
-
left: currentSize.left
|
|
1142
|
-
};
|
|
1143
|
-
set((s) => ({
|
|
1144
|
-
size: newSize,
|
|
1145
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
1146
|
-
}));
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
|
-
const w = width;
|
|
1152
|
-
const h = height ?? width;
|
|
1153
|
-
const t = top ?? state2.size.top;
|
|
1154
|
-
const l = left ?? state2.size.left;
|
|
1155
|
-
const size = { width: w, height: h, top: t, left: l };
|
|
1156
|
-
set((s) => ({
|
|
1157
|
-
size,
|
|
1158
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
1159
|
-
_sizeImperative: true
|
|
1160
|
-
}));
|
|
1161
|
-
},
|
|
1162
|
-
setDpr: (dpr) => set((state2) => {
|
|
1163
|
-
const resolved = calculateDpr(dpr);
|
|
1164
|
-
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
1165
|
-
}),
|
|
1166
|
-
setFrameloop: (frameloop = "always") => {
|
|
1167
|
-
set(() => ({ frameloop }));
|
|
1168
|
-
},
|
|
1169
|
-
setError: (error) => set(() => ({ error })),
|
|
1170
|
-
error: null,
|
|
1171
|
-
//* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
|
|
1172
|
-
uniforms: {},
|
|
1173
|
-
nodes: {},
|
|
1174
|
-
textures: /* @__PURE__ */ new Map(),
|
|
1175
|
-
postProcessing: null,
|
|
1176
|
-
passes: {},
|
|
1177
|
-
_hmrVersion: 0,
|
|
1178
|
-
_sizeImperative: false,
|
|
1179
|
-
_sizeProps: null,
|
|
1180
|
-
previousRoot: void 0,
|
|
1181
|
-
internal: {
|
|
1182
|
-
// Events
|
|
1183
|
-
interaction: [],
|
|
1184
|
-
hovered: /* @__PURE__ */ new Map(),
|
|
1185
|
-
subscribers: [],
|
|
1186
|
-
initialClick: [0, 0],
|
|
1187
|
-
initialHits: [],
|
|
1188
|
-
capturedMap: /* @__PURE__ */ new Map(),
|
|
1189
|
-
lastEvent: React__namespace.createRef(),
|
|
1190
|
-
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1191
|
-
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1192
|
-
// Occlusion system (WebGPU only)
|
|
1193
|
-
occlusionEnabled: false,
|
|
1194
|
-
occlusionObserver: null,
|
|
1195
|
-
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1196
|
-
helperGroup: null,
|
|
1197
|
-
// Updates
|
|
1198
|
-
active: false,
|
|
1199
|
-
frames: 0,
|
|
1200
|
-
priority: 0,
|
|
1201
|
-
subscribe: (ref, priority, store) => {
|
|
1202
|
-
const internal = get().internal;
|
|
1203
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1204
|
-
internal.subscribers.push({ ref, priority, store });
|
|
1205
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
1206
|
-
return () => {
|
|
1207
|
-
const internal2 = get().internal;
|
|
1208
|
-
if (internal2?.subscribers) {
|
|
1209
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
1210
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
1211
|
-
}
|
|
1212
|
-
};
|
|
1213
|
-
},
|
|
1214
|
-
// Renderer Storage (single source of truth)
|
|
1215
|
-
actualRenderer: null,
|
|
1216
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
1217
|
-
scheduler: null
|
|
1218
|
-
}
|
|
1219
|
-
};
|
|
1220
|
-
return rootState;
|
|
1221
|
-
});
|
|
1222
|
-
const state = rootStore.getState();
|
|
1223
|
-
Object.defineProperty(state, "gl", {
|
|
1224
|
-
get() {
|
|
1225
|
-
const currentState = rootStore.getState();
|
|
1226
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
1227
|
-
const stack = new Error().stack || "";
|
|
1228
|
-
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
1229
|
-
if (!isInternalAccess) {
|
|
1230
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1231
|
-
notifyDepreciated({
|
|
1232
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1233
|
-
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
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
return currentState.internal.actualRenderer;
|
|
1238
|
-
},
|
|
1239
|
-
set(value) {
|
|
1240
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1241
|
-
},
|
|
1242
|
-
enumerable: true,
|
|
1243
|
-
configurable: true
|
|
1244
|
-
});
|
|
1245
|
-
Object.defineProperty(state, "renderer", {
|
|
1246
|
-
get() {
|
|
1247
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1248
|
-
},
|
|
1249
|
-
set(value) {
|
|
1250
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1251
|
-
},
|
|
1252
|
-
enumerable: true,
|
|
1253
|
-
configurable: true
|
|
1254
|
-
});
|
|
1255
|
-
let oldScene = state.scene;
|
|
1256
|
-
rootStore.subscribe(() => {
|
|
1257
|
-
const currentState = rootStore.getState();
|
|
1258
|
-
const { scene, rootScene, set } = currentState;
|
|
1259
|
-
if (scene !== oldScene) {
|
|
1260
|
-
oldScene = scene;
|
|
1261
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1262
|
-
set({ rootScene: scene });
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
let oldSize = state.size;
|
|
1267
|
-
let oldDpr = state.viewport.dpr;
|
|
1268
|
-
let oldCamera = state.camera;
|
|
1269
|
-
rootStore.subscribe(() => {
|
|
1270
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1271
|
-
const actualRenderer = internal.actualRenderer;
|
|
1272
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1273
|
-
oldSize = size;
|
|
1274
|
-
oldDpr = viewport.dpr;
|
|
1275
|
-
updateCamera(camera, size);
|
|
1276
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1277
|
-
const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
|
|
1278
|
-
actualRenderer.setSize(size.width, size.height, updateStyle);
|
|
1279
|
-
}
|
|
1280
|
-
if (camera !== oldCamera) {
|
|
1281
|
-
oldCamera = camera;
|
|
1282
|
-
const { rootScene } = rootStore.getState();
|
|
1283
|
-
if (camera && rootScene && !camera.parent) {
|
|
1284
|
-
rootScene.add(camera);
|
|
1285
|
-
}
|
|
1286
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1287
|
-
const currentState = rootStore.getState();
|
|
1288
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1289
|
-
updateFrustum(camera, currentState.frustum);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1294
|
-
return rootStore;
|
|
1295
|
-
};
|
|
1296
|
-
|
|
1297
|
-
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
1298
|
-
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
1299
|
-
function getLoader(Proto) {
|
|
1300
|
-
if (isConstructor$1(Proto)) {
|
|
1301
|
-
let loader = memoizedLoaders.get(Proto);
|
|
1302
|
-
if (!loader) {
|
|
1303
|
-
loader = new Proto();
|
|
1304
|
-
memoizedLoaders.set(Proto, loader);
|
|
1305
|
-
}
|
|
1306
|
-
return loader;
|
|
1307
|
-
}
|
|
1308
|
-
return Proto;
|
|
1309
|
-
}
|
|
1310
|
-
function loadingFn(extensions, onProgress) {
|
|
1311
|
-
return function(Proto, input) {
|
|
1312
|
-
const loader = getLoader(Proto);
|
|
1313
|
-
if (extensions) extensions(loader);
|
|
1314
|
-
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
1315
|
-
return loader.loadAsync(input, onProgress).then((data) => {
|
|
1316
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1317
|
-
return data;
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return new Promise(
|
|
1321
|
-
(res, reject) => loader.load(
|
|
1322
|
-
input,
|
|
1323
|
-
(data) => {
|
|
1324
|
-
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1325
|
-
res(data);
|
|
1326
|
-
},
|
|
1327
|
-
onProgress,
|
|
1328
|
-
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
1329
|
-
)
|
|
1330
|
-
);
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
1333
|
-
function useLoader(loader, input, extensions, onProgress) {
|
|
1334
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1335
|
-
const fn = loadingFn(extensions, onProgress);
|
|
1336
|
-
const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
|
|
1337
|
-
return Array.isArray(input) ? results : results[0];
|
|
1338
|
-
}
|
|
1339
|
-
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
1340
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1341
|
-
keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
1342
|
-
};
|
|
1343
|
-
useLoader.clear = function(loader, input) {
|
|
1344
|
-
const keys = Array.isArray(input) ? input : [input];
|
|
1345
|
-
keys.forEach((key) => suspendReact.clear([loader, key]));
|
|
1346
|
-
};
|
|
1347
|
-
useLoader.loader = getLoader;
|
|
1348
|
-
|
|
1349
|
-
var __defProp$1 = Object.defineProperty;
|
|
1350
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1351
|
-
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1352
|
-
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1353
|
-
class PhaseGraph {
|
|
1354
|
-
constructor() {
|
|
1355
|
-
/** Ordered list of phase nodes */
|
|
1356
|
-
__publicField$1(this, "phases", []);
|
|
1357
|
-
/** Quick lookup by name */
|
|
1358
|
-
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1359
|
-
/** Cached ordered names (invalidated on changes) */
|
|
1360
|
-
__publicField$1(this, "orderedNamesCache", null);
|
|
1361
|
-
this.initializeDefaultPhases();
|
|
1362
|
-
}
|
|
1363
|
-
//* Initialization --------------------------------
|
|
1364
|
-
initializeDefaultPhases() {
|
|
1365
|
-
for (const name of DEFAULT_PHASES) {
|
|
1366
|
-
const node = { name, isAutoGenerated: false };
|
|
1367
|
-
this.phases.push(node);
|
|
1368
|
-
this.phaseMap.set(name, node);
|
|
1651
|
+
var __defProp$1 = Object.defineProperty;
|
|
1652
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1653
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1654
|
+
const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
|
|
1655
|
+
class PhaseGraph {
|
|
1656
|
+
constructor() {
|
|
1657
|
+
/** Ordered list of phase nodes */
|
|
1658
|
+
__publicField$1(this, "phases", []);
|
|
1659
|
+
/** Quick lookup by name */
|
|
1660
|
+
__publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
|
|
1661
|
+
/** Cached ordered names (invalidated on changes) */
|
|
1662
|
+
__publicField$1(this, "orderedNamesCache", null);
|
|
1663
|
+
this.initializeDefaultPhases();
|
|
1664
|
+
}
|
|
1665
|
+
//* Initialization --------------------------------
|
|
1666
|
+
initializeDefaultPhases() {
|
|
1667
|
+
for (const name of DEFAULT_PHASES) {
|
|
1668
|
+
const node = { name, isAutoGenerated: false };
|
|
1669
|
+
this.phases.push(node);
|
|
1670
|
+
this.phaseMap.set(name, node);
|
|
1369
1671
|
}
|
|
1370
1672
|
this.invalidateCache();
|
|
1371
1673
|
}
|
|
@@ -1598,7 +1900,7 @@ function shouldRun(job, now) {
|
|
|
1598
1900
|
const minInterval = 1e3 / job.fps;
|
|
1599
1901
|
const lastRun = job.lastRun ?? 0;
|
|
1600
1902
|
const elapsed = now - lastRun;
|
|
1601
|
-
if (elapsed < minInterval) return false;
|
|
1903
|
+
if (elapsed < minInterval - 1) return false;
|
|
1602
1904
|
if (job.drop) {
|
|
1603
1905
|
job.lastRun = now;
|
|
1604
1906
|
} else {
|
|
@@ -2267,116 +2569,444 @@ const _Scheduler = class _Scheduler {
|
|
|
2267
2569
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
2268
2570
|
root.needsRebuild = false;
|
|
2269
2571
|
}
|
|
2270
|
-
const providedState = root.getState?.() ?? {};
|
|
2271
|
-
const frameState = {
|
|
2272
|
-
...providedState,
|
|
2273
|
-
time: timestamp,
|
|
2274
|
-
delta,
|
|
2275
|
-
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2276
|
-
// Convert ms to seconds
|
|
2277
|
-
frame: this.loopState.frameCount
|
|
2278
|
-
};
|
|
2279
|
-
for (const job of root.sortedJobs) {
|
|
2280
|
-
if (!shouldRun(job, timestamp)) continue;
|
|
2281
|
-
try {
|
|
2282
|
-
job.callback(frameState, delta);
|
|
2283
|
-
} catch (error) {
|
|
2284
|
-
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2285
|
-
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2572
|
+
const providedState = root.getState?.() ?? {};
|
|
2573
|
+
const frameState = {
|
|
2574
|
+
...providedState,
|
|
2575
|
+
time: timestamp,
|
|
2576
|
+
delta,
|
|
2577
|
+
elapsed: this.loopState.elapsedTime / 1e3,
|
|
2578
|
+
// Convert ms to seconds
|
|
2579
|
+
frame: this.loopState.frameCount
|
|
2580
|
+
};
|
|
2581
|
+
for (const job of root.sortedJobs) {
|
|
2582
|
+
if (!shouldRun(job, timestamp)) continue;
|
|
2583
|
+
try {
|
|
2584
|
+
job.callback(frameState, delta);
|
|
2585
|
+
} catch (error) {
|
|
2586
|
+
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2587
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
//* Debug & Inspection Methods ================================
|
|
2592
|
+
/**
|
|
2593
|
+
* Get the total number of registered jobs across all roots.
|
|
2594
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2595
|
+
* @returns {number} Total job count
|
|
2596
|
+
*/
|
|
2597
|
+
getJobCount() {
|
|
2598
|
+
let count = 0;
|
|
2599
|
+
for (const root of this.roots.values()) {
|
|
2600
|
+
count += root.jobs.size;
|
|
2601
|
+
}
|
|
2602
|
+
return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Get all registered job IDs across all roots.
|
|
2606
|
+
* Includes both per-root jobs and global before/after jobs.
|
|
2607
|
+
* @returns {string[]} Array of all job IDs
|
|
2608
|
+
*/
|
|
2609
|
+
getJobIds() {
|
|
2610
|
+
const ids = [];
|
|
2611
|
+
for (const root of this.roots.values()) {
|
|
2612
|
+
ids.push(...root.jobs.keys());
|
|
2613
|
+
}
|
|
2614
|
+
ids.push(...this.globalBeforeJobs.keys());
|
|
2615
|
+
ids.push(...this.globalAfterJobs.keys());
|
|
2616
|
+
return ids;
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* Get the number of registered roots (Canvas instances).
|
|
2620
|
+
* @returns {number} Number of registered roots
|
|
2621
|
+
*/
|
|
2622
|
+
getRootCount() {
|
|
2623
|
+
return this.roots.size;
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* Check if any user (non-system) jobs are registered in a specific phase.
|
|
2627
|
+
* Used by the default render job to know if a user has taken over rendering.
|
|
2628
|
+
*
|
|
2629
|
+
* @param phase The phase to check
|
|
2630
|
+
* @param rootId Optional root ID to check (checks all roots if not provided)
|
|
2631
|
+
* @returns true if any user jobs exist in the phase
|
|
2632
|
+
*/
|
|
2633
|
+
hasUserJobsInPhase(phase, rootId) {
|
|
2634
|
+
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2635
|
+
return rootsToCheck.some((root) => {
|
|
2636
|
+
if (!root) return false;
|
|
2637
|
+
for (const job of root.jobs.values()) {
|
|
2638
|
+
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2639
|
+
}
|
|
2640
|
+
return false;
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
//* Utility Methods ================================
|
|
2644
|
+
/**
|
|
2645
|
+
* Generate a unique root ID for automatic root registration.
|
|
2646
|
+
* @returns {string} A unique root ID in the format 'root_N'
|
|
2647
|
+
*/
|
|
2648
|
+
generateRootId() {
|
|
2649
|
+
return `root_${this.nextRootIndex++}`;
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Generate a unique job ID.
|
|
2653
|
+
* @returns {string} A unique job ID in the format 'job_N'
|
|
2654
|
+
* @private
|
|
2655
|
+
*/
|
|
2656
|
+
generateJobId() {
|
|
2657
|
+
return `job_${this.nextJobIndex}`;
|
|
2658
|
+
}
|
|
2659
|
+
/**
|
|
2660
|
+
* Normalize before/after constraints to a Set.
|
|
2661
|
+
* Handles undefined, single string, or array inputs.
|
|
2662
|
+
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2663
|
+
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2664
|
+
* @private
|
|
2665
|
+
*/
|
|
2666
|
+
normalizeConstraints(value) {
|
|
2667
|
+
if (!value) return /* @__PURE__ */ new Set();
|
|
2668
|
+
if (Array.isArray(value)) return new Set(value);
|
|
2669
|
+
return /* @__PURE__ */ new Set([value]);
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2673
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2674
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2675
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2676
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2677
|
+
let Scheduler = _Scheduler;
|
|
2678
|
+
const getScheduler = () => Scheduler.get();
|
|
2679
|
+
if (hmrData) {
|
|
2680
|
+
hmrData.accept?.();
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
2684
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
|
|
2685
|
+
const createStore = (invalidate, advance) => {
|
|
2686
|
+
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
2687
|
+
const position = new webgpu.Vector3();
|
|
2688
|
+
const defaultTarget = new webgpu.Vector3();
|
|
2689
|
+
const tempTarget = new webgpu.Vector3();
|
|
2690
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
2691
|
+
const { width, height, top, left } = size;
|
|
2692
|
+
const aspect = width / height;
|
|
2693
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
2694
|
+
else tempTarget.set(...target);
|
|
2695
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2696
|
+
if (isOrthographicCamera(camera)) {
|
|
2697
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
2698
|
+
} else {
|
|
2699
|
+
const fov = camera.fov * Math.PI / 180;
|
|
2700
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
2701
|
+
const w = h * (width / height);
|
|
2702
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
let performanceTimeout = void 0;
|
|
2706
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
2707
|
+
const pointer = new webgpu.Vector2();
|
|
2708
|
+
const rootState = {
|
|
2709
|
+
set,
|
|
2710
|
+
get,
|
|
2711
|
+
// Mock objects that have to be configured
|
|
2712
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
2713
|
+
primaryStore: null,
|
|
2714
|
+
gl: null,
|
|
2715
|
+
renderer: null,
|
|
2716
|
+
camera: null,
|
|
2717
|
+
frustum: new webgpu.Frustum(),
|
|
2718
|
+
autoUpdateFrustum: true,
|
|
2719
|
+
raycaster: null,
|
|
2720
|
+
events: {
|
|
2721
|
+
priority: 1,
|
|
2722
|
+
enabled: true,
|
|
2723
|
+
connected: false,
|
|
2724
|
+
frameTimedRaycasts: true,
|
|
2725
|
+
alwaysFireOnScroll: true,
|
|
2726
|
+
updateOnFrame: false
|
|
2727
|
+
},
|
|
2728
|
+
scene: null,
|
|
2729
|
+
rootScene: null,
|
|
2730
|
+
xr: null,
|
|
2731
|
+
inspector: null,
|
|
2732
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
2733
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
2734
|
+
textureColorSpace: webgpu.SRGBColorSpace,
|
|
2735
|
+
isLegacy: false,
|
|
2736
|
+
webGPUSupported: false,
|
|
2737
|
+
isNative: false,
|
|
2738
|
+
controls: null,
|
|
2739
|
+
pointer,
|
|
2740
|
+
mouse: pointer,
|
|
2741
|
+
frameloop: "always",
|
|
2742
|
+
onPointerMissed: void 0,
|
|
2743
|
+
onDragOverMissed: void 0,
|
|
2744
|
+
onDropMissed: void 0,
|
|
2745
|
+
performance: {
|
|
2746
|
+
current: 1,
|
|
2747
|
+
min: 0.5,
|
|
2748
|
+
max: 1,
|
|
2749
|
+
debounce: 200,
|
|
2750
|
+
regress: () => {
|
|
2751
|
+
const state2 = get();
|
|
2752
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
2753
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
2754
|
+
performanceTimeout = setTimeout(
|
|
2755
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
2756
|
+
state2.performance.debounce
|
|
2757
|
+
);
|
|
2758
|
+
}
|
|
2759
|
+
},
|
|
2760
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2761
|
+
viewport: {
|
|
2762
|
+
initialDpr: 0,
|
|
2763
|
+
dpr: 0,
|
|
2764
|
+
width: 0,
|
|
2765
|
+
height: 0,
|
|
2766
|
+
top: 0,
|
|
2767
|
+
left: 0,
|
|
2768
|
+
aspect: 0,
|
|
2769
|
+
distance: 0,
|
|
2770
|
+
factor: 0,
|
|
2771
|
+
getCurrentViewport
|
|
2772
|
+
},
|
|
2773
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
2774
|
+
setSize: (width, height, top, left) => {
|
|
2775
|
+
const state2 = get();
|
|
2776
|
+
if (width === void 0) {
|
|
2777
|
+
set({ _sizeImperative: false });
|
|
2778
|
+
if (state2._sizeProps) {
|
|
2779
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
2780
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
2781
|
+
const currentSize = state2.size;
|
|
2782
|
+
const newSize = {
|
|
2783
|
+
width: propW ?? currentSize.width,
|
|
2784
|
+
height: propH ?? currentSize.height,
|
|
2785
|
+
top: currentSize.top,
|
|
2786
|
+
left: currentSize.left
|
|
2787
|
+
};
|
|
2788
|
+
set((s) => ({
|
|
2789
|
+
size: newSize,
|
|
2790
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
2791
|
+
}));
|
|
2792
|
+
getScheduler().invalidate();
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
const w = width;
|
|
2798
|
+
const h = height ?? width;
|
|
2799
|
+
const t = top ?? state2.size.top;
|
|
2800
|
+
const l = left ?? state2.size.left;
|
|
2801
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
2802
|
+
set((s) => ({
|
|
2803
|
+
size,
|
|
2804
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
2805
|
+
_sizeImperative: true
|
|
2806
|
+
}));
|
|
2807
|
+
getScheduler().invalidate();
|
|
2808
|
+
},
|
|
2809
|
+
setDpr: (dpr) => set((state2) => {
|
|
2810
|
+
const resolved = calculateDpr(dpr);
|
|
2811
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
2812
|
+
}),
|
|
2813
|
+
setFrameloop: (frameloop = "always") => {
|
|
2814
|
+
set(() => ({ frameloop }));
|
|
2815
|
+
},
|
|
2816
|
+
setError: (error) => set(() => ({ error })),
|
|
2817
|
+
error: null,
|
|
2818
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
|
|
2819
|
+
uniforms: {},
|
|
2820
|
+
nodes: {},
|
|
2821
|
+
buffers: {},
|
|
2822
|
+
gpuStorage: {},
|
|
2823
|
+
textures: /* @__PURE__ */ new Map(),
|
|
2824
|
+
renderPipeline: null,
|
|
2825
|
+
passes: {},
|
|
2826
|
+
_hmrVersion: 0,
|
|
2827
|
+
_sizeImperative: false,
|
|
2828
|
+
_sizeProps: null,
|
|
2829
|
+
previousRoot: void 0,
|
|
2830
|
+
internal: {
|
|
2831
|
+
// Events
|
|
2832
|
+
interaction: [],
|
|
2833
|
+
subscribers: [],
|
|
2834
|
+
// Per-pointer state (new unified structure)
|
|
2835
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
2836
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
2837
|
+
lastEvent: React__namespace.createRef(),
|
|
2838
|
+
// Deprecated but kept for backwards compatibility
|
|
2839
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
2840
|
+
initialClick: [0, 0],
|
|
2841
|
+
initialHits: [],
|
|
2842
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
2843
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
2844
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
2845
|
+
// Occlusion system (WebGPU only)
|
|
2846
|
+
occlusionEnabled: false,
|
|
2847
|
+
occlusionObserver: null,
|
|
2848
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
2849
|
+
helperGroup: null,
|
|
2850
|
+
// Updates
|
|
2851
|
+
active: false,
|
|
2852
|
+
frames: 0,
|
|
2853
|
+
priority: 0,
|
|
2854
|
+
subscribe: (ref, priority, store) => {
|
|
2855
|
+
const internal = get().internal;
|
|
2856
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2857
|
+
internal.subscribers.push({ ref, priority, store });
|
|
2858
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
2859
|
+
return () => {
|
|
2860
|
+
const internal2 = get().internal;
|
|
2861
|
+
if (internal2?.subscribers) {
|
|
2862
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
2863
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
},
|
|
2867
|
+
// Renderer Storage (single source of truth)
|
|
2868
|
+
actualRenderer: null,
|
|
2869
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
2870
|
+
scheduler: null
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
return rootState;
|
|
2874
|
+
});
|
|
2875
|
+
const state = rootStore.getState();
|
|
2876
|
+
Object.defineProperty(state, "gl", {
|
|
2877
|
+
get() {
|
|
2878
|
+
const currentState = rootStore.getState();
|
|
2879
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
2880
|
+
const stack = new Error().stack || "";
|
|
2881
|
+
const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
|
|
2882
|
+
if (!isInternalAccess) {
|
|
2883
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
2884
|
+
notifyDepreciated({
|
|
2885
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
2886
|
+
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
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
return currentState.internal.actualRenderer;
|
|
2891
|
+
},
|
|
2892
|
+
set(value) {
|
|
2893
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2894
|
+
},
|
|
2895
|
+
enumerable: true,
|
|
2896
|
+
configurable: true
|
|
2897
|
+
});
|
|
2898
|
+
Object.defineProperty(state, "renderer", {
|
|
2899
|
+
get() {
|
|
2900
|
+
return rootStore.getState().internal.actualRenderer;
|
|
2901
|
+
},
|
|
2902
|
+
set(value) {
|
|
2903
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
2904
|
+
},
|
|
2905
|
+
enumerable: true,
|
|
2906
|
+
configurable: true
|
|
2907
|
+
});
|
|
2908
|
+
let oldScene = state.scene;
|
|
2909
|
+
rootStore.subscribe(() => {
|
|
2910
|
+
const currentState = rootStore.getState();
|
|
2911
|
+
const { scene, rootScene, set } = currentState;
|
|
2912
|
+
if (scene !== oldScene) {
|
|
2913
|
+
oldScene = scene;
|
|
2914
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
2915
|
+
set({ rootScene: scene });
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
});
|
|
2919
|
+
let oldSize = state.size;
|
|
2920
|
+
let oldDpr = state.viewport.dpr;
|
|
2921
|
+
let oldCamera = state.camera;
|
|
2922
|
+
rootStore.subscribe(() => {
|
|
2923
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
2924
|
+
const actualRenderer = internal.actualRenderer;
|
|
2925
|
+
const canvasTarget = internal.canvasTarget;
|
|
2926
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
2927
|
+
oldSize = size;
|
|
2928
|
+
oldDpr = viewport.dpr;
|
|
2929
|
+
updateCamera(camera, size);
|
|
2930
|
+
if (internal.isSecondary && canvasTarget) {
|
|
2931
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2932
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2933
|
+
} else {
|
|
2934
|
+
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
2935
|
+
actualRenderer.setSize(size.width, size.height, false);
|
|
2936
|
+
if (canvasTarget) {
|
|
2937
|
+
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
2938
|
+
canvasTarget.setSize(size.width, size.height, false);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
if (camera !== oldCamera) {
|
|
2943
|
+
oldCamera = camera;
|
|
2944
|
+
const { rootScene } = rootStore.getState();
|
|
2945
|
+
if (camera && rootScene && !camera.parent) {
|
|
2946
|
+
rootScene.add(camera);
|
|
2947
|
+
}
|
|
2948
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
2949
|
+
const currentState = rootStore.getState();
|
|
2950
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
2951
|
+
updateFrustum(camera, currentState.frustum);
|
|
2286
2952
|
}
|
|
2287
2953
|
}
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2954
|
+
});
|
|
2955
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
2956
|
+
return rootStore;
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2959
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
2960
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
2961
|
+
function getLoader(Proto) {
|
|
2962
|
+
if (isConstructor$1(Proto)) {
|
|
2963
|
+
let loader = memoizedLoaders.get(Proto);
|
|
2964
|
+
if (!loader) {
|
|
2965
|
+
loader = new Proto();
|
|
2966
|
+
memoizedLoaders.set(Proto, loader);
|
|
2299
2967
|
}
|
|
2300
|
-
return
|
|
2968
|
+
return loader;
|
|
2301
2969
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2970
|
+
return Proto;
|
|
2971
|
+
}
|
|
2972
|
+
function loadingFn(extensions, onProgress) {
|
|
2973
|
+
return function(Proto, input) {
|
|
2974
|
+
const loader = getLoader(Proto);
|
|
2975
|
+
if (extensions) extensions(loader);
|
|
2976
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
2977
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
2978
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2979
|
+
return data;
|
|
2980
|
+
});
|
|
2311
2981
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
*/
|
|
2331
|
-
hasUserJobsInPhase(phase, rootId) {
|
|
2332
|
-
const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
|
|
2333
|
-
return rootsToCheck.some((root) => {
|
|
2334
|
-
if (!root) return false;
|
|
2335
|
-
for (const job of root.jobs.values()) {
|
|
2336
|
-
if (job.phase === phase && !job.system && job.enabled) return true;
|
|
2337
|
-
}
|
|
2338
|
-
return false;
|
|
2339
|
-
});
|
|
2340
|
-
}
|
|
2341
|
-
//* Utility Methods ================================
|
|
2342
|
-
/**
|
|
2343
|
-
* Generate a unique root ID for automatic root registration.
|
|
2344
|
-
* @returns {string} A unique root ID in the format 'root_N'
|
|
2345
|
-
*/
|
|
2346
|
-
generateRootId() {
|
|
2347
|
-
return `root_${this.nextRootIndex++}`;
|
|
2348
|
-
}
|
|
2349
|
-
/**
|
|
2350
|
-
* Generate a unique job ID.
|
|
2351
|
-
* @returns {string} A unique job ID in the format 'job_N'
|
|
2352
|
-
* @private
|
|
2353
|
-
*/
|
|
2354
|
-
generateJobId() {
|
|
2355
|
-
return `job_${this.nextJobIndex}`;
|
|
2356
|
-
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Normalize before/after constraints to a Set.
|
|
2359
|
-
* Handles undefined, single string, or array inputs.
|
|
2360
|
-
* @param {string | string[] | undefined} value - The constraint value(s)
|
|
2361
|
-
* @returns {Set<string>} Normalized Set of constraint strings
|
|
2362
|
-
* @private
|
|
2363
|
-
*/
|
|
2364
|
-
normalizeConstraints(value) {
|
|
2365
|
-
if (!value) return /* @__PURE__ */ new Set();
|
|
2366
|
-
if (Array.isArray(value)) return new Set(value);
|
|
2367
|
-
return /* @__PURE__ */ new Set([value]);
|
|
2368
|
-
}
|
|
2369
|
-
};
|
|
2370
|
-
//* Static State & Methods (Singleton Usage) ================================
|
|
2371
|
-
//* Cross-Bundle Singleton Key ==============================
|
|
2372
|
-
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2373
|
-
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2374
|
-
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2375
|
-
let Scheduler = _Scheduler;
|
|
2376
|
-
const getScheduler = () => Scheduler.get();
|
|
2377
|
-
if (hmrData) {
|
|
2378
|
-
hmrData.accept?.();
|
|
2982
|
+
return new Promise(
|
|
2983
|
+
(res, reject) => loader.load(
|
|
2984
|
+
input,
|
|
2985
|
+
(data) => {
|
|
2986
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2987
|
+
res(data);
|
|
2988
|
+
},
|
|
2989
|
+
onProgress,
|
|
2990
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
2991
|
+
)
|
|
2992
|
+
);
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
2996
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
2997
|
+
const fn = loadingFn(extensions, onProgress);
|
|
2998
|
+
const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
|
|
2999
|
+
return Array.isArray(input) ? results : results[0];
|
|
2379
3000
|
}
|
|
3001
|
+
useLoader.preload = function(loader, input, extensions, onProgress) {
|
|
3002
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
3003
|
+
keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
|
|
3004
|
+
};
|
|
3005
|
+
useLoader.clear = function(loader, input) {
|
|
3006
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
3007
|
+
keys.forEach((key) => suspendReact.clear([loader, key]));
|
|
3008
|
+
};
|
|
3009
|
+
useLoader.loader = getLoader;
|
|
2380
3010
|
|
|
2381
3011
|
function useFrame(callback, priorityOrOptions) {
|
|
2382
3012
|
const store = React__namespace.useContext(context);
|
|
@@ -2557,6 +3187,9 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2557
3187
|
const textureCache = useThree((state) => state.textures);
|
|
2558
3188
|
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
2559
3189
|
const { onLoad, cache = false } = options;
|
|
3190
|
+
const onLoadRef = React.useRef(onLoad);
|
|
3191
|
+
onLoadRef.current = onLoad;
|
|
3192
|
+
const onLoadCalledForRef = React.useRef(null);
|
|
2560
3193
|
const urls = React.useMemo(() => getUrls(input), [input]);
|
|
2561
3194
|
const cachedResult = React.useMemo(() => {
|
|
2562
3195
|
if (!cache) return null;
|
|
@@ -2567,9 +3200,13 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
2567
3200
|
webgpu.TextureLoader,
|
|
2568
3201
|
IsObject(input) ? Object.values(input) : input
|
|
2569
3202
|
);
|
|
3203
|
+
const inputKey = urls.join("\0");
|
|
2570
3204
|
React.useLayoutEffect(() => {
|
|
2571
|
-
if (
|
|
2572
|
-
|
|
3205
|
+
if (cachedResult) return;
|
|
3206
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
3207
|
+
onLoadCalledForRef.current = inputKey;
|
|
3208
|
+
onLoadRef.current?.(loadedTextures);
|
|
3209
|
+
}, [cachedResult, loadedTextures, inputKey]);
|
|
2573
3210
|
React.useEffect(() => {
|
|
2574
3211
|
if (cachedResult) return;
|
|
2575
3212
|
if ("initTexture" in renderer) {
|
|
@@ -2736,16 +3373,33 @@ function useTextures() {
|
|
|
2736
3373
|
}, [store]);
|
|
2737
3374
|
}
|
|
2738
3375
|
|
|
2739
|
-
function useRenderTarget(
|
|
3376
|
+
function useRenderTarget(widthOrOptions, heightOrOptions, options) {
|
|
2740
3377
|
const isLegacy = useThree((s) => s.isLegacy);
|
|
2741
3378
|
const size = useThree((s) => s.size);
|
|
3379
|
+
let width;
|
|
3380
|
+
let height;
|
|
3381
|
+
let opts;
|
|
3382
|
+
if (typeof widthOrOptions === "object") {
|
|
3383
|
+
opts = widthOrOptions;
|
|
3384
|
+
} else if (typeof widthOrOptions === "number") {
|
|
3385
|
+
width = widthOrOptions;
|
|
3386
|
+
if (typeof heightOrOptions === "object") {
|
|
3387
|
+
height = widthOrOptions;
|
|
3388
|
+
opts = heightOrOptions;
|
|
3389
|
+
} else if (typeof heightOrOptions === "number") {
|
|
3390
|
+
height = heightOrOptions;
|
|
3391
|
+
opts = options;
|
|
3392
|
+
} else {
|
|
3393
|
+
height = widthOrOptions;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
2742
3396
|
return React.useMemo(() => {
|
|
2743
3397
|
const w = width ?? size.width;
|
|
2744
3398
|
const h = height ?? size.height;
|
|
2745
3399
|
{
|
|
2746
|
-
return isLegacy ? new three.WebGLRenderTarget(w, h,
|
|
3400
|
+
return isLegacy ? new three.WebGLRenderTarget(w, h, opts) : new webgpu.RenderTarget(w, h, opts);
|
|
2747
3401
|
}
|
|
2748
|
-
}, [width, height, size.width, size.height,
|
|
3402
|
+
}, [width, height, size.width, size.height, opts, isLegacy]);
|
|
2749
3403
|
}
|
|
2750
3404
|
|
|
2751
3405
|
function useStore() {
|
|
@@ -2795,7 +3449,7 @@ function addTail(callback) {
|
|
|
2795
3449
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
2796
3450
|
getScheduler().invalidate(frames, stackFrames);
|
|
2797
3451
|
}
|
|
2798
|
-
function advance(timestamp
|
|
3452
|
+
function advance(timestamp) {
|
|
2799
3453
|
getScheduler().step(timestamp);
|
|
2800
3454
|
}
|
|
2801
3455
|
|
|
@@ -14249,6 +14903,7 @@ function swapInstances() {
|
|
|
14249
14903
|
instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
|
|
14250
14904
|
instance.object.__r3f = instance;
|
|
14251
14905
|
setFiberRef(fiber, instance.object);
|
|
14906
|
+
delete instance.appliedOnce;
|
|
14252
14907
|
applyProps(instance.object, instance.props);
|
|
14253
14908
|
if (instance.props.attach) {
|
|
14254
14909
|
attach(parent, instance);
|
|
@@ -14322,8 +14977,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
14322
14977
|
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
14323
14978
|
if (isTailSibling) swapInstances();
|
|
14324
14979
|
},
|
|
14325
|
-
finalizeInitialChildren: () =>
|
|
14326
|
-
|
|
14980
|
+
finalizeInitialChildren: (instance) => {
|
|
14981
|
+
for (const prop in instance.props) {
|
|
14982
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
14983
|
+
}
|
|
14984
|
+
return false;
|
|
14985
|
+
},
|
|
14986
|
+
commitMount(instance) {
|
|
14987
|
+
const resolved = {};
|
|
14988
|
+
for (const prop in instance.props) {
|
|
14989
|
+
const value = instance.props[prop];
|
|
14990
|
+
if (isFromRef(value)) {
|
|
14991
|
+
const ref = value[FROM_REF];
|
|
14992
|
+
if (ref.current != null) resolved[prop] = ref.current;
|
|
14993
|
+
}
|
|
14994
|
+
}
|
|
14995
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
14327
14996
|
},
|
|
14328
14997
|
getPublicInstance: (instance) => instance?.object,
|
|
14329
14998
|
prepareForCommit: () => null,
|
|
@@ -14544,6 +15213,9 @@ function createRoot(canvas) {
|
|
|
14544
15213
|
let resolve;
|
|
14545
15214
|
pending = new Promise((_resolve) => resolve = _resolve);
|
|
14546
15215
|
const {
|
|
15216
|
+
id: canvasId,
|
|
15217
|
+
primaryCanvas,
|
|
15218
|
+
scheduler: schedulerConfig,
|
|
14547
15219
|
gl: glConfig,
|
|
14548
15220
|
renderer: rendererConfig,
|
|
14549
15221
|
size: propsSize,
|
|
@@ -14551,10 +15223,6 @@ function createRoot(canvas) {
|
|
|
14551
15223
|
events,
|
|
14552
15224
|
onCreated: onCreatedCallback,
|
|
14553
15225
|
shadows = false,
|
|
14554
|
-
linear = false,
|
|
14555
|
-
flat = false,
|
|
14556
|
-
textureColorSpace = webgpu.SRGBColorSpace,
|
|
14557
|
-
legacy = false,
|
|
14558
15226
|
orthographic = false,
|
|
14559
15227
|
frameloop = "always",
|
|
14560
15228
|
dpr = [1, 2],
|
|
@@ -14566,8 +15234,10 @@ function createRoot(canvas) {
|
|
|
14566
15234
|
onDropMissed,
|
|
14567
15235
|
autoUpdateFrustum = true,
|
|
14568
15236
|
occlusion = false,
|
|
14569
|
-
_sizeProps
|
|
15237
|
+
_sizeProps,
|
|
15238
|
+
forceEven
|
|
14570
15239
|
} = props;
|
|
15240
|
+
const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || webgpu.SRGBColorSpace;
|
|
14571
15241
|
const state = store.getState();
|
|
14572
15242
|
const defaultGLProps = {
|
|
14573
15243
|
canvas,
|
|
@@ -14576,7 +15246,8 @@ function createRoot(canvas) {
|
|
|
14576
15246
|
alpha: true
|
|
14577
15247
|
};
|
|
14578
15248
|
const defaultGPUProps = {
|
|
14579
|
-
canvas
|
|
15249
|
+
canvas,
|
|
15250
|
+
antialias: true
|
|
14580
15251
|
};
|
|
14581
15252
|
const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
|
|
14582
15253
|
if (glConfig && rendererConfig) {
|
|
@@ -14590,19 +15261,61 @@ function createRoot(canvas) {
|
|
|
14590
15261
|
});
|
|
14591
15262
|
}
|
|
14592
15263
|
let renderer = state.internal.actualRenderer;
|
|
15264
|
+
if (primaryCanvas && wantsGL) {
|
|
15265
|
+
throw new Error(
|
|
15266
|
+
"The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
|
|
15267
|
+
);
|
|
15268
|
+
}
|
|
14593
15269
|
if (wantsGL && !state.internal.actualRenderer) {
|
|
14594
15270
|
renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
|
|
14595
15271
|
state.internal.actualRenderer = renderer;
|
|
14596
|
-
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
15272
|
+
state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
|
|
15273
|
+
} else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
|
|
15274
|
+
const primary = await waitForPrimary(primaryCanvas);
|
|
15275
|
+
renderer = primary.renderer;
|
|
15276
|
+
state.internal.actualRenderer = renderer;
|
|
15277
|
+
const canvasTarget = new webgpu.CanvasTarget(canvas);
|
|
15278
|
+
primary.store.setState((prev) => ({
|
|
15279
|
+
internal: { ...prev.internal, isMultiCanvas: true }
|
|
15280
|
+
}));
|
|
15281
|
+
state.set((prev) => ({
|
|
15282
|
+
webGPUSupported: primary.store.getState().webGPUSupported,
|
|
15283
|
+
renderer,
|
|
15284
|
+
primaryStore: primary.store,
|
|
15285
|
+
internal: {
|
|
15286
|
+
...prev.internal,
|
|
15287
|
+
canvasTarget,
|
|
15288
|
+
isMultiCanvas: true,
|
|
15289
|
+
isSecondary: true,
|
|
15290
|
+
targetId: primaryCanvas
|
|
15291
|
+
}
|
|
15292
|
+
}));
|
|
14597
15293
|
} else if (!wantsGL && !state.internal.actualRenderer) {
|
|
14598
15294
|
renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
|
|
14599
15295
|
if (!renderer.hasInitialized?.()) {
|
|
15296
|
+
const size2 = computeInitialSize(canvas, propsSize);
|
|
15297
|
+
if (size2.width > 0 && size2.height > 0) {
|
|
15298
|
+
const pixelRatio = calculateDpr(dpr);
|
|
15299
|
+
canvas.width = size2.width * pixelRatio;
|
|
15300
|
+
canvas.height = size2.height * pixelRatio;
|
|
15301
|
+
}
|
|
14600
15302
|
await renderer.init();
|
|
14601
15303
|
}
|
|
14602
15304
|
const backend = renderer.backend;
|
|
14603
15305
|
const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
|
|
14604
15306
|
state.internal.actualRenderer = renderer;
|
|
14605
|
-
state.set({ webGPUSupported: isWebGPUBackend, renderer });
|
|
15307
|
+
state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
|
|
15308
|
+
if (canvasId && !state.internal.isSecondary) {
|
|
15309
|
+
const canvasTarget = new webgpu.CanvasTarget(canvas);
|
|
15310
|
+
const unregisterPrimary = registerPrimary(canvasId, renderer, store);
|
|
15311
|
+
state.set((prev) => ({
|
|
15312
|
+
internal: {
|
|
15313
|
+
...prev.internal,
|
|
15314
|
+
canvasTarget,
|
|
15315
|
+
unregisterPrimary
|
|
15316
|
+
}
|
|
15317
|
+
}));
|
|
15318
|
+
}
|
|
14606
15319
|
}
|
|
14607
15320
|
let raycaster = state.raycaster;
|
|
14608
15321
|
if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
|
|
@@ -14611,6 +15324,7 @@ function createRoot(canvas) {
|
|
|
14611
15324
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
14612
15325
|
applyProps(raycaster, { params: { ...raycaster.params, ...params } });
|
|
14613
15326
|
}
|
|
15327
|
+
let tempCamera = state.camera;
|
|
14614
15328
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
14615
15329
|
lastCamera = cameraOptions;
|
|
14616
15330
|
const isCamera = cameraOptions?.isCamera;
|
|
@@ -14630,6 +15344,7 @@ function createRoot(canvas) {
|
|
|
14630
15344
|
if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
|
|
14631
15345
|
}
|
|
14632
15346
|
state.set({ camera });
|
|
15347
|
+
tempCamera = camera;
|
|
14633
15348
|
raycaster.camera = camera;
|
|
14634
15349
|
}
|
|
14635
15350
|
if (!state.scene) {
|
|
@@ -14647,7 +15362,7 @@ function createRoot(canvas) {
|
|
|
14647
15362
|
rootScene: scene,
|
|
14648
15363
|
internal: { ...prev.internal, container: scene }
|
|
14649
15364
|
}));
|
|
14650
|
-
const camera =
|
|
15365
|
+
const camera = tempCamera;
|
|
14651
15366
|
if (camera && !camera.parent) scene.add(camera);
|
|
14652
15367
|
}
|
|
14653
15368
|
if (events && !state.events.handlers) {
|
|
@@ -14664,6 +15379,9 @@ function createRoot(canvas) {
|
|
|
14664
15379
|
if (_sizeProps !== void 0) {
|
|
14665
15380
|
state.set({ _sizeProps });
|
|
14666
15381
|
}
|
|
15382
|
+
if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
|
|
15383
|
+
state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
|
|
15384
|
+
}
|
|
14667
15385
|
const size = computeInitialSize(canvas, propsSize);
|
|
14668
15386
|
if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
|
|
14669
15387
|
const wasImperative = state._sizeImperative;
|
|
@@ -14690,10 +15408,10 @@ function createRoot(canvas) {
|
|
|
14690
15408
|
lastConfiguredProps.performance = performance;
|
|
14691
15409
|
}
|
|
14692
15410
|
if (!state.xr) {
|
|
14693
|
-
const handleXRFrame = (timestamp,
|
|
15411
|
+
const handleXRFrame = (timestamp, _frame) => {
|
|
14694
15412
|
const state2 = store.getState();
|
|
14695
15413
|
if (state2.frameloop === "never") return;
|
|
14696
|
-
advance(timestamp
|
|
15414
|
+
advance(timestamp);
|
|
14697
15415
|
};
|
|
14698
15416
|
const actualRenderer = state.internal.actualRenderer;
|
|
14699
15417
|
const handleSessionChange = () => {
|
|
@@ -14705,16 +15423,16 @@ function createRoot(canvas) {
|
|
|
14705
15423
|
};
|
|
14706
15424
|
const xr = {
|
|
14707
15425
|
connect() {
|
|
14708
|
-
const { gl, renderer: renderer2
|
|
14709
|
-
const
|
|
14710
|
-
|
|
14711
|
-
|
|
15426
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15427
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15428
|
+
xrManager.addEventListener("sessionstart", handleSessionChange);
|
|
15429
|
+
xrManager.addEventListener("sessionend", handleSessionChange);
|
|
14712
15430
|
},
|
|
14713
15431
|
disconnect() {
|
|
14714
|
-
const { gl, renderer: renderer2
|
|
14715
|
-
const
|
|
14716
|
-
|
|
14717
|
-
|
|
15432
|
+
const { gl, renderer: renderer2 } = store.getState();
|
|
15433
|
+
const xrManager = (renderer2 || gl).xr;
|
|
15434
|
+
xrManager.removeEventListener("sessionstart", handleSessionChange);
|
|
15435
|
+
xrManager.removeEventListener("sessionend", handleSessionChange);
|
|
14718
15436
|
}
|
|
14719
15437
|
};
|
|
14720
15438
|
if (typeof renderer.xr?.addEventListener === "function") xr.connect();
|
|
@@ -14726,15 +15444,22 @@ function createRoot(canvas) {
|
|
|
14726
15444
|
const oldType = renderer.shadowMap.type;
|
|
14727
15445
|
renderer.shadowMap.enabled = !!shadows;
|
|
14728
15446
|
if (is.boo(shadows)) {
|
|
14729
|
-
renderer.shadowMap.type = webgpu.
|
|
15447
|
+
renderer.shadowMap.type = webgpu.PCFShadowMap;
|
|
14730
15448
|
} else if (is.str(shadows)) {
|
|
15449
|
+
if (shadows === "soft") {
|
|
15450
|
+
notifyDepreciated({
|
|
15451
|
+
heading: 'shadows="soft" is deprecated',
|
|
15452
|
+
body: "Three has depreciated soft and improved basic PCFShadows, we converted for you.",
|
|
15453
|
+
link: "https://github.com/mrdoob/three.js/wiki/Migration-Guide?utm_source=chatgpt.com#181--182"
|
|
15454
|
+
});
|
|
15455
|
+
}
|
|
14731
15456
|
const types = {
|
|
14732
15457
|
basic: webgpu.BasicShadowMap,
|
|
14733
15458
|
percentage: webgpu.PCFShadowMap,
|
|
14734
|
-
soft: webgpu.
|
|
15459
|
+
soft: webgpu.PCFShadowMap,
|
|
14735
15460
|
variance: webgpu.VSMShadowMap
|
|
14736
15461
|
};
|
|
14737
|
-
renderer.shadowMap.type = types[shadows] ?? webgpu.
|
|
15462
|
+
renderer.shadowMap.type = types[shadows] ?? webgpu.PCFShadowMap;
|
|
14738
15463
|
} else if (is.obj(shadows)) {
|
|
14739
15464
|
Object.assign(renderer.shadowMap, shadows);
|
|
14740
15465
|
}
|
|
@@ -14742,56 +15467,69 @@ function createRoot(canvas) {
|
|
|
14742
15467
|
renderer.shadowMap.needsUpdate = true;
|
|
14743
15468
|
}
|
|
14744
15469
|
}
|
|
14745
|
-
{
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
const flatChanged = flat !== lastConfiguredProps.flat;
|
|
14749
|
-
if (legacyChanged) {
|
|
14750
|
-
if (legacy) {
|
|
14751
|
-
notifyDepreciated({
|
|
14752
|
-
heading: "Legacy Color Management",
|
|
14753
|
-
body: "Legacy color management is deprecated and will be removed in a future version.",
|
|
14754
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
14755
|
-
});
|
|
14756
|
-
}
|
|
14757
|
-
}
|
|
14758
|
-
if (legacyChanged) {
|
|
14759
|
-
webgpu.ColorManagement.enabled = !legacy;
|
|
14760
|
-
lastConfiguredProps.legacy = legacy;
|
|
14761
|
-
}
|
|
14762
|
-
if (!configured || linearChanged) {
|
|
14763
|
-
renderer.outputColorSpace = linear ? webgpu.LinearSRGBColorSpace : webgpu.SRGBColorSpace;
|
|
14764
|
-
lastConfiguredProps.linear = linear;
|
|
14765
|
-
}
|
|
14766
|
-
if (!configured || flatChanged) {
|
|
14767
|
-
renderer.toneMapping = flat ? webgpu.NoToneMapping : webgpu.ACESFilmicToneMapping;
|
|
14768
|
-
lastConfiguredProps.flat = flat;
|
|
14769
|
-
}
|
|
14770
|
-
if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
|
|
14771
|
-
if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
|
|
14772
|
-
if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
|
|
15470
|
+
if (!configured) {
|
|
15471
|
+
renderer.outputColorSpace = webgpu.SRGBColorSpace;
|
|
15472
|
+
renderer.toneMapping = webgpu.ACESFilmicToneMapping;
|
|
14773
15473
|
}
|
|
14774
15474
|
if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
|
|
14775
15475
|
if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
|
|
14776
15476
|
lastConfiguredProps.textureColorSpace = textureColorSpace;
|
|
14777
15477
|
}
|
|
15478
|
+
const r3fProps = ["textureColorSpace"];
|
|
15479
|
+
const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
|
|
15480
|
+
const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
|
|
14778
15481
|
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
|
|
14779
|
-
|
|
15482
|
+
const glProps = {};
|
|
15483
|
+
for (const key in glConfig) {
|
|
15484
|
+
if (!nonApplyProps.includes(key)) glProps[key] = glConfig[key];
|
|
15485
|
+
}
|
|
15486
|
+
applyProps(renderer, glProps);
|
|
14780
15487
|
}
|
|
14781
15488
|
if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
|
|
14782
15489
|
const currentRenderer = state.renderer;
|
|
14783
15490
|
if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
|
|
14784
|
-
|
|
15491
|
+
const rendererProps = {};
|
|
15492
|
+
for (const key in rendererConfig) {
|
|
15493
|
+
if (!nonApplyProps.includes(key)) rendererProps[key] = rendererConfig[key];
|
|
15494
|
+
}
|
|
15495
|
+
applyProps(currentRenderer, rendererProps);
|
|
14785
15496
|
}
|
|
14786
15497
|
}
|
|
14787
15498
|
const scheduler = getScheduler();
|
|
14788
15499
|
const rootId = state.internal.rootId;
|
|
14789
15500
|
if (!rootId) {
|
|
14790
|
-
const newRootId = scheduler.generateRootId();
|
|
15501
|
+
const newRootId = canvasId || scheduler.generateRootId();
|
|
14791
15502
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14792
15503
|
getState: () => store.getState(),
|
|
14793
15504
|
onError: (err) => store.getState().setError(err)
|
|
14794
15505
|
});
|
|
15506
|
+
const unregisterCanvasTarget = scheduler.register(
|
|
15507
|
+
() => {
|
|
15508
|
+
const state2 = store.getState();
|
|
15509
|
+
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
15510
|
+
const renderer2 = state2.internal.actualRenderer;
|
|
15511
|
+
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
15512
|
+
}
|
|
15513
|
+
},
|
|
15514
|
+
{
|
|
15515
|
+
id: `${newRootId}_canvasTarget`,
|
|
15516
|
+
rootId: newRootId,
|
|
15517
|
+
phase: "start",
|
|
15518
|
+
system: true
|
|
15519
|
+
}
|
|
15520
|
+
);
|
|
15521
|
+
const unregisterEventsFlush = scheduler.register(
|
|
15522
|
+
() => {
|
|
15523
|
+
const state2 = store.getState();
|
|
15524
|
+
state2.events.flush?.();
|
|
15525
|
+
},
|
|
15526
|
+
{
|
|
15527
|
+
id: `${newRootId}_events`,
|
|
15528
|
+
rootId: newRootId,
|
|
15529
|
+
phase: "input",
|
|
15530
|
+
system: true
|
|
15531
|
+
}
|
|
15532
|
+
);
|
|
14795
15533
|
const unregisterFrustum = scheduler.register(
|
|
14796
15534
|
() => {
|
|
14797
15535
|
const state2 = store.getState();
|
|
@@ -14826,18 +15564,22 @@ function createRoot(canvas) {
|
|
|
14826
15564
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14827
15565
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14828
15566
|
try {
|
|
14829
|
-
if (state2.
|
|
15567
|
+
if (state2.renderPipeline?.render) state2.renderPipeline.render();
|
|
14830
15568
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14831
15569
|
} catch (error) {
|
|
14832
15570
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
14833
15571
|
}
|
|
14834
15572
|
},
|
|
14835
15573
|
{
|
|
14836
|
-
|
|
15574
|
+
// Use canvas ID directly as job ID if available, otherwise use generated rootId
|
|
15575
|
+
id: canvasId || `${newRootId}_render`,
|
|
14837
15576
|
rootId: newRootId,
|
|
14838
15577
|
phase: "render",
|
|
14839
|
-
system: true
|
|
15578
|
+
system: true,
|
|
14840
15579
|
// Internal flag: this is a system job, not user-controlled
|
|
15580
|
+
// Apply scheduler config for render ordering and rate limiting
|
|
15581
|
+
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
15582
|
+
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14841
15583
|
}
|
|
14842
15584
|
);
|
|
14843
15585
|
state.set((state2) => ({
|
|
@@ -14846,6 +15588,8 @@ function createRoot(canvas) {
|
|
|
14846
15588
|
rootId: newRootId,
|
|
14847
15589
|
unregisterRoot: () => {
|
|
14848
15590
|
unregisterRoot();
|
|
15591
|
+
unregisterCanvasTarget();
|
|
15592
|
+
unregisterEventsFlush();
|
|
14849
15593
|
unregisterFrustum();
|
|
14850
15594
|
unregisterVisibility();
|
|
14851
15595
|
unregisterRender();
|
|
@@ -14904,15 +15648,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14904
15648
|
const renderer = state.internal.actualRenderer;
|
|
14905
15649
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14906
15650
|
if (unregisterRoot) unregisterRoot();
|
|
15651
|
+
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
15652
|
+
if (unregisterPrimary) unregisterPrimary();
|
|
15653
|
+
const canvasTarget = state.internal.canvasTarget;
|
|
15654
|
+
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14907
15655
|
state.events.disconnect?.();
|
|
14908
15656
|
cleanupHelperGroup(root.store);
|
|
14909
|
-
renderer
|
|
14910
|
-
|
|
14911
|
-
|
|
15657
|
+
if (state.isLegacy && renderer) {
|
|
15658
|
+
;
|
|
15659
|
+
renderer.renderLists?.dispose?.();
|
|
15660
|
+
renderer.forceContextLoss?.();
|
|
15661
|
+
}
|
|
15662
|
+
if (!state.internal.isSecondary) {
|
|
15663
|
+
if (renderer?.xr) state.xr.disconnect();
|
|
15664
|
+
}
|
|
14912
15665
|
dispose(state.scene);
|
|
14913
15666
|
_roots.delete(canvas);
|
|
14914
15667
|
if (callback) callback(canvas);
|
|
14915
|
-
} catch
|
|
15668
|
+
} catch {
|
|
14916
15669
|
}
|
|
14917
15670
|
}, 500);
|
|
14918
15671
|
}
|
|
@@ -14920,36 +15673,34 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14920
15673
|
}
|
|
14921
15674
|
}
|
|
14922
15675
|
function createPortal(children, container, state) {
|
|
14923
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15676
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
|
|
14924
15677
|
}
|
|
14925
|
-
function
|
|
15678
|
+
function Portal({ children, container, state }) {
|
|
14926
15679
|
const isRef = React.useCallback((obj) => obj && "current" in obj, []);
|
|
14927
|
-
const [resolvedContainer,
|
|
15680
|
+
const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
|
|
14928
15681
|
if (isRef(container)) return container.current ?? null;
|
|
14929
15682
|
return container;
|
|
14930
15683
|
});
|
|
15684
|
+
const setResolvedContainer = React.useCallback(
|
|
15685
|
+
(newContainer) => {
|
|
15686
|
+
if (!newContainer || newContainer === resolvedContainer) return;
|
|
15687
|
+
_setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
|
|
15688
|
+
},
|
|
15689
|
+
[resolvedContainer, _setResolvedContainer, isRef]
|
|
15690
|
+
);
|
|
14931
15691
|
React.useMemo(() => {
|
|
14932
|
-
if (isRef(container)) {
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
const updated = container.current;
|
|
14937
|
-
if (updated && updated !== resolvedContainer) {
|
|
14938
|
-
setResolvedContainer(updated);
|
|
14939
|
-
}
|
|
14940
|
-
});
|
|
14941
|
-
} else if (current !== resolvedContainer) {
|
|
14942
|
-
setResolvedContainer(current);
|
|
14943
|
-
}
|
|
14944
|
-
} else if (container !== resolvedContainer) {
|
|
14945
|
-
setResolvedContainer(container);
|
|
15692
|
+
if (isRef(container) && !container.current) {
|
|
15693
|
+
return queueMicrotask(() => {
|
|
15694
|
+
setResolvedContainer(container.current);
|
|
15695
|
+
});
|
|
14946
15696
|
}
|
|
14947
|
-
|
|
15697
|
+
setResolvedContainer(container);
|
|
15698
|
+
}, [container, isRef, setResolvedContainer]);
|
|
14948
15699
|
if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
14949
15700
|
const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
|
|
14950
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15701
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
|
|
14951
15702
|
}
|
|
14952
|
-
function
|
|
15703
|
+
function PortalInner({ state = {}, children, container }) {
|
|
14953
15704
|
const { events, size, injectScene = true, ...rest } = state;
|
|
14954
15705
|
const previousRoot = useStore();
|
|
14955
15706
|
const [raycaster] = React.useState(() => new webgpu.Raycaster());
|
|
@@ -14970,11 +15721,12 @@ function Portal({ state = {}, children, container }) {
|
|
|
14970
15721
|
};
|
|
14971
15722
|
}, [portalScene, container, injectScene]);
|
|
14972
15723
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
15724
|
+
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14973
15725
|
let viewport = void 0;
|
|
14974
|
-
if (injectState.camera && size) {
|
|
15726
|
+
if (injectState.camera && (size || injectState.size)) {
|
|
14975
15727
|
const camera = injectState.camera;
|
|
14976
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(),
|
|
14977
|
-
if (camera !== rootState.camera) updateCamera(camera,
|
|
15728
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
|
|
15729
|
+
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14978
15730
|
}
|
|
14979
15731
|
return {
|
|
14980
15732
|
// The intersect consists of the previous root state
|
|
@@ -14991,7 +15743,7 @@ function Portal({ state = {}, children, container }) {
|
|
|
14991
15743
|
previousRoot,
|
|
14992
15744
|
// Events, size and viewport can be overridden by the inject layer
|
|
14993
15745
|
events: { ...rootState.events, ...injectState.events, ...events },
|
|
14994
|
-
size:
|
|
15746
|
+
size: resolvedSize,
|
|
14995
15747
|
viewport: { ...rootState.viewport, ...viewport },
|
|
14996
15748
|
// Layers are allowed to override events
|
|
14997
15749
|
setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
|
|
@@ -15003,9 +15755,13 @@ function Portal({ state = {}, children, container }) {
|
|
|
15003
15755
|
const store = traditional.createWithEqualityFn((set, get) => ({ ...rest, set, get }));
|
|
15004
15756
|
const onMutate = (prev) => store.setState((state2) => inject.current(prev, state2));
|
|
15005
15757
|
onMutate(previousRoot.getState());
|
|
15006
|
-
previousRoot.subscribe(onMutate);
|
|
15007
15758
|
return store;
|
|
15008
15759
|
}, [previousRoot, container]);
|
|
15760
|
+
useIsomorphicLayoutEffect(() => {
|
|
15761
|
+
const onMutate = (prev) => usePortalStore.setState((state2) => inject.current(prev, state2));
|
|
15762
|
+
const unsubscribe = previousRoot.subscribe(onMutate);
|
|
15763
|
+
return unsubscribe;
|
|
15764
|
+
}, [previousRoot, usePortalStore]);
|
|
15009
15765
|
return (
|
|
15010
15766
|
// @ts-ignore, reconciler types are not maintained
|
|
15011
15767
|
/* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reconciler.createPortal(
|
|
@@ -15025,15 +15781,13 @@ function CanvasImpl({
|
|
|
15025
15781
|
fallback,
|
|
15026
15782
|
resize,
|
|
15027
15783
|
style,
|
|
15784
|
+
id,
|
|
15028
15785
|
gl,
|
|
15029
|
-
renderer,
|
|
15786
|
+
renderer: rendererProp,
|
|
15030
15787
|
events = createPointerEvents,
|
|
15031
15788
|
eventSource,
|
|
15032
15789
|
eventPrefix,
|
|
15033
15790
|
shadows,
|
|
15034
|
-
linear,
|
|
15035
|
-
flat,
|
|
15036
|
-
legacy,
|
|
15037
15791
|
orthographic,
|
|
15038
15792
|
frameloop,
|
|
15039
15793
|
dpr,
|
|
@@ -15048,10 +15802,53 @@ function CanvasImpl({
|
|
|
15048
15802
|
hmr,
|
|
15049
15803
|
width,
|
|
15050
15804
|
height,
|
|
15805
|
+
background,
|
|
15806
|
+
forceEven,
|
|
15051
15807
|
...props
|
|
15052
15808
|
}) {
|
|
15809
|
+
const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
|
|
15810
|
+
let primaryCanvas;
|
|
15811
|
+
let scheduler;
|
|
15812
|
+
let renderer;
|
|
15813
|
+
if (isRendererConfig) {
|
|
15814
|
+
const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
|
|
15815
|
+
primaryCanvas = pc;
|
|
15816
|
+
scheduler = sc;
|
|
15817
|
+
renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
|
|
15818
|
+
} else {
|
|
15819
|
+
renderer = rendererProp;
|
|
15820
|
+
}
|
|
15053
15821
|
React__namespace.useMemo(() => extend(THREE), []);
|
|
15054
15822
|
const Bridge = useBridge();
|
|
15823
|
+
const backgroundProps = React__namespace.useMemo(() => {
|
|
15824
|
+
if (!background) return null;
|
|
15825
|
+
if (typeof background === "object" && !background.isColor) {
|
|
15826
|
+
const { backgroundMap, envMap, files, preset, ...rest } = background;
|
|
15827
|
+
return {
|
|
15828
|
+
...rest,
|
|
15829
|
+
preset,
|
|
15830
|
+
files: envMap || files,
|
|
15831
|
+
backgroundFiles: backgroundMap,
|
|
15832
|
+
background: true
|
|
15833
|
+
};
|
|
15834
|
+
}
|
|
15835
|
+
if (typeof background === "number") {
|
|
15836
|
+
return { color: background, background: true };
|
|
15837
|
+
}
|
|
15838
|
+
if (typeof background === "string") {
|
|
15839
|
+
if (background in presetsObj) {
|
|
15840
|
+
return { preset: background, background: true };
|
|
15841
|
+
}
|
|
15842
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
|
|
15843
|
+
return { files: background, background: true };
|
|
15844
|
+
}
|
|
15845
|
+
return { color: background, background: true };
|
|
15846
|
+
}
|
|
15847
|
+
if (background.isColor) {
|
|
15848
|
+
return { color: background, background: true };
|
|
15849
|
+
}
|
|
15850
|
+
return null;
|
|
15851
|
+
}, [background]);
|
|
15055
15852
|
const hasInitialSizeRef = React__namespace.useRef(false);
|
|
15056
15853
|
const measureConfig = React__namespace.useMemo(() => {
|
|
15057
15854
|
if (!hasInitialSizeRef.current) {
|
|
@@ -15068,15 +15865,20 @@ function CanvasImpl({
|
|
|
15068
15865
|
};
|
|
15069
15866
|
}, [resize, hasInitialSizeRef.current]);
|
|
15070
15867
|
const [containerRef, containerRect] = useMeasure__default(measureConfig);
|
|
15071
|
-
const effectiveSize = React__namespace.useMemo(
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15868
|
+
const effectiveSize = React__namespace.useMemo(() => {
|
|
15869
|
+
let w = width ?? containerRect.width;
|
|
15870
|
+
let h = height ?? containerRect.height;
|
|
15871
|
+
if (forceEven) {
|
|
15872
|
+
w = Math.ceil(w / 2) * 2;
|
|
15873
|
+
h = Math.ceil(h / 2) * 2;
|
|
15874
|
+
}
|
|
15875
|
+
return {
|
|
15876
|
+
width: w,
|
|
15877
|
+
height: h,
|
|
15075
15878
|
top: containerRect.top,
|
|
15076
15879
|
left: containerRect.left
|
|
15077
|
-
}
|
|
15078
|
-
|
|
15079
|
-
);
|
|
15880
|
+
};
|
|
15881
|
+
}, [width, height, containerRect, forceEven]);
|
|
15080
15882
|
if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
|
|
15081
15883
|
hasInitialSizeRef.current = true;
|
|
15082
15884
|
}
|
|
@@ -15116,14 +15918,14 @@ function CanvasImpl({
|
|
|
15116
15918
|
async function run() {
|
|
15117
15919
|
if (!effectActiveRef.current || !root.current) return;
|
|
15118
15920
|
await root.current.configure({
|
|
15921
|
+
id,
|
|
15922
|
+
primaryCanvas,
|
|
15923
|
+
scheduler,
|
|
15119
15924
|
gl,
|
|
15120
15925
|
renderer,
|
|
15121
15926
|
scene,
|
|
15122
15927
|
events,
|
|
15123
15928
|
shadows,
|
|
15124
|
-
linear,
|
|
15125
|
-
flat,
|
|
15126
|
-
legacy,
|
|
15127
15929
|
orthographic,
|
|
15128
15930
|
frameloop,
|
|
15129
15931
|
dpr,
|
|
@@ -15133,6 +15935,7 @@ function CanvasImpl({
|
|
|
15133
15935
|
size: effectiveSize,
|
|
15134
15936
|
// Store size props for reset functionality
|
|
15135
15937
|
_sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
|
|
15938
|
+
forceEven,
|
|
15136
15939
|
// Pass mutable reference to onPointerMissed so it's free to update
|
|
15137
15940
|
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
|
|
15138
15941
|
onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
|
|
@@ -15156,7 +15959,10 @@ function CanvasImpl({
|
|
|
15156
15959
|
});
|
|
15157
15960
|
if (!effectActiveRef.current || !root.current) return;
|
|
15158
15961
|
root.current.render(
|
|
15159
|
-
/* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.
|
|
15962
|
+
/* @__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: [
|
|
15963
|
+
backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
|
|
15964
|
+
children ?? null
|
|
15965
|
+
] }) }) })
|
|
15160
15966
|
);
|
|
15161
15967
|
}
|
|
15162
15968
|
run();
|
|
@@ -15183,20 +15989,22 @@ function CanvasImpl({
|
|
|
15183
15989
|
const canvas = canvasRef.current;
|
|
15184
15990
|
if (!canvas) return;
|
|
15185
15991
|
const handleHMR = () => {
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
rootEntry
|
|
15189
|
-
nodes
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15992
|
+
queueMicrotask(() => {
|
|
15993
|
+
const rootEntry = _roots.get(canvas);
|
|
15994
|
+
if (rootEntry?.store) {
|
|
15995
|
+
console.log("[R3F] HMR detected \u2014 rebuilding nodes/uniforms");
|
|
15996
|
+
rootEntry.store.setState((state) => ({
|
|
15997
|
+
nodes: {},
|
|
15998
|
+
uniforms: {},
|
|
15999
|
+
_hmrVersion: state._hmrVersion + 1
|
|
16000
|
+
}));
|
|
16001
|
+
}
|
|
16002
|
+
});
|
|
15194
16003
|
};
|
|
15195
16004
|
if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
|
|
15196
16005
|
const hot = undefined;
|
|
15197
16006
|
hot.on("vite:afterUpdate", handleHMR);
|
|
15198
|
-
return () => hot.
|
|
15199
|
-
});
|
|
16007
|
+
return () => hot.off?.("vite:afterUpdate", handleHMR);
|
|
15200
16008
|
}
|
|
15201
16009
|
if (typeof module !== "undefined" && module.hot) {
|
|
15202
16010
|
const hot = module.hot;
|
|
@@ -15219,7 +16027,16 @@ function CanvasImpl({
|
|
|
15219
16027
|
...style
|
|
15220
16028
|
},
|
|
15221
16029
|
...props,
|
|
15222
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16030
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16031
|
+
"canvas",
|
|
16032
|
+
{
|
|
16033
|
+
ref: canvasRef,
|
|
16034
|
+
id,
|
|
16035
|
+
className: "r3f-canvas",
|
|
16036
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
16037
|
+
children: fallback
|
|
16038
|
+
}
|
|
16039
|
+
) })
|
|
15223
16040
|
}
|
|
15224
16041
|
);
|
|
15225
16042
|
}
|
|
@@ -15231,8 +16048,15 @@ extend(THREE);
|
|
|
15231
16048
|
|
|
15232
16049
|
exports.Block = Block;
|
|
15233
16050
|
exports.Canvas = Canvas;
|
|
16051
|
+
exports.Environment = Environment;
|
|
16052
|
+
exports.EnvironmentCube = EnvironmentCube;
|
|
16053
|
+
exports.EnvironmentMap = EnvironmentMap;
|
|
16054
|
+
exports.EnvironmentPortal = EnvironmentPortal;
|
|
15234
16055
|
exports.ErrorBoundary = ErrorBoundary;
|
|
16056
|
+
exports.FROM_REF = FROM_REF;
|
|
15235
16057
|
exports.IsObject = IsObject;
|
|
16058
|
+
exports.ONCE = ONCE;
|
|
16059
|
+
exports.Portal = Portal;
|
|
15236
16060
|
exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
|
|
15237
16061
|
exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
|
|
15238
16062
|
exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
|
|
@@ -15262,30 +16086,41 @@ exports.events = createPointerEvents;
|
|
|
15262
16086
|
exports.extend = extend;
|
|
15263
16087
|
exports.findInitialRoot = findInitialRoot;
|
|
15264
16088
|
exports.flushSync = flushSync;
|
|
16089
|
+
exports.fromRef = fromRef;
|
|
15265
16090
|
exports.getInstanceProps = getInstanceProps;
|
|
16091
|
+
exports.getPrimary = getPrimary;
|
|
16092
|
+
exports.getPrimaryIds = getPrimaryIds;
|
|
15266
16093
|
exports.getRootState = getRootState;
|
|
15267
16094
|
exports.getScheduler = getScheduler;
|
|
15268
16095
|
exports.getUuidPrefix = getUuidPrefix;
|
|
15269
16096
|
exports.hasConstructor = hasConstructor;
|
|
16097
|
+
exports.hasPrimary = hasPrimary;
|
|
15270
16098
|
exports.invalidate = invalidate;
|
|
15271
16099
|
exports.invalidateInstance = invalidateInstance;
|
|
15272
16100
|
exports.is = is;
|
|
15273
16101
|
exports.isColorRepresentation = isColorRepresentation;
|
|
15274
16102
|
exports.isCopyable = isCopyable;
|
|
16103
|
+
exports.isFromRef = isFromRef;
|
|
15275
16104
|
exports.isObject3D = isObject3D;
|
|
16105
|
+
exports.isOnce = isOnce;
|
|
15276
16106
|
exports.isOrthographicCamera = isOrthographicCamera;
|
|
15277
16107
|
exports.isRef = isRef;
|
|
15278
16108
|
exports.isRenderer = isRenderer;
|
|
15279
16109
|
exports.isTexture = isTexture;
|
|
15280
16110
|
exports.isVectorLike = isVectorLike;
|
|
16111
|
+
exports.once = once;
|
|
15281
16112
|
exports.prepare = prepare;
|
|
16113
|
+
exports.presetsObj = presetsObj;
|
|
15282
16114
|
exports.reconciler = reconciler;
|
|
16115
|
+
exports.registerPrimary = registerPrimary;
|
|
15283
16116
|
exports.removeInteractivity = removeInteractivity;
|
|
15284
16117
|
exports.resolve = resolve;
|
|
15285
16118
|
exports.unmountComponentAtNode = unmountComponentAtNode;
|
|
16119
|
+
exports.unregisterPrimary = unregisterPrimary;
|
|
15286
16120
|
exports.updateCamera = updateCamera;
|
|
15287
16121
|
exports.updateFrustum = updateFrustum;
|
|
15288
16122
|
exports.useBridge = useBridge;
|
|
16123
|
+
exports.useEnvironment = useEnvironment;
|
|
15289
16124
|
exports.useFrame = useFrame;
|
|
15290
16125
|
exports.useGraph = useGraph;
|
|
15291
16126
|
exports.useInstanceHandle = useInstanceHandle;
|
|
@@ -15297,3 +16132,4 @@ exports.useStore = useStore;
|
|
|
15297
16132
|
exports.useTexture = useTexture;
|
|
15298
16133
|
exports.useTextures = useTextures;
|
|
15299
16134
|
exports.useThree = useThree;
|
|
16135
|
+
exports.waitForPrimary = waitForPrimary;
|