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

BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -591,7 +591,7 @@ if (allTargets || targets?.includes('tankModels')) {
const nationVehicleId = tank.id; const nationVehicleId = tank.id;
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
// if (id !== 15697) continue; // chieftain TODO: investigate vertices stream over read // if (id !== 15697) continue; // chieftain
// if (id !== 24609) continue; // concept 1b // if (id !== 24609) continue; // concept 1b
// if (id !== 16401) continue; // waffle // if (id !== 16401) continue; // waffle
// if (id !== 7425) continue; // isu 152 // if (id !== 7425) continue; // isu 152
@@ -600,9 +600,10 @@ if (allTargets || targets?.includes('tankModels')) {
// if (id !== 7297) continue; // 60tp // if (id !== 7297) continue; // 60tp
// if (id !== 1) continue; // t-34 // if (id !== 1) continue; // t-34
// if (id !== 6753) continue; // type 71 // if (id !== 6753) continue; // type 71
if (id !== 5137) continue; // tiger ii // if (id !== 5137) continue; // tiger ii
// if (id !== 11633) continue; // forest witch // if (id !== 11633) continue; // forest witch
// if (id !== 6225) continue; // fv215b // if (id !== 6225) continue; // fv215b
if (id !== 4481) continue; // kran
console.log(`Building model ${id} @ ${nation}/${tankIndex}`); console.log(`Building model ${id} @ ${nation}/${tankIndex}`);
@@ -3,15 +3,16 @@ import { useThree } from '@react-three/fiber';
import { RefObject, useEffect, useRef } from 'react'; import { RefObject, useEffect, useRef } from 'react';
import { Box3, Object3D } from 'three'; import { Box3, Object3D } from 'three';
import { OrbitControls as OrbitControlsClass } from 'three-stdlib'; import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
import { useTankopedia } from '../../../../../stores/tankopedia';
interface ControlsProps { interface ControlsProps {
enabled: boolean;
fit: RefObject<Object3D>; fit: RefObject<Object3D>;
} }
export function Controls({ enabled, fit }: ControlsProps) { export function Controls({ fit }: ControlsProps) {
const camera = useThree((state) => state.camera); const camera = useThree((state) => state.camera);
const orbitControls = useRef<OrbitControlsClass>(null); const orbitControls = useRef<OrbitControlsClass>(null);
const enabled = useTankopedia((state) => state.model.controlsEnabled);
useEffect(() => { useEffect(() => {
const box = new Box3().setFromObject(fit.current!); 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'; } from '@radix-ui/themes';
import { Canvas, useLoader } from '@react-three/fiber'; import { Canvas, useLoader } from '@react-three/fiber';
import { go } from 'fuzzysort'; import { go } from 'fuzzysort';
import { clamp, debounce } from 'lodash'; import { debounce } from 'lodash';
import Link from 'next/link'; import Link from 'next/link';
import { use, useEffect, useRef, useState } from 'react'; import { use, useRef, useState } from 'react';
import { Group, Vector3 } from 'three'; import { Group, Vector3 } from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { Flag } from '../../../../components/Flag'; import { Flag } from '../../../../components/Flag';
@@ -36,11 +36,11 @@ import {
tankDefinitions, tankDefinitions,
tankNamesDiacritics, tankNamesDiacritics,
} from '../../../../core/blitzkrieg/tankDefinitions'; } from '../../../../core/blitzkrieg/tankDefinitions';
import { normalizeAnglePI } from '../../../../core/math/normalizeAngle180';
import * as styles from '../page.css'; import * as styles from '../page.css';
import { Controls } from './components/Control'; import { Controls } from './components/Control';
import { GunContainer } from './components/GunContainer'; import { GunContainer } from './components/GunContainer';
import { HullContainer } from './components/HullContainer'; import { HullContainer } from './components/HullContainer';
import { RotationInputs } from './components/RotationInputs';
import { SceneProps } from './components/SceneProps'; import { SceneProps } from './components/SceneProps';
import { TurretContainer } from './components/TurretContainer'; import { TurretContainer } from './components/TurretContainer';
@@ -85,23 +85,8 @@ export default function Page({ params }: { params: { id: string } }) {
const [hullYaw, setHullYaw] = useState(0); const [hullYaw, setHullYaw] = useState(0);
const [gunPitch, setGunPitch] = useState(0); const [gunPitch, setGunPitch] = useState(0);
const [controlsEnabled, setControlsEnabled] = useState(true); 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); 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() { function handlePointerDown() {
window.addEventListener('pointermove', handlePointerMove); window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp); window.addEventListener('pointerup', handlePointerUp);
@@ -153,7 +138,7 @@ export default function Page({ params }: { params: { id: string } }) {
camera={{ fov: 20 }} camera={{ fov: 20 }}
onPointerDown={handlePointerDown} onPointerDown={handlePointerDown}
> >
<Controls enabled={controlsEnabled} fit={hullContainer} /> <Controls fit={hullContainer} />
<SceneProps /> <SceneProps />
<HullContainer <HullContainer
@@ -220,104 +205,11 @@ export default function Page({ params }: { params: { id: string } }) {
</Flex> </Flex>
)} )}
<Flex <RotationInputs
gap="2" gunId={gun.id}
align="center" tankId={tank.id}
style={{ turretId={turret.id}
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>
</Flex> </Flex>
</Card> </Card>
+5
View File
@@ -0,0 +1,5 @@
import { Vector3 } from 'three';
export const X_AXIS = new Vector3(1, 0, 0);
export const Y_AXIS = new Vector3(0, 1, 0);
export const Z_AXIS = new Vector3(0, 0, 1);
+12
View File
@@ -19,6 +19,12 @@ type Tankopedia = {
nations: string[]; nations: string[];
test: TankopediaTestTankDisplay; test: TankopediaTestTankDisplay;
}; };
model: {
hullYaw: number;
turretYaw: number;
gunPitch: number;
controlsEnabled: boolean;
};
}; };
export const useTankopedia = create<Tankopedia>()(() => ({ export const useTankopedia = create<Tankopedia>()(() => ({
@@ -33,6 +39,12 @@ export const useTankopedia = create<Tankopedia>()(() => ({
nations: [], nations: [],
test: 'include', test: 'include',
}, },
model: {
gunPitch: 0,
hullYaw: 0,
turretYaw: 0,
controlsEnabled: true,
},
})); }));
export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) { export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) {