diff --git a/public/test/11633.glb b/public/test/11633.glb index 379cca1ba..b408aa2ae 100644 Binary files a/public/test/11633.glb and b/public/test/11633.glb differ diff --git a/scripts/buildAssets.ts b/scripts/buildAssets.ts index 5f918f1a1..0a6d69627 100644 --- a/scripts/buildAssets.ts +++ b/scripts/buildAssets.ts @@ -533,7 +533,7 @@ if (allTargets || targets?.includes('tankModels')) { // if (id !== 1) continue; // t-34 // if (id !== 6753) continue; // type 71 // if (id !== 5137) continue; // tiger ii - if (id !== 11633) continue; // tiger ii + if (id !== 11633) continue; // forest witch // if (id !== 6225) continue; // fv215b console.log(`Building model ${id} @ ${nation}/${tankIndex}`); diff --git a/src/app/tools/tankopedia/[id]/page.tsx b/src/app/tools/tankopedia/[id]/page.tsx index 2db1e28d5..b3c865b6e 100644 --- a/src/app/tools/tankopedia/[id]/page.tsx +++ b/src/app/tools/tankopedia/[id]/page.tsx @@ -21,7 +21,7 @@ import { } from '@radix-ui/themes'; import { Canvas, ThreeEvent, useLoader } from '@react-three/fiber'; import { go } from 'fuzzysort'; -import { debounce } from 'lodash'; +import { clamp, debounce } from 'lodash'; import Link from 'next/link'; import { use, useEffect, useRef, useState } from 'react'; import { Color, Group, Mesh, Vector3 } from 'three'; @@ -67,9 +67,14 @@ export default function Page({ params }: { params: { id: string } }) { 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 [hullRotation, setHullRotation] = useState(0); + const gunOrigin = new Vector3( + turret.gunOrigin[0], + turret.gunOrigin[1], + -turret.gunOrigin[2], + ).applyAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + const [turretYaw, setTurretYaw] = useState(0); + const [hullYaw, setHullYaw] = useState(0); + const [mantletPitch, setMantletPitch] = useState(0); const [orbitControlsEnabled, setOrbitControlsEnabled] = useState(true); const turretRotationInput = useRef(null); const hullRotationInput = useRef(null); @@ -93,14 +98,19 @@ export default function Page({ params }: { params: { id: string } }) { }, [versusTurret]); useEffect(() => { turretRotationInput.current!.value = `${Math.round( - turretRotation * (180 / Math.PI), + turretYaw * (180 / Math.PI), )}`; - }, [turretRotation]); + }, [turretYaw]); useEffect(() => { hullRotationInput.current!.value = `${Math.round( - hullRotation * (180 / Math.PI), + hullYaw * (180 / Math.PI), )}`; - }); + }, [hullYaw]); + useEffect(() => { + gunRotationInput.current!.value = `${Math.round( + mantletPitch * (180 / Math.PI), + )}`; + }, [mantletPitch]); return ( @@ -173,7 +183,7 @@ export default function Page({ params }: { params: { id: string } }) { {gltf.scene.children[0].children.map((child) => { const isHull = child.name === 'hull'; @@ -188,7 +198,7 @@ export default function Page({ params }: { params: { id: string } }) { if (isHull) { event.stopPropagation(); - draftTurretRotation = hullRotation; + draftTurretRotation = hullYaw; setOrbitControlsEnabled(false); window.addEventListener( @@ -214,7 +224,7 @@ export default function Page({ params }: { params: { id: string } }) { } function handlePointerUp(event: PointerEvent) { setOrbitControlsEnabled(true); - setHullRotation(normalizeAngle180(draftTurretRotation)); + setHullYaw(normalizeAngle180(draftTurretRotation)); window.removeEventListener( 'pointermove', @@ -246,9 +256,9 @@ export default function Page({ params }: { params: { id: string } }) { {gltf.scene.children[0].children.map((child) => { @@ -270,7 +280,7 @@ export default function Page({ params }: { params: { id: string } }) { if (isTurret) { event.stopPropagation(); - draftTurretRotation = turretRotation; + draftTurretRotation = turretYaw; setOrbitControlsEnabled(false); window.addEventListener( @@ -303,9 +313,7 @@ export default function Page({ params }: { params: { id: string } }) { } function handlePointerUp(event: PointerEvent) { setOrbitControlsEnabled(true); - setTurretRotation( - normalizeAngle180(draftTurretRotation), - ); + setTurretYaw(normalizeAngle180(draftTurretRotation)); window.removeEventListener( 'pointermove', @@ -335,11 +343,13 @@ export default function Page({ params }: { params: { id: string } }) { })} {gltf.scene.children[0].children.map((child) => { @@ -365,7 +375,7 @@ export default function Page({ params }: { params: { id: string } }) { ) { event.stopPropagation(); - draftMantletPitch = turretRotation; + draftMantletPitch = mantletPitch; setOrbitControlsEnabled(false); window.addEventListener( @@ -385,11 +395,22 @@ export default function Page({ params }: { params: { id: string } }) { (2 * Math.PI) / canvas.current!.width; const delta = -event.movementY * rotationSpeed; draftMantletPitch += delta; + draftMantletPitch = clamp( + draftMantletPitch, + -gun.pitch[1] * (Math.PI / 180), + -gun.pitch[0] * (Math.PI / 180), + ); if (mantletContainer.current) { mantletContainer.current.position + .set(0, 0, 0) + .sub(turretOrigin) .sub(gunOrigin) - .applyAxisAngle(new Vector3(1, 0, 0), delta) + .applyAxisAngle( + new Vector3(1, 0, 0), + draftMantletPitch, + ) + .add(turretOrigin) .add(gunOrigin); mantletContainer.current.rotation.x = draftMantletPitch; @@ -397,7 +418,7 @@ export default function Page({ params }: { params: { id: string } }) { } function handlePointerUp(event: PointerEvent) { setOrbitControlsEnabled(true); - setTurretRotation( + setMantletPitch( normalizeAngle180(draftMantletPitch), ); @@ -458,7 +479,7 @@ export default function Page({ params }: { params: { id: string } }) { Hull { - setHullRotation( + setHullYaw( normalizeAngle180( Number(hullRotationInput.current?.value) * (Math.PI / 180), @@ -485,7 +506,7 @@ export default function Page({ params }: { params: { id: string } }) { Turret { - setTurretRotation( + setTurretYaw( normalizeAngle180( Number(turretRotationInput.current?.value) * (Math.PI / 180), @@ -511,8 +532,24 @@ export default function Page({ params }: { params: { id: string } }) { > Gun { + setMantletPitch( + clamp( + normalizeAngle180( + Number(gunRotationInput.current?.value) * + (Math.PI / 180), + ), + -gun.pitch[1] * (Math.PI / 180), + -gun.pitch[0] * (Math.PI / 180), + ), + ); + }} + onKeyDown={(event) => { + if (event.key === 'Enter') { + gunRotationInput.current?.blur(); + } + }} ref={gunRotationInput} - value={0} style={{ textAlign: 'right' }} /> ° diff --git a/src/core/blitz/extractModel.ts b/src/core/blitz/extractModel.ts index 6931e4521..6320c0ef1 100644 --- a/src/core/blitz/extractModel.ts +++ b/src/core/blitz/extractModel.ts @@ -14,9 +14,9 @@ import { ScgStream, vertexAttributeVectorSizes } from '../streams/scg'; import { VertexAttribute } from '../streams/scpg'; import { readDVPLFile } from './readDVPLFile'; -BigInt.prototype.toJSON = function () { - return this.toString(); -}; +// BigInt.prototype.toJSON = function () { +// return this.toString(); +// }; const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23); @@ -175,11 +175,11 @@ export async function extractModel( case 'ActionComponent': case 'ParticleEffectComponent': case 'SlotComponent': + case 'MotionComponent': case 'TankElementComponent': + case 'SkeletonComponent': break; - // console.log(JSON.stringify(component, null, 2)); - case 'TransformComponent': { const translation = new Vector3(); const rotation = new Quaternion(); diff --git a/src/core/streams/sc2.ts b/src/core/streams/sc2.ts index 64b69d783..8567f6f21 100644 --- a/src/core/streams/sc2.ts +++ b/src/core/streams/sc2.ts @@ -232,6 +232,38 @@ export type Component = isDestructible: boolean; shouldCastShadow: boolean; shouldGenerateSilhouette: boolean; + } + | { + 'comp.typename': 'MotionComponent'; + 'motion.filepath': string; + 'motion.playbackRate': number; + 'simpleMotion.repeatsCount': number; + } + | { + 'comp.typename': 'SkeletonComponent'; + joints: Record< + string, // 0000, 0001, ... + { + 'joint.bbox.max': Vector3Tuple; + 'joint.bbox.min': Vector3Tuple; + 'joint.bindPose': [ + Vector4Tuple, + Vector4Tuple, + Vector4Tuple, + Vector4Tuple, + ]; + 'joint.invBindPose': [ + Vector4Tuple, + Vector4Tuple, + Vector4Tuple, + Vector4Tuple, + ]; + 'joint.name': string; + 'joint.parentIndex': number; + 'joint.uid': string; + } + >; + jointsCount: number; }; interface SceneComponents {