feat: 添加独立的输入系统和平台抽象层

- 新增 Platform 模块:PlatformTypes.h, Window.h, WindowsWindow
- 新增 Input 模块:InputTypes, InputEvent, InputAxis, InputModule, InputManager
- 新增 WindowsInputModule 处理 Win32 消息转换
- 将 RHI 集成测试从 render_model 迁移到 sphere
- 更新 CMakeLists.txt 添加 Platform 和 Input 模块
This commit is contained in:
2026-03-22 15:21:52 +08:00
parent 6af872e9eb
commit 36d3decef6
29 changed files with 2896 additions and 236 deletions

View File

@@ -0,0 +1,304 @@
#include "Input/InputManager.h"
#include <algorithm>
namespace XCEngine {
namespace Input {
InputManager& InputManager::Get() {
static InputManager instance;
return instance;
}
void InputManager::Initialize(void* platformWindowHandle) {
if (m_initialized) return;
m_platformWindowHandle = platformWindowHandle;
m_keyDownThisFrame.resize(256, false);
m_keyDownLastFrame.resize(256, false);
m_keyDown.resize(256, false);
m_mouseButtonDownThisFrame.resize(5, false);
m_mouseButtonDownLastFrame.resize(5, false);
m_mouseButtonDown.resize(5, false);
m_buttonDownThisFrame.resize(32, false);
m_buttonDownLastFrame.resize(32, false);
RegisterAxis(InputAxis("Horizontal", KeyCode::D, KeyCode::A));
RegisterAxis(InputAxis("Vertical", KeyCode::W, KeyCode::S));
RegisterAxis(InputAxis("Mouse X", KeyCode::None, KeyCode::None));
RegisterAxis(InputAxis("Mouse Y", KeyCode::None, KeyCode::None));
RegisterButton("Jump", KeyCode::Space);
RegisterButton("Fire1", KeyCode::LeftCtrl);
RegisterButton("Fire2", KeyCode::LeftAlt);
RegisterButton("Fire3", KeyCode::LeftShift);
m_initialized = true;
}
void InputManager::Shutdown() {
if (!m_initialized) return;
m_keyDownThisFrame.clear();
m_keyDownLastFrame.clear();
m_keyDown.clear();
m_mouseButtonDownThisFrame.clear();
m_mouseButtonDownLastFrame.clear();
m_mouseButtonDown.clear();
m_axes.clear();
m_buttons.clear();
m_buttonDownThisFrame.clear();
m_buttonDownLastFrame.clear();
m_platformWindowHandle = nullptr;
m_initialized = false;
}
void InputManager::Update(float deltaTime) {
if (!m_initialized) return;
m_keyDownLastFrame = m_keyDownThisFrame;
m_mouseButtonDownLastFrame = m_mouseButtonDownThisFrame;
m_buttonDownLastFrame = m_buttonDownThisFrame;
m_mouseDelta = Math::Vector2::Zero();
m_mouseScrollDelta = 0.0f;
}
size_t InputManager::GetKeyIndex(KeyCode key) const {
return static_cast<size_t>(key);
}
size_t InputManager::GetMouseButtonIndex(MouseButton button) const {
return static_cast<size_t>(button);
}
bool InputManager::IsKeyDown(KeyCode key) const {
if (!m_initialized) return false;
size_t index = GetKeyIndex(key);
if (index >= m_keyDown.size()) return false;
return m_keyDown[index];
}
bool InputManager::IsKeyUp(KeyCode key) const {
if (!m_initialized) return true;
return !IsKeyDown(key);
}
bool InputManager::IsKeyPressed(KeyCode key) const {
if (!m_initialized) return false;
size_t index = GetKeyIndex(key);
if (index >= m_keyDownThisFrame.size()) return false;
return m_keyDownThisFrame[index] && !m_keyDownLastFrame[index];
}
Math::Vector2 InputManager::GetMousePosition() const {
return m_mousePosition;
}
Math::Vector2 InputManager::GetMouseDelta() const {
return m_mouseDelta;
}
float InputManager::GetMouseScrollDelta() const {
return m_mouseScrollDelta;
}
bool InputManager::IsMouseButtonDown(MouseButton button) const {
if (!m_initialized) return false;
size_t index = GetMouseButtonIndex(button);
if (index >= m_mouseButtonDown.size()) return false;
return m_mouseButtonDown[index];
}
bool InputManager::IsMouseButtonUp(MouseButton button) const {
if (!m_initialized) return true;
return !IsMouseButtonDown(button);
}
bool InputManager::IsMouseButtonClicked(MouseButton button) const {
if (!m_initialized) return false;
size_t index = GetMouseButtonIndex(button);
if (index >= m_mouseButtonDownThisFrame.size()) return false;
return m_mouseButtonDownThisFrame[index] && !m_mouseButtonDownLastFrame[index];
}
int InputManager::GetTouchCount() const {
return static_cast<int>(m_touches.size());
}
TouchState InputManager::GetTouch(int index) const {
if (index >= 0 && index < static_cast<int>(m_touches.size())) {
return m_touches[index];
}
return TouchState{};
}
float InputManager::GetAxis(const Containers::String& axisName) const {
auto it = m_axes.find(axisName);
if (it == m_axes.end()) return 0.0f;
const auto& axis = it->second;
float value = 0.0f;
if (axis.GetPositiveKey() != KeyCode::None && IsKeyDown(axis.GetPositiveKey())) {
value += 1.0f;
}
if (axis.GetNegativeKey() != KeyCode::None && IsKeyDown(axis.GetNegativeKey())) {
value -= 1.0f;
}
return value;
}
float InputManager::GetAxisRaw(const Containers::String& axisName) const {
auto it = m_axes.find(axisName);
if (it == m_axes.end()) return 0.0f;
const auto& axis = it->second;
float value = 0.0f;
if (axis.GetPositiveKey() != KeyCode::None && IsKeyPressed(axis.GetPositiveKey())) {
value += 1.0f;
}
if (axis.GetNegativeKey() != KeyCode::None && IsKeyPressed(axis.GetNegativeKey())) {
value -= 1.0f;
}
return value;
}
bool InputManager::GetButton(const Containers::String& buttonName) const {
auto it = m_buttons.find(buttonName);
if (it == m_buttons.end()) return false;
return IsKeyDown(it->second);
}
bool InputManager::GetButtonDown(const Containers::String& buttonName) const {
auto it = m_buttons.find(buttonName);
if (it == m_buttons.end()) return false;
return IsKeyPressed(it->second);
}
bool InputManager::GetButtonUp(const Containers::String& buttonName) const {
auto it = m_buttons.find(buttonName);
if (it == m_buttons.end()) return true;
return IsKeyUp(it->second);
}
void InputManager::RegisterAxis(const InputAxis& axis) {
m_axes[axis.GetName()] = axis;
}
void InputManager::RegisterButton(const Containers::String& name, KeyCode key) {
m_buttons[name] = key;
}
void InputManager::ClearAxes() {
m_axes.clear();
m_buttons.clear();
}
void InputManager::ProcessKeyDown(KeyCode key, bool repeat) {
if (!m_initialized) return;
size_t index = GetKeyIndex(key);
if (index >= m_keyDown.size()) return;
m_keyDown[index] = true;
m_keyDownThisFrame[index] = true;
KeyEvent event;
event.keyCode = key;
event.type = repeat ? KeyEvent::Repeat : KeyEvent::Down;
event.alt = false;
event.ctrl = false;
event.shift = false;
event.meta = false;
m_onKeyEvent.Invoke(event);
}
void InputManager::ProcessKeyUp(KeyCode key) {
if (!m_initialized) return;
size_t index = GetKeyIndex(key);
if (index >= m_keyDown.size()) return;
m_keyDown[index] = false;
KeyEvent event;
event.keyCode = key;
event.type = KeyEvent::Up;
event.alt = false;
event.ctrl = false;
event.shift = false;
event.meta = false;
m_onKeyEvent.Invoke(event);
}
void InputManager::ProcessMouseMove(int x, int y, int deltaX, int deltaY) {
if (!m_initialized) return;
m_mousePosition.x = static_cast<float>(x);
m_mousePosition.y = static_cast<float>(y);
m_mouseDelta.x = static_cast<float>(deltaX);
m_mouseDelta.y = static_cast<float>(deltaY);
MouseMoveEvent event;
event.position = m_mousePosition;
event.delta = m_mouseDelta;
m_onMouseMove.Invoke(event);
}
void InputManager::ProcessMouseButton(MouseButton button, bool pressed, int x, int y) {
if (!m_initialized) return;
size_t index = GetMouseButtonIndex(button);
if (index >= m_mouseButtonDown.size()) return;
m_mouseButtonDown[index] = pressed;
if (pressed) {
m_mouseButtonDownThisFrame[index] = true;
}
MouseButtonEvent event;
event.button = button;
event.position.x = static_cast<float>(x);
event.position.y = static_cast<float>(y);
event.type = pressed ? MouseButtonEvent::Pressed : MouseButtonEvent::Released;
m_onMouseButton.Invoke(event);
}
void InputManager::ProcessMouseWheel(float delta, int x, int y) {
if (!m_initialized) return;
m_mouseScrollDelta = delta;
MouseWheelEvent event;
event.position.x = static_cast<float>(x);
event.position.y = static_cast<float>(y);
event.delta = delta;
m_onMouseWheel.Invoke(event);
}
void InputManager::ProcessTextInput(char c) {
if (!m_initialized) return;
TextInputEvent event;
event.character = c;
event.text = Containers::String(&c, 1);
m_onTextInput.Invoke(event);
}
} // namespace Input
} // namespace XCEngine

