@react-three/fiber 10.0.0-canary.da7a768 → 10.0.0-canary.dc0ec15
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 +994 -898
- package/dist/index.d.cts +331 -155
- package/dist/index.d.mts +331 -155
- package/dist/index.d.ts +331 -183
- package/dist/index.mjs +995 -901
- package/dist/legacy.cjs +1345 -1245
- package/dist/legacy.d.cts +353 -205
- package/dist/legacy.d.mts +353 -205
- package/dist/legacy.d.ts +353 -231
- package/dist/legacy.mjs +1346 -1248
- package/dist/webgpu/index.cjs +908 -713
- package/dist/webgpu/index.d.cts +389 -212
- package/dist/webgpu/index.d.mts +389 -212
- package/dist/webgpu/index.d.ts +389 -240
- package/dist/webgpu/index.mjs +910 -717
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -1,9 +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
4
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
import * as React from 'react';
|
|
6
|
-
import React__default, { useRef, useMemo, useLayoutEffect, useEffect,
|
|
6
|
+
import React__default, { useContext, useRef, useMemo, useLayoutEffect, useEffect, useImperativeHandle, useCallback, useState } from 'react';
|
|
7
7
|
import useMeasure from 'react-use-measure';
|
|
8
8
|
import { useFiber, useContextBridge, traverseFiber, FiberProvider } from 'its-fine';
|
|
9
9
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
@@ -12,6 +12,7 @@ export { Scheduler, getScheduler } from '@pmndrs/scheduler';
|
|
|
12
12
|
import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
|
|
13
13
|
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
14
14
|
import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
|
|
15
|
+
import { HDRCubeTextureLoader } from 'three/examples/jsm/loaders/HDRCubeTextureLoader.js';
|
|
15
16
|
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
|
|
16
17
|
import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
|
|
17
18
|
import { GainMapLoader } from '@monogrid/gainmap-js';
|
|
@@ -293,7 +294,6 @@ function prepare(target, root, type, props) {
|
|
|
293
294
|
}
|
|
294
295
|
function invalidateInstance(instance) {
|
|
295
296
|
if (!instance.parent) return;
|
|
296
|
-
instance.props.onUpdate?.(instance.object);
|
|
297
297
|
const state = instance.root?.getState?.();
|
|
298
298
|
if (state && state.internal.frames === 0) state.invalidate();
|
|
299
299
|
}
|
|
@@ -509,8 +509,10 @@ function hasVisibilityHandlers(handlers) {
|
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
|
|
512
|
-
function fromRef(ref) {
|
|
513
|
-
|
|
512
|
+
function fromRef(ref, transform) {
|
|
513
|
+
const marker = { [FROM_REF]: ref };
|
|
514
|
+
if (transform) marker.transform = transform;
|
|
515
|
+
return marker;
|
|
514
516
|
}
|
|
515
517
|
function isFromRef(value) {
|
|
516
518
|
return value !== null && typeof value === "object" && FROM_REF in value;
|
|
@@ -524,6 +526,63 @@ function isOnce(value) {
|
|
|
524
526
|
return value !== null && typeof value === "object" && ONCE in value;
|
|
525
527
|
}
|
|
526
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
|
+
|
|
527
586
|
const RESERVED_PROPS = [
|
|
528
587
|
"children",
|
|
529
588
|
"key",
|
|
@@ -533,7 +592,6 @@ const RESERVED_PROPS = [
|
|
|
533
592
|
"dispose",
|
|
534
593
|
"attach",
|
|
535
594
|
"object",
|
|
536
|
-
"onUpdate",
|
|
537
595
|
// Behavior flags
|
|
538
596
|
"dispose"
|
|
539
597
|
];
|
|
@@ -611,11 +669,9 @@ function diffProps(instance, newProps) {
|
|
|
611
669
|
for (const prop in instance.props) {
|
|
612
670
|
if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
|
|
613
671
|
const { root, key } = resolve(instance.object, prop);
|
|
614
|
-
if (
|
|
672
|
+
if (typeof root?.constructor === "function") {
|
|
615
673
|
const ctor = getMemoizedPrototype(root);
|
|
616
674
|
if (!is.und(ctor)) changedProps[prop] = ctor[key];
|
|
617
|
-
} else {
|
|
618
|
-
changedProps[prop] = 0;
|
|
619
675
|
}
|
|
620
676
|
}
|
|
621
677
|
return changedProps;
|
|
@@ -627,6 +683,13 @@ function applyProps(object, props) {
|
|
|
627
683
|
for (const prop in props) {
|
|
628
684
|
const value = props[prop];
|
|
629
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
|
+
}
|
|
630
693
|
if (instance && EVENT_REGEX.test(prop)) {
|
|
631
694
|
if (typeof value === "function") instance.handlers[prop] = value;
|
|
632
695
|
else delete instance.handlers[prop];
|
|
@@ -732,682 +795,378 @@ function applyProps(object, props) {
|
|
|
732
795
|
return object;
|
|
733
796
|
}
|
|
734
797
|
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (
|
|
741
|
-
|
|
742
|
-
const caller = getCallerFrame();
|
|
743
|
-
if (caller) {
|
|
744
|
-
console.log();
|
|
745
|
-
}
|
|
746
|
-
const boxStyle = "background: #ff9800; color: #1a1a1a; padding: 8px 12px; border-radius: 4px; font-weight: 500;";
|
|
747
|
-
if (caller) {
|
|
748
|
-
console.log(`%c\u26A0\uFE0F ${heading}`, boxStyle);
|
|
749
|
-
}
|
|
750
|
-
if (body || link) {
|
|
751
|
-
let message = "";
|
|
752
|
-
if (body) message += body;
|
|
753
|
-
if (link) message += (body ? "\n\n" : "") + `More info: ${link}`;
|
|
754
|
-
console.warn(`%c${message}`, "font-weight: bold;");
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
function getCallerFrame(depth = 3) {
|
|
758
|
-
const stack = new Error().stack;
|
|
759
|
-
if (!stack) return null;
|
|
760
|
-
const lines = stack.split("\n");
|
|
761
|
-
const frame = lines[depth];
|
|
762
|
-
if (!frame) return null;
|
|
763
|
-
let match = frame.match(/^\s*at (?:(.+?) )?\(?(.+?):(\d+):(\d+)\)?$/);
|
|
764
|
-
if (!match) {
|
|
765
|
-
match = frame.match(/^(?:(.+?)@)?(.+?):(\d+):(\d+)$/);
|
|
766
|
-
}
|
|
767
|
-
if (!match) return null;
|
|
768
|
-
const [, fn, url, line] = match;
|
|
769
|
-
return {
|
|
770
|
-
functionName: fn ?? "<anonymous>",
|
|
771
|
-
location: formatLocation(url, Number(line))
|
|
772
|
-
};
|
|
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;
|
|
773
805
|
}
|
|
774
|
-
function
|
|
775
|
-
|
|
776
|
-
const file = clean.split("/").pop() ?? clean;
|
|
777
|
-
return `${file}:${line}`;
|
|
806
|
+
function useThree(selector = (state) => state, equalityFn) {
|
|
807
|
+
return useStore()(selector, equalityFn);
|
|
778
808
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
if (
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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;
|
|
789
820
|
}
|
|
821
|
+
return Proto;
|
|
790
822
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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];
|
|
797
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;
|
|
798
871
|
|
|
799
|
-
|
|
800
|
-
const
|
|
801
|
-
const
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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
|
+
});
|
|
819
911
|
}
|
|
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
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
inspector: null,
|
|
848
|
-
invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
|
|
849
|
-
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
|
|
850
|
-
textureColorSpace: SRGBColorSpace,
|
|
851
|
-
isLegacy: false,
|
|
852
|
-
webGPUSupported: false,
|
|
853
|
-
isNative: false,
|
|
854
|
-
controls: null,
|
|
855
|
-
pointer,
|
|
856
|
-
mouse: pointer,
|
|
857
|
-
frameloop: "always",
|
|
858
|
-
onPointerMissed: void 0,
|
|
859
|
-
onDragOverMissed: void 0,
|
|
860
|
-
onDropMissed: void 0,
|
|
861
|
-
performance: {
|
|
862
|
-
current: 1,
|
|
863
|
-
min: 0.5,
|
|
864
|
-
max: 1,
|
|
865
|
-
debounce: 200,
|
|
866
|
-
regress: () => {
|
|
867
|
-
const state2 = get();
|
|
868
|
-
if (performanceTimeout) clearTimeout(performanceTimeout);
|
|
869
|
-
if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
|
|
870
|
-
performanceTimeout = setTimeout(
|
|
871
|
-
() => setPerformanceCurrent(get().performance.max),
|
|
872
|
-
state2.performance.debounce
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
},
|
|
876
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
877
|
-
viewport: {
|
|
878
|
-
initialDpr: 0,
|
|
879
|
-
dpr: 0,
|
|
880
|
-
width: 0,
|
|
881
|
-
height: 0,
|
|
882
|
-
top: 0,
|
|
883
|
-
left: 0,
|
|
884
|
-
aspect: 0,
|
|
885
|
-
distance: 0,
|
|
886
|
-
factor: 0,
|
|
887
|
-
getCurrentViewport
|
|
888
|
-
},
|
|
889
|
-
setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
|
|
890
|
-
setSize: (width, height, top, left) => {
|
|
891
|
-
const state2 = get();
|
|
892
|
-
if (width === void 0) {
|
|
893
|
-
set({ _sizeImperative: false });
|
|
894
|
-
if (state2._sizeProps) {
|
|
895
|
-
const { width: propW, height: propH } = state2._sizeProps;
|
|
896
|
-
if (propW !== void 0 || propH !== void 0) {
|
|
897
|
-
const currentSize = state2.size;
|
|
898
|
-
const newSize = {
|
|
899
|
-
width: propW ?? currentSize.width,
|
|
900
|
-
height: propH ?? currentSize.height,
|
|
901
|
-
top: currentSize.top,
|
|
902
|
-
left: currentSize.left
|
|
903
|
-
};
|
|
904
|
-
set((s) => ({
|
|
905
|
-
size: newSize,
|
|
906
|
-
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
907
|
-
}));
|
|
908
|
-
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;
|
|
909
939
|
}
|
|
910
940
|
}
|
|
911
|
-
return;
|
|
912
941
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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);
|
|
924
959
|
},
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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);
|
|
932
983
|
},
|
|
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
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
internal.subscribers.push({ ref, priority, store });
|
|
976
|
-
internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
|
|
977
|
-
return () => {
|
|
978
|
-
const internal2 = get().internal;
|
|
979
|
-
if (internal2?.subscribers) {
|
|
980
|
-
internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
|
|
981
|
-
internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
|
|
982
|
-
}
|
|
983
|
-
};
|
|
984
|
-
},
|
|
985
|
-
// Renderer Storage (single source of truth)
|
|
986
|
-
actualRenderer: null,
|
|
987
|
-
// Scheduler for useFrameNext (initialized in renderer.tsx)
|
|
988
|
-
scheduler: null
|
|
989
|
-
}
|
|
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
|
|
990
1026
|
};
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
Object.defineProperty(state, "gl", {
|
|
995
|
-
get() {
|
|
996
|
-
const currentState = rootStore.getState();
|
|
997
|
-
if (!currentState.isLegacy && currentState.internal.actualRenderer) {
|
|
998
|
-
const stack = new Error().stack || "";
|
|
999
|
-
if (!isInternalRendererAccess(stack)) {
|
|
1000
|
-
const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
|
|
1001
|
-
notifyDepreciated({
|
|
1002
|
-
heading: "Accessing state.gl in WebGPU mode",
|
|
1003
|
-
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
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
return currentState.internal.actualRenderer;
|
|
1008
|
-
},
|
|
1009
|
-
set(value) {
|
|
1010
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1011
|
-
},
|
|
1012
|
-
enumerable: true,
|
|
1013
|
-
configurable: true
|
|
1014
|
-
});
|
|
1015
|
-
Object.defineProperty(state, "renderer", {
|
|
1016
|
-
get() {
|
|
1017
|
-
return rootStore.getState().internal.actualRenderer;
|
|
1018
|
-
},
|
|
1019
|
-
set(value) {
|
|
1020
|
-
rootStore.getState().internal.actualRenderer = value;
|
|
1021
|
-
},
|
|
1022
|
-
enumerable: true,
|
|
1023
|
-
configurable: true
|
|
1024
|
-
});
|
|
1025
|
-
let oldScene = state.scene;
|
|
1026
|
-
rootStore.subscribe(() => {
|
|
1027
|
-
const currentState = rootStore.getState();
|
|
1028
|
-
const { scene, rootScene, set } = currentState;
|
|
1029
|
-
if (scene !== oldScene) {
|
|
1030
|
-
oldScene = scene;
|
|
1031
|
-
if (scene?.isScene && scene !== rootScene) {
|
|
1032
|
-
set({ rootScene: scene });
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
});
|
|
1036
|
-
let oldSize = state.size;
|
|
1037
|
-
let oldDpr = state.viewport.dpr;
|
|
1038
|
-
let oldCamera = state.camera;
|
|
1039
|
-
rootStore.subscribe(() => {
|
|
1040
|
-
const { camera, size, viewport, set, internal } = rootStore.getState();
|
|
1041
|
-
const actualRenderer = internal.actualRenderer;
|
|
1042
|
-
const canvasTarget = internal.canvasTarget;
|
|
1043
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1044
|
-
oldSize = size;
|
|
1045
|
-
oldDpr = viewport.dpr;
|
|
1046
|
-
updateCamera(camera, size);
|
|
1047
|
-
if (internal.isSecondary && canvasTarget) {
|
|
1048
|
-
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
1049
|
-
canvasTarget.setSize(size.width, size.height, false);
|
|
1050
|
-
} else {
|
|
1051
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1052
|
-
actualRenderer.setSize(size.width, size.height, false);
|
|
1053
|
-
if (canvasTarget) {
|
|
1054
|
-
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
1055
|
-
canvasTarget.setSize(size.width, size.height, false);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
if (camera !== oldCamera) {
|
|
1060
|
-
oldCamera = camera;
|
|
1061
|
-
const { rootScene } = rootStore.getState();
|
|
1062
|
-
if (camera && rootScene && !camera.parent) {
|
|
1063
|
-
rootScene.add(camera);
|
|
1064
|
-
}
|
|
1065
|
-
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1066
|
-
const currentState = rootStore.getState();
|
|
1067
|
-
if (currentState.autoUpdateFrustum && camera) {
|
|
1068
|
-
updateFrustum(camera, currentState.frustum);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
});
|
|
1072
|
-
rootStore.subscribe((state2) => invalidate(state2));
|
|
1073
|
-
return rootStore;
|
|
1074
|
-
};
|
|
1027
|
+
}, [id, isPaused]);
|
|
1028
|
+
return controls;
|
|
1029
|
+
}
|
|
1075
1030
|
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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]);
|
|
1084
1055
|
}
|
|
1085
|
-
return
|
|
1056
|
+
return result;
|
|
1086
1057
|
}
|
|
1087
|
-
return Proto;
|
|
1088
1058
|
}
|
|
1089
|
-
function
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
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
|
+
}
|
|
1097
1113
|
});
|
|
1098
1114
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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;
|
|
1120
1158
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
};
|
|
1132
|
-
useLoader.loader = getLoader$1;
|
|
1133
|
-
|
|
1134
|
-
function useFrame(callback, priorityOrOptions) {
|
|
1135
|
-
const store = React.useContext(context);
|
|
1136
|
-
const isInsideCanvas = store !== null;
|
|
1137
|
-
const scheduler = getScheduler();
|
|
1138
|
-
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
1139
|
-
id: priorityOrOptions.id,
|
|
1140
|
-
phase: priorityOrOptions.phase,
|
|
1141
|
-
priority: priorityOrOptions.priority,
|
|
1142
|
-
fps: priorityOrOptions.fps,
|
|
1143
|
-
drop: priorityOrOptions.drop,
|
|
1144
|
-
enabled: priorityOrOptions.enabled,
|
|
1145
|
-
before: priorityOrOptions.before,
|
|
1146
|
-
after: priorityOrOptions.after
|
|
1147
|
-
}) : "";
|
|
1148
|
-
const options = React.useMemo(() => {
|
|
1149
|
-
return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
|
|
1150
|
-
}, [optionsKey]);
|
|
1151
|
-
const reactId = React.useId();
|
|
1152
|
-
const id = options.id ?? reactId;
|
|
1153
|
-
const callbackRef = useMutableCallback(callback);
|
|
1154
|
-
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
1155
|
-
useIsomorphicLayoutEffect(() => {
|
|
1156
|
-
if (!callback) return;
|
|
1157
|
-
if (isInsideCanvas) {
|
|
1158
|
-
const state = store.getState();
|
|
1159
|
-
const rootId = state.internal.rootId;
|
|
1160
|
-
if (isLegacyPriority) {
|
|
1161
|
-
state.internal.priority++;
|
|
1162
|
-
let parentRoot = state.previousRoot;
|
|
1163
|
-
while (parentRoot) {
|
|
1164
|
-
const parentState = parentRoot.getState();
|
|
1165
|
-
if (parentState?.internal) parentState.internal.priority++;
|
|
1166
|
-
parentRoot = parentState?.previousRoot;
|
|
1167
|
-
}
|
|
1168
|
-
notifyDepreciated({
|
|
1169
|
-
heading: "useFrame with numeric priority is deprecated",
|
|
1170
|
-
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 })',
|
|
1171
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
const wrappedCallback = (frameState, delta) => {
|
|
1175
|
-
const localState = store.getState();
|
|
1176
|
-
const mergedState = {
|
|
1177
|
-
...localState,
|
|
1178
|
-
time: frameState.time,
|
|
1179
|
-
delta: frameState.delta,
|
|
1180
|
-
elapsed: frameState.elapsed,
|
|
1181
|
-
frame: frameState.frame
|
|
1182
|
-
};
|
|
1183
|
-
callbackRef.current?.(mergedState, delta);
|
|
1184
|
-
};
|
|
1185
|
-
const unregister = scheduler.register(wrappedCallback, {
|
|
1186
|
-
id,
|
|
1187
|
-
rootId,
|
|
1188
|
-
...options
|
|
1189
|
-
});
|
|
1190
|
-
return () => {
|
|
1191
|
-
unregister();
|
|
1192
|
-
if (isLegacyPriority) {
|
|
1193
|
-
const currentState = store.getState();
|
|
1194
|
-
if (currentState.internal) {
|
|
1195
|
-
currentState.internal.priority--;
|
|
1196
|
-
let parentRoot = currentState.previousRoot;
|
|
1197
|
-
while (parentRoot) {
|
|
1198
|
-
const parentState = parentRoot.getState();
|
|
1199
|
-
if (parentState?.internal) parentState.internal.priority--;
|
|
1200
|
-
parentRoot = parentState?.previousRoot;
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
} else {
|
|
1206
|
-
return scheduler.register(
|
|
1207
|
-
(state, delta) => {
|
|
1208
|
-
const frameState = state;
|
|
1209
|
-
if (!frameState.renderer) return;
|
|
1210
|
-
callbackRef.current?.(frameState, delta);
|
|
1211
|
-
},
|
|
1212
|
-
{ id, ...options }
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
1216
|
-
const isPaused = React.useSyncExternalStore(
|
|
1217
|
-
// Subscribe function
|
|
1218
|
-
React.useCallback(
|
|
1219
|
-
(onStoreChange) => {
|
|
1220
|
-
return getScheduler().subscribeJobState(id, onStoreChange);
|
|
1221
|
-
},
|
|
1222
|
-
[id]
|
|
1223
|
-
),
|
|
1224
|
-
// getSnapshot function
|
|
1225
|
-
React.useCallback(() => getScheduler().isJobPaused(id), [id]),
|
|
1226
|
-
// getServerSnapshot function (SSR)
|
|
1227
|
-
React.useCallback(() => false, [])
|
|
1228
|
-
);
|
|
1229
|
-
const controls = React.useMemo(() => {
|
|
1230
|
-
const scheduler2 = getScheduler();
|
|
1231
|
-
return {
|
|
1232
|
-
/** The job's unique ID */
|
|
1233
|
-
id,
|
|
1234
|
-
/**
|
|
1235
|
-
* Access to the global scheduler for frame loop control.
|
|
1236
|
-
* Use for controlling the entire frame loop, adding phases, etc.
|
|
1237
|
-
*/
|
|
1238
|
-
scheduler: scheduler2,
|
|
1239
|
-
/**
|
|
1240
|
-
* Manually step this job only.
|
|
1241
|
-
* Bypasses FPS limiting - always runs.
|
|
1242
|
-
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
1243
|
-
*/
|
|
1244
|
-
step: (timestamp) => {
|
|
1245
|
-
getScheduler().stepJob(id, timestamp);
|
|
1246
|
-
},
|
|
1247
|
-
/**
|
|
1248
|
-
* Manually step ALL jobs in the scheduler.
|
|
1249
|
-
* Useful for frameloop='never' mode.
|
|
1250
|
-
* @param timestamp Optional timestamp (defaults to performance.now())
|
|
1251
|
-
*/
|
|
1252
|
-
stepAll: (timestamp) => {
|
|
1253
|
-
getScheduler().step(timestamp);
|
|
1254
|
-
},
|
|
1255
|
-
/**
|
|
1256
|
-
* Pause this job (set enabled=false).
|
|
1257
|
-
* Job remains registered but won't run.
|
|
1258
|
-
*/
|
|
1259
|
-
pause: () => {
|
|
1260
|
-
getScheduler().pauseJob(id);
|
|
1261
|
-
},
|
|
1262
|
-
/**
|
|
1263
|
-
* Resume this job (set enabled=true).
|
|
1264
|
-
*/
|
|
1265
|
-
resume: () => {
|
|
1266
|
-
getScheduler().resumeJob(id);
|
|
1267
|
-
},
|
|
1268
|
-
/**
|
|
1269
|
-
* Reactive paused state - automatically updates when pause/resume is called.
|
|
1270
|
-
* No need for forceUpdate() in your components.
|
|
1271
|
-
*/
|
|
1272
|
-
isPaused
|
|
1273
|
-
};
|
|
1274
|
-
}, [id, isPaused]);
|
|
1275
|
-
return controls;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
const IsObject = (url) => url === Object(url) && !Array.isArray(url) && typeof url !== "function";
|
|
1279
|
-
function getUrls(input) {
|
|
1280
|
-
if (typeof input === "string") return [input];
|
|
1281
|
-
if (Array.isArray(input)) return input;
|
|
1282
|
-
return Object.values(input);
|
|
1283
|
-
}
|
|
1284
|
-
function allUrlsCached(urls, textureCache) {
|
|
1285
|
-
return urls.every((url) => textureCache.has(url));
|
|
1286
|
-
}
|
|
1287
|
-
function buildFromCache(input, textureCache) {
|
|
1288
|
-
if (typeof input === "string") {
|
|
1289
|
-
return textureCache.get(input);
|
|
1290
|
-
} else if (Array.isArray(input)) {
|
|
1291
|
-
return input.map((url) => textureCache.get(url));
|
|
1292
|
-
} else {
|
|
1293
|
-
const result = {};
|
|
1294
|
-
const objInput = input;
|
|
1295
|
-
for (const key in objInput) {
|
|
1296
|
-
result[key] = textureCache.get(objInput[key]);
|
|
1297
|
-
}
|
|
1298
|
-
return result;
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
function useTexture(input, optionsOrOnLoad) {
|
|
1302
|
-
const renderer = useThree((state) => state.internal.actualRenderer);
|
|
1303
|
-
const store = useStore();
|
|
1304
|
-
const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
|
|
1305
|
-
const { onLoad, cache = true } = options;
|
|
1306
|
-
const onLoadRef = useRef(onLoad);
|
|
1307
|
-
onLoadRef.current = onLoad;
|
|
1308
|
-
const onLoadCalledForRef = useRef(null);
|
|
1309
|
-
const urls = useMemo(() => getUrls(input), [input]);
|
|
1310
|
-
const cachedResult = useMemo(() => {
|
|
1311
|
-
if (!cache) return null;
|
|
1312
|
-
const textures = store.getState().textures;
|
|
1313
|
-
if (!allUrlsCached(urls, textures)) return null;
|
|
1314
|
-
return buildFromCache(input, textures);
|
|
1315
|
-
}, [cache, urls, input, store]);
|
|
1316
|
-
const loadedTextures = useLoader(
|
|
1317
|
-
TextureLoader,
|
|
1318
|
-
IsObject(input) ? Object.values(input) : input
|
|
1319
|
-
);
|
|
1320
|
-
const inputKey = urls.join("\0");
|
|
1321
|
-
useLayoutEffect(() => {
|
|
1322
|
-
if (cachedResult) return;
|
|
1323
|
-
if (onLoadCalledForRef.current === inputKey) return;
|
|
1324
|
-
onLoadCalledForRef.current = inputKey;
|
|
1325
|
-
onLoadRef.current?.(loadedTextures);
|
|
1326
|
-
}, [cachedResult, loadedTextures, inputKey]);
|
|
1327
|
-
useEffect(() => {
|
|
1328
|
-
if (cachedResult) return;
|
|
1329
|
-
if ("initTexture" in renderer) {
|
|
1330
|
-
let textureArray = [];
|
|
1331
|
-
if (Array.isArray(loadedTextures)) {
|
|
1332
|
-
textureArray = loadedTextures;
|
|
1333
|
-
} else if (loadedTextures instanceof Texture$1) {
|
|
1334
|
-
textureArray = [loadedTextures];
|
|
1335
|
-
} else if (IsObject(loadedTextures)) {
|
|
1336
|
-
textureArray = Object.values(loadedTextures);
|
|
1337
|
-
}
|
|
1338
|
-
textureArray.forEach((texture) => {
|
|
1339
|
-
if (texture instanceof Texture$1) {
|
|
1340
|
-
renderer.initTexture(texture);
|
|
1341
|
-
}
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
}, [renderer, loadedTextures, cachedResult]);
|
|
1345
|
-
const mappedTextures = useMemo(() => {
|
|
1346
|
-
if (cachedResult) return cachedResult;
|
|
1347
|
-
if (IsObject(input)) {
|
|
1348
|
-
const keyed = {};
|
|
1349
|
-
const textureArray = loadedTextures;
|
|
1350
|
-
let i = 0;
|
|
1351
|
-
for (const key in input) keyed[key] = textureArray[i++];
|
|
1352
|
-
return keyed;
|
|
1353
|
-
} else {
|
|
1354
|
-
return loadedTextures;
|
|
1355
|
-
}
|
|
1356
|
-
}, [input, loadedTextures, cachedResult]);
|
|
1357
|
-
useEffect(() => {
|
|
1358
|
-
if (!cache) return;
|
|
1359
|
-
const urlTextureMap = [];
|
|
1360
|
-
if (typeof input === "string") {
|
|
1361
|
-
urlTextureMap.push([input, mappedTextures]);
|
|
1362
|
-
} else if (Array.isArray(input)) {
|
|
1363
|
-
const textureArray = mappedTextures;
|
|
1364
|
-
input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
|
|
1365
|
-
} else if (IsObject(input)) {
|
|
1366
|
-
const textureRecord = mappedTextures;
|
|
1367
|
-
for (const key in input) {
|
|
1368
|
-
const url = input[key];
|
|
1369
|
-
urlTextureMap.push([url, textureRecord[key]]);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
store.setState((state) => {
|
|
1373
|
-
const refs = new Map(state._textureRefs);
|
|
1374
|
-
let textures = state.textures;
|
|
1375
|
-
let added = false;
|
|
1376
|
-
for (const [url, texture] of urlTextureMap) {
|
|
1377
|
-
if (!textures.has(url)) {
|
|
1378
|
-
if (!added) {
|
|
1379
|
-
textures = new Map(textures);
|
|
1380
|
-
added = true;
|
|
1381
|
-
}
|
|
1382
|
-
textures.set(url, texture);
|
|
1383
|
-
}
|
|
1384
|
-
refs.set(url, (refs.get(url) ?? 0) + 1);
|
|
1385
|
-
}
|
|
1386
|
-
return added ? { textures, _textureRefs: refs } : { _textureRefs: refs };
|
|
1387
|
-
});
|
|
1388
|
-
return () => store.setState((state) => {
|
|
1389
|
-
const refs = new Map(state._textureRefs);
|
|
1390
|
-
for (const [url] of urlTextureMap) {
|
|
1391
|
-
const next = (refs.get(url) ?? 0) - 1;
|
|
1392
|
-
if (next <= 0) refs.delete(url);
|
|
1393
|
-
else refs.set(url, next);
|
|
1394
|
-
}
|
|
1395
|
-
return { _textureRefs: refs };
|
|
1396
|
-
});
|
|
1397
|
-
}, [cache, input, mappedTextures, store]);
|
|
1398
|
-
return mappedTextures;
|
|
1399
|
-
}
|
|
1400
|
-
useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
|
|
1401
|
-
useTexture.clear = (input) => useLoader.clear(TextureLoader, input);
|
|
1402
|
-
const Texture = ({
|
|
1403
|
-
children,
|
|
1404
|
-
input,
|
|
1405
|
-
onLoad,
|
|
1406
|
-
cache
|
|
1407
|
-
}) => {
|
|
1408
|
-
const options = typeof onLoad === "function" ? { onLoad, cache } : { ...onLoad, cache };
|
|
1409
|
-
const ret = useTexture(input, options);
|
|
1410
|
-
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) });
|
|
1411
1170
|
};
|
|
1412
1171
|
|
|
1413
1172
|
function useTextures(selector) {
|
|
@@ -1557,7 +1316,7 @@ function useEnvironment({
|
|
|
1557
1316
|
texture = texture.renderTarget?.texture;
|
|
1558
1317
|
}
|
|
1559
1318
|
texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
|
|
1560
|
-
texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
|
|
1319
|
+
texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
|
|
1561
1320
|
return texture;
|
|
1562
1321
|
}
|
|
1563
1322
|
const preloadDefaultOptions = {
|
|
@@ -1610,22 +1369,15 @@ function getExtension(files) {
|
|
|
1610
1369
|
const isCubemap = isArray(files) && files.length === 6;
|
|
1611
1370
|
const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
|
|
1612
1371
|
const firstEntry = isArray(files) ? files[0] : files;
|
|
1613
|
-
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;
|
|
1614
1374
|
return { extension, isCubemap, isGainmap };
|
|
1615
1375
|
}
|
|
1616
1376
|
function getLoader(extension) {
|
|
1617
|
-
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;
|
|
1618
1378
|
return loader;
|
|
1619
1379
|
}
|
|
1620
1380
|
|
|
1621
|
-
function useStore() {
|
|
1622
|
-
const store = useContext(context);
|
|
1623
|
-
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
1624
|
-
return store;
|
|
1625
|
-
}
|
|
1626
|
-
function useThree(selector = (state) => state, equalityFn) {
|
|
1627
|
-
return useStore()(selector, equalityFn);
|
|
1628
|
-
}
|
|
1629
1381
|
function useInstanceHandle(ref) {
|
|
1630
1382
|
const instance = useRef(null);
|
|
1631
1383
|
useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
@@ -1665,13 +1417,19 @@ function addTail(callback) {
|
|
|
1665
1417
|
return getScheduler().onIdle(callback);
|
|
1666
1418
|
}
|
|
1667
1419
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
1668
|
-
|
|
1420
|
+
const rootId = state?.internal?.rootId;
|
|
1421
|
+
const scheduler = getScheduler();
|
|
1422
|
+
if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
|
|
1423
|
+
else scheduler.invalidate(frames, stackFrames);
|
|
1669
1424
|
}
|
|
1670
|
-
function advance(timestamp) {
|
|
1671
|
-
|
|
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);
|
|
1672
1430
|
}
|
|
1673
1431
|
|
|
1674
|
-
const version = "10.0.0-alpha.
|
|
1432
|
+
const version = "10.0.0-alpha.5";
|
|
1675
1433
|
const packageData = {
|
|
1676
1434
|
version: version};
|
|
1677
1435
|
|
|
@@ -13530,7 +13288,10 @@ function extend(objects) {
|
|
|
13530
13288
|
catalogue[Component] = objects;
|
|
13531
13289
|
return Component;
|
|
13532
13290
|
} else {
|
|
13533
|
-
|
|
13291
|
+
for (const name in objects) {
|
|
13292
|
+
const object = objects[name];
|
|
13293
|
+
if (isConstructor(object)) catalogue[name] = object;
|
|
13294
|
+
}
|
|
13534
13295
|
}
|
|
13535
13296
|
}
|
|
13536
13297
|
function validateInstance(type, props) {
|
|
@@ -13878,7 +13639,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
13878
13639
|
getRootHostContext: () => NO_CONTEXT,
|
|
13879
13640
|
getChildHostContext: () => NO_CONTEXT,
|
|
13880
13641
|
commitUpdate(instance, type, oldProps, newProps, fiber) {
|
|
13881
|
-
validateInstance(type, newProps);
|
|
13642
|
+
validateInstance(instance.type, newProps);
|
|
13882
13643
|
let reconstruct = false;
|
|
13883
13644
|
if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
|
|
13884
13645
|
else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
|
|
@@ -13893,162 +13654,491 @@ const reconciler = /* @__PURE__ */ createReconciler({
|
|
|
13893
13654
|
else delete instance.props.attach;
|
|
13894
13655
|
if (Object.keys(changedProps).length) applyProps(instance.object, changedProps);
|
|
13895
13656
|
}
|
|
13896
|
-
},
|
|
13897
|
-
finalizeInitialChildren: (instance) => {
|
|
13898
|
-
for (const prop in instance.props) {
|
|
13899
|
-
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;
|
|
13900
14075
|
}
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
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);
|
|
13910
14086
|
}
|
|
13911
14087
|
}
|
|
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
|
-
|
|
13964
|
-
),
|
|
13965
|
-
|
|
13966
|
-
currentUpdatePriority = newPriority;
|
|
13967
|
-
},
|
|
13968
|
-
getCurrentUpdatePriority() {
|
|
13969
|
-
return currentUpdatePriority;
|
|
13970
|
-
},
|
|
13971
|
-
resolveUpdatePriority() {
|
|
13972
|
-
if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
|
|
13973
|
-
const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
|
|
13974
|
-
if (eventType === void 0) return r;
|
|
13975
|
-
return getEventPriority(eventType);
|
|
13976
|
-
},
|
|
13977
|
-
resetFormInstance() {
|
|
13978
|
-
},
|
|
13979
|
-
// @ts-ignore DefinitelyTyped is not up to date
|
|
13980
|
-
rendererPackageName: "@react-three/fiber",
|
|
13981
|
-
rendererVersion: packageData.version,
|
|
13982
|
-
// 19.2 Reconciler
|
|
13983
|
-
// https://github.com/facebook/react/pull/31975
|
|
13984
|
-
// https://github.com/facebook/react/pull/31999
|
|
13985
|
-
applyViewTransitionName(_instance, _name, _className) {
|
|
13986
|
-
},
|
|
13987
|
-
restoreViewTransitionName(_instance, _props) {
|
|
13988
|
-
},
|
|
13989
|
-
cancelViewTransitionName(_instance, _name, _props) {
|
|
13990
|
-
},
|
|
13991
|
-
cancelRootViewTransitionName(_rootContainer) {
|
|
13992
|
-
},
|
|
13993
|
-
restoreRootViewTransitionName(_rootContainer) {
|
|
13994
|
-
},
|
|
13995
|
-
InstanceMeasurement: null,
|
|
13996
|
-
measureInstance: (_instance) => null,
|
|
13997
|
-
wasInstanceInViewport: (_measurement) => true,
|
|
13998
|
-
hasInstanceChanged: (_oldMeasurement, _newMeasurement) => false,
|
|
13999
|
-
hasInstanceAffectedParent: (_oldMeasurement, _newMeasurement) => false,
|
|
14000
|
-
// https://github.com/facebook/react/pull/32002
|
|
14001
|
-
// https://github.com/facebook/react/pull/34486
|
|
14002
|
-
suspendOnActiveViewTransition(_state, _container) {
|
|
14003
|
-
},
|
|
14004
|
-
// https://github.com/facebook/react/pull/32451
|
|
14005
|
-
// https://github.com/facebook/react/pull/32760
|
|
14006
|
-
startGestureTransition: () => null,
|
|
14007
|
-
startViewTransition: () => null,
|
|
14008
|
-
stopViewTransition(_transition) {
|
|
14009
|
-
},
|
|
14010
|
-
// https://github.com/facebook/react/pull/32038
|
|
14011
|
-
createViewTransitionInstance: (_name) => null,
|
|
14012
|
-
// https://github.com/facebook/react/pull/32379
|
|
14013
|
-
// https://github.com/facebook/react/pull/32786
|
|
14014
|
-
getCurrentGestureOffset(_provider) {
|
|
14015
|
-
throw new Error("startGestureTransition is not yet supported in react-three-fiber.");
|
|
14016
|
-
},
|
|
14017
|
-
// https://github.com/facebook/react/pull/32500
|
|
14018
|
-
cloneMutableInstance(instance, _keepChildren) {
|
|
14019
|
-
return instance;
|
|
14020
|
-
},
|
|
14021
|
-
cloneMutableTextInstance(textInstance) {
|
|
14022
|
-
return textInstance;
|
|
14023
|
-
},
|
|
14024
|
-
// React 19.2 added these for root-level view transitions. They only run for hosts that opt into
|
|
14025
|
-
// view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
|
|
14026
|
-
// into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
|
|
14027
|
-
// React never calls them. They stay as no-ops rather than throws: the host config is shared by
|
|
14028
|
-
// every root, and a throw here would turn a React-internal probe into a hard crash for users.
|
|
14029
|
-
// Guarded by a test asserting a full render + unmount never reaches them.
|
|
14030
|
-
cloneRootViewTransitionContainer(_rootContainer) {
|
|
14031
|
-
},
|
|
14032
|
-
removeRootViewTransitionClone(_rootContainer, _clone) {
|
|
14033
|
-
},
|
|
14034
|
-
// https://github.com/facebook/react/pull/32465
|
|
14035
|
-
createFragmentInstance: (_fiber) => null,
|
|
14036
|
-
updateFragmentInstanceFiber(_fiber, _instance) {
|
|
14037
|
-
},
|
|
14038
|
-
commitNewChildToFragmentInstance(_child, _fragmentInstance) {
|
|
14039
|
-
},
|
|
14040
|
-
deleteChildFromFragmentInstance(_child, _fragmentInstance) {
|
|
14041
|
-
},
|
|
14042
|
-
// https://github.com/facebook/react/pull/32653
|
|
14043
|
-
measureClonedInstance: (_instance) => null,
|
|
14044
|
-
// https://github.com/facebook/react/pull/32819
|
|
14045
|
-
maySuspendCommitOnUpdate: (_type, _oldProps, _newProps) => false,
|
|
14046
|
-
maySuspendCommitInSyncRender: (_type, _props) => false,
|
|
14047
|
-
// https://github.com/facebook/react/pull/34486
|
|
14048
|
-
startSuspendingCommit: () => null,
|
|
14049
|
-
// https://github.com/facebook/react/pull/34522
|
|
14050
|
-
getSuspendedCommitReason: (_state, _rootContainer) => null
|
|
14051
|
-
});
|
|
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
|
+
}
|
|
14052
14142
|
|
|
14053
14143
|
const isRenderer = (def) => !!def?.render;
|
|
14054
14144
|
const _roots = /* @__PURE__ */ new Map();
|
|
@@ -14205,9 +14295,8 @@ function createRoot(canvas) {
|
|
|
14205
14295
|
if (!renderer.hasInitialized?.()) {
|
|
14206
14296
|
const size2 = computeInitialSize(canvas, propsSize);
|
|
14207
14297
|
if (size2.width > 0 && size2.height > 0) {
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
canvas.height = size2.height * pixelRatio;
|
|
14298
|
+
renderer.setPixelRatio(calculateDpr(dpr));
|
|
14299
|
+
renderer.setSize(size2.width, size2.height, false);
|
|
14211
14300
|
}
|
|
14212
14301
|
await renderer.init();
|
|
14213
14302
|
}
|
|
@@ -14216,7 +14305,7 @@ function createRoot(canvas) {
|
|
|
14216
14305
|
state.internal.actualRenderer = renderer;
|
|
14217
14306
|
state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
|
|
14218
14307
|
if (canvasId && !state.internal.isSecondary) {
|
|
14219
|
-
const canvasTarget =
|
|
14308
|
+
const canvasTarget = renderer.getCanvasTarget?.();
|
|
14220
14309
|
const unregisterPrimary = registerPrimary(canvasId, renderer, store);
|
|
14221
14310
|
state.set((prev) => ({
|
|
14222
14311
|
internal: {
|
|
@@ -14330,7 +14419,7 @@ function createRoot(canvas) {
|
|
|
14330
14419
|
const handleXRFrame = (timestamp, _frame) => {
|
|
14331
14420
|
const state2 = store.getState();
|
|
14332
14421
|
if (state2.frameloop === "never") return;
|
|
14333
|
-
advance(timestamp);
|
|
14422
|
+
advance(timestamp, true, state2);
|
|
14334
14423
|
};
|
|
14335
14424
|
const actualRenderer = state.internal.actualRenderer;
|
|
14336
14425
|
const handleSessionChange = () => {
|
|
@@ -14338,7 +14427,7 @@ function createRoot(canvas) {
|
|
|
14338
14427
|
const renderer2 = state2.internal.actualRenderer;
|
|
14339
14428
|
actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
|
|
14340
14429
|
renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
|
|
14341
|
-
if (!renderer2.xr.isPresenting) invalidate();
|
|
14430
|
+
if (!renderer2.xr.isPresenting) invalidate(state2);
|
|
14342
14431
|
};
|
|
14343
14432
|
const xr = {
|
|
14344
14433
|
connect() {
|
|
@@ -14420,14 +14509,23 @@ function createRoot(canvas) {
|
|
|
14420
14509
|
const newRootId = canvasId || scheduler.generateRootId();
|
|
14421
14510
|
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14422
14511
|
getState: () => store.getState(),
|
|
14423
|
-
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
|
|
14424
14517
|
});
|
|
14425
14518
|
const unregisterCanvasTarget = scheduler.register(
|
|
14426
14519
|
() => {
|
|
14427
14520
|
const state2 = store.getState();
|
|
14428
|
-
|
|
14521
|
+
const canvasTarget = state2.internal.canvasTarget;
|
|
14522
|
+
if (canvasTarget) {
|
|
14429
14523
|
const renderer2 = state2.internal.actualRenderer;
|
|
14430
|
-
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
|
+
}
|
|
14431
14529
|
}
|
|
14432
14530
|
},
|
|
14433
14531
|
{
|
|
@@ -14483,7 +14581,8 @@ function createRoot(canvas) {
|
|
|
14483
14581
|
const userHandlesRender = scheduler.hasUserJobsInPhase("render", newRootId);
|
|
14484
14582
|
if (userHandlesRender || state2.internal.priority) return;
|
|
14485
14583
|
try {
|
|
14486
|
-
|
|
14584
|
+
const renderOverride = state2.internal.renderOverride;
|
|
14585
|
+
if (renderOverride) renderOverride();
|
|
14487
14586
|
else if (renderer2?.render) renderer2.render(state2.scene, state2.camera);
|
|
14488
14587
|
} catch (error) {
|
|
14489
14588
|
state2.setError(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -14496,8 +14595,7 @@ function createRoot(canvas) {
|
|
|
14496
14595
|
phase: "render",
|
|
14497
14596
|
system: true,
|
|
14498
14597
|
// Internal flag: this is a system job, not user-controlled
|
|
14499
|
-
//
|
|
14500
|
-
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
14598
|
+
// FPS throttles only the default render job; Canvas ordering belongs to the root.
|
|
14501
14599
|
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14502
14600
|
}
|
|
14503
14601
|
);
|
|
@@ -14516,8 +14614,22 @@ function createRoot(canvas) {
|
|
|
14516
14614
|
scheduler
|
|
14517
14615
|
}
|
|
14518
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
|
+
}
|
|
14519
14628
|
}
|
|
14520
|
-
|
|
14629
|
+
lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
|
|
14630
|
+
lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
|
|
14631
|
+
lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
|
|
14632
|
+
attachRootExtensions(store);
|
|
14521
14633
|
onCreated = onCreatedCallback;
|
|
14522
14634
|
configured = true;
|
|
14523
14635
|
resolve();
|
|
@@ -14559,18 +14671,24 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14559
14671
|
const fiber = root?.fiber;
|
|
14560
14672
|
if (fiber) {
|
|
14561
14673
|
const state = root?.store.getState();
|
|
14562
|
-
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
|
+
}
|
|
14563
14682
|
reconciler.updateContainer(null, fiber, null, () => {
|
|
14683
|
+
if (root?.store) detachRootExtensions(root.store);
|
|
14564
14684
|
if (state) {
|
|
14565
14685
|
setTimeout(() => {
|
|
14566
14686
|
try {
|
|
14567
14687
|
const renderer = state.internal.actualRenderer;
|
|
14568
|
-
const unregisterRoot = state.internal.unregisterRoot;
|
|
14569
|
-
if (unregisterRoot) unregisterRoot();
|
|
14570
14688
|
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
14571
14689
|
if (unregisterPrimary) unregisterPrimary();
|
|
14572
14690
|
const canvasTarget = state.internal.canvasTarget;
|
|
14573
|
-
if (canvasTarget?.dispose) canvasTarget.dispose();
|
|
14691
|
+
if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
|
|
14574
14692
|
state.events.disconnect?.();
|
|
14575
14693
|
cleanupHelperGroup(root.store);
|
|
14576
14694
|
if (state.isLegacy && renderer) {
|
|
@@ -14640,11 +14758,24 @@ function PortalInner({ state = {}, children, container }) {
|
|
|
14640
14758
|
}
|
|
14641
14759
|
};
|
|
14642
14760
|
}, [portalScene, container, injectScene]);
|
|
14761
|
+
const lastRootState = useRef(null);
|
|
14643
14762
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
14763
|
+
const followed = {};
|
|
14764
|
+
const lastRoot = lastRootState.current;
|
|
14765
|
+
if (lastRoot) {
|
|
14766
|
+
for (const key in rootState) {
|
|
14767
|
+
const field = key;
|
|
14768
|
+
const changed = rootState[field] !== lastRoot[field];
|
|
14769
|
+
const inherited = !(key in rest) && injectState[field] === lastRoot[field];
|
|
14770
|
+
if (changed && inherited) followed[field] = rootState[field];
|
|
14771
|
+
}
|
|
14772
|
+
}
|
|
14773
|
+
lastRootState.current = rootState;
|
|
14644
14774
|
const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
|
|
14645
14775
|
let viewport = void 0;
|
|
14646
|
-
|
|
14647
|
-
|
|
14776
|
+
const portalCamera = followed.camera ?? injectState.camera;
|
|
14777
|
+
if (portalCamera && (size || injectState.size)) {
|
|
14778
|
+
const camera = portalCamera;
|
|
14648
14779
|
viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
|
|
14649
14780
|
if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
|
|
14650
14781
|
}
|
|
@@ -14653,6 +14784,7 @@ function PortalInner({ state = {}, children, container }) {
|
|
|
14653
14784
|
// The intersect consists of the previous root state
|
|
14654
14785
|
...rootState,
|
|
14655
14786
|
...injectState,
|
|
14787
|
+
...followed,
|
|
14656
14788
|
// Portals have their own scene - always a real THREE.Scene (injected if needed)
|
|
14657
14789
|
scene: portalScene,
|
|
14658
14790
|
// rootScene always points to the actual THREE.Scene, even inside portals
|
|
@@ -14711,16 +14843,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
|
|
|
14711
14843
|
const oldbg = target.background;
|
|
14712
14844
|
const oldenv = target.environment;
|
|
14713
14845
|
const oldSceneProps = {
|
|
14714
|
-
// @ts-ignore
|
|
14715
14846
|
backgroundBlurriness: target.backgroundBlurriness,
|
|
14716
|
-
// @ts-ignore
|
|
14717
14847
|
backgroundIntensity: target.backgroundIntensity,
|
|
14718
|
-
|
|
14719
|
-
backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
|
|
14720
|
-
// @ts-ignore
|
|
14848
|
+
backgroundRotation: target.backgroundRotation.clone(),
|
|
14721
14849
|
environmentIntensity: target.environmentIntensity,
|
|
14722
|
-
|
|
14723
|
-
environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
|
|
14850
|
+
environmentRotation: target.environmentRotation.clone()
|
|
14724
14851
|
};
|
|
14725
14852
|
if (background !== "only") target.environment = texture;
|
|
14726
14853
|
if (background) target.background = texture;
|
|
@@ -14945,38 +15072,6 @@ function parseRendererConfig(rendererProp) {
|
|
|
14945
15072
|
return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
|
|
14946
15073
|
}
|
|
14947
15074
|
|
|
14948
|
-
const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
|
|
14949
|
-
const registries = /* @__PURE__ */ new WeakMap();
|
|
14950
|
-
function getKindRegistry(store, kind) {
|
|
14951
|
-
let registry = registries.get(store);
|
|
14952
|
-
if (!registry) {
|
|
14953
|
-
registry = {
|
|
14954
|
-
nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14955
|
-
uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14956
|
-
buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
|
|
14957
|
-
gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
|
|
14958
|
-
};
|
|
14959
|
-
registries.set(store, registry);
|
|
14960
|
-
}
|
|
14961
|
-
return registry[kind];
|
|
14962
|
-
}
|
|
14963
|
-
function invalidateResource(store, kind, scopeKey) {
|
|
14964
|
-
const registry = getKindRegistry(store, kind);
|
|
14965
|
-
{
|
|
14966
|
-
registry.valid.clear();
|
|
14967
|
-
registry.staged.clear();
|
|
14968
|
-
}
|
|
14969
|
-
}
|
|
14970
|
-
function invalidateAllResources(store) {
|
|
14971
|
-
for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
|
|
14972
|
-
}
|
|
14973
|
-
|
|
14974
|
-
function clearHmrCaches(store) {
|
|
14975
|
-
const primary = store.getState().primaryStore ?? store;
|
|
14976
|
-
invalidateAllResources(primary);
|
|
14977
|
-
primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
|
|
14978
|
-
}
|
|
14979
|
-
|
|
14980
15075
|
function CanvasImpl({
|
|
14981
15076
|
ref,
|
|
14982
15077
|
children,
|
|
@@ -15014,9 +15109,9 @@ function CanvasImpl({
|
|
|
15014
15109
|
React.useMemo(() => extend(THREE), []);
|
|
15015
15110
|
const Bridge = useBridge();
|
|
15016
15111
|
const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
|
|
15017
|
-
const
|
|
15112
|
+
const [hasInitialSize, setHasInitialSize] = React.useState(false);
|
|
15018
15113
|
const measureConfig = React.useMemo(() => {
|
|
15019
|
-
if (!
|
|
15114
|
+
if (!hasInitialSize) {
|
|
15020
15115
|
return {
|
|
15021
15116
|
...resize,
|
|
15022
15117
|
scroll: resize?.scroll ?? true,
|
|
@@ -15028,7 +15123,7 @@ function CanvasImpl({
|
|
|
15028
15123
|
debounce: { scroll: 50, resize: 0 },
|
|
15029
15124
|
...resize
|
|
15030
15125
|
};
|
|
15031
|
-
}, [
|
|
15126
|
+
}, [hasInitialSize, resize]);
|
|
15032
15127
|
const [containerRef, containerRect] = useMeasure(measureConfig);
|
|
15033
15128
|
const effectiveSize = React.useMemo(() => {
|
|
15034
15129
|
let w = width ?? containerRect.width;
|
|
@@ -15044,9 +15139,7 @@ function CanvasImpl({
|
|
|
15044
15139
|
left: containerRect.left
|
|
15045
15140
|
};
|
|
15046
15141
|
}, [width, height, containerRect, forceEven]);
|
|
15047
|
-
if (!
|
|
15048
|
-
hasInitialSizeRef.current = true;
|
|
15049
|
-
}
|
|
15142
|
+
if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
|
|
15050
15143
|
const canvasRef = React.useRef(null);
|
|
15051
15144
|
const divRef = React.useRef(null);
|
|
15052
15145
|
React.useImperativeHandle(ref, () => canvasRef.current);
|
|
@@ -15160,6 +15253,7 @@ function CanvasImpl({
|
|
|
15160
15253
|
const canvas = canvasRef.current;
|
|
15161
15254
|
if (canvas) {
|
|
15162
15255
|
return () => {
|
|
15256
|
+
if (canvas.isConnected) return;
|
|
15163
15257
|
unmountComponentAtNode(canvas);
|
|
15164
15258
|
root.current = null;
|
|
15165
15259
|
};
|
|
@@ -15172,7 +15266,7 @@ function CanvasImpl({
|
|
|
15172
15266
|
const handleHMR = () => {
|
|
15173
15267
|
queueMicrotask(() => {
|
|
15174
15268
|
const rootEntry = _roots.get(canvas);
|
|
15175
|
-
if (rootEntry?.store)
|
|
15269
|
+
if (rootEntry?.store) notifyRootExtensionsHmr(rootEntry.store);
|
|
15176
15270
|
});
|
|
15177
15271
|
};
|
|
15178
15272
|
if (typeof import.meta !== "undefined" && import.meta.hot) {
|
|
@@ -15224,4 +15318,4 @@ function Canvas(props) {
|
|
|
15224
15318
|
|
|
15225
15319
|
extend(THREE);
|
|
15226
15320
|
|
|
15227
|
-
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 };
|
|
15321
|
+
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 };
|