mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-07-11 14:34:51 +00:00
press 1-9 to select cameras, only show error placeholders in debug mode
This commit is contained in:
parent
825b21acfd
commit
3bb3f7afbd
17 changed files with 101 additions and 6472 deletions
|
|
@ -21,6 +21,8 @@ interface CamerasContextValue {
|
|||
registerCamera: (camera: any) => void;
|
||||
unregisterCamera: (camera: any) => void;
|
||||
nextCamera: () => void;
|
||||
setCameraIndex: (index: number) => void;
|
||||
cameraCount: number;
|
||||
}
|
||||
|
||||
const CamerasContext = createContext<CamerasContextValue | null>(null);
|
||||
|
|
@ -52,15 +54,25 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const cameraCount = Object.keys(cameraMap).length;
|
||||
|
||||
const nextCamera = useCallback(() => {
|
||||
setCameraIndex((prev) => {
|
||||
const cameraCount = Object.keys(cameraMap).length;
|
||||
if (cameraCount === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (prev + 1) % cameraCount;
|
||||
});
|
||||
}, [cameraMap]);
|
||||
}, [cameraCount]);
|
||||
|
||||
const setCamera = useCallback(
|
||||
(index: number) => {
|
||||
if (index >= 0 && index < cameraCount) {
|
||||
setCameraIndex(index);
|
||||
}
|
||||
},
|
||||
[cameraCount]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const cameraCount = Object.keys(cameraMap).length;
|
||||
|
|
@ -82,8 +94,10 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
|
|||
registerCamera,
|
||||
unregisterCamera,
|
||||
nextCamera,
|
||||
setCameraIndex: setCamera,
|
||||
cameraCount,
|
||||
}),
|
||||
[registerCamera, unregisterCamera, nextCamera]
|
||||
[registerCamera, unregisterCamera, nextCamera, setCamera, cameraCount]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ export function ShapePlaceholder({ color }: { color: string }) {
|
|||
);
|
||||
}
|
||||
|
||||
export function DebugPlaceholder({ color }: { color: string }) {
|
||||
const { debugMode } = useDebug();
|
||||
return debugMode ? <ShapePlaceholder color={color} /> : null;
|
||||
}
|
||||
|
||||
export type StaticShapeType = "StaticShape" | "TSStatic" | "Item" | "Turret";
|
||||
|
||||
export const ShapeModel = memo(function ShapeModel() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo, Suspense, useMemo } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { Mesh } from "three";
|
||||
import { useGLTF, useTexture } from "@react-three/drei";
|
||||
import { BASE_URL, interiorTextureToUrl, interiorToUrl } from "../loaders";
|
||||
|
|
@ -75,15 +76,20 @@ export const InteriorModel = memo(
|
|||
}
|
||||
);
|
||||
|
||||
function InteriorPlaceholder() {
|
||||
function InteriorPlaceholder({ color }: { color: string }) {
|
||||
return (
|
||||
<mesh>
|
||||
<boxGeometry args={[10, 10, 10]} />
|
||||
<meshStandardMaterial color="orange" wireframe />
|
||||
<meshStandardMaterial color={color} wireframe />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
function DebugInteriorPlaceholder() {
|
||||
const { debugMode } = useDebug();
|
||||
return debugMode ? <InteriorPlaceholder color="red" /> : null;
|
||||
}
|
||||
|
||||
export const InteriorInstance = memo(function InteriorInstance({
|
||||
object,
|
||||
}: {
|
||||
|
|
@ -96,9 +102,11 @@ export const InteriorInstance = memo(function InteriorInstance({
|
|||
|
||||
return (
|
||||
<group position={position} quaternion={q} scale={scale}>
|
||||
<Suspense fallback={<InteriorPlaceholder />}>
|
||||
<InteriorModel interiorFile={interiorFile} />
|
||||
</Suspense>
|
||||
<ErrorBoundary fallback={<DebugInteriorPlaceholder />}>
|
||||
<Suspense fallback={<InteriorPlaceholder color="orange" />}>
|
||||
<InteriorModel interiorFile={interiorFile} />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</group>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRotation,
|
||||
getScale,
|
||||
} from "../mission";
|
||||
import { ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { DebugPlaceholder, ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { ShapeInfoProvider } from "./ShapeInfoProvider";
|
||||
import { useSimGroup } from "./SimGroup";
|
||||
import { FloatingLabel } from "./FloatingLabel";
|
||||
|
|
@ -84,7 +84,7 @@ export function Item({ object }: { object: ConsoleObject }) {
|
|||
<ShapeInfoProvider shapeName={shapeName} type="Item">
|
||||
<group position={position} quaternion={q} scale={scale}>
|
||||
{shapeName ? (
|
||||
<ErrorBoundary fallback={<ShapePlaceholder color="red" />}>
|
||||
<ErrorBoundary fallback={<DebugPlaceholder color="red" />}>
|
||||
<Suspense fallback={<ShapePlaceholder color="pink" />}>
|
||||
<ShapeModel />
|
||||
{label ? (
|
||||
|
|
@ -93,7 +93,7 @@ export function Item({ object }: { object: ConsoleObject }) {
|
|||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<ShapePlaceholder color="orange" />
|
||||
<DebugPlaceholder color="orange" />
|
||||
)}
|
||||
</group>
|
||||
</ShapeInfoProvider>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,15 @@ enum Controls {
|
|||
right = "right",
|
||||
up = "up",
|
||||
down = "down",
|
||||
camera1 = "camera1",
|
||||
camera2 = "camera2",
|
||||
camera3 = "camera3",
|
||||
camera4 = "camera4",
|
||||
camera5 = "camera5",
|
||||
camera6 = "camera6",
|
||||
camera7 = "camera7",
|
||||
camera8 = "camera8",
|
||||
camera9 = "camera9",
|
||||
}
|
||||
|
||||
const BASE_SPEED = 80;
|
||||
|
|
@ -23,7 +32,7 @@ function CameraMovement() {
|
|||
const { speedMultiplier, setSpeedMultiplier } = useControls();
|
||||
const [subscribe, getKeys] = useKeyboardControls<Controls>();
|
||||
const { camera, gl } = useThree();
|
||||
const { nextCamera } = useCameras();
|
||||
const { nextCamera, setCameraIndex, cameraCount } = useCameras();
|
||||
const controlsRef = useRef<PointerLockControls | null>(null);
|
||||
|
||||
// Scratch vectors to avoid allocations each frame
|
||||
|
|
@ -53,6 +62,30 @@ function CameraMovement() {
|
|||
};
|
||||
}, [camera, gl, nextCamera]);
|
||||
|
||||
// Handle number keys 1-9 for camera selection
|
||||
useEffect(() => {
|
||||
const cameraControls = [
|
||||
Controls.camera1,
|
||||
Controls.camera2,
|
||||
Controls.camera3,
|
||||
Controls.camera4,
|
||||
Controls.camera5,
|
||||
Controls.camera6,
|
||||
Controls.camera7,
|
||||
Controls.camera8,
|
||||
Controls.camera9,
|
||||
];
|
||||
|
||||
return subscribe((state) => {
|
||||
for (let i = 0; i < cameraControls.length; i++) {
|
||||
if (state[cameraControls[i]] && i < cameraCount) {
|
||||
setCameraIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [subscribe, setCameraIndex, cameraCount]);
|
||||
|
||||
// Handle mousewheel for speed adjustment
|
||||
useEffect(() => {
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
|
|
@ -135,6 +168,15 @@ const KEYBOARD_CONTROLS = [
|
|||
{ name: Controls.right, keys: ["KeyD"] },
|
||||
{ name: Controls.up, keys: ["Space"] },
|
||||
{ name: Controls.down, keys: ["ShiftLeft", "ShiftRight"] },
|
||||
{ name: Controls.camera1, keys: ["Digit1"] },
|
||||
{ name: Controls.camera2, keys: ["Digit2"] },
|
||||
{ name: Controls.camera3, keys: ["Digit3"] },
|
||||
{ name: Controls.camera4, keys: ["Digit4"] },
|
||||
{ name: Controls.camera5, keys: ["Digit5"] },
|
||||
{ name: Controls.camera6, keys: ["Digit6"] },
|
||||
{ name: Controls.camera7, keys: ["Digit7"] },
|
||||
{ name: Controls.camera8, keys: ["Digit8"] },
|
||||
{ name: Controls.camera9, keys: ["Digit9"] },
|
||||
];
|
||||
|
||||
export function ObserverControls() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRotation,
|
||||
getScale,
|
||||
} from "../mission";
|
||||
import { ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { DebugPlaceholder, ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { ShapeInfoProvider } from "./ShapeInfoProvider";
|
||||
|
||||
const dataBlockToShapeName = {
|
||||
|
|
@ -61,13 +61,13 @@ export function StaticShape({ object }: { object: ConsoleObject }) {
|
|||
<ShapeInfoProvider shapeName={shapeName} type="StaticShape">
|
||||
<group position={position} quaternion={q} scale={scale}>
|
||||
{shapeName ? (
|
||||
<ErrorBoundary fallback={<ShapePlaceholder color="red" />}>
|
||||
<ErrorBoundary fallback={<DebugPlaceholder color="red" />}>
|
||||
<Suspense fallback={<ShapePlaceholder color="yellow" />}>
|
||||
<ShapeModel />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<ShapePlaceholder color="orange" />
|
||||
<DebugPlaceholder color="orange" />
|
||||
)}
|
||||
</group>
|
||||
</ShapeInfoProvider>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRotation,
|
||||
getScale,
|
||||
} from "../mission";
|
||||
import { ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { DebugPlaceholder, ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { ShapeInfoProvider } from "./ShapeInfoProvider";
|
||||
|
||||
export function TSStatic({ object }: { object: ConsoleObject }) {
|
||||
|
|
@ -24,7 +24,7 @@ export function TSStatic({ object }: { object: ConsoleObject }) {
|
|||
return (
|
||||
<ShapeInfoProvider shapeName={shapeName} type="TSStatic">
|
||||
<group position={position} quaternion={q} scale={scale}>
|
||||
<ErrorBoundary fallback={<ShapePlaceholder color="red" />}>
|
||||
<ErrorBoundary fallback={<DebugPlaceholder color="red" />}>
|
||||
<Suspense fallback={<ShapePlaceholder color="yellow" />}>
|
||||
<ShapeModel />
|
||||
</Suspense>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getRotation,
|
||||
getScale,
|
||||
} from "../mission";
|
||||
import { ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { DebugPlaceholder, ShapeModel, ShapePlaceholder } from "./GenericShape";
|
||||
import { ShapeInfoProvider } from "./ShapeInfoProvider";
|
||||
|
||||
const dataBlockToShapeName = {
|
||||
|
|
@ -36,14 +36,16 @@ function getDataBlockShape(dataBlock: string) {
|
|||
|
||||
export function Turret({ object }: { object: ConsoleObject }) {
|
||||
const dataBlock = getProperty(object, "dataBlock").value;
|
||||
const initialBarrel = getProperty(object, "initialBarrel").value;
|
||||
const initialBarrel = getProperty(object, "initialBarrel")?.value;
|
||||
|
||||
const position = useMemo(() => getPosition(object), [object]);
|
||||
const q = useMemo(() => getRotation(object), [object]);
|
||||
const scale = useMemo(() => getScale(object), [object]);
|
||||
|
||||
const shapeName = getDataBlockShape(dataBlock);
|
||||
const barrelShapeName = getDataBlockShape(initialBarrel);
|
||||
const barrelShapeName = initialBarrel
|
||||
? getDataBlockShape(initialBarrel)
|
||||
: undefined;
|
||||
|
||||
if (!shapeName) {
|
||||
console.error(`<Turret> missing shape for dataBlock: ${dataBlock}`);
|
||||
|
|
@ -58,24 +60,24 @@ export function Turret({ object }: { object: ConsoleObject }) {
|
|||
<ShapeInfoProvider shapeName={shapeName} type="Turret">
|
||||
<group position={position} quaternion={q} scale={scale}>
|
||||
{shapeName ? (
|
||||
<ErrorBoundary fallback={<ShapePlaceholder color="red" />}>
|
||||
<ErrorBoundary fallback={<DebugPlaceholder color="red" />}>
|
||||
<Suspense fallback={<ShapePlaceholder color="yellow" />}>
|
||||
<ShapeModel />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<ShapePlaceholder color="orange" />
|
||||
<DebugPlaceholder color="orange" />
|
||||
)}
|
||||
<ShapeInfoProvider shapeName={barrelShapeName} type="Turret">
|
||||
<group position={[0, 1.5, 0]}>
|
||||
{barrelShapeName ? (
|
||||
<ErrorBoundary fallback={<ShapePlaceholder color="red" />}>
|
||||
<ErrorBoundary fallback={<DebugPlaceholder color="red" />}>
|
||||
<Suspense fallback={<ShapePlaceholder color="yellow" />}>
|
||||
<ShapeModel />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<ShapePlaceholder color="orange" />
|
||||
<DebugPlaceholder color="orange" />
|
||||
)}
|
||||
</group>
|
||||
</ShapeInfoProvider>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue