From 28c30f210e5fa868fbf3fb947c8b03258d9df53e Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:02:48 -0500 Subject: [PATCH] cool --- packages/website/src/components/TankModel.tsx | 465 ++++++++---------- 1 file changed, 210 insertions(+), 255 deletions(-) diff --git a/packages/website/src/components/TankModel.tsx b/packages/website/src/components/TankModel.tsx index 8c808ddde..59a5a84a4 100644 --- a/packages/website/src/components/TankModel.tsx +++ b/packages/website/src/components/TankModel.tsx @@ -14,6 +14,9 @@ import { Object3D, Vector2, } from "three"; +import { applyPitchYawLimits } from "../core/blitz/applyPitchYawLimits"; +import { hasEquipment } from "../core/blitzkit/hasEquipment"; +import { modelTransformEvent } from "../core/blitzkit/modelTransform"; import { controlsEnabledEvent } from "../core/controlsEnabled"; import { useModel } from "../hooks/useModel"; import { useTankModelDefinition } from "../hooks/useTankModelDefinition"; @@ -25,10 +28,10 @@ import { ModelTankWrapper } from "./Armor/components/ModelTankWrapper"; interface BatchedProps extends ComponentProps<"group"> { slicer: (node: Object3D) => boolean; - mutator: (mesh: Mesh) => void; + mutator?: (mesh: Mesh) => void; } -function useBatched(map: ObjectMap) { +function useBatched(map: ObjectMap, shadows = false) { return function Batched({ slicer, mutator, ...props }: BatchedProps) { const groups = new Map(); @@ -68,7 +71,13 @@ function useBatched(map: ObjectMap) { ); for (const mesh of meshes) { - mutator(mesh); + if (shadows) { + mesh.castShadow = true; + mesh.receiveShadow = true; + } + + mutator?.(mesh); + batched.add(mesh); } @@ -87,39 +96,36 @@ interface SectionProps { map: ObjectMap; } -function Hull({ map }: SectionProps) { - const Batched = useBatched(map); - - function handlePointerDown(event: ThreeEvent) { - // event.stopPropagation(); - } - - return ( - { - const isHull = node.name === "hull"; - const isWheel = node.name.startsWith("chassis_wheel_"); - const isTrack = node.name.startsWith("chassis_track_"); - - return isHull || isWheel || isTrack; - }} - mutator={(mesh) => { - mesh.castShadow = true; - mesh.receiveShadow = true; - }} - /> - ); -} - function Tracks({ map }: SectionProps) { - const Batched = useBatched(map); + const Batched = useBatched(map, true); const position = useRef(new Vector2()); const delta = useRef(new Vector2()); const textures = useRef(new Set()); + function handlePointerMove(event: PointerEvent) { + delta.current.set(event.clientX, event.clientY).sub(position.current); + position.current.set(event.clientX, event.clientY); + + const deltaX = delta.current.x / window.innerWidth; + const deltaY = delta.current.y / window.innerHeight; + + const offset = deltaX + deltaY; + + for (const texture of textures.current) { + if (!texture) continue; + texture.offset.y += offset; + } + + invalidate(); + } + function handlePointerUp() { + controlsEnabledEvent.dispatch(true); + + window.removeEventListener("pointermove", handlePointerMove); + window.removeEventListener("pointerup", handlePointerUp); + } function handlePointerDown(event: ThreeEvent) { if (Tankopedia.state.display !== TankopediaDisplay.Model) { return; @@ -143,27 +149,22 @@ function Tracks({ map }: SectionProps) { window.addEventListener("pointermove", handlePointerMove); window.addEventListener("pointerup", handlePointerUp); } - function handlePointerMove(event: PointerEvent) { - delta.current.set(event.clientX, event.clientY).sub(position.current); - position.current.set(event.clientX, event.clientY); - const deltaX = delta.current.x / window.innerWidth; - const deltaY = delta.current.y / window.innerHeight; + return ( + { + return node.name.startsWith("chassis_track_"); + }} + /> + ); +} - const offset = deltaX + deltaY; +function Hull({ map }: SectionProps) { + const Batched = useBatched(map, true); - for (const texture of textures.current) { - if (!texture) continue; - texture.offset.y += offset; - } - - invalidate(); - } - function handlePointerUp() { - controlsEnabledEvent.dispatch(true); - - window.removeEventListener("pointermove", handlePointerMove); - window.removeEventListener("pointerup", handlePointerUp); + function handlePointerDown(event: ThreeEvent) { + event.stopPropagation(); } return ( @@ -172,13 +173,166 @@ function Tracks({ map }: SectionProps) { slicer={(node) => { const isHull = node.name === "hull"; const isWheel = node.name.startsWith("chassis_wheel_"); - const isTrack = node.name.startsWith("chassis_track_"); - return isHull || isWheel || isTrack; + return isHull || isWheel; }} - mutator={(mesh) => { - mesh.castShadow = true; - mesh.receiveShadow = true; + /> + ); +} + +function Turret({ map }: SectionProps) { + const turret = Duel.use((state) => state.protagonist.turret); + const gun = Duel.use((state) => state.protagonist.gun); + + const tankModelDefinition = useTankModelDefinition(); + const turretModelDefinition = tankModelDefinition.turrets[turret.id]; + const gunModelDefinition = turretModelDefinition.guns[gun.id]; + + const Batched = useBatched(map, true); + + const position = useRef(new Vector2()); + const delta = useRef(new Vector2()); + + const canvas = useThree((state) => state.gl.domElement); + + function onPointerDown(event: ThreeEvent) { + event.stopPropagation(); + + position.current.set(event.clientX, event.clientY); + + controlsEnabledEvent.dispatch(false); + Tankopedia.mutate((draft) => { + draft.shot = undefined; + draft.highlightArmor = undefined; + }); + window.addEventListener("pointermove", handlePointerMove); + window.addEventListener("pointerup", handlePointerUp); + } + function handlePointerMove(event: PointerEvent) { + const hasImprovedVerticalStabilizer = hasEquipment( + 122, + Duel.state.protagonist.tank.equipment_preset, + Duel.state.protagonist.equipmentMatrix, + ); + const hasDownImprovedVerticalStabilizer = hasEquipment( + 124, + Duel.state.protagonist.tank.equipment_preset, + Duel.state.protagonist.equipmentMatrix, + ); + const boundingRect = canvas.getBoundingClientRect(); + + delta.current.set(event.clientX, event.clientY).sub(position.current); + position.current.set(event.clientX, event.clientY); + + const [pitch, yaw] = applyPitchYawLimits( + modelTransformEvent.last!.pitch, + modelTransformEvent.last!.yaw + + delta.current.x * (Math.PI / boundingRect.width), + gunModelDefinition.pitch!, + turretModelDefinition.yaw, + hasImprovedVerticalStabilizer, + hasDownImprovedVerticalStabilizer, + ); + modelTransformEvent.dispatch({ pitch, yaw }); + } + function handlePointerUp() { + controlsEnabledEvent.dispatch(true); + window.removeEventListener("pointermove", handlePointerMove); + window.removeEventListener("pointerup", handlePointerUp); + } + + return ( + { + const isCurrentTurret = + node.name === + `turret_${turretModelDefinition.model_id + .toString() + .padStart(2, "0")}`; + + return isCurrentTurret; + }} + /> + ); +} + +function Gun({ map }: SectionProps) { + const Batched = useBatched(map, true); + + const turret = Duel.use((state) => state.protagonist.turret); + const gun = Duel.use((state) => state.protagonist.gun); + + const tankModelDefinition = useTankModelDefinition(); + const turretModelDefinition = tankModelDefinition.turrets[turret.id]; + const gunModelDefinition = turretModelDefinition.guns[gun.id]; + + const position = useRef(new Vector2()); + const delta = useRef(new Vector2()); + const pitch = useRef(0); + const yaw = useRef(0); + + const canvas = useThree((state) => state.gl.domElement); + + function handlePointerMove(event: PointerEvent) { + const hasImprovedVerticalStabilizer = hasEquipment( + 122, + Duel.state.protagonist.tank.equipment_preset, + Duel.state.protagonist.equipmentMatrix, + ); + const hasDownImprovedVerticalStabilizer = hasEquipment( + 124, + Duel.state.protagonist.tank.equipment_preset, + Duel.state.protagonist.equipmentMatrix, + ); + const boundingRect = canvas.getBoundingClientRect(); + delta.current.set(event.clientX, event.clientY).sub(position.current); + position.current.set(event.clientX, event.clientY); + + [pitch.current, yaw.current] = applyPitchYawLimits( + pitch.current - delta.current.y * (Math.PI / boundingRect.height), + yaw.current + delta.current.x * (Math.PI / boundingRect.width), + gunModelDefinition.pitch!, + turretModelDefinition.yaw, + hasImprovedVerticalStabilizer, + hasDownImprovedVerticalStabilizer, + ); + modelTransformEvent.dispatch({ pitch: pitch.current, yaw: yaw.current }); + } + function handlePointerUp() { + controlsEnabledEvent.dispatch(true); + window.removeEventListener("pointermove", handlePointerMove); + window.removeEventListener("pointerup", handlePointerUp); + } + function onPointerDown(event: ThreeEvent) { + event.stopPropagation(); + + controlsEnabledEvent.dispatch(false); + Tankopedia.mutate((draft) => { + draft.shot = undefined; + draft.highlightArmor = undefined; + }); + + position.current.set(event.clientX, event.clientY); + pitch.current = modelTransformEvent.last!.pitch; + yaw.current = modelTransformEvent.last!.yaw; + + window.addEventListener("pointermove", handlePointerMove); + window.addEventListener("pointerup", handlePointerUp); + } + + return ( + { + const isCurrentMantlet = + node.name === + `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}_mask`; + const isCurrentGun = + node.name === + `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`; + + return isCurrentGun || isCurrentMantlet; }} /> ); @@ -195,7 +349,6 @@ export function TankModel() { const tankModelDefinition = useTankModelDefinition(); const turretModelDefinition = tankModelDefinition.turrets[protagonist.turret.id]; - const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id]; const { gltf } = useModel(protagonist.tank.id); @@ -203,214 +356,16 @@ export function TankModel() { return ( - - - {/* {nodes.map((node) => { - const isHull = node.name === "hull"; - const isWheel = node.name.startsWith("chassis_wheel_"); - const isTrack = node.name.startsWith("chassis_track_"); - const isVisible = isHull || isWheel || isTrack; - const position = new Vector2(); - const delta = new Vector2(); - - if (!isVisible) return null; - - function translateTexture(offset: number) { - const mesh = node.children[0] as Mesh; - const material = mesh?.material as MeshStandardMaterial; - - if (!material) return; - - invalidate(); - - const textures = new Set([ - material.map, - material.aoMap, - material.normalMap, - material.roughnessMap, - material.metalnessMap, - ]); - - for (const texture of textures) { - if (texture) texture.offset.y += offset; - } - } - - return jsxTree(node, { - mesh(_, props, key) { - return ( - - ); - }, - }); - })} + - {nodes.map((node) => { - const isTurret = node.name.startsWith("turret_"); - const isCurrentTurret = - node.name === - `turret_${turretModelDefinition.model_id - .toString() - .padStart(2, "0")}`; - const isVisible = isCurrentTurret; - const position = new Vector2(); - const delta = new Vector2(); - - if (!isVisible) return null; - - function onPointerDown(event: ThreeEvent) { - event.stopPropagation(); - - if (!isTurret) return; - - position.set(event.clientX, event.clientY); - - controlsEnabledEvent.dispatch(false); - Tankopedia.mutate((draft) => { - draft.shot = undefined; - draft.highlightArmor = undefined; - }); - window.addEventListener("pointermove", handlePointerMove); - window.addEventListener("pointerup", handlePointerUp); - } - function handlePointerMove(event: PointerEvent) { - const hasImprovedVerticalStabilizer = hasEquipment( - 122, - Duel.state.protagonist.tank.equipment_preset, - Duel.state.protagonist.equipmentMatrix, - ); - const hasDownImprovedVerticalStabilizer = hasEquipment( - 124, - Duel.state.protagonist.tank.equipment_preset, - Duel.state.protagonist.equipmentMatrix, - ); - const boundingRect = canvas.getBoundingClientRect(); - - delta.set(event.clientX, event.clientY).sub(position); - position.set(event.clientX, event.clientY); - - const [pitch, yaw] = applyPitchYawLimits( - modelTransformEvent.last!.pitch, - modelTransformEvent.last!.yaw + - delta.x * (Math.PI / boundingRect.width), - gunModelDefinition.pitch!, - turretModelDefinition.yaw, - hasImprovedVerticalStabilizer, - hasDownImprovedVerticalStabilizer, - ); - modelTransformEvent.dispatch({ pitch, yaw }); - } - function handlePointerUp() { - controlsEnabledEvent.dispatch(true); - window.removeEventListener("pointermove", handlePointerMove); - window.removeEventListener("pointerup", handlePointerUp); - } - - return jsxTree(node, { - mesh(_, props, key) { - return ( - - ); - }, - }); - })} + - {nodes.map((node) => { - const isCurrentMantlet = - node.name === - `gun_${gunModelDefinition.model_id - .toString() - .padStart(2, "0")}_mask`; - const isCurrentGun = - node.name === - `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`; - const isVisible = isCurrentGun || isCurrentMantlet; - const position = new Vector2(); - const delta = new Vector2(); - - if (!isVisible) return null; - - let pitch = 0; - let yaw = 0; - - function onPointerDown(event: ThreeEvent) { - event.stopPropagation(); - - controlsEnabledEvent.dispatch(false); - Tankopedia.mutate((draft) => { - draft.shot = undefined; - draft.highlightArmor = undefined; - }); - - position.set(event.clientX, event.clientY); - pitch = modelTransformEvent.last!.pitch; - yaw = modelTransformEvent.last!.yaw; - - window.addEventListener("pointermove", handlePointerMove); - window.addEventListener("pointerup", handlePointerUp); - } - function handlePointerMove(event: PointerEvent) { - const hasImprovedVerticalStabilizer = hasEquipment( - 122, - Duel.state.protagonist.tank.equipment_preset, - Duel.state.protagonist.equipmentMatrix, - ); - const hasDownImprovedVerticalStabilizer = hasEquipment( - 124, - Duel.state.protagonist.tank.equipment_preset, - Duel.state.protagonist.equipmentMatrix, - ); - const boundingRect = canvas.getBoundingClientRect(); - delta.set(event.clientX, event.clientY).sub(position); - position.set(event.clientX, event.clientY); - - [pitch, yaw] = applyPitchYawLimits( - pitch - delta.y * (Math.PI / boundingRect.height), - yaw + delta.x * (Math.PI / boundingRect.width), - gunModelDefinition.pitch!, - turretModelDefinition.yaw, - hasImprovedVerticalStabilizer, - hasDownImprovedVerticalStabilizer, - ); - modelTransformEvent.dispatch({ pitch, yaw }); - } - function handlePointerUp() { - controlsEnabledEvent.dispatch(true); - window.removeEventListener("pointermove", handlePointerMove); - window.removeEventListener("pointerup", handlePointerUp); - } - - return jsxTree(node, { - mesh(_, props, key) { - return ( - - ); - }, - }); - })} + - */} + ); }