mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-03-28 00:29:38 +00:00
add FPS limit option
This commit is contained in:
parent
ceb9fea9f4
commit
4565f88210
36 changed files with 135 additions and 58 deletions
|
|
@ -70,6 +70,8 @@ export const InspectorControls = memo(function InspectorControls({
|
|||
setAudioVolume,
|
||||
animationEnabled,
|
||||
setAnimationEnabled,
|
||||
fpsLimit,
|
||||
setFpsLimit,
|
||||
} = useSettings();
|
||||
const {
|
||||
speedMultiplier,
|
||||
|
|
@ -377,6 +379,28 @@ export const InspectorControls = memo(function InspectorControls({
|
|||
Enable animations
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.Field}>
|
||||
<label htmlFor="fpsLimitInput">FPS limit</label>
|
||||
<div className={styles.Control}>
|
||||
<select
|
||||
id="fpsLimitInput"
|
||||
value={fpsLimit ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setFpsLimit(val === "" ? null : parseInt(val));
|
||||
}}
|
||||
>
|
||||
<option value="30">30</option>
|
||||
<option value="60">60</option>
|
||||
<option value="120">120</option>
|
||||
<option value="144">144</option>
|
||||
<option value="">No limit</option>
|
||||
</select>
|
||||
</div>
|
||||
<p className={styles.Description}>
|
||||
Give your device a break by capping the framerate.
|
||||
</p>
|
||||
</div>
|
||||
</Accordion>
|
||||
<Accordion value="debug" label="Debug">
|
||||
<div className={styles.CheckboxField}>
|
||||
|
|
|
|||
37
src/components/LimitFPS.tsx
Normal file
37
src/components/LimitFPS.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
|
||||
function useFPSLimit() {
|
||||
const { fpsLimit } = useSettings();
|
||||
const invalidate = useThree((state) => state.invalidate);
|
||||
|
||||
useEffect(() => {
|
||||
if (fpsLimit == null) return;
|
||||
|
||||
const interval = 1000 / fpsLimit;
|
||||
let lastTime = 0;
|
||||
let rafId: number;
|
||||
|
||||
function tick(time: number) {
|
||||
rafId = requestAnimationFrame(tick);
|
||||
if (time - lastTime >= interval) {
|
||||
// Snap lastTime forward to avoid drift accumulation
|
||||
lastTime = time - ((time - lastTime) % interval);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
rafId = requestAnimationFrame(tick);
|
||||
|
||||
return () => cancelAnimationFrame(rafId);
|
||||
}, [fpsLimit, invalidate]);
|
||||
|
||||
return fpsLimit;
|
||||
}
|
||||
|
||||
export function LimitFPS() {
|
||||
useFPSLimit();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -42,6 +42,8 @@ type SettingsContext = {
|
|||
setAudioVolume: StateSetter<number>;
|
||||
sidebarOpen: boolean;
|
||||
setSidebarOpen: StateSetter<boolean>;
|
||||
fpsLimit: number | null;
|
||||
setFpsLimit: StateSetter<number | null>;
|
||||
};
|
||||
|
||||
type DebugContext = {
|
||||
|
|
@ -86,6 +88,7 @@ type PersistedSettings = {
|
|||
invertDrag?: boolean;
|
||||
invertJoystick?: boolean;
|
||||
sidebarOpen?: boolean;
|
||||
fpsLimit?: number | null;
|
||||
};
|
||||
|
||||
export function useSettings() {
|
||||
|
|
@ -136,6 +139,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
const [invertDrag, setInvertDrag] = useState(false);
|
||||
const [invertJoystick, setInvertJoystick] = useState(false);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [fpsLimit, setFpsLimit] = useState<number | null>(null);
|
||||
const [renderOnDemand, setRenderOnDemand] = useState(false);
|
||||
|
||||
const [fogEnabledOverride, setFogEnabledOverride] = useFogQueryState();
|
||||
|
|
@ -170,6 +174,8 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
setAudioVolume,
|
||||
sidebarOpen,
|
||||
setSidebarOpen,
|
||||
fpsLimit,
|
||||
setFpsLimit,
|
||||
}),
|
||||
[
|
||||
fogEnabled,
|
||||
|
|
@ -183,6 +189,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
warriorName,
|
||||
audioVolume,
|
||||
sidebarOpen,
|
||||
fpsLimit,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -288,6 +295,9 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
if (savedSettings.invertJoystick != null) {
|
||||
setInvertJoystick(savedSettings.invertJoystick);
|
||||
}
|
||||
if (savedSettings.fpsLimit === null || Number.isInteger(savedSettings.fpsLimit)) {
|
||||
setFpsLimit(savedSettings.fpsLimit!);
|
||||
}
|
||||
if (savedSettings.sidebarOpen != null) {
|
||||
// Don't restore on touch devices!
|
||||
if (!isTouch) {
|
||||
|
|
@ -323,6 +333,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
invertDrag,
|
||||
invertJoystick,
|
||||
sidebarOpen,
|
||||
fpsLimit,
|
||||
};
|
||||
try {
|
||||
localStorage.setItem("settings", JSON.stringify(settingsToSave));
|
||||
|
|
@ -352,6 +363,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
|||
invertDrag,
|
||||
invertJoystick,
|
||||
sidebarOpen,
|
||||
fpsLimit,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { ReactNode, Suspense } from "react";
|
||||
import { Canvas, GLProps, RootState } from "@react-three/fiber";
|
||||
import { NoToneMapping, PCFShadowMap, SRGBColorSpace } from "three";
|
||||
import { useDebug } from "./SettingsProvider";
|
||||
import { useDebug, useSettings } from "./SettingsProvider";
|
||||
import { LimitFPS } from "./LimitFPS";
|
||||
|
||||
export type InvalidateFunction = RootState["invalidate"];
|
||||
|
||||
|
|
@ -26,16 +27,19 @@ export function ThreeCanvas({
|
|||
}) {
|
||||
const { renderOnDemand: renderOnDemandFromSettings } = useDebug();
|
||||
const renderOnDemand = renderOnDemandFromProps || renderOnDemandFromSettings;
|
||||
const { fpsLimit } = useSettings();
|
||||
const fpsLimitActive = fpsLimit != null;
|
||||
|
||||
return (
|
||||
<Canvas
|
||||
frameloop={renderOnDemand ? "demand" : "always"}
|
||||
frameloop={renderOnDemand || fpsLimitActive ? "demand" : "always"}
|
||||
dpr={dprFromProps}
|
||||
gl={glSettings}
|
||||
shadows={{ type: PCFShadowMap }}
|
||||
onCreated={onCreated}
|
||||
>
|
||||
<Suspense>{children}</Suspense>
|
||||
{fpsLimitActive ? <LimitFPS /> : null}
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue