diff --git a/src/app/tools/tankopedia/[id]/components/tankAlignment.ts b/src/app/tools/tankopedia/[id]/components/tankAlignment.ts index 3157d256b..e3e5962e1 100644 --- a/src/app/tools/tankopedia/[id]/components/tankAlignment.ts +++ b/src/app/tools/tankopedia/[id]/components/tankAlignment.ts @@ -14,8 +14,7 @@ export function TankAlignment({ model }: TankAlignmentProps) { const diameter = Math.max(box.max.x - box.min.x, box.max.z - box.min.z); const radius = diameter / 2; - model.current?.position.set(0, -(box.min.y + box.max.y) / 2, 0); - // camera.position.set(radius * -0.25, radius * 0.1, radius * -1.5); + camera.position.set(0, (box.min.y + box.max.y) / 2, -10); }, []); return null; diff --git a/src/app/tools/tankopedia/[id]/page.tsx b/src/app/tools/tankopedia/[id]/page.tsx index 3837d6587..d89ffb27e 100644 --- a/src/app/tools/tankopedia/[id]/page.tsx +++ b/src/app/tools/tankopedia/[id]/page.tsx @@ -21,13 +21,13 @@ import { Tooltip, } from '@radix-ui/themes'; import { Environment, OrbitControls } from '@react-three/drei'; -import { Canvas, useLoader } from '@react-three/fiber'; +import { Canvas, ThreeEvent, useLoader } from '@react-three/fiber'; import { EffectComposer, SSAO } from '@react-three/postprocessing'; import { go } from 'fuzzysort'; import { debounce } from 'lodash'; import Link from 'next/link'; import { use, useEffect, useRef, useState } from 'react'; -import { Group, Mesh } from 'three'; +import { Group, Mesh, Vector3 } from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { Flag } from '../../../../components/Flag'; import { ModuleButtons } from '../../../../components/ModuleButton'; @@ -60,7 +60,19 @@ export default function Page({ params }: { params: { id: string } }) { const [versusTurret, setVersusTurret] = useState(versusTank.turrets.at(-1)!); const [versusGun, setVersusGun] = useState(versusTurret.guns.at(-1)!); const [versusTankTab, setVersusTankTab] = useState('search'); + const turretObject3D = useRef(null); + const mantletObject3D = useRef(null); + const gunObject3D = useRef(null); + const canvas = useRef(null); const gltf = useLoader(GLTFLoader, '/test/5137.glb'); + const turretOrigin = new Vector3( + tank.turretOrigin[0], + tank.turretOrigin[1], + -tank.turretOrigin[2], + ).applyAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + const gunOrigin = new Vector3(...turret.gunOrigin); + const [turretRotation, setTurretRotation] = useState(0); + const [orbitControlsEnabled, setOrbitControlsEnabled] = useState(true); useEffect(() => { if (!turret.guns.some(({ id }) => gun.id === id)) { @@ -110,11 +122,13 @@ export default function Page({ params }: { params: { id: string } }) {
event.preventDefault()} camera={{ fov: 20, position: [4, 0, -16] }} > - + + {/* I really like apartment, dawn, and sunset */} @@ -132,34 +146,152 @@ export default function Page({ params }: { params: { id: string } }) { - {gltf.scene.children[0].children.map((child) => ( - { + const isHull = child.name === 'hull'; + const isTurret = child.name.startsWith('turret_'); + const isMantlet = + child.name.startsWith('gun_') && + child.name.endsWith('_mask'); + const isGun = child.name.startsWith('gun_') && !isMantlet; + const isVisible = + isHull || + child.name.startsWith('chassis_track_') || + child.name.startsWith('chassis_wheel_') || + (child.name.startsWith('turret_') && child.name === - `gun_${gun.model.toString().padStart(2, '0')}_mask` + `turret_${turret.model + .toString() + .padStart(2, '0')}`) || + (child.name.startsWith('gun_') && + child.name === + `gun_${gun.model.toString().padStart(2, '0')}`) || + child.name === + `gun_${gun.model.toString().padStart(2, '0')}_mask`; + let draftTurretRotation = 0; + const position = child.position.clone(); + const rotation = new Vector3().setFromEuler( + child.rotation, + ); + + if (!isVisible) return null; + + if (isTurret || isMantlet) { + position + .sub(turretOrigin) + .applyAxisAngle(new Vector3(0, 0, 1), turretRotation) + .add(turretOrigin); + rotation.add(new Vector3(0, 0, turretRotation)); + } else if (isGun) { + position + .sub(turretOrigin) + .applyAxisAngle(new Vector3(0, 0, 1), turretRotation) + .add(turretOrigin); + rotation.add(new Vector3(0, 0, turretRotation)); + } + + function handlePointerDown( + event: ThreeEvent, + ) { + if (isTurret) { + event.stopPropagation(); + + draftTurretRotation = turretRotation; + + setOrbitControlsEnabled(false); + window.addEventListener( + 'pointermove', + handlePointerMove, + ); + window.addEventListener('pointerup', handlePointerUp); } - children={resolveJsxTree(child as Mesh)} - key={child.uuid} - castShadow - receiveShadow - geometry={(child as Mesh).geometry} - material={(child as Mesh).material} - position={child.position} - rotation={child.rotation} - scale={child.scale} - /> - ))} + } + function handlePointerMove(event: PointerEvent) { + event.stopPropagation(); + event.preventDefault(); + + const rotationSpeed = + (2 * Math.PI) / canvas.current!.width; + const deltaTurretRotation = + event.movementX * rotationSpeed; + draftTurretRotation += deltaTurretRotation; + + if (isTurret) { + if (turretObject3D.current) { + turretObject3D.current.position + .sub(turretOrigin) + .applyAxisAngle( + new Vector3(0, 0, 1), + deltaTurretRotation, + ) + .add(turretOrigin); + turretObject3D.current.rotation.z = + draftTurretRotation; + } + + if (mantletObject3D.current) { + mantletObject3D.current.position + .sub(turretOrigin) + .applyAxisAngle( + new Vector3(0, 0, 1), + deltaTurretRotation, + ) + .add(turretOrigin); + mantletObject3D.current.rotation.z = + draftTurretRotation; + } + + if (gunObject3D.current) { + gunObject3D.current.position + .sub(turretOrigin) + .applyAxisAngle( + new Vector3(0, 0, 1), + deltaTurretRotation, + ) + .add(turretOrigin); + gunObject3D.current.rotation.z = + draftTurretRotation; + } + } + } + function handlePointerUp(event: PointerEvent) { + setOrbitControlsEnabled(true); + setTurretRotation(draftTurretRotation); + + window.removeEventListener( + 'pointermove', + handlePointerMove, + ); + window.removeEventListener( + 'pointerup', + handlePointerUp, + ); + } + + return ( + + ); + })}
diff --git a/src/core/blitzkrieg/tankDefinitions.ts b/src/core/blitzkrieg/tankDefinitions.ts index e76fd0504..58c791d74 100644 --- a/src/core/blitzkrieg/tankDefinitions.ts +++ b/src/core/blitzkrieg/tankDefinitions.ts @@ -33,7 +33,7 @@ export interface TurretDefinition { } export type GunDefinition = GunDefinitionBase & (GunDefinitionRegular | GunDefinitionAutoLoader | GunDefinitionAutoReloader); -interface GunDefinitionBase { +interface GunDefinitionBase { id: number; name: string; tier: Tier;