diff --git a/scripts/buildAssets/tankArmors.ts b/scripts/buildAssets/tankArmors.ts index ba4b33cf9..ee4e0be98 100644 --- a/scripts/buildAssets/tankArmors.ts +++ b/scripts/buildAssets/tankArmors.ts @@ -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}`); diff --git a/scripts/buildAssets/tankModels.ts b/scripts/buildAssets/tankModels.ts index 56ce0faec..79b8798fd 100644 --- a/scripts/buildAssets/tankModels.ts +++ b/scripts/buildAssets/tankModels.ts @@ -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 diff --git a/src/app/tools/tankopedia/[id]/components/Model/TankDisplay.tsx b/src/app/tools/tankopedia/[id]/components/Model/TankDisplay.tsx index 04c20a320..d88ab8f0e 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/TankDisplay.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/TankDisplay.tsx @@ -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(null); const [isFullScreen, setIsFullScreen] = useState(false); const mode = useTankopedia((state) => state.mode); - const hullContainer = useRef(null); const showGrid = useTankopedia((state) => state.model.showGrid); const protagonist = useTankopedia((state) => { if (!state.areTanksAssigned) return; @@ -134,8 +132,8 @@ export function TankDisplay() { } > - - + + {mode === 'armor' && } diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor.tsx index f1b67c521..7ea7b21b9 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankArmor.tsx @@ -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((_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 ( - - - - - - {/* {armorObjects.map((object) => { - const isHull = object.name.startsWith('hull_'); - const isVisible = isHull; - - if (!isVisible) return null; - - return ( - - ); - })} */} - - {/* {armorObjects.map((object) => { - const isCurrentTurret = object.name.startsWith( - `turret_${model.toString().padStart(2, '0')}`, - ); - const isVisible = isCurrentTurret; + + {nodes.map((node) => { + const isHull = node.name.startsWith('hull_'); + const isVisible = isHull; if (!isVisible) return null; - return ( - - - + ); - })} */} + })} - {/* {armorObjects.map((object) => { - const isCurrentGun = object.name.startsWith( - `gun_${model.toString().padStart(2, '0')}`, - ); - const isVisible = isCurrentGun; + + {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 ( + + ); + })} - return ( - - - - ); - })} */} + + {nodes.map((node) => { + const isCurrentGun = node.name.startsWith( + `gun_${gunModelDefinition.model.toString().padStart(2, '0')}`, + ); + const isVisible = isCurrentGun; + + if (!isVisible) return null; + + return ( + + ); + })} + + + ); -}); +} diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx index 53a437d02..50ee792cd 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx @@ -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((_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(null); const turretContainer = useRef(null); const gunContainer = useRef(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((_props, ref) => { ); -}); +} diff --git a/src/app/tools/tankopedia/[id]/page.tsx b/src/app/tools/tankopedia/[id]/page.tsx index bb1a7f86f..d7ba0668d 100644 --- a/src/app/tools/tankopedia/[id]/page.tsx +++ b/src/app/tools/tankopedia/[id]/page.tsx @@ -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; diff --git a/src/components/ArmorMesh.tsx b/src/components/ArmorMesh.tsx index a7389bf79..d081d5dbc 100644 --- a/src/components/ArmorMesh.tsx +++ b/src/components/ArmorMesh.tsx @@ -12,7 +12,8 @@ export function ArmorMesh(props: ArmorMeshProps) {