feat: tank armor position stateful

This commit is contained in:
TrèsAbhi committed 2024-01-10 13:22:55 -06:00
1 parent 9e0c4ddb9a
commit 6239a72bce
7 files changed
+140 -77

No files matched your search

+2 -2
View File
@@ -28,8 +28,8 @@ export async function buildTankArmors() {
const tank = tanks.root[tankKey];
const id = toUniqueId(nation, tank.id);
if (id !== 7297) continue; // 60tp
// if (id !== 5137) continue; // tiger ii
// if (id !== 7297) continue; // 60tp
if (id !== 5137) continue; // tiger ii
// if (id !== 24609) continue; // concept 1b
console.log(`Building armor ${id} @ ${nation}/${tankKey}`);
+2 -2
View File
@@ -49,10 +49,10 @@ export async function buildTankModels() {
// if (id !== 7425) continue; // isu 152
// if (id !== 10369) continue; // mino
// if (id !== 4417) continue; // amx m4 mle
if (id !== 7297) continue; // 60tp
// if (id !== 7297) continue; // 60tp
// if (id !== 1) continue; // t-34
// 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 !== 6225) continue; // fv215b
// if (id !== 4481) continue; // kran
@@ -7,7 +7,6 @@ import { Button, Card, Flex, Tabs, Theme } from '@radix-ui/themes';
import { Html } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { Suspense, use, useEffect, useRef, useState } from 'react';
import { Group } from 'three';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
import { theme } from '../../../../../../stitches.config';
@@ -29,7 +28,6 @@ export function TankDisplay() {
const canvasWrapper = useRef<HTMLDivElement>(null);
const [isFullScreen, setIsFullScreen] = useState(false);
const mode = useTankopedia((state) => state.mode);
const hullContainer = useRef<Group>(null);
const showGrid = useTankopedia((state) => state.model.showGrid);
const protagonist = useTankopedia((state) => {
if (!state.areTanksAssigned) return;
@@ -134,8 +132,8 @@ export function TankDisplay() {
</Html>
}
>
<TankModel ref={hullContainer} />
<TankArmor />
<TankModel />
{mode === 'armor' && <TankArmor />}
</Suspense>
</Canvas>
</div>
@@ -1,65 +1,140 @@
import { forwardRef } from 'react';
import { Group } from 'three';
import { useLoader } from '@react-three/fiber';
import { useEffect, useState } from 'react';
import { Euler, Mesh, Vector3 } from 'three';
import { GLTFLoader } from 'three-stdlib';
import { ArmorMesh } from '../../../../../../../components/ArmorMesh';
import { HeadsUpDisplay } from '../../../../../../../components/HeadsUpDisplay';
import { X_AXIS } from '../../../../../../../constants/axis';
import { asset } from '../../../../../../../core/blitzkrieg/asset';
import {
ModelDefinitions,
modelDefinitions,
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
import { useTankopedia } from '../../../../../../../stores/tankopedia';
interface TankArmorProps {}
export const TankArmor = forwardRef<Group, TankArmorProps>((_props, ref) => {
export function TankArmor() {
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
ModelDefinitions | undefined
>(undefined);
const protagonist = useTankopedia((state) => {
if (!state.areTanksAssigned) return;
return state.protagonist;
});
if (!protagonist) return null;
const gltf = useLoader(
GLTFLoader,
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
);
const model = useTankopedia((state) => state.model);
useEffect(() => {
(async () => {
setAwaitedModelDefinitions(await modelDefinitions);
})();
}, []);
if (!awaitedModelDefinitions) return null;
const nodes = Object.values(gltf.nodes);
const tankModelDefinition = awaitedModelDefinitions[protagonist.tank.id];
const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
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);
const turretPosition = new Vector3()
.sub(turretOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), model.turretYaw);
const turretRotation = new Euler(0, 0, model.turretYaw);
if (tankModelDefinition.turretRotation) {
const pitch = -tankModelDefinition.turretRotation.pitch * (Math.PI / 180);
const yaw = -tankModelDefinition.turretRotation.yaw * (Math.PI / 180);
const roll = -tankModelDefinition.turretRotation.roll * (Math.PI / 180);
turretPosition
.applyAxisAngle(new Vector3(1, 0, 0), pitch)
.applyAxisAngle(new Vector3(0, 1, 0), roll)
.applyAxisAngle(new Vector3(0, 0, 1), yaw);
turretRotation.x += pitch;
turretRotation.y += roll;
turretRotation.z += yaw;
}
turretPosition.add(turretOrigin);
return (
<HeadsUpDisplay>
<mesh>
<boxGeometry />
<meshBasicMaterial color="red" />
</mesh>
{/* {armorObjects.map((object) => {
const isHull = object.name.startsWith('hull_');
const isVisible = isHull;
if (!isVisible) return null;
return (
<ArmorMesh key={object.uuid} geometry={(object as Mesh).geometry} />
);
})} */}
{/* {armorObjects.map((object) => {
const isCurrentTurret = object.name.startsWith(
`turret_${model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentTurret;
<group rotation={[-Math.PI / 2, 0, model.hullYaw]}>
{nodes.map((node) => {
const isHull = node.name.startsWith('hull_');
const isVisible = isHull;
if (!isVisible) return null;
return (
<mesh
position={turretOrigin}
key={object.uuid}
geometry={(object as Mesh).geometry}
>
<ArmorMesh />
</mesh>
<ArmorMesh key={node.uuid} geometry={(node as Mesh).geometry} />
);
})} */}
})}
{/* {armorObjects.map((object) => {
const isCurrentGun = object.name.startsWith(
`gun_${model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentGun;
<group position={turretPosition} rotation={turretRotation}>
{nodes.map((node) => {
const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model
.toString()
.padStart(2, '0')}`,
);
const isVisible = isCurrentTurret;
if (!isVisible) return null;
if (!isVisible) return null;
return (
<ArmorMesh
key={node.uuid}
position={turretOrigin}
geometry={(node as Mesh).geometry}
/>
);
})}
return (
<mesh
position={turretOrigin.clone().add(gunOrigin)}
key={object.uuid}
geometry={(object as Mesh).geometry}
>
<ArmorHighlightMaterial />
</mesh>
);
})} */}
<group
position={new Vector3()
.sub(turretOrigin)
.sub(gunOrigin)
.applyAxisAngle(new Vector3(1, 0, 0), model.gunPitch)
.add(turretOrigin)
.add(gunOrigin)}
rotation={[model.gunPitch, 0, 0]}
>
{nodes.map((node) => {
const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentGun;
if (!isVisible) return null;
return (
<ArmorMesh
key={node.uuid}
position={turretOrigin.clone().add(gunOrigin)}
geometry={(node as Mesh).geometry}
/>
);
})}
</group>
</group>
</group>
</HeadsUpDisplay>
);
});
}
@@ -1,11 +1,5 @@
import { ThreeEvent, useLoader, useThree } from '@react-three/fiber';
import {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
import { useEffect, useRef, useState } from 'react';
import {
Euler,
Group,
@@ -28,35 +22,30 @@ import mutateTankopedia, {
useTankopedia,
} from '../../../../../../../stores/tankopedia';
export const TankModel = forwardRef<Group>((_props, ref) => {
export function TankModel() {
// "TypeError: dispatcher.use is not a function"
// const awaitedModelDefinitions = use(modelDefinitions);
const [awaitedModelDefinitions, setAwaitedModelDefinitions] = useState<
ModelDefinitions | undefined
>(undefined);
const canvas = useThree((state) => state.gl.domElement);
const protagonist = useTankopedia((state) => {
if (!state.areTanksAssigned) return;
return state.protagonist;
});
const canvas = useThree((state) => state.gl.domElement);
if (!protagonist) return null;
const modelGltf = useLoader(
const gltf = useLoader(
GLTFLoader,
asset(`3d/tanks/models/${protagonist.tank.id}.glb`),
);
const armorGltf = useLoader(
GLTFLoader,
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
);
const model = useTankopedia((state) => state.model);
const hullContainer = useRef<Group>(null);
const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null);
const nodes = Object.values(modelGltf.nodes);
const nodes = Object.values(gltf.nodes);
useImperativeHandle(ref, () => hullContainer.current!);
useEffect(() => {
(async () => {
setAwaitedModelDefinitions(await modelDefinitions);
@@ -411,4 +400,4 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
</group>
</group>
);
});
}
+1 -1
View File
@@ -24,7 +24,7 @@ export default function Page({ params }: { params: { id: string } }) {
draft.model.hullYaw = 0;
draft.model.turretYaw = 0;
draft.model.gunPitch = 0;
draft.mode = 'model';
draft.mode = 'armor';
draft.areTanksAssigned = true;
if (!draft.areTanksAssigned) return;
+2 -1
View File
@@ -12,7 +12,8 @@ export function ArmorMesh(props: ArmorMeshProps) {
<mesh {...props} renderOrder={1}>
<meshBasicMaterial
transparent
opacity={Math.random() / 2}
// opacity={Math.random() / 2}
opacity={0.25}
color="red"
depthWrite={false}
/>