From 39aeba07668ebb88c6f52951e38690be2cfc4a35 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sun, 14 Jun 2026 12:29:52 -0500 Subject: [PATCH] first try at custom camera controls --- cspell.json | 5 +- .../src/components/TankopediaControls.tsx | 70 +++++++++++++++++++ .../components/TankopediaSandbox/index.tsx | 5 +- 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 packages/website-ue/src/components/TankopediaControls.tsx diff --git a/cspell.json b/cspell.json index b922dff13..3a671e892 100644 --- a/cspell.json +++ b/cspell.json @@ -14,7 +14,10 @@ "RMAO", "Verts", "drei", - "frameloop" + "frameloop", + "pointermove", + "pointerup", + "pointerdown" ], "userWords": [ "WOTB", diff --git a/packages/website-ue/src/components/TankopediaControls.tsx b/packages/website-ue/src/components/TankopediaControls.tsx new file mode 100644 index 000000000..4d8a7fbfd --- /dev/null +++ b/packages/website-ue/src/components/TankopediaControls.tsx @@ -0,0 +1,70 @@ +import { J_HAT, K_HAT } from "@blitzkit/core"; +import { invalidate, useThree } from "@react-three/fiber"; +import { useEffect } from "react"; +import { Vector2, Vector3 } from "three"; +import { degToRad } from "three/src/math/MathUtils.js"; + +const initialDistance = 8; +const initialRotation = new Vector2(degToRad(45), degToRad(45)); + +const clientPosition = new Vector2(); +const workingVector2 = new Vector2(); +const workingVector3 = new Vector3(); + +const cameraCenter = new Vector3(0, 1, 0); + +const cameraRotation = initialRotation.clone(); +let cameraDistance = initialDistance; + +export function TankopediaControls() { + const canvas = useThree((state) => state.gl.domElement); + const camera = useThree((state) => state.camera); + + useEffect(() => { + function handlePointerDown(event: PointerEvent) { + clientPosition.set(event.clientX, event.clientY); + + window.addEventListener("pointermove", handlePointerMove); + window.addEventListener("pointerup", handlePointerUp); + } + + function handlePointerMove(event: PointerEvent) { + workingVector2.set(event.clientX, event.clientY).sub(clientPosition); + + workingVector2.x *= -(2 * Math.PI) / canvas.width; + workingVector2.y *= (2 * Math.PI) / canvas.height; + + cameraRotation.x += workingVector2.x; + + if (cameraRotation.y + workingVector2.y < Math.PI / 2) { + cameraRotation.y += workingVector2.y; + } + + clientPosition.set(event.clientX, event.clientY); + + updateCameraPosition(); + } + + function handlePointerUp() { + window.removeEventListener("pointermove", handlePointerMove); + window.removeEventListener("pointerup", handlePointerUp); + } + + function updateCameraPosition() { + camera.position + .set(cameraDistance, 0, 0) + .applyAxisAngle(J_HAT, cameraRotation.x) + .applyAxisAngle(K_HAT, cameraRotation.y) + .add(cameraCenter); + camera.lookAt(cameraCenter); + + invalidate(); + } + + canvas.addEventListener("pointerdown", handlePointerDown); + + return () => { + canvas.removeEventListener("pointerdown", handlePointerDown); + }; + }, []); +} diff --git a/packages/website-ue/src/components/TankopediaSandbox/index.tsx b/packages/website-ue/src/components/TankopediaSandbox/index.tsx index cfde0340a..ca0df20ae 100644 --- a/packages/website-ue/src/components/TankopediaSandbox/index.tsx +++ b/packages/website-ue/src/components/TankopediaSandbox/index.tsx @@ -1,10 +1,10 @@ import { type StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage"; -import { OrbitControls } from "@react-three/drei"; import { Canvas } from "@react-three/fiber"; import { useTankChassisModel } from "../../hooks/useTankChassisModel"; import { useTankGunModel } from "../../hooks/useTankGunModel"; import { useTankHullModel } from "../../hooks/useTankHullModel"; import { useTankTurretModel } from "../../hooks/useTankTurretModel"; +import { TankopediaControls } from "../TankopediaControls"; import { TankopediaLighting } from "../TankopediaLighting"; import { SceneProps } from "../TankopediaSceneProps"; import styles from "./index.module.css"; @@ -53,8 +53,7 @@ function Content({ parameters }: TankopediaSandboxProps) { - - + ); }