New Engine/Core/Input.h/.cpp: static input facade with a Key/MouseButton enum (values map to GLFW key codes internally) so scripts and game logic no longer touch GLFW directly - window backend becomes swappable and the facade is the surface to expose when embedding scripting later. Wired up in Application's constructor; Camera (editor camera WASD/RMB/MMB), the WASDMove script and SandboxApp key handling now query Input only. Ctrl+P hotkey fix: the !m_Playing guard made the hotkey dead exactly when it was needed - after pressing Play. Now toggles both ways. Toolbar Pause button now reads "Resume" while paused (tooltip follows).
179 lines
7.9 KiB
C++
179 lines
7.9 KiB
C++
//
|
||
// Created by atdunbg on 2026/9/22.
|
||
//
|
||
|
||
#include <Engine/Renderer/Camera.h>
|
||
#include <Engine/Core/Window.h>
|
||
#include <Engine/ECS/Components.h>
|
||
|
||
#include <glm/gtc/matrix_transform.hpp>
|
||
|
||
#include <cmath>
|
||
|
||
#include "Engine/Core/Input.h" // 键查询走统一输入门面(不依赖 GLFW)
|
||
|
||
namespace Engine {
|
||
|
||
namespace {
|
||
// 欧拉(度)→ 旋转矩阵:应用序 Y→X,与 TransformComponent::
|
||
// Compose 一致(roll 不参与 FPS/编辑器相机)
|
||
glm::mat3 EulerRotMat(const glm::vec3& rotDeg)
|
||
{
|
||
return static_cast<glm::mat3>(
|
||
glm::rotate(glm::mat4(1.0f), glm::radians(rotDeg.y),
|
||
glm::vec3(0, 1, 0)) *
|
||
glm::rotate(glm::mat4(1.0f), glm::radians(rotDeg.x),
|
||
glm::vec3(1, 0, 0)));
|
||
}
|
||
}
|
||
|
||
void Camera::Update(Window& window, ECS::TransformComponent& transform,
|
||
const f32 deltaTime, const bool flyRequiresRMB)
|
||
{
|
||
// ---- 输入模式(UE Scene view 语义):RMB=自由视角(鼠标转向
|
||
// + 飞行),MMB=焦点环绕(Shift+MMB=平移),滚轮=推拉。三种
|
||
// 光标型交互互斥共用捕获;编辑器模式 flyRequiresRMB=true 时
|
||
// 移动键仅 RMB 按住生效(否则与 Gizmo W/E/R 热键冲突)----
|
||
const bool wantLook = m_MouseLookEnabled &&
|
||
Input::IsMouseButtonDown(Input::MouseButton::Right);
|
||
const bool wantOrbit = m_MouseLookEnabled &&
|
||
Input::IsMouseButtonDown(Input::MouseButton::Middle);
|
||
|
||
if (wantLook || wantOrbit)
|
||
{
|
||
if (!window.IsCursorCaptured())
|
||
{
|
||
window.SetCursorCaptured(true);
|
||
}
|
||
|
||
f32 mouseDX = 0, mouseDY = 0;
|
||
window.GetMouseDelta(mouseDX, mouseDY);
|
||
|
||
if (mouseDX != 0 || mouseDY != 0)
|
||
{
|
||
glm::vec3 rot = transform.GetRotation();
|
||
// GetRotation 是 0-360 显示域(UE Details 约定);pitch 的
|
||
// clamp 需要连续域,wrap 回 [-180,180)(yaw 同步处理,
|
||
// 跨 ±180 继续累减不打折返)
|
||
for (int i = 0; i < 2; ++i) {
|
||
rot[i] = std::fmod(rot[i], 360.0f);
|
||
if (rot[i] >= 180.0f) rot[i] -= 360.0f;
|
||
if (rot[i] < -180.0f) rot[i] += 360.0f;
|
||
}
|
||
// 右手系绕 Y 正角度 = 向左转,鼠标右移(DX>0)应看向右
|
||
// → yaw 取负。pitch 正角度 = 抬头,鼠标下移(DY>0)低头
|
||
// → rot.x 直接用负号,两者符号已核对一致
|
||
rot.y -= mouseDX * m_MouseSensitivity;
|
||
rot.x = glm::clamp(
|
||
rot.x - mouseDY * m_MouseSensitivity, -89.0f, 89.0f);
|
||
transform.SetRotation(rot);
|
||
|
||
if (wantOrbit)
|
||
{
|
||
if (Input::IsKeyDown(Input::Key::LeftShift))
|
||
{
|
||
// ---- 屏幕平面平移:内容跟手(拖右 → 相机左移、
|
||
// 拖下 → 相机上移),pivot 同步平移保持环绕点的
|
||
// 屏幕相对位置 ----
|
||
const glm::mat3 r = EulerRotMat(rot);
|
||
const glm::vec3 delta =
|
||
(-r * glm::vec3(1, 0, 0)) * mouseDX +
|
||
(r * glm::vec3(0, 1, 0)) * mouseDY;
|
||
const f32 k = m_OrbitDistance * 0.002f; // m/px
|
||
transform.SetPosition(transform.GetPosition() + delta * k);
|
||
if (m_HasPivot) m_OrbitPivot += delta * k;
|
||
}
|
||
else
|
||
{
|
||
// ---- 焦点环绕:转向后保持到 pivot 的距离。首次
|
||
// 环绕无 pivot:取当前视线上 m_OrbitDistance 处
|
||
if (!m_HasPivot)
|
||
{
|
||
m_OrbitPivot = transform.GetPosition() +
|
||
EulerRotMat(transform.GetRotation()) *
|
||
glm::vec3(0, 0, -1) *
|
||
m_OrbitDistance;
|
||
m_HasPivot = true;
|
||
}
|
||
m_OrbitDistance = glm::length(
|
||
transform.GetPosition() - m_OrbitPivot);
|
||
const glm::vec3 orbitFwd =
|
||
EulerRotMat(rot) * glm::vec3(0, 0, -1);
|
||
transform.SetPosition(m_OrbitPivot -
|
||
orbitFwd * m_OrbitDistance);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (window.IsCursorCaptured())
|
||
{
|
||
window.SetCursorCaptured(false);
|
||
}
|
||
|
||
// 基向量由(可能已更新的)实体旋转决定(本地 -Z 为视线方向)
|
||
const glm::mat3 r = EulerRotMat(transform.GetRotation());
|
||
const glm::vec3 forward = r * glm::vec3(0, 0, -1);
|
||
const glm::vec3 right = r * glm::vec3(1, 0, 0);
|
||
|
||
// ---- 飞行移动:编辑器模式仅 RMB(wantLook)时生效;游戏模式
|
||
// 常开(原行为)----
|
||
if (wantLook || !flyRequiresRMB)
|
||
{
|
||
const f32 speed = m_MoveSpeed * deltaTime;
|
||
if (Input::IsKeyDown(Input::Key::W)) { transform.SetPosition(transform.GetPosition() + forward * speed); }
|
||
if (Input::IsKeyDown(Input::Key::S)) { transform.SetPosition(transform.GetPosition() - forward * speed); }
|
||
if (Input::IsKeyDown(Input::Key::A)) { transform.SetPosition(transform.GetPosition() - right * speed); }
|
||
if (Input::IsKeyDown(Input::Key::D)) { transform.SetPosition(transform.GetPosition() + right * speed); }
|
||
if (Input::IsKeyDown(Input::Key::Space)) { transform.SetPosition(transform.GetPosition() + glm::vec3(0, speed, 0)); }
|
||
if (Input::IsKeyDown(Input::Key::LeftShift)) { transform.SetPosition(transform.GetPosition() - glm::vec3(0, speed, 0)); }
|
||
}
|
||
|
||
// ---- 滚轮推拉:有 pivot 时改环绕距离(指数步进,近处精细/
|
||
// 远处大步);无 pivot 时沿视线平移 ----
|
||
const f32 wheel = window.ConsumeScrollDelta();
|
||
if (m_MouseLookEnabled && wheel != 0.0f)
|
||
{
|
||
if (m_HasPivot)
|
||
{
|
||
m_OrbitDistance = glm::clamp(
|
||
m_OrbitDistance * std::pow(0.9f, wheel), 0.5f, 500.0f);
|
||
transform.SetPosition(m_OrbitPivot - forward * m_OrbitDistance);
|
||
}
|
||
else
|
||
{
|
||
transform.SetPosition(transform.GetPosition() +
|
||
forward * wheel * m_MoveSpeed * deltaTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
void Camera::FocusOn(ECS::TransformComponent& transform, const glm::vec3& point)
|
||
{
|
||
const glm::vec3 fwd =
|
||
EulerRotMat(transform.GetRotation()) * glm::vec3(0, 0, -1);
|
||
m_OrbitPivot = point;
|
||
m_HasPivot = true;
|
||
transform.SetPosition(point - fwd * m_OrbitDistance);
|
||
}
|
||
|
||
glm::mat4 Camera::GetView(const ECS::TransformComponent& t) const
|
||
{
|
||
// 相机作为实体:视图 = 世界矩阵的逆(无重复位姿状态)
|
||
return glm::inverse(t.World());
|
||
}
|
||
|
||
glm::mat4 Camera::GetProjection(const f32 aspect, const f32 nearZ, const f32 farZ) const
|
||
{
|
||
auto proj = glm::perspective(glm::radians(45.0f), aspect, nearZ, farZ);
|
||
proj[1][1] *= -1.0f; // Vulkan Y 翻转
|
||
|
||
// Vulkan clip 深度 [0,1]:z' = 0.5*z_clip + 0.5*w_clip。
|
||
// perspective 的 w_clip = -z_eye,偏移折算进 z 系数
|
||
//(注意:与 ortho 的公式不同,ortho 的 w=1 才能直接加到 [3][2])
|
||
proj[2][2] = proj[2][2] * 0.5f - 0.5f;
|
||
proj[3][2] *= 0.5f;
|
||
return proj;
|
||
}
|
||
|
||
} // namespace Engine
|