ADD: Input system

This commit is contained in:
2025-12-16 17:59:19 +09:00
parent ac83f8dc48
commit 5b62c57d0c
8 changed files with 748 additions and 110 deletions

View File

@@ -11,6 +11,8 @@ add_executable (vulkan_engine
core/engine.h
core/engine.cpp
core/engine_ui.cpp
core/input/input_system.h
core/input/input_system.cpp
core/ui/imgui_system.h
core/ui/imgui_system.cpp
core/picking/picking_system.h

View File

@@ -32,6 +32,7 @@ class RenderGraph;
class RayTracingManager;
class TextureCache;
class IBLManager;
class InputSystem;
struct ShadowSettings
{
@@ -66,6 +67,7 @@ public:
PipelineManager* pipelines = nullptr; // graphics pipeline manager
RenderGraph* renderGraph = nullptr; // render graph (built per-frame)
SDL_Window* window = nullptr; // SDL window handle
InputSystem* input = nullptr; // input system (engine-owned)
// Frequently used values
VkExtent2D drawExtent{};

View File

@@ -15,6 +15,8 @@
//> includes
#include "engine.h"
#include "core/input/input_system.h"
#include "SDL2/SDL.h"
#include "SDL2/SDL_vulkan.h"
@@ -237,6 +239,8 @@ void VulkanEngine::init()
// Build dependency-injection context
_context = std::make_shared<EngineContext>();
_input = std::make_unique<InputSystem>();
_context->input = _input.get();
_context->device = _deviceManager;
_context->resources = _resourceManager;
_context->descriptors = std::make_shared<DescriptorAllocatorGrowable>(); {
@@ -922,6 +926,16 @@ void VulkanEngine::cleanup()
dump_vma_json(_deviceManager.get(), "before_DeviceManager");
_deviceManager->cleanup();
if (_input)
{
_input->set_cursor_mode(CursorMode::Normal);
_input.reset();
}
if (_context)
{
_context->input = nullptr;
}
SDL_DestroyWindow(_window);
}
}
@@ -1264,75 +1278,78 @@ void VulkanEngine::draw()
_frameNumber++;
}
namespace
{
struct NativeEventDispatchCtx
{
VulkanEngine *engine = nullptr;
bool ui_capture_mouse = false;
};
void dispatch_native_event(void *user, InputSystem::NativeEventView view)
{
auto *ctx = static_cast<NativeEventDispatchCtx *>(user);
if (!ctx || !ctx->engine || view.backend != InputSystem::NativeBackend::SDL2 || view.data == nullptr)
{
return;
}
const SDL_Event &e = *static_cast<const SDL_Event *>(view.data);
if (ctx->engine->ui())
{
ctx->engine->ui()->process_event(e);
}
if (ctx->engine->picking())
{
ctx->engine->picking()->process_event(e, ctx->ui_capture_mouse);
}
}
} // namespace
void VulkanEngine::run()
{
SDL_Event e;
bool bQuit = false;
//main loop
while (!bQuit)
{
auto start = std::chrono::system_clock::now();
//Handle events on queue
while (SDL_PollEvent(&e) != 0)
if (_input)
{
//close the window when user alt-f4s or clicks the X button
if (e.type == SDL_QUIT)
_input->begin_frame();
_input->pump_events();
if (_input->quit_requested())
{
bQuit = true;
}
if (e.type == SDL_WINDOWEVENT)
{
switch (e.window.event)
{
case SDL_WINDOWEVENT_MINIMIZED:
freeze_rendering = true;
break;
case SDL_WINDOWEVENT_RESTORED:
freeze_rendering = false;
resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
case SDL_WINDOWEVENT_MOVED:
// Moving between monitors can change DPI scale; ensure swapchain is refreshed.
resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
default:
break;
}
}
freeze_rendering = _input->window_minimized();
const bool ui_capture_mouse = _ui && _ui->want_capture_mouse();
const bool ui_capture_keyboard = _ui && _ui->want_capture_keyboard();
if (_input->resize_requested())
{
resize_requested = true;
_last_resize_event_ms = _input->last_resize_event_ms();
_input->clear_resize_request();
}
}
if (_ui)
{
_ui->process_event(e);
}
if (_picking)
{
_picking->process_event(e, ui_capture_mouse);
}
if (_sceneManager)
{
const bool key_event = (e.type == SDL_KEYDOWN) || (e.type == SDL_KEYUP);
const bool mouse_event = (e.type == SDL_MOUSEBUTTONDOWN) ||
(e.type == SDL_MOUSEBUTTONUP) ||
(e.type == SDL_MOUSEMOTION) ||
(e.type == SDL_MOUSEWHEEL);
const bool ui_capture_mouse = _ui && _ui->want_capture_mouse();
const bool ui_capture_keyboard = _ui && _ui->want_capture_keyboard();
if (!(ui_capture_keyboard && key_event) && !(ui_capture_mouse && mouse_event))
{
_sceneManager->getMainCamera().processSDLEvent(e);
}
}
if (_input)
{
NativeEventDispatchCtx ctx{};
ctx.engine = this;
ctx.ui_capture_mouse = ui_capture_mouse;
_input->for_each_native_event(dispatch_native_event, &ctx);
}
if (_sceneManager && _input)
{
_sceneManager->getMainCamera().process_input(*_input, ui_capture_keyboard, ui_capture_mouse);
}
if (freeze_rendering)

View File

@@ -40,6 +40,8 @@
#include "core/ui/imgui_system.h"
#include "core/picking/picking_system.h"
class InputSystem;
// Number of frames-in-flight. Affects per-frame command buffers, fences,
// semaphores, and transient descriptor pools in FrameResources.
constexpr unsigned int FRAME_OVERLAP = 2;
@@ -81,9 +83,10 @@ public:
std::unique_ptr<SwapchainManager> _swapchainManager;
std::shared_ptr<ResourceManager> _resourceManager;
std::unique_ptr<RenderPassManager> _renderPassManager;
std::unique_ptr<ImGuiSystem> _ui;
std::unique_ptr<ImGuiSystem> _ui;
std::unique_ptr<SceneManager> _sceneManager;
std::unique_ptr<PickingSystem> _picking;
std::unique_ptr<InputSystem> _input;
std::unique_ptr<PipelineManager> _pipelineManager;
std::unique_ptr<AssetManager> _assetManager;
std::unique_ptr<AsyncAssetLoader> _asyncLoader;
@@ -170,6 +173,9 @@ public:
PickingSystem *picking() { return _picking.get(); }
const PickingSystem *picking() const { return _picking.get(); }
InputSystem *input() { return _input.get(); }
const InputSystem *input() const { return _input.get(); }
// Debug: persistent pass enable overrides (by pass name)
std::unordered_map<std::string, bool> _rgPassToggles;

View File

@@ -0,0 +1,364 @@
#include "input_system.h"
#include "SDL2/SDL.h"
#include <algorithm>
#include <cstring>
struct InputSystem::Impl
{
std::vector<SDL_Event> sdl_events{};
};
namespace
{
InputModifiers mods_from_sdl(const SDL_Keymod mod)
{
InputModifiers out{};
out.shift = (mod & KMOD_SHIFT) != 0;
out.ctrl = (mod & KMOD_CTRL) != 0;
out.alt = (mod & KMOD_ALT) != 0;
out.super = (mod & KMOD_GUI) != 0;
return out;
}
bool map_sdl_mouse_button(uint8_t sdl_button, MouseButton &out)
{
switch (sdl_button)
{
case SDL_BUTTON_LEFT:
out = MouseButton::Left;
return true;
case SDL_BUTTON_MIDDLE:
out = MouseButton::Middle;
return true;
case SDL_BUTTON_RIGHT:
out = MouseButton::Right;
return true;
case SDL_BUTTON_X1:
out = MouseButton::X1;
return true;
case SDL_BUTTON_X2:
out = MouseButton::X2;
return true;
default:
return false;
}
}
glm::vec2 wheel_from_sdl(const SDL_MouseWheelEvent &wheel)
{
glm::vec2 delta(static_cast<float>(wheel.x), static_cast<float>(wheel.y));
if (wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
{
delta = -delta;
}
return delta;
}
} // namespace
void InputState::begin_frame()
{
std::fill(_keys_pressed.begin(), _keys_pressed.end(), 0);
std::fill(_keys_released.begin(), _keys_released.end(), 0);
std::fill(_mouse_pressed.begin(), _mouse_pressed.end(), 0);
std::fill(_mouse_released.begin(), _mouse_released.end(), 0);
_mouse_delta = glm::vec2(0.0f);
_wheel_delta = glm::vec2(0.0f);
}
bool InputState::key_down(Key key) const
{
const size_t idx = key_index(key);
if (idx >= _keys_down.size()) return false;
return _keys_down[idx] != 0;
}
bool InputState::key_pressed(Key key) const
{
const size_t idx = key_index(key);
if (idx >= _keys_pressed.size()) return false;
return _keys_pressed[idx] != 0;
}
bool InputState::key_released(Key key) const
{
const size_t idx = key_index(key);
if (idx >= _keys_released.size()) return false;
return _keys_released[idx] != 0;
}
bool InputState::mouse_down(MouseButton button) const
{
const size_t idx = mouse_index(button);
if (idx >= _mouse_down.size()) return false;
return _mouse_down[idx] != 0;
}
bool InputState::mouse_pressed(MouseButton button) const
{
const size_t idx = mouse_index(button);
if (idx >= _mouse_pressed.size()) return false;
return _mouse_pressed[idx] != 0;
}
bool InputState::mouse_released(MouseButton button) const
{
const size_t idx = mouse_index(button);
if (idx >= _mouse_released.size()) return false;
return _mouse_released[idx] != 0;
}
size_t InputState::key_index(Key key)
{
return static_cast<size_t>(static_cast<uint16_t>(key));
}
size_t InputState::mouse_index(MouseButton button)
{
return static_cast<size_t>(static_cast<uint8_t>(button));
}
void InputState::set_key(Key key, bool down, bool repeat)
{
const size_t idx = key_index(key);
if (idx >= _keys_down.size()) return;
const bool was_down = _keys_down[idx] != 0;
if (down)
{
_keys_down[idx] = 1;
if (!was_down && !repeat)
{
_keys_pressed[idx] = 1;
}
}
else
{
_keys_down[idx] = 0;
if (was_down)
{
_keys_released[idx] = 1;
}
}
}
void InputState::set_mouse_button(MouseButton button, bool down)
{
const size_t idx = mouse_index(button);
if (idx >= _mouse_down.size()) return;
const bool was_down = _mouse_down[idx] != 0;
if (down)
{
_mouse_down[idx] = 1;
if (!was_down)
{
_mouse_pressed[idx] = 1;
}
}
else
{
_mouse_down[idx] = 0;
if (was_down)
{
_mouse_released[idx] = 1;
}
}
}
void InputState::add_mouse_motion(const glm::vec2 &pos, const glm::vec2 &delta)
{
_mouse_pos = pos;
_mouse_delta += delta;
}
void InputState::add_mouse_wheel(const glm::vec2 &delta)
{
_wheel_delta += delta;
}
void InputState::set_modifiers(const InputModifiers &mods)
{
_mods = mods;
}
InputSystem::InputSystem()
: _impl(std::make_unique<Impl>())
{
}
InputSystem::~InputSystem() = default;
void InputSystem::begin_frame()
{
_state.begin_frame();
_events.clear();
if (_impl)
{
_impl->sdl_events.clear();
}
}
void InputSystem::pump_events()
{
if (!_impl)
{
return;
}
SDL_Event e{};
while (SDL_PollEvent(&e) != 0)
{
_impl->sdl_events.push_back(e);
// Track app/window state
if (e.type == SDL_QUIT)
{
_quit_requested = true;
}
if (e.type == SDL_WINDOWEVENT)
{
switch (e.window.event)
{
case SDL_WINDOWEVENT_MINIMIZED:
_window_minimized = true;
break;
case SDL_WINDOWEVENT_RESTORED:
_window_minimized = false;
_resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
_resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
case SDL_WINDOWEVENT_MOVED:
_resize_requested = true;
_last_resize_event_ms = SDL_GetTicks();
break;
default:
break;
}
}
// Convert to engine input events + state
if (e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
{
const bool down = (e.type == SDL_KEYDOWN);
const bool repeat = down && (e.key.repeat != 0);
const uint16_t code = static_cast<uint16_t>(e.key.keysym.scancode);
const Key key = static_cast<Key>(code);
const InputModifiers mods = mods_from_sdl(static_cast<SDL_Keymod>(e.key.keysym.mod));
_state.set_modifiers(mods);
_state.set_key(key, down, repeat);
InputEvent ev{};
ev.type = down ? InputEvent::Type::KeyDown : InputEvent::Type::KeyUp;
ev.timestamp_ms = e.key.timestamp;
ev.mods = mods;
ev.key = key;
_events.push_back(ev);
}
else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP)
{
MouseButton btn{};
if (!map_sdl_mouse_button(e.button.button, btn))
{
continue;
}
const bool down = (e.type == SDL_MOUSEBUTTONDOWN);
const InputModifiers mods = mods_from_sdl(static_cast<SDL_Keymod>(SDL_GetModState()));
_state.set_modifiers(mods);
_state.set_mouse_button(btn, down);
_state.add_mouse_motion(glm::vec2(static_cast<float>(e.button.x), static_cast<float>(e.button.y)),
glm::vec2(0.0f));
InputEvent ev{};
ev.type = down ? InputEvent::Type::MouseButtonDown : InputEvent::Type::MouseButtonUp;
ev.timestamp_ms = e.button.timestamp;
ev.mods = mods;
ev.mouse_button = btn;
ev.mouse_pos = glm::vec2(static_cast<float>(e.button.x), static_cast<float>(e.button.y));
_events.push_back(ev);
}
else if (e.type == SDL_MOUSEMOTION)
{
const InputModifiers mods = mods_from_sdl(static_cast<SDL_Keymod>(SDL_GetModState()));
_state.set_modifiers(mods);
_state.add_mouse_motion(glm::vec2(static_cast<float>(e.motion.x), static_cast<float>(e.motion.y)),
glm::vec2(static_cast<float>(e.motion.xrel), static_cast<float>(e.motion.yrel)));
InputEvent ev{};
ev.type = InputEvent::Type::MouseMove;
ev.timestamp_ms = e.motion.timestamp;
ev.mods = mods;
ev.mouse_pos = glm::vec2(static_cast<float>(e.motion.x), static_cast<float>(e.motion.y));
ev.mouse_delta = glm::vec2(static_cast<float>(e.motion.xrel), static_cast<float>(e.motion.yrel));
_events.push_back(ev);
}
else if (e.type == SDL_MOUSEWHEEL)
{
const InputModifiers mods = mods_from_sdl(static_cast<SDL_Keymod>(SDL_GetModState()));
const glm::vec2 delta = wheel_from_sdl(e.wheel);
_state.set_modifiers(mods);
_state.add_mouse_wheel(delta);
InputEvent ev{};
ev.type = InputEvent::Type::MouseWheel;
ev.timestamp_ms = e.wheel.timestamp;
ev.mods = mods;
ev.wheel_delta = delta;
_events.push_back(ev);
}
}
}
void InputSystem::set_cursor_mode(CursorMode mode)
{
if (_cursor_mode == mode)
{
return;
}
switch (mode)
{
case CursorMode::Normal:
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(SDL_ENABLE);
break;
case CursorMode::Hidden:
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(SDL_DISABLE);
break;
case CursorMode::Relative:
SDL_ShowCursor(SDL_DISABLE);
SDL_SetRelativeMouseMode(SDL_TRUE);
break;
}
_cursor_mode = mode;
}
void InputSystem::for_each_native_event(NativeEventCallback callback, void *user) const
{
if (!_impl || !callback)
{
return;
}
for (const SDL_Event &e: _impl->sdl_events)
{
NativeEventView view{};
view.backend = NativeBackend::SDL2;
view.data = &e;
callback(user, view);
}
}

View File

@@ -0,0 +1,220 @@
#pragma once
#include <core/types.h>
#include <array>
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
// Cross-platform input codes loosely based on USB HID usage IDs (and SDL scancodes).
// Keep this list minimal and extend as needed.
enum class Key : uint16_t
{
Unknown = 0,
A = 4,
B = 5,
C = 6,
D = 7,
E = 8,
F = 9,
G = 10,
H = 11,
I = 12,
J = 13,
K = 14,
L = 15,
M = 16,
N = 17,
O = 18,
P = 19,
Q = 20,
R = 21,
S = 22,
T = 23,
U = 24,
V = 25,
W = 26,
X = 27,
Y = 28,
Z = 29,
Num1 = 30,
Num2 = 31,
Num3 = 32,
Num4 = 33,
Num5 = 34,
Num6 = 35,
Num7 = 36,
Num8 = 37,
Num9 = 38,
Num0 = 39,
Enter = 40,
Escape = 41,
Backspace = 42,
Tab = 43,
Space = 44,
LeftCtrl = 224,
LeftShift = 225,
LeftAlt = 226,
LeftSuper = 227,
RightCtrl = 228,
RightShift = 229,
RightAlt = 230,
RightSuper = 231,
};
enum class MouseButton : uint8_t
{
Left = 0,
Middle = 1,
Right = 2,
X1 = 3,
X2 = 4,
};
enum class CursorMode : uint8_t
{
Normal = 0,
Hidden = 1,
Relative = 2,
};
struct InputModifiers
{
bool shift = false;
bool ctrl = false;
bool alt = false;
bool super = false;
};
struct InputEvent
{
enum class Type : uint8_t
{
KeyDown,
KeyUp,
MouseButtonDown,
MouseButtonUp,
MouseMove,
MouseWheel,
};
Type type = Type::KeyDown;
uint32_t timestamp_ms = 0;
InputModifiers mods{};
Key key = Key::Unknown;
MouseButton mouse_button = MouseButton::Left;
glm::vec2 mouse_pos{0.0f};
glm::vec2 mouse_delta{0.0f};
glm::vec2 wheel_delta{0.0f};
};
class InputState
{
public:
static constexpr uint16_t kMaxKeys = 512;
static constexpr uint8_t kMouseButtonCount = 5;
void begin_frame();
bool key_down(Key key) const;
bool key_pressed(Key key) const;
bool key_released(Key key) const;
bool mouse_down(MouseButton button) const;
bool mouse_pressed(MouseButton button) const;
bool mouse_released(MouseButton button) const;
glm::vec2 mouse_position() const { return _mouse_pos; }
glm::vec2 mouse_delta() const { return _mouse_delta; }
glm::vec2 wheel_delta() const { return _wheel_delta; }
InputModifiers modifiers() const { return _mods; }
private:
friend class InputSystem;
static size_t key_index(Key key);
static size_t mouse_index(MouseButton button);
void set_key(Key key, bool down, bool repeat);
void set_mouse_button(MouseButton button, bool down);
void add_mouse_motion(const glm::vec2 &pos, const glm::vec2 &delta);
void add_mouse_wheel(const glm::vec2 &delta);
void set_modifiers(const InputModifiers &mods);
std::array<uint8_t, kMaxKeys> _keys_down{};
std::array<uint8_t, kMaxKeys> _keys_pressed{};
std::array<uint8_t, kMaxKeys> _keys_released{};
std::array<uint8_t, kMouseButtonCount> _mouse_down{};
std::array<uint8_t, kMouseButtonCount> _mouse_pressed{};
std::array<uint8_t, kMouseButtonCount> _mouse_released{};
glm::vec2 _mouse_pos{0.0f};
glm::vec2 _mouse_delta{0.0f};
glm::vec2 _wheel_delta{0.0f};
InputModifiers _mods{};
};
class InputSystem
{
public:
enum class NativeBackend : uint8_t
{
SDL2 = 0,
};
struct NativeEventView
{
NativeBackend backend = NativeBackend::SDL2;
const void *data = nullptr;
};
using NativeEventCallback = void (*)(void *user, NativeEventView event);
InputSystem();
~InputSystem();
InputSystem(const InputSystem &) = delete;
InputSystem &operator=(const InputSystem &) = delete;
void begin_frame();
void pump_events();
const InputState &state() const { return _state; }
std::span<const InputEvent> events() const { return _events; }
bool quit_requested() const { return _quit_requested; }
bool window_minimized() const { return _window_minimized; }
bool resize_requested() const { return _resize_requested; }
uint32_t last_resize_event_ms() const { return _last_resize_event_ms; }
void clear_resize_request() { _resize_requested = false; }
CursorMode cursor_mode() const { return _cursor_mode; }
void set_cursor_mode(CursorMode mode);
// Engine-internal: dispatch native platform events (SDL events today).
void for_each_native_event(NativeEventCallback callback, void *user) const;
private:
struct Impl;
std::unique_ptr<Impl> _impl;
InputState _state{};
std::vector<InputEvent> _events{};
bool _quit_requested = false;
bool _window_minimized = false;
bool _resize_requested = false;
uint32_t _last_resize_event_ms = 0;
CursorMode _cursor_mode = CursorMode::Normal;
};

View File

@@ -1,7 +1,6 @@
#include "camera.h"
#include <glm/gtx/transform.hpp>
#include <glm/gtx/quaternion.hpp>
#include <SDL2/SDL.h>
#include <algorithm>
#include <cmath>
@@ -12,63 +11,90 @@ void Camera::update()
position_world += glm::dvec3(delta);
}
void Camera::processSDLEvent(SDL_Event& e)
void Camera::process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse)
{
if (e.type == SDL_KEYDOWN) {
// Camera uses -Z forward convention (right-handed)
if (e.key.keysym.sym == SDLK_w) { velocity.z = -1; }
if (e.key.keysym.sym == SDLK_s) { velocity.z = 1; }
if (e.key.keysym.sym == SDLK_a) { velocity.x = -1; }
if (e.key.keysym.sym == SDLK_d) { velocity.x = 1; }
const InputState &st = input.state();
// Movement is state-based so simultaneous keys work naturally.
if (ui_capture_keyboard)
{
velocity = glm::vec3(0.0f);
}
else
{
glm::vec3 v(0.0f);
if (st.key_down(Key::W)) { v.z -= 1.0f; }
if (st.key_down(Key::S)) { v.z += 1.0f; }
if (st.key_down(Key::A)) { v.x -= 1.0f; }
if (st.key_down(Key::D)) { v.x += 1.0f; }
velocity = v;
}
if (e.type == SDL_KEYUP) {
if (e.key.keysym.sym == SDLK_w) { velocity.z = 0; }
if (e.key.keysym.sym == SDLK_s) { velocity.z = 0; }
if (e.key.keysym.sym == SDLK_a) { velocity.x = 0; }
if (e.key.keysym.sym == SDLK_d) { velocity.x = 0; }
}
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) {
rmbDown = true;
SDL_SetRelativeMouseMode(SDL_TRUE);
}
if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_RIGHT) {
rmbDown = false;
SDL_SetRelativeMouseMode(SDL_FALSE);
}
if (e.type == SDL_MOUSEMOTION && rmbDown) {
// Convert mouse motion to incremental yaw/pitch angles.
float dx = static_cast<float>(e.motion.xrel) * lookSensitivity;
float dy = static_cast<float>(e.motion.yrel) * lookSensitivity;
// Mouse right (xrel > 0) turns view right with -Z-forward: yaw around +Y.
glm::quat yawRotation = glm::angleAxis(dx, glm::vec3 { 0.f, 1.f, 0.f });
// Mouse up (yrel < 0) looks up with -Z-forward: negative dy.
float pitchDelta = -dy;
// Pitch around the camera's local X (right) axis in world space.
glm::vec3 right = glm::rotate(orientation, glm::vec3 { 1.f, 0.f, 0.f });
glm::quat pitchRotation = glm::angleAxis(pitchDelta, glm::vec3(right));
// Apply yaw, then pitch, to the current orientation.
orientation = glm::normalize(pitchRotation * yawRotation * orientation);
}
if (e.type == SDL_MOUSEWHEEL) {
// Ctrl modifies FOV, otherwise adjust move speed
const bool ctrl = (SDL_GetModState() & KMOD_CTRL) != 0;
const int steps = e.wheel.y; // positive = wheel up
if (ctrl) {
// Wheel up -> zoom in (smaller FOV)
fovDegrees -= steps * 2.0f;
fovDegrees = std::clamp(fovDegrees, 30.0f, 110.0f);
} else {
// Exponential scale for pleasant feel
float factor = std::pow(1.15f, (float)steps);
moveSpeed = std::clamp(moveSpeed * factor, 0.001f, 5.0f);
// Event-based mouse handling so we don't apply motion that happened before RMB was pressed in the same frame.
for (const InputEvent &e : input.events())
{
if (ui_capture_mouse)
{
continue;
}
if (e.type == InputEvent::Type::MouseButtonDown && e.mouse_button == MouseButton::Right)
{
rmbDown = true;
input.set_cursor_mode(CursorMode::Relative);
}
else if (e.type == InputEvent::Type::MouseButtonUp && e.mouse_button == MouseButton::Right)
{
rmbDown = false;
input.set_cursor_mode(CursorMode::Normal);
}
else if (e.type == InputEvent::Type::MouseMove && rmbDown)
{
// Convert mouse motion to incremental yaw/pitch angles.
float dx = e.mouse_delta.x * lookSensitivity;
float dy = e.mouse_delta.y * lookSensitivity;
// Mouse right (xrel > 0) turns view right with -Z-forward: yaw around +Y.
glm::quat yawRotation = glm::angleAxis(dx, glm::vec3 { 0.f, 1.f, 0.f });
// Mouse up (yrel < 0) looks up with -Z-forward: negative dy.
float pitchDelta = -dy;
// Pitch around the camera's local X (right) axis in world space.
glm::vec3 right = glm::rotate(orientation, glm::vec3 { 1.f, 0.f, 0.f });
glm::quat pitchRotation = glm::angleAxis(pitchDelta, glm::vec3(right));
// Apply yaw, then pitch, to the current orientation.
orientation = glm::normalize(pitchRotation * yawRotation * orientation);
}
else if (e.type == InputEvent::Type::MouseWheel)
{
const float steps = e.wheel_delta.y; // positive = wheel up
if (std::abs(steps) < 0.001f)
{
continue;
}
// Ctrl modifies FOV, otherwise adjust move speed
if (e.mods.ctrl)
{
// Wheel up -> zoom in (smaller FOV)
fovDegrees -= steps * 2.0f;
fovDegrees = std::clamp(fovDegrees, 30.0f, 110.0f);
}
else
{
// Exponential scale for pleasant feel
float factor = std::pow(1.15f, steps);
moveSpeed = std::clamp(moveSpeed * factor, 0.001f, 5.0f);
}
}
}
// Safety: if mouse state shows RMB is no longer down, release relative mode.
if (rmbDown && !st.mouse_down(MouseButton::Right))
{
rmbDown = false;
input.set_cursor_mode(CursorMode::Normal);
}
}

View File

@@ -1,7 +1,8 @@
#pragma once
#include <core/types.h>
#include <SDL_events.h>
#include <core/input/input_system.h>
#include "glm/vec3.hpp"
@@ -23,7 +24,7 @@ public:
glm::mat4 getViewMatrix(const glm::vec3 &position_local) const;
glm::mat4 getRotationMatrix() const;
void processSDLEvent(SDL_Event& e);
void process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse);
void update();
};