View File

@@ -0,0 +1,240 @@
#include "Input/Platform/WindowsInputModule.h"
#include "Input/InputManager.h"
#include <Windows.h>
namespace XCEngine {
namespace Input {
namespace Platform {
WindowsInputModule::WindowsInputModule()
: m_hwnd(nullptr)
, m_isInitialized(false) {
}
WindowsInputModule::~WindowsInputModule() {
Shutdown();
}
void WindowsInputModule::Initialize(void* windowHandle) {
if (m_isInitialized) return;
m_hwnd = reinterpret_cast<HWND>(windowHandle);
m_isInitialized = true;
m_lastMousePosition = Math::Vector2::Zero();
}
void WindowsInputModule::Shutdown() {
if (!m_isInitialized) return;
m_hwnd = nullptr;
m_isInitialized = false;
}
void WindowsInputModule::PumpEvents() {
}
void WindowsInputModule::HandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (!m_isInitialized) return;
switch (message) {
case WM_KEYDOWN:
ProcessKeyDown(wParam, lParam);
break;
case WM_KEYUP:
ProcessKeyUp(wParam);
break;
case WM_CHAR:
ProcessCharInput(wParam);
break;
case WM_MOUSEMOVE:
ProcessMouseMove(wParam, lParam);
break;
case WM_LBUTTONDOWN:
ProcessMouseButton(wParam, lParam, true, MouseButton::Left);
break;
case WM_LBUTTONUP:
ProcessMouseButton(wParam, lParam, false, MouseButton::Left);
break;
case WM_RBUTTONDOWN:
ProcessMouseButton(wParam, lParam, true, MouseButton::Right);
break;
case WM_RBUTTONUP:
ProcessMouseButton(wParam, lParam, false, MouseButton::Right);
break;
case WM_MBUTTONDOWN:
ProcessMouseButton(wParam, lParam, true, MouseButton::Middle);
break;
case WM_MBUTTONUP:
ProcessMouseButton(wParam, lParam, false, MouseButton::Middle);
break;
case WM_MOUSEWHEEL:
ProcessMouseWheel(wParam, lParam);
break;
case WM_XBUTTONDOWN:
ProcessMouseButton(wParam, lParam, true,
(HIWORD(wParam) == 1) ? MouseButton::Button4 : MouseButton::Button5);
break;
case WM_XBUTTONUP:
ProcessMouseButton(wParam, lParam, false,
(HIWORD(wParam) == 1) ? MouseButton::Button4 : MouseButton::Button5);
break;
}
}
void WindowsInputModule::ProcessKeyDown(WPARAM wParam, LPARAM lParam) {
bool repeat = (lParam & (1 << 30)) != 0;
KeyCode keyCode = VKCodeToKeyCode(static_cast<int>(wParam));
if (keyCode != KeyCode::None) {
InputManager::Get().ProcessKeyDown(keyCode, repeat);
}
}
void WindowsInputModule::ProcessKeyUp(WPARAM wParam) {
KeyCode keyCode = VKCodeToKeyCode(static_cast<int>(wParam));
if (keyCode != KeyCode::None) {
InputManager::Get().ProcessKeyUp(keyCode);
}
}
void WindowsInputModule::ProcessMouseMove(WPARAM wParam, LPARAM lParam) {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
int deltaX = x - static_cast<int>(m_lastMousePosition.x);
int deltaY = y - static_cast<int>(m_lastMousePosition.y);
m_lastMousePosition.x = static_cast<float>(x);
m_lastMousePosition.y = static_cast<float>(y);
InputManager::Get().ProcessMouseMove(x, y, deltaX, deltaY);
}
void WindowsInputModule::ProcessMouseButton(WPARAM wParam, LPARAM lParam, bool pressed, MouseButton button) {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
InputManager::Get().ProcessMouseButton(button, pressed, x, y);
}
void WindowsInputModule::ProcessMouseWheel(WPARAM wParam, LPARAM lParam) {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
short delta = static_cast<short>(HIWORD(wParam));
InputManager::Get().ProcessMouseWheel(static_cast<float>(delta) / 120.0f, x, y);
}
void WindowsInputModule::ProcessCharInput(WPARAM wParam) {
char c = static_cast<char>(wParam);
if (c >= 32 && c < 127) {
InputManager::Get().ProcessTextInput(c);
}
}
KeyCode WindowsInputModule::VKCodeToKeyCode(int vkCode) {
switch (vkCode) {
case 'A': return KeyCode::A;
case 'B': return KeyCode::B;
case 'C': return KeyCode::C;
case 'D': return KeyCode::D;
case 'E': return KeyCode::E;
case 'F': return KeyCode::F;
case 'G': return KeyCode::G;
case 'H': return KeyCode::H;
case 'I': return KeyCode::I;
case 'J': return KeyCode::J;
case 'K': return KeyCode::K;
case 'L': return KeyCode::L;
case 'M': return KeyCode::M;
case 'N': return KeyCode::N;
case 'O': return KeyCode::O;
case 'P': return KeyCode::P;
case 'Q': return KeyCode::Q;
case 'R': return KeyCode::R;
case 'S': return KeyCode::S;
case 'T': return KeyCode::T;
case 'U': return KeyCode::U;
case 'V': return KeyCode::V;
case 'W': return KeyCode::W;
case 'X': return KeyCode::X;
case 'Y': return KeyCode::Y;
case 'Z': return KeyCode::Z;
case '0': return KeyCode::Zero;
case '1': return KeyCode::One;
case '2': return KeyCode::Two;
case '3': return KeyCode::Three;
case '4': return KeyCode::Four;
case '5': return KeyCode::Five;
case '6': return KeyCode::Six;
case '7': return KeyCode::Seven;
case '8': return KeyCode::Eight;
case '9': return KeyCode::Nine;
case VK_SPACE: return KeyCode::Space;
case VK_TAB: return KeyCode::Tab;
case VK_RETURN: return KeyCode::Enter;
case VK_ESCAPE: return KeyCode::Escape;
case VK_SHIFT: return KeyCode::LeftShift;
case VK_CONTROL: return KeyCode::LeftCtrl;
case VK_MENU: return KeyCode::LeftAlt;
case VK_UP: return KeyCode::Up;
case VK_DOWN: return KeyCode::Down;
case VK_LEFT: return KeyCode::Left;
case VK_RIGHT: return KeyCode::Right;
case VK_HOME: return KeyCode::Home;
case VK_END: return KeyCode::End;
case VK_PRIOR: return KeyCode::PageUp;
case VK_NEXT: return KeyCode::PageDown;
case VK_DELETE: return KeyCode::Delete;
case VK_BACK: return KeyCode::Backspace;
case VK_F1: return KeyCode::F1;
case VK_F2: return KeyCode::F2;
case VK_F3: return KeyCode::F3;
case VK_F4: return KeyCode::F4;
case VK_F5: return KeyCode::F5;
case VK_F6: return KeyCode::F6;
case VK_F7: return KeyCode::F7;
case VK_F8: return KeyCode::F8;
case VK_F9: return KeyCode::F9;
case VK_F10: return KeyCode::F10;
case VK_F11: return KeyCode::F11;
case VK_F12: return KeyCode::F12;
case VK_OEM_MINUS: return KeyCode::Minus;
case VK_OEM_PLUS: return KeyCode::Equals;
case VK_OEM_4: return KeyCode::BracketLeft;
case VK_OEM_6: return KeyCode::BracketRight;
case 0xBA: return KeyCode::Semicolon;
case 0xDE: return KeyCode::Quote;
case VK_OEM_COMMA: return KeyCode::Comma;
case VK_OEM_PERIOD: return KeyCode::Period;
case VK_OEM_2: return KeyCode::Slash;
case VK_OEM_5: return KeyCode::Backslash;
case VK_OEM_3: return KeyCode::Backtick;
default: return KeyCode::None;
}
}
} // namespace Platform
} // namespace Input
} // namespace XCEngine