feat: code split

This commit is contained in:
TrèsAbhi committed 2024-01-04 13:06:40 -06:00
1 parent 2cf0bda352
commit 0a4e696bc1
15 files changed
+308 -121

No files matched your search

@@ -3,15 +3,16 @@ import { useThree } from '@react-three/fiber';
import { RefObject, useEffect, useRef } from 'react';
import { Box3, Object3D } from 'three';
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
import { useTankopedia } from '../../../../../stores/tankopedia';
interface ControlsProps {
enabled: boolean;
fit: RefObject<Object3D>;
}
export function Controls({ enabled, fit }: ControlsProps) {
export function Controls({ fit }: ControlsProps) {
const camera = useThree((state) => state.camera);
const orbitControls = useRef<OrbitControlsClass>(null);
const enabled = useTankopedia((state) => state.model.controlsEnabled);
useEffect(() => {
const box = new Box3().setFromObject(fit.current!);
@@ -0,0 +1,126 @@
import { useLoader } from '@react-three/fiber';
import { forwardRef, use, useImperativeHandle, useRef } from 'react';
import { Group, Vector3 } from 'three';
import { GLTFLoader } from 'three-stdlib';
import { X_AXIS } from '../../../../../constants/axis';
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
import mutateTankopedia, {
useTankopedia,
} from '../../../../../stores/tankopedia';
import { GunContainer } from './GunContainer';
import { HullContainer } from './HullContainer';
import { TurretContainer } from './TurretContainer';
interface ModelProps {
tankId: number;
gunId: number;
turretId: number;
}
export const Model = forwardRef<Group, ModelProps>(
({ gunId, tankId, turretId }, ref) => {
const awaitedModelDefinitions = use(modelDefinitions);
const model = useTankopedia((state) => state.model);
const gltf = useLoader(GLTFLoader, `/test/${tankId}.glb`);
const gunContainer = useRef<Group>(null);
const hullContainer = useRef<Group>(null);
const turretContainer = useRef<Group>(null);
useImperativeHandle(ref, () => hullContainer.current!);
const tankModelDefinition = awaitedModelDefinitions[tankId];
const turretModelDefinition = tankModelDefinition.turrets[turretId];
const gunModelDefinition = turretModelDefinition.guns[gunId];
const turretOrigin = new Vector3(
tankModelDefinition.turretOrigin[0],
tankModelDefinition.turretOrigin[1],
-tankModelDefinition.turretOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2);
const gunOrigin = new Vector3(
turretModelDefinition.gunOrigin[0],
turretModelDefinition.gunOrigin[1],
-turretModelDefinition.gunOrigin[2],
).applyAxisAngle(X_AXIS, Math.PI / 2);
return (
<HullContainer
objects={gltf.scene.children[0].children}
yaw={model.hullYaw}
ref={hullContainer}
onYawStart={() =>
mutateTankopedia((state) => {
state.model.controlsEnabled = false;
})
}
onYawEnd={(yaw) => {
mutateTankopedia((state) => {
state.model.controlsEnabled = true;
state.model.hullYaw = yaw;
});
}}
onTrackStart={() => {
mutateTankopedia((state) => {
state.model.controlsEnabled = false;
});
}}
onTrackEnd={() => {
mutateTankopedia((state) => {
state.model.controlsEnabled = true;
});
}}
>
<TurretContainer
initialTurretRotation={tankModelDefinition.turretRotation}
gunOrigin={gunOrigin}
ref={turretContainer}
objects={gltf.scene.children[0].children}
model={turretModelDefinition.model}
onYawStart={() =>
mutateTankopedia((state) => {
state.model.controlsEnabled = false;
})
}
yawLimits={turretModelDefinition.yaw}
pitchLimits={gunModelDefinition.pitch}
pitch={model.gunPitch}
onYawEnd={(pitch, yaw) => {
mutateTankopedia((state) => {
state.model.controlsEnabled = true;
state.model.gunPitch = pitch;
state.model.turretYaw = yaw;
});
}}
gunContainer={gunContainer}
turretOrigin={turretOrigin}
yaw={model.turretYaw}
>
<GunContainer
ref={gunContainer}
initialTurretRotation={tankModelDefinition.turretRotation}
onPitchStart={() => {
mutateTankopedia((state) => {
state.model.controlsEnabled = false;
});
}}
pitchLimits={gunModelDefinition.pitch}
turretContainer={turretContainer}
yaw={model.turretYaw}
onPitchEnd={(pitch, yaw) => {
mutateTankopedia((state) => {
state.model.controlsEnabled = true;
state.model.gunPitch = pitch;
state.model.turretYaw = yaw;
});
}}
yawLimits={turretModelDefinition.yaw}
gunOrigin={gunOrigin}
pitch={model.gunPitch}
model={gunModelDefinition.model}
objects={gltf.scene.children[0].children}
turretOrigin={turretOrigin}
/>
</TurretContainer>
</HullContainer>
);
},
);
@@ -0,0 +1,150 @@
import { Flex, TextField } from '@radix-ui/themes';
import { clamp } from 'lodash';
import { use, useEffect, useRef } from 'react';
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
import mutateTankopedia, {
useTankopedia,
} from '../../../../../stores/tankopedia';
interface RotationInputsProps {
tankId: number;
gunId: number;
turretId: number;
}
export function RotationInputs({
tankId,
gunId,
turretId,
}: RotationInputsProps) {
const model = useTankopedia((state) => state.model);
const awaitedModelDefinitions = use(modelDefinitions);
const turretYawInput = useRef<HTMLInputElement>(null);
const hullYawInput = useRef<HTMLInputElement>(null);
const gunPitchInput = useRef<HTMLInputElement>(null);
const tankModelDefinition = awaitedModelDefinitions[tankId];
const turretModelDefinition = tankModelDefinition.turrets[turretId];
const gunModelDefinition = turretModelDefinition.guns[gunId];
useEffect(() => {
hullYawInput.current!.value = `${-Math.round(
model.hullYaw * (180 / Math.PI),
)}`;
}, [model.hullYaw]);
useEffect(() => {
turretYawInput.current!.value = `${-Math.round(
model.turretYaw * (180 / Math.PI),
)}`;
}, [model.turretYaw]);
useEffect(() => {
gunPitchInput.current!.value = `${-Math.round(
model.gunPitch * (180 / Math.PI),
)}`;
}, [model.gunPitch]);
return (
<Flex
gap="2"
align="center"
style={{
position: 'absolute',
left: 16,
bottom: 16,
}}
>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Hull</TextField.Slot>
<TextField.Input
defaultValue={Math.round(model.hullYaw * (180 / Math.PI))}
onBlur={() => {
const normalized = normalizeAnglePI(
-Number(hullYawInput.current?.value) * (Math.PI / 180),
);
mutateTankopedia((state) => {
state.model.hullYaw = normalized;
});
hullYawInput.current!.value = `${Math.round(normalized)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
hullYawInput.current?.blur();
}
}}
onFocus={() => hullYawInput.current?.focus()}
ref={hullYawInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Turret</TextField.Slot>
<TextField.Input
defaultValue={Math.round(model.turretYaw * (180 / Math.PI))}
onBlur={() => {
const normalized = normalizeAnglePI(
-Number(turretYawInput.current?.value) * (Math.PI / 180),
);
mutateTankopedia((state) => {
state.model.turretYaw = normalized;
});
turretYawInput.current!.value = `${Math.round(normalized)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
turretYawInput.current?.blur();
}
}}
onFocus={() => turretYawInput.current?.focus()}
ref={turretYawInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Gun</TextField.Slot>
<TextField.Input
defaultValue={-Math.round(model.gunPitch * (180 / Math.PI))}
onBlur={() => {
const clamped = clamp(
Number(gunPitchInput.current?.value),
gunModelDefinition.pitch.min,
gunModelDefinition.pitch.max,
);
mutateTankopedia((state) => {
state.model.gunPitch = -clamped * (Math.PI / 180);
});
gunPitchInput.current!.value = `${Math.round(clamped)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
gunPitchInput.current?.blur();
}
}}
onFocus={() => gunPitchInput.current?.focus()}
ref={gunPitchInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
</Flex>
);
}
+9 -117
View File
@@ -21,9 +21,9 @@ import {
} from '@radix-ui/themes';
import { Canvas, useLoader } from '@react-three/fiber';
import { go } from 'fuzzysort';
import { clamp, debounce } from 'lodash';
import { debounce } from 'lodash';
import Link from 'next/link';
import { use, useEffect, useRef, useState } from 'react';
import { use, useRef, useState } from 'react';
import { Group, Vector3 } from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { Flag } from '../../../../components/Flag';
@@ -36,11 +36,11 @@ import {
tankDefinitions,
tankNamesDiacritics,
} from '../../../../core/blitzkrieg/tankDefinitions';
import { normalizeAnglePI } from '../../../../core/math/normalizeAngle180';
import * as styles from '../page.css';
import { Controls } from './components/Control';
import { GunContainer } from './components/GunContainer';
import { HullContainer } from './components/HullContainer';
import { RotationInputs } from './components/RotationInputs';
import { SceneProps } from './components/SceneProps';
import { TurretContainer } from './components/TurretContainer';
@@ -85,23 +85,8 @@ export default function Page({ params }: { params: { id: string } }) {
const [hullYaw, setHullYaw] = useState(0);
const [gunPitch, setGunPitch] = useState(0);
const [controlsEnabled, setControlsEnabled] = useState(true);
const turretYawInput = useRef<HTMLInputElement>(null);
const hullYawInput = useRef<HTMLInputElement>(null);
const gunPitchInput = useRef<HTMLInputElement>(null);
const gunContainer = useRef<Group>(null);
useEffect(() => {
hullYawInput.current!.value = `${-Math.round(hullYaw * (180 / Math.PI))}`;
}, [hullYaw]);
useEffect(() => {
turretYawInput.current!.value = `${-Math.round(
turretYaw * (180 / Math.PI),
)}`;
}, [turretYaw]);
useEffect(() => {
gunPitchInput.current!.value = `${-Math.round(gunPitch * (180 / Math.PI))}`;
}, [gunPitch]);
function handlePointerDown() {
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
@@ -153,7 +138,7 @@ export default function Page({ params }: { params: { id: string } }) {
camera={{ fov: 20 }}
onPointerDown={handlePointerDown}
>
<Controls enabled={controlsEnabled} fit={hullContainer} />
<Controls fit={hullContainer} />
<SceneProps />
<HullContainer
@@ -220,104 +205,11 @@ export default function Page({ params }: { params: { id: string } }) {
</Flex>
)}
<Flex
gap="2"
align="center"
style={{
position: 'absolute',
left: 16,
bottom: 16,
}}
>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Hull</TextField.Slot>
<TextField.Input
defaultValue={Math.round(hullYaw * (180 / Math.PI))}
onBlur={() => {
const normalized = normalizeAnglePI(
-Number(hullYawInput.current?.value) * (Math.PI / 180),
);
setHullYaw(normalized);
hullYawInput.current!.value = `${Math.round(normalized)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
hullYawInput.current?.blur();
}
}}
onFocus={() => hullYawInput.current?.focus()}
ref={hullYawInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Turret</TextField.Slot>
<TextField.Input
defaultValue={Math.round(turretYaw * (180 / Math.PI))}
onBlur={() => {
const normalized = normalizeAnglePI(
-Number(turretYawInput.current?.value) *
(Math.PI / 180),
);
setTurretYaw(normalized);
turretYawInput.current!.value = `${Math.round(
normalized,
)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
turretYawInput.current?.blur();
}
}}
onFocus={() => turretYawInput.current?.focus()}
ref={turretYawInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
<TextField.Root
variant="surface"
style={{
width: 115,
}}
>
<TextField.Slot>Gun</TextField.Slot>
<TextField.Input
defaultValue={-Math.round(gunPitch * (180 / Math.PI))}
onBlur={() => {
const clamped = clamp(
Number(gunPitchInput.current?.value),
gunModelDefinition.pitch.min,
gunModelDefinition.pitch.max,
);
setGunPitch(-clamped * (Math.PI / 180));
gunPitchInput.current!.value = `${Math.round(clamped)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
gunPitchInput.current?.blur();
}
}}
onFocus={() => gunPitchInput.current?.focus()}
ref={gunPitchInput}
style={{ textAlign: 'right' }}
/>
<TextField.Slot>°</TextField.Slot>
</TextField.Root>
</Flex>
<RotationInputs
gunId={gun.id}
tankId={tank.id}
turretId={turret.id}
/>
</Flex>
</Card>