mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-07-13 23:44:52 +00:00
remove unnecessary assets, add setting persistence
This commit is contained in:
parent
6ae7a19332
commit
fc4146c824
70 changed files with 180 additions and 18618 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { getResourceList } from "@/src/manifest";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
|
||||
const excludeMissions = new Set([
|
||||
"SkiFree",
|
||||
|
|
@ -15,14 +16,19 @@ const missions = getResourceList()
|
|||
export function InspectorControls({
|
||||
missionName,
|
||||
onChangeMission,
|
||||
fogEnabled,
|
||||
onChangeFogEnabled,
|
||||
}: {
|
||||
missionName: string;
|
||||
onChangeMission: (name: string) => void;
|
||||
fogEnabled: boolean;
|
||||
onChangeFogEnabled: (enabled: boolean) => void;
|
||||
}) {
|
||||
const {
|
||||
fogEnabled,
|
||||
setFogEnabled,
|
||||
speedMultiplier,
|
||||
setSpeedMultiplier,
|
||||
fov,
|
||||
setFov,
|
||||
} = useSettings();
|
||||
|
||||
return (
|
||||
<div
|
||||
id="controls"
|
||||
|
|
@ -44,11 +50,38 @@ export function InspectorControls({
|
|||
type="checkbox"
|
||||
checked={fogEnabled}
|
||||
onChange={(event) => {
|
||||
onChangeFogEnabled(event.target.checked);
|
||||
setFogEnabled(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="fogInput">Fog?</label>
|
||||
</div>
|
||||
<div className="Field">
|
||||
<label htmlFor="fovInput">FOV</label>
|
||||
<input
|
||||
id="speedInput"
|
||||
type="range"
|
||||
min={75}
|
||||
max={120}
|
||||
step={5}
|
||||
value={fov}
|
||||
onChange={(event) => setFov(parseInt(event.target.value))}
|
||||
/>
|
||||
<output htmlFor="speedInput">{fov}</output>
|
||||
</div>
|
||||
<div className="Field">
|
||||
<label htmlFor="speedInput">Speed</label>
|
||||
<input
|
||||
id="speedInput"
|
||||
type="range"
|
||||
min={0.1}
|
||||
max={5}
|
||||
step={0.05}
|
||||
value={speedMultiplier}
|
||||
onChange={(event) =>
|
||||
setSpeedMultiplier(parseFloat(event.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
10
app/ObserverCamera.tsx
Normal file
10
app/ObserverCamera.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { PerspectiveCamera } from "@react-three/drei";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
|
||||
export function ObserverCamera() {
|
||||
const { fov } = useSettings();
|
||||
|
||||
return (
|
||||
<PerspectiveCamera makeDefault position={[-512, 256, -512]} fov={fov} />
|
||||
);
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import { KeyboardControls } from "@react-three/drei";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
import { PointerLockControls } from "three-stdlib";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
import { Vector3 } from "three";
|
||||
|
||||
enum Controls {
|
||||
forward = "forward",
|
||||
|
|
@ -16,17 +17,19 @@ enum Controls {
|
|||
}
|
||||
|
||||
const BASE_SPEED = 100; // units per second
|
||||
const MIN_SPEED_ADJUSTMENT = 0.05;
|
||||
const MAX_SPEED_ADJUSTMENT = 1;
|
||||
|
||||
function CameraMovement() {
|
||||
const { speedMultiplier, setSpeedMultiplier } = useSettings();
|
||||
const [subscribe, getKeys] = useKeyboardControls<Controls>();
|
||||
const { camera, gl } = useThree();
|
||||
const [speedMultiplier, setSpeedMultiplier] = useState(1);
|
||||
const controlsRef = useRef<PointerLockControls | null>(null);
|
||||
|
||||
// Scratch vectors to avoid allocations each frame
|
||||
const forwardVec = useRef(new THREE.Vector3());
|
||||
const sideVec = useRef(new THREE.Vector3());
|
||||
const moveVec = useRef(new THREE.Vector3());
|
||||
const forwardVec = useRef(new Vector3());
|
||||
const sideVec = useRef(new Vector3());
|
||||
const moveVec = useRef(new Vector3());
|
||||
|
||||
// Setup pointer lock controls
|
||||
useEffect(() => {
|
||||
|
|
@ -53,10 +56,20 @@ function CameraMovement() {
|
|||
const handleWheel = (e: WheelEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Adjust speed based on wheel direction
|
||||
const delta = e.deltaY > 0 ? 0.75 : 1.25;
|
||||
const direction = e.deltaY > 0 ? 1 : -1;
|
||||
|
||||
setSpeedMultiplier((prev) => Math.max(0.05, Math.min(5, prev * delta)));
|
||||
const delta =
|
||||
// Helps normalize sensitivity; trackpad scrolling will have many small
|
||||
// updates while mouse wheels have fewer updates but large deltas.
|
||||
Math.max(
|
||||
MIN_SPEED_ADJUSTMENT,
|
||||
Math.min(MAX_SPEED_ADJUSTMENT, Math.abs(e.deltaY * 0.01))
|
||||
) * direction;
|
||||
|
||||
setSpeedMultiplier((prev) => {
|
||||
const newSpeed = Math.round((prev + delta) * 20) / 20;
|
||||
return Math.max(0.1, Math.min(5, newSpeed));
|
||||
});
|
||||
};
|
||||
|
||||
const canvas = gl.domElement;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,67 @@
|
|||
import React, { useContext, useMemo } from "react";
|
||||
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { PerspectiveCamera } from "three";
|
||||
|
||||
const SettingsContext = React.createContext(null);
|
||||
|
||||
type PersistedSettings = {
|
||||
fogEnabled?: boolean;
|
||||
speedMultiplier?: number;
|
||||
fov?: number;
|
||||
};
|
||||
|
||||
export function useSettings() {
|
||||
return useContext(SettingsContext);
|
||||
}
|
||||
|
||||
export function SettingsProvider({
|
||||
children,
|
||||
fogEnabled,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
fogEnabled: boolean;
|
||||
}) {
|
||||
const value = useMemo(() => ({ fogEnabled }), [fogEnabled]);
|
||||
export function SettingsProvider({ children }: { children: React.ReactNode }) {
|
||||
const [fogEnabled, setFogEnabled] = useState(true);
|
||||
const [speedMultiplier, setSpeedMultiplier] = useState(1);
|
||||
const [fov, setFov] = useState(90);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
fogEnabled,
|
||||
setFogEnabled,
|
||||
speedMultiplier,
|
||||
setSpeedMultiplier,
|
||||
fov,
|
||||
setFov,
|
||||
}),
|
||||
[fogEnabled, speedMultiplier, fov]
|
||||
);
|
||||
|
||||
// Read persisted settings from localStoarge.
|
||||
useEffect(() => {
|
||||
let savedSettings: PersistedSettings = {};
|
||||
try {
|
||||
savedSettings = JSON.parse(localStorage.getItem("settings"));
|
||||
} catch (err) {
|
||||
// Ignore.
|
||||
}
|
||||
if (savedSettings.fogEnabled != null) {
|
||||
setFogEnabled(savedSettings.fogEnabled);
|
||||
}
|
||||
if (savedSettings.speedMultiplier != null) {
|
||||
setSpeedMultiplier(savedSettings.speedMultiplier);
|
||||
}
|
||||
if (savedSettings.fov != null) {
|
||||
setFov(savedSettings.fov);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Persist settings to localStoarge.
|
||||
useEffect(() => {
|
||||
const settingsToSave: PersistedSettings = {
|
||||
fogEnabled,
|
||||
speedMultiplier,
|
||||
fov,
|
||||
};
|
||||
try {
|
||||
localStorage.setItem("settings", JSON.stringify(settingsToSave));
|
||||
} catch (err) {
|
||||
// Probably forbidden by browser settings.
|
||||
}
|
||||
}, [fogEnabled, speedMultiplier, fov]);
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={value}>
|
||||
|
|
|
|||
|
|
@ -59,6 +59,18 @@ function BlendedTerrainTextures({
|
|||
[alphaMaps]
|
||||
);
|
||||
|
||||
const tiling = useMemo(
|
||||
() => ({
|
||||
0: 32,
|
||||
1: 32,
|
||||
2: 32,
|
||||
3: 32,
|
||||
4: 32,
|
||||
5: 32,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const onBeforeCompile = useCallback(
|
||||
(shader) => {
|
||||
updateTerrainTextureShader({
|
||||
|
|
@ -66,13 +78,16 @@ function BlendedTerrainTextures({
|
|||
baseTextures,
|
||||
alphaTextures,
|
||||
visibilityMask,
|
||||
tiling,
|
||||
});
|
||||
},
|
||||
[baseTextures, alphaTextures, visibilityMask]
|
||||
[baseTextures, alphaTextures, visibilityMask, tiling]
|
||||
);
|
||||
|
||||
return (
|
||||
<meshStandardMaterial
|
||||
// For testing tiling values; forces recompile.
|
||||
key={JSON.stringify(tiling)}
|
||||
displacementMap={displacementMap}
|
||||
map={displacementMap}
|
||||
displacementScale={2048}
|
||||
|
|
|
|||
28
app/page.tsx
28
app/page.tsx
|
|
@ -6,9 +6,9 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|||
import { ObserverControls } from "./ObserverControls";
|
||||
import { InspectorControls } from "./InspectorControls";
|
||||
import { SettingsProvider } from "./SettingsProvider";
|
||||
import { PerspectiveCamera } from "@react-three/drei";
|
||||
import { EffectComposer, N8AO } from "@react-three/postprocessing";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
import { ObserverCamera } from "./ObserverCamera";
|
||||
|
||||
// three.js has its own loaders for textures and models, but we need to load other
|
||||
// stuff too, e.g. missions, terrains, and more. This client is used for those.
|
||||
|
|
@ -22,30 +22,22 @@ export default function HomePage() {
|
|||
const [missionName, setMissionName] = useState(
|
||||
searchParams.get("mission") || "TWL2_WoodyMyrk"
|
||||
);
|
||||
const [fogEnabled, setFogEnabled] = useState(
|
||||
searchParams.get("fog") === "true"
|
||||
);
|
||||
|
||||
// Update query params when state changes
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("mission", missionName);
|
||||
params.set("fog", String(fogEnabled));
|
||||
router.replace(`?${params.toString()}`, { scroll: false });
|
||||
}, [missionName, fogEnabled, router]);
|
||||
}, [missionName, router]);
|
||||
|
||||
return (
|
||||
<SettingsProvider fogEnabled={fogEnabled}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<main>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<main>
|
||||
<SettingsProvider>
|
||||
<Canvas shadows>
|
||||
<ObserverControls />
|
||||
<Mission key={missionName} name={missionName} />
|
||||
<PerspectiveCamera
|
||||
makeDefault
|
||||
position={[-512, 256, -512]}
|
||||
fov={90}
|
||||
/>
|
||||
<ObserverCamera />
|
||||
<EffectComposer>
|
||||
<N8AO intensity={3} aoRadius={3} quality="performance" />
|
||||
</EffectComposer>
|
||||
|
|
@ -53,11 +45,9 @@ export default function HomePage() {
|
|||
<InspectorControls
|
||||
missionName={missionName}
|
||||
onChangeMission={setMissionName}
|
||||
fogEnabled={fogEnabled}
|
||||
onChangeFogEnabled={setFogEnabled}
|
||||
/>
|
||||
</main>
|
||||
</QueryClientProvider>
|
||||
</SettingsProvider>
|
||||
</SettingsProvider>
|
||||
</main>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ main {
|
|||
#controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
@ -35,3 +35,13 @@ main {
|
|||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.Field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
#speedInput {
|
||||
max-width: 80px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue