@react-three/fiber 10.0.0-canary.4bc4a97 → 10.0.0-canary.4bf885a
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/dist/extension.cjs +278 -0
- package/dist/extension.d.cts +1675 -0
- package/dist/extension.d.mts +1675 -0
- package/dist/extension.d.ts +1647 -0
- package/dist/extension.mjs +258 -0
- package/dist/index.cjs +984 -899
- package/dist/index.d.cts +337 -156
- package/dist/index.d.mts +337 -156
- package/dist/index.d.ts +335 -182
- package/dist/index.mjs +985 -902
- package/dist/legacy.cjs +997 -913
- package/dist/legacy.d.cts +358 -209
- package/dist/legacy.d.mts +358 -209
- package/dist/legacy.d.ts +356 -233
- package/dist/legacy.mjs +998 -916
- package/dist/webgpu/index.cjs +899 -736
- package/dist/webgpu/index.d.cts +412 -262
- package/dist/webgpu/index.d.mts +412 -262
- package/dist/webgpu/index.d.ts +410 -288
- package/dist/webgpu/index.mjs +900 -739
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as webgpu from 'three/webgpu';
|
|
2
|
-
import { CubeRenderTarget, RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, ShaderMaterial, Uniform, SRGBColorSpace, RGBAFormat, UnsignedByteType,
|
|
2
|
+
import { CubeRenderTarget, RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, ShaderMaterial, Uniform, SRGBColorSpace, RGBAFormat, UnsignedByteType, TextureLoader, Texture as Texture$1, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Vector3, Vector2, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer, HalfFloatType, Color } from 'three/webgpu';
|
|
3
3
|
import { WebGLCubeRenderTarget, WebGLRenderTarget, WebGLRenderer } from 'three';
|
|
4
|
-
import { Inspector } from 'three/addons/inspector/Inspector.js';
|
|
5
4
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
6
5
|
import * as React from 'react';
|
|
7
|
-
import React__default, { useRef, useMemo, useLayoutEffect, useEffect,
|
|
6
|
+
import React__default, { useContext, useRef, useMemo, useLayoutEffect, useEffect, useImperativeHandle, useCallback, useState } from 'react';
|
|
8
7
|
import useMeasure from 'react-use-measure';
|
|
9
8
|
import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
|
|
10
9
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
@@ -13,6 +12,7 @@ export { Scheduler, getScheduler } from '@pmndrs/scheduler';
|
|
|
13
12
|
import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
|
|
14
13
|
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
15
14
|
import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
|
|
15
|
+
import { HDRCubeTextureLoader } from 'three/examples/jsm/loaders/HDRCubeTextureLoader.js';
|
|
16
16
|
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
|
|
17
17
|
import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
|
|
18
18
|
import { GainMapLoader } from '@monogrid/gainmap-js';
|
|
@@ -32,18 +32,22 @@ function _mergeNamespaces(n, m) {
|
|
|
32
32
|
|
|
33
33
|
const R3F_BUILD_LEGACY = true;
|
|
34
34
|
const R3F_BUILD_WEBGPU = true;
|
|
35
|
+
async function loadInspector() {
|
|
36
|
+
const { Inspector } = await import('three/addons/inspector/Inspector.js');
|
|
37
|
+
return Inspector;
|
|
38
|
+
}
|
|
35
39
|
|
|
36
40
|
const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
37
41
|
__proto__: null,
|
|
38
42
|
CubeRenderTarget: CubeRenderTarget,
|
|
39
43
|
CubeRenderTargetCompat: CubeRenderTarget,
|
|
40
|
-
Inspector: Inspector,
|
|
41
44
|
R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
|
|
42
45
|
R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
|
|
43
46
|
RenderTargetCompat: RenderTarget,
|
|
44
47
|
WebGLCubeRenderTarget: WebGLCubeRenderTarget,
|
|
45
48
|
WebGLRenderTarget: WebGLRenderTarget,
|
|
46
|
-
WebGLRenderer: WebGLRenderer
|
|
49
|
+
WebGLRenderer: WebGLRenderer,
|
|
50
|
+
loadInspector: loadInspector
|
|
47
51
|
}, [webgpu]);
|
|
48
52
|
|
|
49
53
|
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
@@ -290,7 +294,6 @@ function prepare(target, root, type, props) {
|
|
|
290
294
|
}
|
|
291
295
|
function invalidateInstance(instance) {
|
|
292
296
|
if (!instance.parent) return;
|
|
293
|
-
instance.props.onUpdate?.(instance.object);
|
|
294
297
|
const state = instance.root?.getState?.();
|
|
295
298
|
if (state && state.internal.frames === 0) state.invalidate();
|
|
296
299
|
}
|
|
@@ -506,8 +509,10 @@ function hasVisibilityHandlers(handlers) {
|
|
|
506
509
|
}
|
|
507
510
|
|
|
508
511
|
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
509
|
-
function fromRef(ref) {
|
|
510
|
-
|
|
512
|
+
function fromRef(ref, transform) {
|
|
513
|
+
const marker = { [FROM_REF]: ref };
|
|
514
|
+
if (transform) marker.transform = transform;
|
|
515
|
+
return marker;
|
|
511
516
|
}
|
|
512
517
|
function isFromRef(value) {
|
|
513
518
|
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
@@ -521,6 +526,63 @@ function isOnce(value) {
|
|
|
521
526
|
return value !== null && typeof value === "object" && ONCE in value;
|
|
522
527
|
}
|
|
523
528
|
|
|
529
|
+
const shownNotices = /* @__PURE__ */ new Set();
|
|
530
|
+
function notifyDepreciated({ heading, body, link }) {
|
|
531
|
+
if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_DEPRECATION_WARNINGS !== "true") {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (shownNotices.has(heading)) return;
|
|
535
|
+
shownNotices.add(heading);
|
|
536
|
+
const caller = getCallerFrame();
|
|
537
|
+
if (caller) {
|
|
538
|
+
console.log();
|
|
539
|
+
}
|
|
540
|
+
const boxStyle = "background: #ff9800; color: #1a1a1a; padding: 8px 12px; border-radius: 4px; font-weight: 500;";
|
|
541
|
+
if (caller) {
|
|
542
|
+
console.log(`%c\u26A0\uFE0F ${heading}`, boxStyle);
|
|
543
|
+
}
|
|
544
|
+
if (body || link) {
|
|
545
|
+
let message = "";
|
|
546
|
+
if (body) message += body;
|
|
547
|
+
if (link) message += (body ? "\n\n" : "") + `More info: ${link}`;
|
|
548
|
+
console.warn(`%c${message}`, "font-weight: bold;");
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function getCallerFrame(depth = 3) {
|
|
552
|
+
const stack = new Error().stack;
|
|
553
|
+
if (!stack) return null;
|
|
554
|
+
const lines = stack.split("\n");
|
|
555
|
+
const frame = lines[depth];
|
|
556
|
+
if (!frame) return null;
|
|
557
|
+
let match = frame.match(/^\s*at (?:(.+?) )?\(?(.+?):(\d+):(\d+)\)?$/);
|
|
558
|
+
if (!match) {
|
|
559
|
+
match = frame.match(/^(?:(.+?)@)?(.+?):(\d+):(\d+)$/);
|
|
560
|
+
}
|
|
561
|
+
if (!match) return null;
|
|
562
|
+
const [, fn, url, line] = match;
|
|
563
|
+
return {
|
|
564
|
+
functionName: fn ?? "<anonymous>",
|
|
565
|
+
location: formatLocation(url, Number(line))
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
function formatLocation(url, line) {
|
|
569
|
+
const clean = url.split("?")[0];
|
|
570
|
+
const file = clean.split("/").pop() ?? clean;
|
|
571
|
+
return `${file}:${line}`;
|
|
572
|
+
}
|
|
573
|
+
function notifyAlpha({ message, link }) {
|
|
574
|
+
if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (shownNotices.has(message)) return;
|
|
578
|
+
shownNotices.add(message);
|
|
579
|
+
const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
|
|
580
|
+
console.log(`%c\u{1F52C} ${message}`, boxStyle);
|
|
581
|
+
{
|
|
582
|
+
console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
524
586
|
const RESERVED_PROPS = [
|
|
525
587
|
"children",
|
|
526
588
|
"key",
|
|
@@ -530,7 +592,6 @@ const RESERVED_PROPS = [
|
|
|
530
592
|
"dispose",
|
|
531
593
|
"attach",
|
|
532
594
|
"object",
|
|
533
|
-
"onUpdate",
|
|
534
595
|
// Behavior flags
|
|
535
596
|
"dispose"
|
|
536
597
|
];
|
|
@@ -608,11 +669,9 @@ function diffProps(instance, newProps) {
|
|
|
608
669
|
for (const prop in instance.props) {
|
|
609
670
|
if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
|
|
610
671
|
const { root, key } = resolve(instance.object, prop);
|
|
611
|
-
if (
|
|
672
|
+
if (typeof root?.constructor === "function") {
|
|
612
673
|
const ctor = getMemoizedPrototype(root);
|
|
613
674
|
if (!is.und(ctor)) changedProps[prop] = ctor[key];
|
|
614
|
-
} else {
|
|
615
|
-
changedProps[prop] = 0;
|
|
616
675
|
}
|
|
617
676
|
}
|
|
618
677
|
return changedProps;
|
|
@@ -624,6 +683,13 @@ function applyProps(object, props) {
|
|
|
624
683
|
for (const prop in props) {
|
|
625
684
|
const value = props[prop];
|
|
626
685
|
if (RESERVED_PROPS.includes(prop)) continue;
|
|
686
|
+
if (prop === "onUpdate") {
|
|
687
|
+
notifyDepreciated({
|
|
688
|
+
heading: "onUpdate is no longer an R3F prop",
|
|
689
|
+
body: "v10 removed the onUpdate hook: R3F no longer calls it after prop updates. The value is assigned to the object by name. On a Texture that sets three's Texture.onUpdate, which the renderer calls after a GPU upload, not after prop changes. On a node it replaces the Node.onUpdate() method. On other objects it is never called.\n\nUse an effect keyed on the props that change, read the object in useFrame, or use a ref callback.",
|
|
690
|
+
link: "https://docs.pmnd.rs/react-three-fiber/migration/v10#onupdate-removed"
|
|
691
|
+
});
|
|
692
|
+
}
|
|
627
693
|
if (instance && EVENT_REGEX.test(prop)) {
|
|
628
694
|
if (typeof value === "function") instance.handlers[prop] = value;
|
|
629
695
|
else delete instance.handlers[prop];
|
|
@@ -729,682 +795,378 @@ function applyProps(object, props) {
|
|
|
729
795
|
return object;
|
|
730
796
|
}
|
|
731
797
|
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (
|
|
738
|
-
|
|
739
|
-
const caller = getCallerFrame();
|
|
740
|
-
if (caller) {
|
|
741
|
-
console.log();
|
|
742
|
-
}
|
|
743
|
-
const boxStyle = "background: #ff9800; color: #1a1a1a; padding: 8px 12px; border-radius: 4px; font-weight: 500;";
|
|
744
|
-
if (caller) {
|
|
745
|
-
console.log(`%c\u26A0\uFE0F ${heading}`, boxStyle);
|
|
746
|
-
}
|
|
747
|
-
if (body || link) {
|
|
748
|
-
let message = "";
|
|
749
|
-
if (body) message += body;
|
|
750
|
-
if (link) message += (body ? "\n\n" : "") + `More info: ${link}`;
|
|
751
|
-
console.warn(`%c${message}`, "font-weight: bold;");
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
function getCallerFrame(depth = 3) {
|
|
755
|
-
const stack = new Error().stack;
|
|
756
|
-
if (!stack) return null;
|
|
757
|
-
const lines = stack.split("\n");
|
|
758
|
-
const frame = lines[depth];
|
|
759
|
-
if (!frame) return null;
|
|
760
|
-
let match = frame.match(/^\s*at (?:(.+?) )?\(?(.+?):(\d+):(\d+)\)?$/);
|
|
761
|
-
if (!match) {
|
|
762
|
-
match = frame.match(/^(?:(.+?)@)?(.+?):(\d+):(\d+)$/);
|
|
763
|
-
}
|
|
764
|
-
if (!match) return null;
|
|
765
|
-
const [, fn, url, line] = match;
|
|
766
|
-
return {
|
|
767
|
-
functionName: fn ?? "<anonymous>",
|
|
768
|
-
location: formatLocation(url, Number(line))
|
|
769
|
-
};
|
|
798
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
799
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
|
|
800
|
+
|
|
801
|
+
function useStore() {
|
|
802
|
+
const store = useContext(context);
|
|
803
|
+
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
804
|
+
return store;
|
|
770
805
|
}
|
|
771
|
-
function
|
|
772
|
-
|
|
773
|
-
const file = clean.split("/").pop() ?? clean;
|
|
774
|
-
return `${file}:${line}`;
|
|
806
|
+
function useThree(selector = (state) => state, equalityFn) {
|
|
807
|
+
return useStore()(selector, equalityFn);
|
|
775
808
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
809
|
+
|
|
810
|
+
const memoizedLoaders = /* @__PURE__ */ new WeakMap();
|
|
811
|
+
const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
|
|
812
|
+
function getLoader$1(Proto) {
|
|
813
|
+
if (isConstructor$1(Proto)) {
|
|
814
|
+
let loader = memoizedLoaders.get(Proto);
|
|
815
|
+
if (!loader) {
|
|
816
|
+
loader = new Proto();
|
|
817
|
+
memoizedLoaders.set(Proto, loader);
|
|
818
|
+
}
|
|
819
|
+
return loader;
|
|
786
820
|
}
|
|
821
|
+
return Proto;
|
|
787
822
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
823
|
+
function loadingFn(extensions, onProgress) {
|
|
824
|
+
return function(Proto, input) {
|
|
825
|
+
const loader = getLoader$1(Proto);
|
|
826
|
+
if (extensions) extensions(loader);
|
|
827
|
+
if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
|
|
828
|
+
return loader.loadAsync(input, onProgress).then((data) => {
|
|
829
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
830
|
+
return data;
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
return new Promise(
|
|
834
|
+
(res, reject) => loader.load(
|
|
835
|
+
input,
|
|
836
|
+
(data) => {
|
|
837
|
+
if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
|
|
838
|
+
res(data);
|
|
839
|
+
},
|
|
840
|
+
onProgress,
|
|
841
|
+
(error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
|
|
842
|
+
)
|
|
843
|
+
);
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
function useLoader(loader, input, extensions, onProgress, cacheKey) {
|
|
847
|
+
const inputs = Array.isArray(input) ? input : [input];
|
|
848
|
+
const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
|
|
849
|
+
const fn = loadingFn(extensions, onProgress);
|
|
850
|
+
const results = cacheKeys.map(
|
|
851
|
+
(key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
|
|
852
|
+
);
|
|
853
|
+
const stableRef = useRef(null);
|
|
854
|
+
const previous = stableRef.current;
|
|
855
|
+
const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
|
|
856
|
+
stableRef.current = stable;
|
|
857
|
+
return Array.isArray(input) ? stable : stable[0];
|
|
794
858
|
}
|
|
859
|
+
useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
|
|
860
|
+
const inputs = Array.isArray(input) ? input : [input];
|
|
861
|
+
const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
|
|
862
|
+
cacheKeys.forEach(
|
|
863
|
+
(key, index) => preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
|
|
864
|
+
);
|
|
865
|
+
};
|
|
866
|
+
useLoader.clear = function(loader, input, cacheKey) {
|
|
867
|
+
const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
|
|
868
|
+
cacheKeys.forEach((key) => clear([loader, key]));
|
|
869
|
+
};
|
|
870
|
+
useLoader.loader = getLoader$1;
|
|
795
871
|
|
|
796
|
-
|
|
797
|
-
const
|
|
798
|
-
const
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
872
|
+
function useFrame(callback, priorityOrOptions) {
|
|
873
|
+
const store = React.useContext(context);
|
|
874
|
+
const isInsideCanvas = store !== null;
|
|
875
|
+
const scheduler = getScheduler();
|
|
876
|
+
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
877
|
+
id: priorityOrOptions.id,
|
|
878
|
+
phase: priorityOrOptions.phase,
|
|
879
|
+
priority: priorityOrOptions.priority,
|
|
880
|
+
fps: priorityOrOptions.fps,
|
|
881
|
+
drop: priorityOrOptions.drop,
|
|
882
|
+
enabled: priorityOrOptions.enabled,
|
|
883
|
+
before: priorityOrOptions.before,
|
|
884
|
+
after: priorityOrOptions.after
|
|
885
|
+
}) : "";
|
|
886
|
+
const options = React.useMemo(() => {
|
|
887
|
+
return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
|
|
888
|
+
}, [optionsKey]);
|
|
889
|
+
const reactId = React.useId();
|
|
890
|
+
const id = options.id ?? reactId;
|
|
891
|
+
const callbackRef = useMutableCallback(callback);
|
|
892
|
+
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
893
|
+
useIsomorphicLayoutEffect(() => {
|
|
894
|
+
if (!callback) return;
|
|
895
|
+
if (isInsideCanvas) {
|
|
896
|
+
const state = store.getState();
|
|
897
|
+
const rootId = state.internal.rootId;
|
|
898
|
+
if (isLegacyPriority) {
|
|
899
|
+
state.internal.priority++;
|
|
900
|
+
let parentRoot = state.previousRoot;
|
|
901
|
+
while (parentRoot) {
|
|
902
|
+
const parentState = parentRoot.getState();
|
|
903
|
+
if (parentState?.internal) parentState.internal.priority++;
|
|
904
|
+
parentRoot = parentState?.previousRoot;
|
|
905
|
+
}
|
|
906
|
+
notifyDepreciated({
|
|
907
|
+
heading: "useFrame with numeric priority is deprecated",
|
|
908
|
+
body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
|
|
909
|
+
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
910
|
+
});
|
|
816
911
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
inspector: null,
|
|
845
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
846
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
847
|
-
textureColorSpace: SRGBColorSpace,
|
|
848
|
-
isLegacy: false,
|
|
849
|
-
webGPUSupported: false,
|
|
850
|
-
isNative: false,
|
|
851
|
-
controls: null,
|
|
852
|
-
pointer,
|
|
853
|
-
mouse: pointer,
|
|
854
|
-
frameloop: "always",
|
|
855
|
-
onPointerMissed: void 0,
|
|
856
|
-
onDragOverMissed: void 0,
|
|
857
|
-
onDropMissed: void 0,
|
|
858
|
-
performance: {
|
|
859
|
-
current: 1,
|
|
860
|
-
min: 0.5,
|
|
861
|
-
max: 1,
|
|
862
|
-
debounce: 200,
|
|
863
|
-
regress: () => {
|
|
864
|
-
const state2 = get();
|
|
865
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
866
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
867
|
-
performanceTimeout = setTimeout(
|
|
868
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
869
|
-
state2.performance.debounce
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
},
|
|
873
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
874
|
-
viewport: {
|
|
875
|
-
initialDpr: 0,
|
|
876
|
-
dpr: 0,
|
|
877
|
-
width: 0,
|
|
878
|
-
height: 0,
|
|
879
|
-
top: 0,
|
|
880
|
-
left: 0,
|
|
881
|
-
aspect: 0,
|
|
882
|
-
distance: 0,
|
|
883
|
-
factor: 0,
|
|
884
|
-
getCurrentViewport
|
|
885
|
-
},
|
|
886
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
887
|
-
setSize: (width, height, top, left) => {
|
|
888
|
-
const state2 = get();
|
|
889
|
-
if (width === void 0) {
|
|
890
|
-
set({ _sizeImperative: false });
|
|
891
|
-
if (state2._sizeProps) {
|
|
892
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
893
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
894
|
-
const currentSize = state2.size;
|
|
895
|
-
const newSize = {
|
|
896
|
-
width: propW ?? currentSize.width,
|
|
897
|
-
height: propH ?? currentSize.height,
|
|
898
|
-
top: currentSize.top,
|
|
899
|
-
left: currentSize.left
|
|
900
|
-
};
|
|
901
|
-
set((s) => ({
|
|
902
|
-
size: newSize,
|
|
903
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
904
|
-
}));
|
|
905
|
-
getScheduler().invalidate();
|
|
912
|
+
const wrappedCallback = (frameState, delta) => {
|
|
913
|
+
const localState = store.getState();
|
|
914
|
+
const mergedState = {
|
|
915
|
+
...localState,
|
|
916
|
+
time: frameState.time,
|
|
917
|
+
delta: frameState.delta,
|
|
918
|
+
elapsed: frameState.elapsed,
|
|
919
|
+
frame: frameState.frame
|
|
920
|
+
};
|
|
921
|
+
callbackRef.current?.(mergedState, delta);
|
|
922
|
+
};
|
|
923
|
+
const unregister = scheduler.register(wrappedCallback, {
|
|
924
|
+
id,
|
|
925
|
+
rootId,
|
|
926
|
+
...options
|
|
927
|
+
});
|
|
928
|
+
return () => {
|
|
929
|
+
unregister();
|
|
930
|
+
if (isLegacyPriority) {
|
|
931
|
+
const currentState = store.getState();
|
|
932
|
+
if (currentState.internal) {
|
|
933
|
+
currentState.internal.priority--;
|
|
934
|
+
let parentRoot = currentState.previousRoot;
|
|
935
|
+
while (parentRoot) {
|
|
936
|
+
const parentState = parentRoot.getState();
|
|
937
|
+
if (parentState?.internal) parentState.internal.priority--;
|
|
938
|
+
parentRoot = parentState?.previousRoot;
|
|
906
939
|
}
|
|
907
940
|
}
|
|
908
|
-
return;
|
|
909
941
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
942
|
+
};
|
|
943
|
+
} else {
|
|
944
|
+
return scheduler.register(
|
|
945
|
+
(state, delta) => {
|
|
946
|
+
const frameState = state;
|
|
947
|
+
if (!frameState.renderer) return;
|
|
948
|
+
callbackRef.current?.(frameState, delta);
|
|
949
|
+
},
|
|
950
|
+
{ id, ...options }
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
954
|
+
const isPaused = React.useSyncExternalStore(
|
|
955
|
+
// Subscribe function
|
|
956
|
+
React.useCallback(
|
|
957
|
+
(onStoreChange) => {
|
|
958
|
+
return getScheduler().subscribeJobState(id, onStoreChange);
|
|
921
959
|
},
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
960
|
+
[id]
|
|
961
|
+
),
|
|
962
|
+
// getSnapshot function
|
|
963
|
+
React.useCallback(() => getScheduler().isJobPaused(id), [id]),
|
|
964
|
+
// getServerSnapshot function (SSR)
|
|
965
|
+
React.useCallback(() => false, [])
|
|
966
|
+
);
|
|
967
|
+
const controls = React.useMemo(() => {
|
|
968
|
+
const scheduler2 = getScheduler();
|
|
969
|
+
return {
|
|
970
|
+
/** The job's unique ID */
|
|
971
|
+
id,
|
|
972
|
+
/**
|
|
973
|
+
* Access to the global scheduler for frame loop control.
|
|
974
|
+
* Use for controlling the entire frame loop, adding phases, etc.
|
|
975
|
+
*/
|
|
976
|
+
scheduler: scheduler2,
|
|
977
|
+
/**
|
|
978
|
+
* The root that currently owns this job. Resolve on access because ambient
|
|
979
|
+
* jobs can be adopted by a Canvas after the controls object is created.
|
|
980
|
+
*/
|
|
981
|
+
get rootId() {
|
|
982
|
+
return getScheduler().getJobRootId(id);
|
|
929
983
|
},
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
internal.subscribers.push({ ref, priority, store });
|
|
973
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
974
|
-
return () => {
|
|
975
|
-
const internal2 = get().internal;
|
|
976
|
-
if (internal2?.subscribers) {
|
|
977
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
978
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
979
|
-
}
|
|
980
|
-
};
|
|
981
|
-
},
|
|
982
|
-
// Renderer Storage (single source of truth)
|
|
983
|
-
actualRenderer: null,
|
|
984
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
985
|
-
scheduler: null
|
|
986
|
-
}
|
|
984
|
+
/**
|
|
985
|
+
* Manually step this job only.
|
|
986
|
+
* Bypasses FPS limiting - always runs.
|
|
987
|
+
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
988
|
+
*/
|
|
989
|
+
step: (timestamp) => {
|
|
990
|
+
getScheduler().stepJob(id, timestamp);
|
|
991
|
+
},
|
|
992
|
+
/**
|
|
993
|
+
* Manually step ALL jobs in the scheduler.
|
|
994
|
+
* Useful for frameloop='never' mode.
|
|
995
|
+
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
996
|
+
*/
|
|
997
|
+
stepAll: (timestamp) => {
|
|
998
|
+
getScheduler().step(timestamp);
|
|
999
|
+
},
|
|
1000
|
+
/**
|
|
1001
|
+
* Request frames for this job's owning root without waking sibling roots.
|
|
1002
|
+
*/
|
|
1003
|
+
invalidate: (frames, stackFrames) => {
|
|
1004
|
+
const scheduler3 = getScheduler();
|
|
1005
|
+
const rootId = scheduler3.getJobRootId(id);
|
|
1006
|
+
if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
|
|
1007
|
+
},
|
|
1008
|
+
/**
|
|
1009
|
+
* Pause this job (set enabled=false).
|
|
1010
|
+
* Job remains registered but won't run.
|
|
1011
|
+
*/
|
|
1012
|
+
pause: () => {
|
|
1013
|
+
getScheduler().pauseJob(id);
|
|
1014
|
+
},
|
|
1015
|
+
/**
|
|
1016
|
+
* Resume this job (set enabled=true).
|
|
1017
|
+
*/
|
|
1018
|
+
resume: () => {
|
|
1019
|
+
getScheduler().resumeJob(id);
|
|
1020
|
+
},
|
|
1021
|
+
/**
|
|
1022
|
+
* Reactive paused state - automatically updates when pause/resume is called.
|
|
1023
|
+
* No need for forceUpdate() in your components.
|
|
1024
|
+
*/
|
|
1025
|
+
isPaused
|
|
987
1026
|
};
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
Object.defineProperty(state, "gl", {
|
|
992
|
-
get() {
|
|
993
|
-
const currentState = rootStore.getState();
|
|
994
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
995
|
-
const stack = new Error().stack || "";
|
|
996
|
-
if (!isInternalRendererAccess(stack)) {
|
|
997
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
998
|
-
notifyDepreciated({
|
|
999
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1000
|
-
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
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
return currentState.internal.actualRenderer;
|
|
1005
|
-
},
|
|
1006
|
-
set(value) {
|
|
1007
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1008
|
-
},
|
|
1009
|
-
enumerable: true,
|
|
1010
|
-
configurable: true
|
|
1011
|
-
});
|
|
1012
|
-
Object.defineProperty(state, "renderer", {
|
|
1013
|
-
get() {
|
|
1014
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1015
|
-
},
|
|
1016
|
-
set(value) {
|
|
1017
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1018
|
-
},
|
|
1019
|
-
enumerable: true,
|
|
1020
|
-
configurable: true
|
|
1021
|
-
});
|
|
1022
|
-
let oldScene = state.scene;
|
|
1023
|
-
rootStore.subscribe(() => {
|
|
1024
|
-
const currentState = rootStore.getState();
|
|
1025
|
-
const { scene, rootScene, set } = currentState;
|
|
1026
|
-
if (scene !== oldScene) {
|
|
1027
|
-
oldScene = scene;
|
|
1028
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1029
|
-
set({ rootScene: scene });
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
});
|
|
1033
|
-
let oldSize = state.size;
|
|
1034
|
-
let oldDpr = state.viewport.dpr;
|
|
1035
|
-
let oldCamera = state.camera;
|
|
1036
|
-
rootStore.subscribe(() => {
|
|
1037
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1038
|
-
const actualRenderer = internal.actualRenderer;
|
|
1039
|
-
const canvasTarget = internal.canvasTarget;
|
|
1040
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1041
|
-
oldSize = size;
|
|
1042
|
-
oldDpr = viewport.dpr;
|
|
1043
|
-
updateCamera(camera, size);
|
|
1044
|
-
if (internal.isSecondary && canvasTarget) {
|
|
1045
|
-
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
1046
|
-
canvasTarget.setSize(size.width, size.height, false);
|
|
1047
|
-
} else {
|
|
1048
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1049
|
-
actualRenderer.setSize(size.width, size.height, false);
|
|
1050
|
-
if (canvasTarget) {
|
|
1051
|
-
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
1052
|
-
canvasTarget.setSize(size.width, size.height, false);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
if (camera !== oldCamera) {
|
|
1057
|
-
oldCamera = camera;
|
|
1058
|
-
const { rootScene } = rootStore.getState();
|
|
1059
|
-
if (camera && rootScene && !camera.parent) {
|
|
1060
|
-
rootScene.add(camera);
|
|
1061
|
-
}
|
|
1062
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1063
|
-
const currentState = rootStore.getState();
|
|
1064
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1065
|
-
updateFrustum(camera, currentState.frustum);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
});
|
|
1069
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1070
|
-
return rootStore;
|
|
1071
|
-
};
|
|
1027
|
+
}, [id, isPaused]);
|
|
1028
|
+
return controls;
|
|
1029
|
+
}
|
|
1072
1030
|
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1031
|
+
const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
|
|
1032
|
+
function getUrls(input) {
|
|
1033
|
+
if (typeof input === "string") return [input];
|
|
1034
|
+
if (Array.isArray(input)) return input;
|
|
1035
|
+
return Object.values(input);
|
|
1036
|
+
}
|
|
1037
|
+
function inputSignature(input) {
|
|
1038
|
+
if (typeof input === "string") return input;
|
|
1039
|
+
if (Array.isArray(input)) return input.join("\0");
|
|
1040
|
+
return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
|
|
1041
|
+
}
|
|
1042
|
+
function allUrlsCached(urls, textureCache) {
|
|
1043
|
+
return urls.every((url) => textureCache.has(url));
|
|
1044
|
+
}
|
|
1045
|
+
function buildFromCache(input, textureCache) {
|
|
1046
|
+
if (typeof input === "string") {
|
|
1047
|
+
return textureCache.get(input);
|
|
1048
|
+
} else if (Array.isArray(input)) {
|
|
1049
|
+
return input.map((url) => textureCache.get(url));
|
|
1050
|
+
} else {
|
|
1051
|
+
const result = {};
|
|
1052
|
+
const objInput = input;
|
|
1053
|
+
for (const key in objInput) {
|
|
1054
|
+
result[key] = textureCache.get(objInput[key]);
|
|
1081
1055
|
}
|
|
1082
|
-
return
|
|
1056
|
+
return result;
|
|
1083
1057
|
}
|
|
1084
|
-
return Proto;
|
|
1085
1058
|
}
|
|
1086
|
-
function
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1059
|
+
function useTexture(input, optionsOrOnLoad) {
|
|
1060
|
+
const renderer = useThree((state) => state.internal.actualRenderer);
|
|
1061
|
+
const store = useStore();
|
|
1062
|
+
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
1063
|
+
const { onLoad, cache = true } = options;
|
|
1064
|
+
const onLoadRef = useRef(onLoad);
|
|
1065
|
+
onLoadRef.current = onLoad;
|
|
1066
|
+
const onLoadCalledForRef = useRef(null);
|
|
1067
|
+
const inputKey = useMemo(() => inputSignature(input), [input]);
|
|
1068
|
+
const stableInput = useMemo(() => input, [inputKey]);
|
|
1069
|
+
const urls = useMemo(() => getUrls(stableInput), [stableInput]);
|
|
1070
|
+
const cachedResult = useMemo(() => {
|
|
1071
|
+
if (!cache) return null;
|
|
1072
|
+
const textures = store.getState().textures;
|
|
1073
|
+
if (!allUrlsCached(urls, textures)) return null;
|
|
1074
|
+
return buildFromCache(stableInput, textures);
|
|
1075
|
+
}, [cache, urls, stableInput, store]);
|
|
1076
|
+
const loadedTextures = useLoader(
|
|
1077
|
+
TextureLoader,
|
|
1078
|
+
IsObject(stableInput) ? Object.values(stableInput) : stableInput
|
|
1079
|
+
);
|
|
1080
|
+
const mappedTextures = useMemo(() => {
|
|
1081
|
+
if (cachedResult) return cachedResult;
|
|
1082
|
+
if (IsObject(stableInput)) {
|
|
1083
|
+
const keyed = {};
|
|
1084
|
+
const textureArray = loadedTextures;
|
|
1085
|
+
let i = 0;
|
|
1086
|
+
for (const key in stableInput) keyed[key] = textureArray[i++];
|
|
1087
|
+
return keyed;
|
|
1088
|
+
} else {
|
|
1089
|
+
return loadedTextures;
|
|
1090
|
+
}
|
|
1091
|
+
}, [stableInput, loadedTextures, cachedResult]);
|
|
1092
|
+
useLayoutEffect(() => {
|
|
1093
|
+
if (cachedResult) return;
|
|
1094
|
+
if (onLoadCalledForRef.current === inputKey) return;
|
|
1095
|
+
onLoadCalledForRef.current = inputKey;
|
|
1096
|
+
onLoadRef.current?.(mappedTextures);
|
|
1097
|
+
}, [cachedResult, mappedTextures, inputKey]);
|
|
1098
|
+
useEffect(() => {
|
|
1099
|
+
if (cachedResult) return;
|
|
1100
|
+
if ("initTexture" in renderer) {
|
|
1101
|
+
let textureArray = [];
|
|
1102
|
+
if (Array.isArray(loadedTextures)) {
|
|
1103
|
+
textureArray = loadedTextures;
|
|
1104
|
+
} else if (loadedTextures instanceof Texture$1) {
|
|
1105
|
+
textureArray = [loadedTextures];
|
|
1106
|
+
} else if (IsObject(loadedTextures)) {
|
|
1107
|
+
textureArray = Object.values(loadedTextures);
|
|
1108
|
+
}
|
|
1109
|
+
textureArray.forEach((texture) => {
|
|
1110
|
+
if (texture instanceof Texture$1) {
|
|
1111
|
+
renderer.initTexture(texture);
|
|
1112
|
+
}
|
|
1094
1113
|
});
|
|
1095
1114
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1115
|
+
}, [renderer, loadedTextures, cachedResult]);
|
|
1116
|
+
useEffect(() => {
|
|
1117
|
+
if (!cache) return;
|
|
1118
|
+
const urlTextureMap = [];
|
|
1119
|
+
if (typeof stableInput === "string") {
|
|
1120
|
+
urlTextureMap.push([stableInput, mappedTextures]);
|
|
1121
|
+
} else if (Array.isArray(stableInput)) {
|
|
1122
|
+
const textureArray = mappedTextures;
|
|
1123
|
+
stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
|
|
1124
|
+
} else if (IsObject(stableInput)) {
|
|
1125
|
+
const textureRecord = mappedTextures;
|
|
1126
|
+
for (const key in stableInput) {
|
|
1127
|
+
const url = stableInput[key];
|
|
1128
|
+
urlTextureMap.push([url, textureRecord[key]]);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
store.setState((state) => {
|
|
1132
|
+
const refs = new Map(state._textureRefs);
|
|
1133
|
+
let textures = state.textures;
|
|
1134
|
+
let added = false;
|
|
1135
|
+
for (const [url, texture] of urlTextureMap) {
|
|
1136
|
+
if (!textures.has(url)) {
|
|
1137
|
+
if (!added) {
|
|
1138
|
+
textures = new Map(textures);
|
|
1139
|
+
added = true;
|
|
1140
|
+
}
|
|
1141
|
+
textures.set(url, texture);
|
|
1142
|
+
}
|
|
1143
|
+
refs.set(url, (refs.get(url) ?? 0) + 1);
|
|
1144
|
+
}
|
|
1145
|
+
return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
|
|
1146
|
+
});
|
|
1147
|
+
return () => store.setState((state) => {
|
|
1148
|
+
const refs = new Map(state._textureRefs);
|
|
1149
|
+
for (const [url] of urlTextureMap) {
|
|
1150
|
+
const next = (refs.get(url) ?? 0) - 1;
|
|
1151
|
+
if (next <= 0) refs.delete(url);
|
|
1152
|
+
else refs.set(url, next);
|
|
1153
|
+
}
|
|
1154
|
+
return { _textureRefs: refs };
|
|
1155
|
+
});
|
|
1156
|
+
}, [cache, stableInput, mappedTextures, store]);
|
|
1157
|
+
return mappedTextures;
|
|
1117
1158
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
};
|
|
1129
|
-
useLoader.loader = getLoader$1;
|
|
1130
|
-
|
|
1131
|
-
function useFrame(callback, priorityOrOptions) {
|
|
1132
|
-
const store = React.useContext(context);
|
|
1133
|
-
const isInsideCanvas = store !== null;
|
|
1134
|
-
const scheduler = getScheduler();
|
|
1135
|
-
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
1136
|
-
id: priorityOrOptions.id,
|
|
1137
|
-
phase: priorityOrOptions.phase,
|
|
1138
|
-
priority: priorityOrOptions.priority,
|
|
1139
|
-
fps: priorityOrOptions.fps,
|
|
1140
|
-
drop: priorityOrOptions.drop,
|
|
1141
|
-
enabled: priorityOrOptions.enabled,
|
|
1142
|
-
before: priorityOrOptions.before,
|
|
1143
|
-
after: priorityOrOptions.after
|
|
1144
|
-
}) : "";
|
|
1145
|
-
const options = React.useMemo(() => {
|
|
1146
|
-
return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
|
|
1147
|
-
}, [optionsKey]);
|
|
1148
|
-
const reactId = React.useId();
|
|
1149
|
-
const id = options.id ?? reactId;
|
|
1150
|
-
const callbackRef = useMutableCallback(callback);
|
|
1151
|
-
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
1152
|
-
useIsomorphicLayoutEffect(() => {
|
|
1153
|
-
if (!callback) return;
|
|
1154
|
-
if (isInsideCanvas) {
|
|
1155
|
-
const state = store.getState();
|
|
1156
|
-
const rootId = state.internal.rootId;
|
|
1157
|
-
if (isLegacyPriority) {
|
|
1158
|
-
state.internal.priority++;
|
|
1159
|
-
let parentRoot = state.previousRoot;
|
|
1160
|
-
while (parentRoot) {
|
|
1161
|
-
const parentState = parentRoot.getState();
|
|
1162
|
-
if (parentState?.internal) parentState.internal.priority++;
|
|
1163
|
-
parentRoot = parentState?.previousRoot;
|
|
1164
|
-
}
|
|
1165
|
-
notifyDepreciated({
|
|
1166
|
-
heading: "useFrame with numeric priority is deprecated",
|
|
1167
|
-
body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
|
|
1168
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
const wrappedCallback = (frameState, delta) => {
|
|
1172
|
-
const localState = store.getState();
|
|
1173
|
-
const mergedState = {
|
|
1174
|
-
...localState,
|
|
1175
|
-
time: frameState.time,
|
|
1176
|
-
delta: frameState.delta,
|
|
1177
|
-
elapsed: frameState.elapsed,
|
|
1178
|
-
frame: frameState.frame
|
|
1179
|
-
};
|
|
1180
|
-
callbackRef.current?.(mergedState, delta);
|
|
1181
|
-
};
|
|
1182
|
-
const unregister = scheduler.register(wrappedCallback, {
|
|
1183
|
-
id,
|
|
1184
|
-
rootId,
|
|
1185
|
-
...options
|
|
1186
|
-
});
|
|
1187
|
-
return () => {
|
|
1188
|
-
unregister();
|
|
1189
|
-
if (isLegacyPriority) {
|
|
1190
|
-
const currentState = store.getState();
|
|
1191
|
-
if (currentState.internal) {
|
|
1192
|
-
currentState.internal.priority--;
|
|
1193
|
-
let parentRoot = currentState.previousRoot;
|
|
1194
|
-
while (parentRoot) {
|
|
1195
|
-
const parentState = parentRoot.getState();
|
|
1196
|
-
if (parentState?.internal) parentState.internal.priority--;
|
|
1197
|
-
parentRoot = parentState?.previousRoot;
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
};
|
|
1202
|
-
} else {
|
|
1203
|
-
return scheduler.register(
|
|
1204
|
-
(state, delta) => {
|
|
1205
|
-
const frameState = state;
|
|
1206
|
-
if (!frameState.renderer) return;
|
|
1207
|
-
callbackRef.current?.(frameState, delta);
|
|
1208
|
-
},
|
|
1209
|
-
{ id, ...options }
|
|
1210
|
-
);
|
|
1211
|
-
}
|
|
1212
|
-
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
1213
|
-
const isPaused = React.useSyncExternalStore(
|
|
1214
|
-
// Subscribe function
|
|
1215
|
-
React.useCallback(
|
|
1216
|
-
(onStoreChange) => {
|
|
1217
|
-
return getScheduler().subscribeJobState(id, onStoreChange);
|
|
1218
|
-
},
|
|
1219
|
-
[id]
|
|
1220
|
-
),
|
|
1221
|
-
// getSnapshot function
|
|
1222
|
-
React.useCallback(() => getScheduler().isJobPaused(id), [id]),
|
|
1223
|
-
// getServerSnapshot function (SSR)
|
|
1224
|
-
React.useCallback(() => false, [])
|
|
1225
|
-
);
|
|
1226
|
-
const controls = React.useMemo(() => {
|
|
1227
|
-
const scheduler2 = getScheduler();
|
|
1228
|
-
return {
|
|
1229
|
-
/** The job's unique ID */
|
|
1230
|
-
id,
|
|
1231
|
-
/**
|
|
1232
|
-
* Access to the global scheduler for frame loop control.
|
|
1233
|
-
* Use for controlling the entire frame loop, adding phases, etc.
|
|
1234
|
-
*/
|
|
1235
|
-
scheduler: scheduler2,
|
|
1236
|
-
/**
|
|
1237
|
-
* Manually step this job only.
|
|
1238
|
-
* Bypasses FPS limiting - always runs.
|
|
1239
|
-
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
1240
|
-
*/
|
|
1241
|
-
step: (timestamp) => {
|
|
1242
|
-
getScheduler().stepJob(id, timestamp);
|
|
1243
|
-
},
|
|
1244
|
-
/**
|
|
1245
|
-
* Manually step ALL jobs in the scheduler.
|
|
1246
|
-
* Useful for frameloop='never' mode.
|
|
1247
|
-
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
1248
|
-
*/
|
|
1249
|
-
stepAll: (timestamp) => {
|
|
1250
|
-
getScheduler().step(timestamp);
|
|
1251
|
-
},
|
|
1252
|
-
/**
|
|
1253
|
-
* Pause this job (set enabled=false).
|
|
1254
|
-
* Job remains registered but won't run.
|
|
1255
|
-
*/
|
|
1256
|
-
pause: () => {
|
|
1257
|
-
getScheduler().pauseJob(id);
|
|
1258
|
-
},
|
|
1259
|
-
/**
|
|
1260
|
-
* Resume this job (set enabled=true).
|
|
1261
|
-
*/
|
|
1262
|
-
resume: () => {
|
|
1263
|
-
getScheduler().resumeJob(id);
|
|
1264
|
-
},
|
|
1265
|
-
/**
|
|
1266
|
-
* Reactive paused state - automatically updates when pause/resume is called.
|
|
1267
|
-
* No need for forceUpdate() in your components.
|
|
1268
|
-
*/
|
|
1269
|
-
isPaused
|
|
1270
|
-
};
|
|
1271
|
-
}, [id, isPaused]);
|
|
1272
|
-
return controls;
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
|
|
1276
|
-
function getUrls(input) {
|
|
1277
|
-
if (typeof input === "string") return [input];
|
|
1278
|
-
if (Array.isArray(input)) return input;
|
|
1279
|
-
return Object.values(input);
|
|
1280
|
-
}
|
|
1281
|
-
function allUrlsCached(urls, textureCache) {
|
|
1282
|
-
return urls.every((url) => textureCache.has(url));
|
|
1283
|
-
}
|
|
1284
|
-
function buildFromCache(input, textureCache) {
|
|
1285
|
-
if (typeof input === "string") {
|
|
1286
|
-
return textureCache.get(input);
|
|
1287
|
-
} else if (Array.isArray(input)) {
|
|
1288
|
-
return input.map((url) => textureCache.get(url));
|
|
1289
|
-
} else {
|
|
1290
|
-
const result = {};
|
|
1291
|
-
const objInput = input;
|
|
1292
|
-
for (const key in objInput) {
|
|
1293
|
-
result[key] = textureCache.get(objInput[key]);
|
|
1294
|
-
}
|
|
1295
|
-
return result;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
function useTexture(input, optionsOrOnLoad) {
|
|
1299
|
-
const renderer = useThree((state) => state.internal.actualRenderer);
|
|
1300
|
-
const store = useStore();
|
|
1301
|
-
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
1302
|
-
const { onLoad, cache = true } = options;
|
|
1303
|
-
const onLoadRef = useRef(onLoad);
|
|
1304
|
-
onLoadRef.current = onLoad;
|
|
1305
|
-
const onLoadCalledForRef = useRef(null);
|
|
1306
|
-
const urls = useMemo(() => getUrls(input), [input]);
|
|
1307
|
-
const cachedResult = useMemo(() => {
|
|
1308
|
-
if (!cache) return null;
|
|
1309
|
-
const textures = store.getState().textures;
|
|
1310
|
-
if (!allUrlsCached(urls, textures)) return null;
|
|
1311
|
-
return buildFromCache(input, textures);
|
|
1312
|
-
}, [cache, urls, input, store]);
|
|
1313
|
-
const loadedTextures = useLoader(
|
|
1314
|
-
TextureLoader,
|
|
1315
|
-
IsObject(input) ? Object.values(input) : input
|
|
1316
|
-
);
|
|
1317
|
-
const inputKey = urls.join("\0");
|
|
1318
|
-
useLayoutEffect(() => {
|
|
1319
|
-
if (cachedResult) return;
|
|
1320
|
-
if (onLoadCalledForRef.current === inputKey) return;
|
|
1321
|
-
onLoadCalledForRef.current = inputKey;
|
|
1322
|
-
onLoadRef.current?.(loadedTextures);
|
|
1323
|
-
}, [cachedResult, loadedTextures, inputKey]);
|
|
1324
|
-
useEffect(() => {
|
|
1325
|
-
if (cachedResult) return;
|
|
1326
|
-
if ("initTexture" in renderer) {
|
|
1327
|
-
let textureArray = [];
|
|
1328
|
-
if (Array.isArray(loadedTextures)) {
|
|
1329
|
-
textureArray = loadedTextures;
|
|
1330
|
-
} else if (loadedTextures instanceof Texture$1) {
|
|
1331
|
-
textureArray = [loadedTextures];
|
|
1332
|
-
} else if (IsObject(loadedTextures)) {
|
|
1333
|
-
textureArray = Object.values(loadedTextures);
|
|
1334
|
-
}
|
|
1335
|
-
textureArray.forEach((texture) => {
|
|
1336
|
-
if (texture instanceof Texture$1) {
|
|
1337
|
-
renderer.initTexture(texture);
|
|
1338
|
-
}
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
}, [renderer, loadedTextures, cachedResult]);
|
|
1342
|
-
const mappedTextures = useMemo(() => {
|
|
1343
|
-
if (cachedResult) return cachedResult;
|
|
1344
|
-
if (IsObject(input)) {
|
|
1345
|
-
const keyed = {};
|
|
1346
|
-
const textureArray = loadedTextures;
|
|
1347
|
-
let i = 0;
|
|
1348
|
-
for (const key in input) keyed[key] = textureArray[i++];
|
|
1349
|
-
return keyed;
|
|
1350
|
-
} else {
|
|
1351
|
-
return loadedTextures;
|
|
1352
|
-
}
|
|
1353
|
-
}, [input, loadedTextures, cachedResult]);
|
|
1354
|
-
useEffect(() => {
|
|
1355
|
-
if (!cache) return;
|
|
1356
|
-
const urlTextureMap = [];
|
|
1357
|
-
if (typeof input === "string") {
|
|
1358
|
-
urlTextureMap.push([input, mappedTextures]);
|
|
1359
|
-
} else if (Array.isArray(input)) {
|
|
1360
|
-
const textureArray = mappedTextures;
|
|
1361
|
-
input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
|
|
1362
|
-
} else if (IsObject(input)) {
|
|
1363
|
-
const textureRecord = mappedTextures;
|
|
1364
|
-
for (const key in input) {
|
|
1365
|
-
const url = input[key];
|
|
1366
|
-
urlTextureMap.push([url, textureRecord[key]]);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
store.setState((state) => {
|
|
1370
|
-
const refs = new Map(state._textureRefs);
|
|
1371
|
-
let textures = state.textures;
|
|
1372
|
-
let added = false;
|
|
1373
|
-
for (const [url, texture] of urlTextureMap) {
|
|
1374
|
-
if (!textures.has(url)) {
|
|
1375
|
-
if (!added) {
|
|
1376
|
-
textures = new Map(textures);
|
|
1377
|
-
added = true;
|
|
1378
|
-
}
|
|
1379
|
-
textures.set(url, texture);
|
|
1380
|
-
}
|
|
1381
|
-
refs.set(url, (refs.get(url) ?? 0) + 1);
|
|
1382
|
-
}
|
|
1383
|
-
return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
|
|
1384
|
-
});
|
|
1385
|
-
return () => store.setState((state) => {
|
|
1386
|
-
const refs = new Map(state._textureRefs);
|
|
1387
|
-
for (const [url] of urlTextureMap) {
|
|
1388
|
-
const next = (refs.get(url) ?? 0) - 1;
|
|
1389
|
-
if (next <= 0) refs.delete(url);
|
|
1390
|
-
else refs.set(url, next);
|
|
1391
|
-
}
|
|
1392
|
-
return { _textureRefs: refs };
|
|
1393
|
-
});
|
|
1394
|
-
}, [cache, input, mappedTextures, store]);
|
|
1395
|
-
return mappedTextures;
|
|
1396
|
-
}
|
|
1397
|
-
useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
|
|
1398
|
-
useTexture.clear = (input) => useLoader.clear(TextureLoader, input);
|
|
1399
|
-
const Texture = ({
|
|
1400
|
-
children,
|
|
1401
|
-
input,
|
|
1402
|
-
onLoad,
|
|
1403
|
-
cache
|
|
1404
|
-
}) => {
|
|
1405
|
-
const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
|
|
1406
|
-
const ret = useTexture(input, options);
|
|
1407
|
-
return /* @__PURE__ */ jsx(Fragment, { children: children?.(ret) });
|
|
1159
|
+
useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
|
|
1160
|
+
useTexture.clear = (input) => useLoader.clear(TextureLoader, input);
|
|
1161
|
+
const Texture = ({
|
|
1162
|
+
children,
|
|
1163
|
+
input,
|
|
1164
|
+
onLoad,
|
|
1165
|
+
cache
|
|
1166
|
+
}) => {
|
|
1167
|
+
const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
|
|
1168
|
+
const ret = useTexture(input, options);
|
|
1169
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children?.(ret) });
|
|
1408
1170
|
};
|
|
1409
1171
|
|
|
1410
1172
|
function useTextures(selector) {
|
|
@@ -1554,7 +1316,7 @@ function useEnvironment({
|
|
|
1554
1316
|
texture = texture.renderTarget?.texture;
|
|
1555
1317
|
}
|
|
1556
1318
|
texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
|
|
1557
|
-
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
1319
|
+
texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
|
|
1558
1320
|
return texture;
|
|
1559
1321
|
}
|
|
1560
1322
|
const preloadDefaultOptions = {
|
|
@@ -1607,22 +1369,15 @@ function getExtension(files) {
|
|
|
1607
1369
|
const isCubemap = isArray(files) && files.length === 6;
|
|
1608
1370
|
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
1609
1371
|
const firstEntry = isArray(files) ? files[0] : files;
|
|
1610
|
-
const
|
|
1372
|
+
const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
|
|
1373
|
+
const extension = isCubemap ? firstExtension === "hdr" ? "hdr-cube" : "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstExtension;
|
|
1611
1374
|
return { extension, isCubemap, isGainmap };
|
|
1612
1375
|
}
|
|
1613
1376
|
function getLoader(extension) {
|
|
1614
|
-
const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
|
|
1377
|
+
const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr-cube" ? HDRCubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
|
|
1615
1378
|
return loader;
|
|
1616
1379
|
}
|
|
1617
1380
|
|
|
1618
|
-
function useStore() {
|
|
1619
|
-
const store = useContext(context);
|
|
1620
|
-
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
1621
|
-
return store;
|
|
1622
|
-
}
|
|
1623
|
-
function useThree(selector = (state) => state, equalityFn) {
|
|
1624
|
-
return useStore()(selector, equalityFn);
|
|
1625
|
-
}
|
|
1626
1381
|
function useInstanceHandle(ref) {
|
|
1627
1382
|
const instance = useRef(null);
|
|
1628
1383
|
useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
@@ -1662,13 +1417,19 @@ function addTail(callback) {
|
|
|
1662
1417
|
return getScheduler().onIdle(callback);
|
|
1663
1418
|
}
|
|
1664
1419
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
1665
|
-
|
|
1420
|
+
const rootId = state?.internal?.rootId;
|
|
1421
|
+
const scheduler = getScheduler();
|
|
1422
|
+
if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
|
|
1423
|
+
else scheduler.invalidate(frames, stackFrames);
|
|
1666
1424
|
}
|
|
1667
|
-
function advance(timestamp) {
|
|
1668
|
-
|
|
1425
|
+
function advance(timestamp, runGlobalEffects, state) {
|
|
1426
|
+
const rootId = state?.internal?.rootId;
|
|
1427
|
+
const scheduler = getScheduler();
|
|
1428
|
+
if (rootId) scheduler.stepRoot(rootId, timestamp);
|
|
1429
|
+
else scheduler.step(timestamp);
|
|
1669
1430
|
}
|
|
1670
1431
|
|
|
1671
|
-
const version = "10.0.0-alpha.
|
|
1432
|
+
const version = "10.0.0-alpha.5";
|
|
1672
1433
|
const packageData = {
|
|
1673
1434
|
version: version};
|
|
1674
1435
|
|
|
@@ -13527,7 +13288,10 @@ function extend(objects) {
|
|
|
13527
13288
|
catalogue[Component] = objects;
|
|
13528
13289
|
return Component;
|
|
13529
13290
|
} else {
|
|
13530
|
-
|
|
13291
|
+
for (const name in objects) {
|
|
13292
|
+
const object = objects[name];
|
|
13293
|
+
if (isConstructor(object)) catalogue[name] = object;
|
|
13294
|
+
}
|
|
13531
13295
|
}
|
|
13532
13296
|
}
|
|
13533
13297
|
function validateInstance(type, props) {
|
|
@@ -13875,7 +13639,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
13875
13639
|
getRootHostContext: () => NO_CONTEXT,
|
|
13876
13640
|
getChildHostContext: () => NO_CONTEXT,
|
|
13877
13641
|
commitUpdate(instance, type, oldProps, newProps, fiber) {
|
|
13878
|
-
validateInstance(type, newProps);
|
|
13642
|
+
validateInstance(instance.type, newProps);
|
|
13879
13643
|
let reconstruct = false;
|
|
13880
13644
|
if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
|
|
13881
13645
|
else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
|
|
@@ -13890,162 +13654,491 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
13890
13654
|
else delete instance.props.attach;
|
|
13891
13655
|
if (Object.keys(changedProps).length) applyProps(instance.object, changedProps);
|
|
13892
13656
|
}
|
|
13893
|
-
},
|
|
13894
|
-
finalizeInitialChildren: (instance) => {
|
|
13895
|
-
for (const prop in instance.props) {
|
|
13896
|
-
if (isFromRef(instance.props[prop])) return true;
|
|
13657
|
+
},
|
|
13658
|
+
finalizeInitialChildren: (instance) => {
|
|
13659
|
+
for (const prop in instance.props) {
|
|
13660
|
+
if (isFromRef(instance.props[prop])) return true;
|
|
13661
|
+
}
|
|
13662
|
+
return false;
|
|
13663
|
+
},
|
|
13664
|
+
commitMount(instance) {
|
|
13665
|
+
const resolved = {};
|
|
13666
|
+
for (const prop in instance.props) {
|
|
13667
|
+
const value = instance.props[prop];
|
|
13668
|
+
if (isFromRef(value)) {
|
|
13669
|
+
const { [FROM_REF]: ref, transform } = value;
|
|
13670
|
+
if (ref.current != null) resolved[prop] = transform ? transform(ref.current) : ref.current;
|
|
13671
|
+
}
|
|
13672
|
+
}
|
|
13673
|
+
if (Object.keys(resolved).length) applyProps(instance.object, resolved);
|
|
13674
|
+
},
|
|
13675
|
+
getPublicInstance: (instance) => instance?.object,
|
|
13676
|
+
prepareForCommit: () => null,
|
|
13677
|
+
preparePortalMount: (container) => {
|
|
13678
|
+
const target = container.getState().internal.container ?? container.getState().scene;
|
|
13679
|
+
return prepare(target, container, "", {});
|
|
13680
|
+
},
|
|
13681
|
+
// Reconstructed instances are swapped once all mutations are committed,
|
|
13682
|
+
// before layout effects run so refs point to the new objects
|
|
13683
|
+
resetAfterCommit: flushReconstructedInstances,
|
|
13684
|
+
shouldSetTextContent: () => false,
|
|
13685
|
+
clearContainer: () => false,
|
|
13686
|
+
hideInstance,
|
|
13687
|
+
unhideInstance,
|
|
13688
|
+
createTextInstance: handleTextInstance,
|
|
13689
|
+
hideTextInstance: handleTextInstance,
|
|
13690
|
+
unhideTextInstance: handleTextInstance,
|
|
13691
|
+
// Mirrors react-dom, which flushes discrete work in microtasks
|
|
13692
|
+
supportsMicrotasks: true,
|
|
13693
|
+
scheduleMicrotask,
|
|
13694
|
+
scheduleTimeout: typeof setTimeout === "function" ? setTimeout : void 0,
|
|
13695
|
+
cancelTimeout: typeof clearTimeout === "function" ? clearTimeout : void 0,
|
|
13696
|
+
noTimeout: -1,
|
|
13697
|
+
getInstanceFromNode: () => null,
|
|
13698
|
+
beforeActiveInstanceBlur() {
|
|
13699
|
+
},
|
|
13700
|
+
afterActiveInstanceBlur() {
|
|
13701
|
+
},
|
|
13702
|
+
detachDeletedInstance() {
|
|
13703
|
+
},
|
|
13704
|
+
prepareScopeUpdate() {
|
|
13705
|
+
},
|
|
13706
|
+
getInstanceFromScope: () => null,
|
|
13707
|
+
shouldAttemptEagerTransition: () => false,
|
|
13708
|
+
trackSchedulerEvent: () => {
|
|
13709
|
+
},
|
|
13710
|
+
resolveEventType: () => null,
|
|
13711
|
+
resolveEventTimeStamp: () => -1.1,
|
|
13712
|
+
requestPostPaintCallback() {
|
|
13713
|
+
},
|
|
13714
|
+
maySuspendCommit: () => false,
|
|
13715
|
+
preloadInstance: () => true,
|
|
13716
|
+
// true indicates already loaded
|
|
13717
|
+
suspendInstance() {
|
|
13718
|
+
},
|
|
13719
|
+
waitForCommitToBeReady: () => null,
|
|
13720
|
+
NotPendingTransition: null,
|
|
13721
|
+
// The reconciler types use the internal ReactContext with all the hidden properties
|
|
13722
|
+
// so we have to cast from the public React.Context type
|
|
13723
|
+
HostTransitionContext: /* @__PURE__ */ React.createContext(
|
|
13724
|
+
null
|
|
13725
|
+
),
|
|
13726
|
+
setCurrentUpdatePriority(newPriority) {
|
|
13727
|
+
currentUpdatePriority = newPriority;
|
|
13728
|
+
},
|
|
13729
|
+
getCurrentUpdatePriority() {
|
|
13730
|
+
return currentUpdatePriority;
|
|
13731
|
+
},
|
|
13732
|
+
resolveUpdatePriority() {
|
|
13733
|
+
if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
|
|
13734
|
+
const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
|
|
13735
|
+
if (eventType === void 0) return r;
|
|
13736
|
+
return getEventPriority(eventType);
|
|
13737
|
+
},
|
|
13738
|
+
resetFormInstance() {
|
|
13739
|
+
},
|
|
13740
|
+
// @ts-ignore DefinitelyTyped is not up to date
|
|
13741
|
+
rendererPackageName: "@react-three/fiber",
|
|
13742
|
+
rendererVersion: packageData.version,
|
|
13743
|
+
// 19.2 Reconciler
|
|
13744
|
+
// https://github.com/facebook/react/pull/31975
|
|
13745
|
+
// https://github.com/facebook/react/pull/31999
|
|
13746
|
+
applyViewTransitionName(_instance, _name, _className) {
|
|
13747
|
+
},
|
|
13748
|
+
restoreViewTransitionName(_instance, _props) {
|
|
13749
|
+
},
|
|
13750
|
+
cancelViewTransitionName(_instance, _name, _props) {
|
|
13751
|
+
},
|
|
13752
|
+
cancelRootViewTransitionName(_rootContainer) {
|
|
13753
|
+
},
|
|
13754
|
+
restoreRootViewTransitionName(_rootContainer) {
|
|
13755
|
+
},
|
|
13756
|
+
InstanceMeasurement: null,
|
|
13757
|
+
measureInstance: (_instance) => null,
|
|
13758
|
+
wasInstanceInViewport: (_measurement) => true,
|
|
13759
|
+
hasInstanceChanged: (_oldMeasurement, _newMeasurement) => false,
|
|
13760
|
+
hasInstanceAffectedParent: (_oldMeasurement, _newMeasurement) => false,
|
|
13761
|
+
// https://github.com/facebook/react/pull/32002
|
|
13762
|
+
// https://github.com/facebook/react/pull/34486
|
|
13763
|
+
suspendOnActiveViewTransition(_state, _container) {
|
|
13764
|
+
},
|
|
13765
|
+
// https://github.com/facebook/react/pull/32451
|
|
13766
|
+
// https://github.com/facebook/react/pull/32760
|
|
13767
|
+
startGestureTransition: () => null,
|
|
13768
|
+
startViewTransition: () => null,
|
|
13769
|
+
stopViewTransition(_transition) {
|
|
13770
|
+
},
|
|
13771
|
+
// https://github.com/facebook/react/pull/32038
|
|
13772
|
+
createViewTransitionInstance: (_name) => null,
|
|
13773
|
+
// https://github.com/facebook/react/pull/32379
|
|
13774
|
+
// https://github.com/facebook/react/pull/32786
|
|
13775
|
+
getCurrentGestureOffset(_provider) {
|
|
13776
|
+
throw new Error("startGestureTransition is not yet supported in react-three-fiber.");
|
|
13777
|
+
},
|
|
13778
|
+
// https://github.com/facebook/react/pull/32500
|
|
13779
|
+
cloneMutableInstance(instance, _keepChildren) {
|
|
13780
|
+
return instance;
|
|
13781
|
+
},
|
|
13782
|
+
cloneMutableTextInstance(textInstance) {
|
|
13783
|
+
return textInstance;
|
|
13784
|
+
},
|
|
13785
|
+
// React 19.2 added these for root-level view transitions. They only run for hosts that opt into
|
|
13786
|
+
// view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
|
|
13787
|
+
// into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
|
|
13788
|
+
// React never calls them. They stay as no-ops rather than throws: the host config is shared by
|
|
13789
|
+
// every root, and a throw here would turn a React-internal probe into a hard crash for users.
|
|
13790
|
+
// Guarded by a test asserting a full render + unmount never reaches them.
|
|
13791
|
+
cloneRootViewTransitionContainer(_rootContainer) {
|
|
13792
|
+
},
|
|
13793
|
+
removeRootViewTransitionClone(_rootContainer, _clone) {
|
|
13794
|
+
},
|
|
13795
|
+
// https://github.com/facebook/react/pull/32465
|
|
13796
|
+
createFragmentInstance: (_fiber) => null,
|
|
13797
|
+
updateFragmentInstanceFiber(_fiber, _instance) {
|
|
13798
|
+
},
|
|
13799
|
+
commitNewChildToFragmentInstance(_child, _fragmentInstance) {
|
|
13800
|
+
},
|
|
13801
|
+
deleteChildFromFragmentInstance(_child, _fragmentInstance) {
|
|
13802
|
+
},
|
|
13803
|
+
// https://github.com/facebook/react/pull/32653
|
|
13804
|
+
measureClonedInstance: (_instance) => null,
|
|
13805
|
+
// https://github.com/facebook/react/pull/32819
|
|
13806
|
+
maySuspendCommitOnUpdate: (_type, _oldProps, _newProps) => false,
|
|
13807
|
+
maySuspendCommitInSyncRender: (_type, _props) => false,
|
|
13808
|
+
// https://github.com/facebook/react/pull/34486
|
|
13809
|
+
startSuspendingCommit: () => null,
|
|
13810
|
+
// https://github.com/facebook/react/pull/34522
|
|
13811
|
+
getSuspendedCommitReason: (_state, _rootContainer) => null
|
|
13812
|
+
});
|
|
13813
|
+
|
|
13814
|
+
const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
|
|
13815
|
+
function isInternalRendererAccess(stack, callerDepth = 2) {
|
|
13816
|
+
if (!stack) return false;
|
|
13817
|
+
const caller = stack.split("\n")[callerDepth] ?? "";
|
|
13818
|
+
return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
|
|
13819
|
+
}
|
|
13820
|
+
|
|
13821
|
+
const createStore = (invalidate, advance) => {
|
|
13822
|
+
const rootStore = createWithEqualityFn((set, get) => {
|
|
13823
|
+
const position = new Vector3();
|
|
13824
|
+
const defaultTarget = new Vector3();
|
|
13825
|
+
const tempTarget = new Vector3();
|
|
13826
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
13827
|
+
const { width, height, top, left } = size;
|
|
13828
|
+
const aspect = width / height;
|
|
13829
|
+
if (target.isVector3) tempTarget.copy(target);
|
|
13830
|
+
else tempTarget.set(...target);
|
|
13831
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
13832
|
+
if (isOrthographicCamera(camera)) {
|
|
13833
|
+
return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
|
|
13834
|
+
} else {
|
|
13835
|
+
const fov = camera.fov * Math.PI / 180;
|
|
13836
|
+
const h = 2 * Math.tan(fov / 2) * distance;
|
|
13837
|
+
const w = h * (width / height);
|
|
13838
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
13839
|
+
}
|
|
13840
|
+
}
|
|
13841
|
+
let performanceTimeout = void 0;
|
|
13842
|
+
const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
|
|
13843
|
+
const pointer = new Vector2();
|
|
13844
|
+
const rootState = {
|
|
13845
|
+
set,
|
|
13846
|
+
get,
|
|
13847
|
+
// Mock objects that have to be configured
|
|
13848
|
+
// primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
|
|
13849
|
+
primaryStore: null,
|
|
13850
|
+
gl: null,
|
|
13851
|
+
renderer: null,
|
|
13852
|
+
camera: null,
|
|
13853
|
+
frustum: new Frustum(),
|
|
13854
|
+
autoUpdateFrustum: true,
|
|
13855
|
+
raycaster: null,
|
|
13856
|
+
events: {
|
|
13857
|
+
priority: 1,
|
|
13858
|
+
enabled: true,
|
|
13859
|
+
connected: false,
|
|
13860
|
+
frameTimedRaycasts: true,
|
|
13861
|
+
alwaysFireOnScroll: true,
|
|
13862
|
+
updateOnFrame: false
|
|
13863
|
+
},
|
|
13864
|
+
scene: null,
|
|
13865
|
+
rootScene: null,
|
|
13866
|
+
xr: null,
|
|
13867
|
+
inspector: null,
|
|
13868
|
+
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
13869
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
13870
|
+
textureColorSpace: SRGBColorSpace,
|
|
13871
|
+
isLegacy: false,
|
|
13872
|
+
webGPUSupported: false,
|
|
13873
|
+
isNative: false,
|
|
13874
|
+
controls: null,
|
|
13875
|
+
pointer,
|
|
13876
|
+
mouse: pointer,
|
|
13877
|
+
frameloop: "always",
|
|
13878
|
+
onPointerMissed: void 0,
|
|
13879
|
+
onDragOverMissed: void 0,
|
|
13880
|
+
onDropMissed: void 0,
|
|
13881
|
+
performance: {
|
|
13882
|
+
current: 1,
|
|
13883
|
+
min: 0.5,
|
|
13884
|
+
max: 1,
|
|
13885
|
+
debounce: 200,
|
|
13886
|
+
regress: () => {
|
|
13887
|
+
const state2 = get();
|
|
13888
|
+
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
13889
|
+
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
13890
|
+
performanceTimeout = setTimeout(
|
|
13891
|
+
() => setPerformanceCurrent(get().performance.max),
|
|
13892
|
+
state2.performance.debounce
|
|
13893
|
+
);
|
|
13894
|
+
}
|
|
13895
|
+
},
|
|
13896
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
13897
|
+
viewport: {
|
|
13898
|
+
initialDpr: 0,
|
|
13899
|
+
dpr: 0,
|
|
13900
|
+
width: 0,
|
|
13901
|
+
height: 0,
|
|
13902
|
+
top: 0,
|
|
13903
|
+
left: 0,
|
|
13904
|
+
aspect: 0,
|
|
13905
|
+
distance: 0,
|
|
13906
|
+
factor: 0,
|
|
13907
|
+
getCurrentViewport
|
|
13908
|
+
},
|
|
13909
|
+
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
13910
|
+
setSize: (width, height, top, left) => {
|
|
13911
|
+
const state2 = get();
|
|
13912
|
+
if (width === void 0) {
|
|
13913
|
+
set({ _sizeImperative: false });
|
|
13914
|
+
if (state2._sizeProps) {
|
|
13915
|
+
const { width: propW, height: propH } = state2._sizeProps;
|
|
13916
|
+
if (propW !== void 0 || propH !== void 0) {
|
|
13917
|
+
const currentSize = state2.size;
|
|
13918
|
+
const newSize = {
|
|
13919
|
+
width: propW ?? currentSize.width,
|
|
13920
|
+
height: propH ?? currentSize.height,
|
|
13921
|
+
top: currentSize.top,
|
|
13922
|
+
left: currentSize.left
|
|
13923
|
+
};
|
|
13924
|
+
set((s) => ({
|
|
13925
|
+
size: newSize,
|
|
13926
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
13927
|
+
}));
|
|
13928
|
+
get().invalidate();
|
|
13929
|
+
}
|
|
13930
|
+
}
|
|
13931
|
+
return;
|
|
13932
|
+
}
|
|
13933
|
+
const w = width;
|
|
13934
|
+
const h = height ?? width;
|
|
13935
|
+
const t = top ?? state2.size.top;
|
|
13936
|
+
const l = left ?? state2.size.left;
|
|
13937
|
+
const size = { width: w, height: h, top: t, left: l };
|
|
13938
|
+
set((s) => ({
|
|
13939
|
+
size,
|
|
13940
|
+
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
13941
|
+
_sizeImperative: true
|
|
13942
|
+
}));
|
|
13943
|
+
get().invalidate();
|
|
13944
|
+
},
|
|
13945
|
+
setDpr: (dpr) => set((state2) => {
|
|
13946
|
+
const resolved = calculateDpr(dpr);
|
|
13947
|
+
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
13948
|
+
}),
|
|
13949
|
+
setFrameloop: (frameloop = "always") => {
|
|
13950
|
+
const rootId = get().internal.rootId;
|
|
13951
|
+
if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
|
|
13952
|
+
set(() => ({ frameloop }));
|
|
13953
|
+
},
|
|
13954
|
+
setError: (error) => set(() => ({ error })),
|
|
13955
|
+
error: null,
|
|
13956
|
+
//* TSL State (managed via hooks: useUniforms, useNodes, useBuffers, useGPUStorage, useTextures, useRenderPipeline) ==============================
|
|
13957
|
+
uniforms: {},
|
|
13958
|
+
nodes: {},
|
|
13959
|
+
buffers: {},
|
|
13960
|
+
gpuStorage: {},
|
|
13961
|
+
textures: /* @__PURE__ */ new Map(),
|
|
13962
|
+
_textureRefs: /* @__PURE__ */ new Map(),
|
|
13963
|
+
renderPipeline: null,
|
|
13964
|
+
passes: {},
|
|
13965
|
+
_hmrVersion: 0,
|
|
13966
|
+
_sizeImperative: false,
|
|
13967
|
+
_sizeProps: null,
|
|
13968
|
+
previousRoot: void 0,
|
|
13969
|
+
internal: {
|
|
13970
|
+
// Events
|
|
13971
|
+
interaction: [],
|
|
13972
|
+
subscribers: [],
|
|
13973
|
+
// Per-pointer state (new unified structure)
|
|
13974
|
+
pointerMap: /* @__PURE__ */ new Map(),
|
|
13975
|
+
pointerDirty: /* @__PURE__ */ new Map(),
|
|
13976
|
+
lastEvent: React.createRef(),
|
|
13977
|
+
// Deprecated but kept for backwards compatibility
|
|
13978
|
+
hovered: /* @__PURE__ */ new Map(),
|
|
13979
|
+
initialClick: [0, 0],
|
|
13980
|
+
initialHits: [],
|
|
13981
|
+
capturedMap: /* @__PURE__ */ new Map(),
|
|
13982
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
13983
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
13984
|
+
// Occlusion system (WebGPU only)
|
|
13985
|
+
occlusionEnabled: false,
|
|
13986
|
+
occlusionObserver: null,
|
|
13987
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
13988
|
+
helperGroup: null,
|
|
13989
|
+
// Updates
|
|
13990
|
+
active: false,
|
|
13991
|
+
frames: 0,
|
|
13992
|
+
priority: 0,
|
|
13993
|
+
subscribe: (ref, priority, store) => {
|
|
13994
|
+
const internal = get().internal;
|
|
13995
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
13996
|
+
internal.subscribers.push({ ref, priority, store });
|
|
13997
|
+
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
13998
|
+
return () => {
|
|
13999
|
+
const internal2 = get().internal;
|
|
14000
|
+
if (internal2?.subscribers) {
|
|
14001
|
+
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
14002
|
+
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
14003
|
+
}
|
|
14004
|
+
};
|
|
14005
|
+
},
|
|
14006
|
+
// Renderer Storage (single source of truth)
|
|
14007
|
+
actualRenderer: null,
|
|
14008
|
+
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
14009
|
+
scheduler: null,
|
|
14010
|
+
// Replaces the default render call when set (see setRenderOverride)
|
|
14011
|
+
renderOverride: null
|
|
14012
|
+
}
|
|
14013
|
+
};
|
|
14014
|
+
return rootState;
|
|
14015
|
+
});
|
|
14016
|
+
const state = rootStore.getState();
|
|
14017
|
+
Object.defineProperty(state, "gl", {
|
|
14018
|
+
get() {
|
|
14019
|
+
const currentState = rootStore.getState();
|
|
14020
|
+
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
14021
|
+
const stack = new Error().stack || "";
|
|
14022
|
+
if (!isInternalRendererAccess(stack)) {
|
|
14023
|
+
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
14024
|
+
notifyDepreciated({
|
|
14025
|
+
heading: "Accessing state.gl in WebGPU mode",
|
|
14026
|
+
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
|
|
14027
|
+
});
|
|
14028
|
+
}
|
|
14029
|
+
}
|
|
14030
|
+
return currentState.internal.actualRenderer;
|
|
14031
|
+
},
|
|
14032
|
+
set(value) {
|
|
14033
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
14034
|
+
},
|
|
14035
|
+
enumerable: true,
|
|
14036
|
+
configurable: true
|
|
14037
|
+
});
|
|
14038
|
+
Object.defineProperty(state, "renderer", {
|
|
14039
|
+
get() {
|
|
14040
|
+
return rootStore.getState().internal.actualRenderer;
|
|
14041
|
+
},
|
|
14042
|
+
set(value) {
|
|
14043
|
+
rootStore.getState().internal.actualRenderer = value;
|
|
14044
|
+
},
|
|
14045
|
+
enumerable: true,
|
|
14046
|
+
configurable: true
|
|
14047
|
+
});
|
|
14048
|
+
let oldScene = state.scene;
|
|
14049
|
+
rootStore.subscribe(() => {
|
|
14050
|
+
const currentState = rootStore.getState();
|
|
14051
|
+
const { scene, rootScene, set } = currentState;
|
|
14052
|
+
if (scene !== oldScene) {
|
|
14053
|
+
oldScene = scene;
|
|
14054
|
+
if (scene?.isScene && scene !== rootScene) {
|
|
14055
|
+
set({ rootScene: scene });
|
|
14056
|
+
}
|
|
14057
|
+
}
|
|
14058
|
+
});
|
|
14059
|
+
let oldSize = state.size;
|
|
14060
|
+
let oldDpr = state.viewport.dpr;
|
|
14061
|
+
let oldCamera = state.camera;
|
|
14062
|
+
rootStore.subscribe(() => {
|
|
14063
|
+
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
14064
|
+
const actualRenderer = internal.actualRenderer;
|
|
14065
|
+
const canvasTarget = internal.canvasTarget;
|
|
14066
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
14067
|
+
oldSize = size;
|
|
14068
|
+
oldDpr = viewport.dpr;
|
|
14069
|
+
updateCamera(camera, size);
|
|
14070
|
+
const resizeTarget = canvasTarget ?? actualRenderer;
|
|
14071
|
+
if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
|
|
14072
|
+
resizeTarget.setSize(size.width, size.height, false);
|
|
14073
|
+
const activeTarget = actualRenderer.getCanvasTarget?.();
|
|
14074
|
+
if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
|
|
13897
14075
|
}
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
14076
|
+
if (camera !== oldCamera) {
|
|
14077
|
+
oldCamera = camera;
|
|
14078
|
+
const { rootScene } = rootStore.getState();
|
|
14079
|
+
if (camera && rootScene && !camera.parent) {
|
|
14080
|
+
rootScene.add(camera);
|
|
14081
|
+
}
|
|
14082
|
+
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
14083
|
+
const currentState = rootStore.getState();
|
|
14084
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
14085
|
+
updateFrustum(camera, currentState.frustum);
|
|
13907
14086
|
}
|
|
13908
14087
|
}
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
|
|
13926
|
-
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
),
|
|
13962
|
-
|
|
13963
|
-
currentUpdatePriority = newPriority;
|
|
13964
|
-
},
|
|
13965
|
-
getCurrentUpdatePriority() {
|
|
13966
|
-
return currentUpdatePriority;
|
|
13967
|
-
},
|
|
13968
|
-
resolveUpdatePriority() {
|
|
13969
|
-
if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
|
|
13970
|
-
const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
|
|
13971
|
-
if (eventType === void 0) return r;
|
|
13972
|
-
return getEventPriority(eventType);
|
|
13973
|
-
},
|
|
13974
|
-
resetFormInstance() {
|
|
13975
|
-
},
|
|
13976
|
-
// @ts-ignore DefinitelyTyped is not up to date
|
|
13977
|
-
rendererPackageName: "@react-three/fiber",
|
|
13978
|
-
rendererVersion: packageData.version,
|
|
13979
|
-
// 19.2 Reconciler
|
|
13980
|
-
// https://github.com/facebook/react/pull/31975
|
|
13981
|
-
// https://github.com/facebook/react/pull/31999
|
|
13982
|
-
applyViewTransitionName(_instance, _name, _className) {
|
|
13983
|
-
},
|
|
13984
|
-
restoreViewTransitionName(_instance, _props) {
|
|
13985
|
-
},
|
|
13986
|
-
cancelViewTransitionName(_instance, _name, _props) {
|
|
13987
|
-
},
|
|
13988
|
-
cancelRootViewTransitionName(_rootContainer) {
|
|
13989
|
-
},
|
|
13990
|
-
restoreRootViewTransitionName(_rootContainer) {
|
|
13991
|
-
},
|
|
13992
|
-
InstanceMeasurement: null,
|
|
13993
|
-
measureInstance: (_instance) => null,
|
|
13994
|
-
wasInstanceInViewport: (_measurement) => true,
|
|
13995
|
-
hasInstanceChanged: (_oldMeasurement, _newMeasurement) => false,
|
|
13996
|
-
hasInstanceAffectedParent: (_oldMeasurement, _newMeasurement) => false,
|
|
13997
|
-
// https://github.com/facebook/react/pull/32002
|
|
13998
|
-
// https://github.com/facebook/react/pull/34486
|
|
13999
|
-
suspendOnActiveViewTransition(_state, _container) {
|
|
14000
|
-
},
|
|
14001
|
-
// https://github.com/facebook/react/pull/32451
|
|
14002
|
-
// https://github.com/facebook/react/pull/32760
|
|
14003
|
-
startGestureTransition: () => null,
|
|
14004
|
-
startViewTransition: () => null,
|
|
14005
|
-
stopViewTransition(_transition) {
|
|
14006
|
-
},
|
|
14007
|
-
// https://github.com/facebook/react/pull/32038
|
|
14008
|
-
createViewTransitionInstance: (_name) => null,
|
|
14009
|
-
// https://github.com/facebook/react/pull/32379
|
|
14010
|
-
// https://github.com/facebook/react/pull/32786
|
|
14011
|
-
getCurrentGestureOffset(_provider) {
|
|
14012
|
-
throw new Error("startGestureTransition is not yet supported in react-three-fiber.");
|
|
14013
|
-
},
|
|
14014
|
-
// https://github.com/facebook/react/pull/32500
|
|
14015
|
-
cloneMutableInstance(instance, _keepChildren) {
|
|
14016
|
-
return instance;
|
|
14017
|
-
},
|
|
14018
|
-
cloneMutableTextInstance(textInstance) {
|
|
14019
|
-
return textInstance;
|
|
14020
|
-
},
|
|
14021
|
-
// React 19.2 added these for root-level view transitions. They only run for hosts that opt into
|
|
14022
|
-
// view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
|
|
14023
|
-
// into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
|
|
14024
|
-
// React never calls them. They stay as no-ops rather than throws: the host config is shared by
|
|
14025
|
-
// every root, and a throw here would turn a React-internal probe into a hard crash for users.
|
|
14026
|
-
// Guarded by a test asserting a full render + unmount never reaches them.
|
|
14027
|
-
cloneRootViewTransitionContainer(_rootContainer) {
|
|
14028
|
-
},
|
|
14029
|
-
removeRootViewTransitionClone(_rootContainer, _clone) {
|
|
14030
|
-
},
|
|
14031
|
-
// https://github.com/facebook/react/pull/32465
|
|
14032
|
-
createFragmentInstance: (_fiber) => null,
|
|
14033
|
-
updateFragmentInstanceFiber(_fiber, _instance) {
|
|
14034
|
-
},
|
|
14035
|
-
commitNewChildToFragmentInstance(_child, _fragmentInstance) {
|
|
14036
|
-
},
|
|
14037
|
-
deleteChildFromFragmentInstance(_child, _fragmentInstance) {
|
|
14038
|
-
},
|
|
14039
|
-
// https://github.com/facebook/react/pull/32653
|
|
14040
|
-
measureClonedInstance: (_instance) => null,
|
|
14041
|
-
// https://github.com/facebook/react/pull/32819
|
|
14042
|
-
maySuspendCommitOnUpdate: (_type, _oldProps, _newProps) => false,
|
|
14043
|
-
maySuspendCommitInSyncRender: (_type, _props) => false,
|
|
14044
|
-
// https://github.com/facebook/react/pull/34486
|
|
14045
|
-
startSuspendingCommit: () => null,
|
|
14046
|
-
// https://github.com/facebook/react/pull/34522
|
|
14047
|
-
getSuspendedCommitReason: (_state, _rootContainer) => null
|
|
14048
|
-
});
|
|
14088
|
+
});
|
|
14089
|
+
rootStore.subscribe((state2) => invalidate(state2));
|
|
14090
|
+
return rootStore;
|
|
14091
|
+
};
|
|
14092
|
+
|
|
14093
|
+
const R3F_EXTENSIONS = Symbol.for("@react-three/fiber.extensions");
|
|
14094
|
+
function getRegistry() {
|
|
14095
|
+
return globalThis[R3F_EXTENSIONS] ?? (globalThis[R3F_EXTENSIONS] = {
|
|
14096
|
+
extensions: /* @__PURE__ */ new Map(),
|
|
14097
|
+
roots: /* @__PURE__ */ new Set(),
|
|
14098
|
+
applied: /* @__PURE__ */ new WeakMap()
|
|
14099
|
+
});
|
|
14100
|
+
}
|
|
14101
|
+
function setupRoot(registry, store, extension) {
|
|
14102
|
+
let applied = registry.applied.get(store);
|
|
14103
|
+
if (!applied) registry.applied.set(store, applied = /* @__PURE__ */ new Map());
|
|
14104
|
+
if (applied.has(extension.name)) return;
|
|
14105
|
+
applied.set(extension.name, extension);
|
|
14106
|
+
const patch = extension.setup?.(store);
|
|
14107
|
+
if (patch) store.setState(patch);
|
|
14108
|
+
}
|
|
14109
|
+
function registerRootExtension(extension) {
|
|
14110
|
+
const registry = getRegistry();
|
|
14111
|
+
registry.extensions.set(extension.name, extension);
|
|
14112
|
+
for (const store of registry.roots) setupRoot(registry, store, extension);
|
|
14113
|
+
return () => {
|
|
14114
|
+
if (registry.extensions.get(extension.name) === extension) registry.extensions.delete(extension.name);
|
|
14115
|
+
};
|
|
14116
|
+
}
|
|
14117
|
+
function setRenderOverride(store, render) {
|
|
14118
|
+
store.getState().internal.renderOverride = render;
|
|
14119
|
+
}
|
|
14120
|
+
function attachRootExtensions(store) {
|
|
14121
|
+
const registry = getRegistry();
|
|
14122
|
+
registry.roots.add(store);
|
|
14123
|
+
for (const extension of registry.extensions.values()) setupRoot(registry, store, extension);
|
|
14124
|
+
}
|
|
14125
|
+
function current(registry, setUpBy) {
|
|
14126
|
+
return registry.extensions.get(setUpBy.name) ?? setUpBy;
|
|
14127
|
+
}
|
|
14128
|
+
function detachRootExtensions(store) {
|
|
14129
|
+
const registry = getRegistry();
|
|
14130
|
+
if (!registry.roots.delete(store)) return;
|
|
14131
|
+
const applied = registry.applied.get(store);
|
|
14132
|
+
registry.applied.delete(store);
|
|
14133
|
+
if (!applied) return;
|
|
14134
|
+
for (const setUpBy of applied.values()) current(registry, setUpBy).dispose?.(store);
|
|
14135
|
+
}
|
|
14136
|
+
function notifyRootExtensionsHmr(store) {
|
|
14137
|
+
const registry = getRegistry();
|
|
14138
|
+
const applied = registry.applied.get(store);
|
|
14139
|
+
if (!applied) return;
|
|
14140
|
+
for (const setUpBy of applied.values()) current(registry, setUpBy).hmr?.(store);
|
|
14141
|
+
}
|
|
14049
14142
|
|
|
14050
14143
|
const isRenderer = (def) => !!def?.render;
|
|
14051
14144
|
const _roots = /* @__PURE__ */ new Map();
|
|
@@ -14202,9 +14295,8 @@ function createRoot(canvas) {
|
|
|
14202
14295
|
if (!renderer.hasInitialized?.()) {
|
|
14203
14296
|
const size2 = computeInitialSize(canvas, propsSize);
|
|
14204
14297
|
if (size2.width > 0 && size2.height > 0) {
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
canvas.height = size2.height * pixelRatio;
|
|
14298
|
+
renderer.setPixelRatio(calculateDpr(dpr));
|
|
14299
|
+
renderer.setSize(size2.width, size2.height, false);
|
|
14208
14300
|
}
|
|
14209
14301
|
await renderer.init();
|
|
14210
14302
|
}
|
|
@@ -14213,7 +14305,7 @@ function createRoot(canvas) {
|
|
|
14213
14305
|
state.internal.actualRenderer = renderer;
|
|
14214
14306
|
state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
|
|
14215
14307
|
if (canvasId && !state.internal.isSecondary) {
|
|
14216
|
-
const canvasTarget =
|
|
14308
|
+
const canvasTarget = renderer.getCanvasTarget?.();
|
|
14217
14309
|
const unregisterPrimary = registerPrimary(canvasId, renderer, store);
|
|
14218
14310
|
state.set((prev) => ({
|
|
14219
14311
|
internal: {
|
|
@@ -14327,7 +14419,7 @@ function createRoot(canvas) {
|
|
|
14327
14419
|
const handleXRFrame = (timestamp, _frame) => {
|
|
14328
14420
|
const state2 = store.getState();
|
|
14329
14421
|
if (state2.frameloop === "never") return;
|
|
14330
|
-
advance(timestamp);
|
|
14422
|
+
advance(timestamp, true, state2);
|
|
14331
14423
|
};
|
|
14332
14424
|
const actualRenderer = state.internal.actualRenderer;
|
|
14333
14425
|
const handleSessionChange = () => {
|
|
@@ -14335,7 +14427,7 @@ function createRoot(canvas) {
|
|
|
14335
14427
|
const renderer2 = state2.internal.actualRenderer;
|
|
14336
14428
|
actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
|
|
14337
14429
|
renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
|
|
14338
|
-
if (!renderer2.xr.isPresenting) invalidate();
|
|
14430
|
+
if (!renderer2.xr.isPresenting) invalidate(state2);
|
|
14339
14431
|
};
|
|
14340
14432
|
const xr = {
|
|
14341
14433
|
connect() {
|
|
@@ -14417,14 +14509,23 @@ function createRoot(canvas) {
|
|
|
14417
14509
|
const newRootId = canvasId || scheduler.generateRootId();
|
|
14418
14510
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14419
14511
|
getState: () => store.getState(),
|
|
14420
|
-
onError: (err) => store.getState().setError(err)
|
|
14512
|
+
onError: (err) => store.getState().setError(err),
|
|
14513
|
+
frameloop: store.getState().frameloop,
|
|
14514
|
+
before: schedulerConfig?.before,
|
|
14515
|
+
after: schedulerConfig?.after,
|
|
14516
|
+
order: schedulerConfig?.order
|
|
14421
14517
|
});
|
|
14422
14518
|
const unregisterCanvasTarget = scheduler.register(
|
|
14423
14519
|
() => {
|
|
14424
14520
|
const state2 = store.getState();
|
|
14425
|
-
|
|
14521
|
+
const canvasTarget = state2.internal.canvasTarget;
|
|
14522
|
+
if (canvasTarget) {
|
|
14426
14523
|
const renderer2 = state2.internal.actualRenderer;
|
|
14427
|
-
renderer2.setCanvasTarget(
|
|
14524
|
+
if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
|
|
14525
|
+
if (state2.internal.canvasTargetSizeDirty) {
|
|
14526
|
+
state2.internal.canvasTargetSizeDirty = false;
|
|
14527
|
+
renderer2.backend?.updateSize?.();
|
|
14528
|
+
}
|
|
14428
14529
|
}
|
|
14429
14530
|
},
|
|
14430
14531
|
{
|
|
@@ -14480,7 +14581,8 @@ function createRoot(canvas) {
|
|
|
14480
14581
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14481
14582
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14482
14583
|
try {
|
|
14483
|
-
|
|
14584
|
+
const renderOverride = state2.internal.renderOverride;
|
|
14585
|
+
if (renderOverride) renderOverride();
|
|
14484
14586
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14485
14587
|
} catch (error) {
|
|
14486
14588
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -14493,8 +14595,7 @@ function createRoot(canvas) {
|
|
|
14493
14595
|
phase: "render",
|
|
14494
14596
|
system: true,
|
|
14495
14597
|
// Internal flag: this is a system job, not user-controlled
|
|
14496
|
-
//
|
|
14497
|
-
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
14598
|
+
// FPS throttles only the default render job; Canvas ordering belongs to the root.
|
|
14498
14599
|
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14499
14600
|
}
|
|
14500
14601
|
);
|
|
@@ -14513,8 +14614,22 @@ function createRoot(canvas) {
|
|
|
14513
14614
|
scheduler
|
|
14514
14615
|
}
|
|
14515
14616
|
}));
|
|
14617
|
+
} else {
|
|
14618
|
+
const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
|
|
14619
|
+
if (constraintsChanged) {
|
|
14620
|
+
scheduler.setRootConstraints(rootId, {
|
|
14621
|
+
before: schedulerConfig?.before,
|
|
14622
|
+
after: schedulerConfig?.after
|
|
14623
|
+
});
|
|
14624
|
+
}
|
|
14625
|
+
if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
|
|
14626
|
+
scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
|
|
14627
|
+
}
|
|
14516
14628
|
}
|
|
14517
|
-
|
|
14629
|
+
lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
|
|
14630
|
+
lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
|
|
14631
|
+
lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
|
|
14632
|
+
attachRootExtensions(store);
|
|
14518
14633
|
onCreated = onCreatedCallback;
|
|
14519
14634
|
configured = true;
|
|
14520
14635
|
resolve();
|
|
@@ -14556,18 +14671,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14556
14671
|
const fiber = root?.fiber;
|
|
14557
14672
|
if (fiber) {
|
|
14558
14673
|
const state = root?.store.getState();
|
|
14559
|
-
if (state)
|
|
14674
|
+
if (state) {
|
|
14675
|
+
state.internal.active = false;
|
|
14676
|
+
const unregisterRoot = state.internal.unregisterRoot;
|
|
14677
|
+
if (unregisterRoot) {
|
|
14678
|
+
unregisterRoot();
|
|
14679
|
+
state.internal.unregisterRoot = void 0;
|
|
14680
|
+
}
|
|
14681
|
+
}
|
|
14560
14682
|
reconciler.updateContainer(null, fiber, null, () => {
|
|
14683
|
+
if (root?.store) detachRootExtensions(root.store);
|
|
14561
14684
|
if (state) {
|
|
14562
14685
|
setTimeout(() => {
|
|
14563
14686
|
try {
|
|
14564
14687
|
const renderer = state.internal.actualRenderer;
|
|
14565
|
-
const unregisterRoot = state.internal.unregisterRoot;
|
|
14566
|
-
if (unregisterRoot) unregisterRoot();
|
|
14567
14688
|
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
14568
14689
|
if (unregisterPrimary) unregisterPrimary();
|
|
14569
14690
|
const canvasTarget = state.internal.canvasTarget;
|
|
14570
|
-
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14691
|
+
if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
|
|
14571
14692
|
state.events.disconnect?.();
|
|
14572
14693
|
cleanupHelperGroup(root.store);
|
|
14573
14694
|
if (state.isLegacy && renderer) {
|
|
@@ -14708,16 +14829,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
|
|
|
14708
14829
|
const oldbg = target.background;
|
|
14709
14830
|
const oldenv = target.environment;
|
|
14710
14831
|
const oldSceneProps = {
|
|
14711
|
-
// @ts-ignore
|
|
14712
14832
|
backgroundBlurriness: target.backgroundBlurriness,
|
|
14713
|
-
// @ts-ignore
|
|
14714
14833
|
backgroundIntensity: target.backgroundIntensity,
|
|
14715
|
-
|
|
14716
|
-
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
14717
|
-
// @ts-ignore
|
|
14834
|
+
backgroundRotation: target.backgroundRotation.clone(),
|
|
14718
14835
|
environmentIntensity: target.environmentIntensity,
|
|
14719
|
-
|
|
14720
|
-
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
14836
|
+
environmentRotation: target.environmentRotation.clone()
|
|
14721
14837
|
};
|
|
14722
14838
|
if (background !== "only") target.environment = texture;
|
|
14723
14839
|
if (background) target.background = texture;
|
|
@@ -14942,38 +15058,6 @@ function parseRendererConfig(rendererProp) {
|
|
|
14942
15058
|
return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
|
|
14943
15059
|
}
|
|
14944
15060
|
|
|
14945
|
-
const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
|
|
14946
|
-
const registries = /* @__PURE__ */ new WeakMap();
|
|
14947
|
-
function getKindRegistry(store, kind) {
|
|
14948
|
-
let registry = registries.get(store);
|
|
14949
|
-
if (!registry) {
|
|
14950
|
-
registry = {
|
|
14951
|
-
nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14952
|
-
uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14953
|
-
buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14954
|
-
gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
|
|
14955
|
-
};
|
|
14956
|
-
registries.set(store, registry);
|
|
14957
|
-
}
|
|
14958
|
-
return registry[kind];
|
|
14959
|
-
}
|
|
14960
|
-
function invalidateResource(store, kind, scopeKey) {
|
|
14961
|
-
const registry = getKindRegistry(store, kind);
|
|
14962
|
-
{
|
|
14963
|
-
registry.valid.clear();
|
|
14964
|
-
registry.staged.clear();
|
|
14965
|
-
}
|
|
14966
|
-
}
|
|
14967
|
-
function invalidateAllResources(store) {
|
|
14968
|
-
for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
|
|
14969
|
-
}
|
|
14970
|
-
|
|
14971
|
-
function clearHmrCaches(store) {
|
|
14972
|
-
const primary = store.getState().primaryStore ?? store;
|
|
14973
|
-
invalidateAllResources(primary);
|
|
14974
|
-
primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
|
|
14975
|
-
}
|
|
14976
|
-
|
|
14977
15061
|
function CanvasImpl({
|
|
14978
15062
|
ref,
|
|
14979
15063
|
children,
|
|
@@ -15011,9 +15095,9 @@ function CanvasImpl({
|
|
|
15011
15095
|
React.useMemo(() => extend(THREE), []);
|
|
15012
15096
|
const Bridge = useBridge();
|
|
15013
15097
|
const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
|
|
15014
|
-
const
|
|
15098
|
+
const [hasInitialSize, setHasInitialSize] = React.useState(false);
|
|
15015
15099
|
const measureConfig = React.useMemo(() => {
|
|
15016
|
-
if (!
|
|
15100
|
+
if (!hasInitialSize) {
|
|
15017
15101
|
return {
|
|
15018
15102
|
...resize,
|
|
15019
15103
|
scroll: resize?.scroll ?? true,
|
|
@@ -15025,7 +15109,7 @@ function CanvasImpl({
|
|
|
15025
15109
|
debounce: { scroll: 50, resize: 0 },
|
|
15026
15110
|
...resize
|
|
15027
15111
|
};
|
|
15028
|
-
}, [
|
|
15112
|
+
}, [hasInitialSize, resize]);
|
|
15029
15113
|
const [containerRef, containerRect] = useMeasure(measureConfig);
|
|
15030
15114
|
const effectiveSize = React.useMemo(() => {
|
|
15031
15115
|
let w = width ?? containerRect.width;
|
|
@@ -15041,9 +15125,7 @@ function CanvasImpl({
|
|
|
15041
15125
|
left: containerRect.left
|
|
15042
15126
|
};
|
|
15043
15127
|
}, [width, height, containerRect, forceEven]);
|
|
15044
|
-
if (!
|
|
15045
|
-
hasInitialSizeRef.current = true;
|
|
15046
|
-
}
|
|
15128
|
+
if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
|
|
15047
15129
|
const canvasRef = React.useRef(null);
|
|
15048
15130
|
const divRef = React.useRef(null);
|
|
15049
15131
|
React.useImperativeHandle(ref, () => canvasRef.current);
|
|
@@ -15157,6 +15239,7 @@ function CanvasImpl({
|
|
|
15157
15239
|
const canvas = canvasRef.current;
|
|
15158
15240
|
if (canvas) {
|
|
15159
15241
|
return () => {
|
|
15242
|
+
if (canvas.isConnected) return;
|
|
15160
15243
|
unmountComponentAtNode(canvas);
|
|
15161
15244
|
root.current = null;
|
|
15162
15245
|
};
|
|
@@ -15169,7 +15252,7 @@ function CanvasImpl({
|
|
|
15169
15252
|
const handleHMR = () => {
|
|
15170
15253
|
queueMicrotask(() => {
|
|
15171
15254
|
const rootEntry = _roots.get(canvas);
|
|
15172
|
-
if (rootEntry?.store)
|
|
15255
|
+
if (rootEntry?.store) notifyRootExtensionsHmr(rootEntry.store);
|
|
15173
15256
|
});
|
|
15174
15257
|
};
|
|
15175
15258
|
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
@@ -15221,4 +15304,4 @@ function Canvas(props) {
|
|
|
15221
15304
|
|
|
15222
15305
|
extend(THREE);
|
|
15223
15306
|
|
|
15224
|
-
export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Texture, _roots, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef$1 as isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, reconciler, registerPrimary, removeInteractivity, resolve, swapInteractivity, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useEnvironment, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree, waitForPrimary };
|
|
15307
|
+
export { Block, Canvas, Environment, EnvironmentCube, EnvironmentMap, EnvironmentPortal, ErrorBoundary, FROM_REF, IsObject, ONCE, Portal, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Texture, _roots, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, fromRef, getInstanceProps, getPrimary, getPrimaryIds, getRootState, getUuidPrefix, hasConstructor, hasPrimary, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isFromRef, isObject3D, isOnce, isOrthographicCamera, isRef$1 as isRef, isRenderer, isTexture, isVectorLike, once, prepare, presetsObj, reconciler, registerPrimary, registerRootExtension, removeInteractivity, resolve, setRenderOverride, swapInteractivity, unmountComponentAtNode, unregisterPrimary, updateCamera, updateFrustum, useBridge, useEnvironment, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree, waitForPrimary };
|