ADD: Camera mode
This commit is contained in:
@@ -117,6 +117,19 @@ add_executable (vulkan_engine
|
||||
scene/tangent_space.cpp
|
||||
scene/camera.h
|
||||
scene/camera.cpp
|
||||
scene/camera/icamera_mode.h
|
||||
scene/camera/camera_rig.h
|
||||
scene/camera/camera_rig.cpp
|
||||
scene/camera/mode_free.h
|
||||
scene/camera/mode_free.cpp
|
||||
scene/camera/mode_orbit.h
|
||||
scene/camera/mode_orbit.cpp
|
||||
scene/camera/mode_follow.h
|
||||
scene/camera/mode_follow.cpp
|
||||
scene/camera/mode_chase.h
|
||||
scene/camera/mode_chase.cpp
|
||||
scene/camera/mode_fixed.h
|
||||
scene/camera/mode_fixed.cpp
|
||||
# compute
|
||||
compute/vk_compute.h
|
||||
compute/vk_compute.cpp
|
||||
|
||||
@@ -1671,7 +1671,7 @@ void VulkanEngine::run()
|
||||
|
||||
if (_sceneManager && _input)
|
||||
{
|
||||
_sceneManager->getMainCamera().process_input(*_input, ui_capture_keyboard, ui_capture_mouse);
|
||||
_sceneManager->getCameraRig().process_input(*_input, ui_capture_keyboard, ui_capture_mouse);
|
||||
}
|
||||
|
||||
if (freeze_rendering)
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
#include "core/assets/ibl_manager.h"
|
||||
#include "core/ui/imgui_system.h"
|
||||
#include "core/picking/picking_system.h"
|
||||
#include "core/input/input_system.h"
|
||||
|
||||
class InputSystem;
|
||||
class DebugDrawSystem;
|
||||
|
||||
struct DebugDrawDeleter
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "render/passes/background.h"
|
||||
#include "render/passes/particles.h"
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "render/graph/graph.h"
|
||||
#include "core/pipeline/manager.h"
|
||||
@@ -781,6 +782,213 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_camera(VulkanEngine *eng)
|
||||
{
|
||||
if (!eng || !eng->_sceneManager)
|
||||
{
|
||||
ImGui::TextUnformatted("SceneManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
SceneManager *sceneMgr = eng->_sceneManager.get();
|
||||
CameraRig &rig = sceneMgr->getCameraRig();
|
||||
Camera &cam = sceneMgr->getMainCamera();
|
||||
|
||||
// Mode switch
|
||||
static const char *k_mode_names[] = {"Free", "Orbit", "Follow", "Chase", "Fixed"};
|
||||
int mode = static_cast<int>(rig.mode());
|
||||
if (ImGui::Combo("Mode", &mode, k_mode_names, IM_ARRAYSIZE(k_mode_names)))
|
||||
{
|
||||
rig.set_mode(static_cast<CameraMode>(mode), *sceneMgr, cam);
|
||||
if (eng->_input)
|
||||
{
|
||||
eng->_input->set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Active mode: %s", rig.mode_name());
|
||||
ImGui::Separator();
|
||||
|
||||
// Camera state (world)
|
||||
double pos[3] = {cam.position_world.x, cam.position_world.y, cam.position_world.z};
|
||||
if (ImGui::InputScalarN("Position (world)", ImGuiDataType_Double, pos, 3, nullptr, nullptr, "%.3f"))
|
||||
{
|
||||
cam.position_world = WorldVec3(pos[0], pos[1], pos[2]);
|
||||
}
|
||||
float fov = cam.fovDegrees;
|
||||
if (ImGui::SliderFloat("FOV (deg)", &fov, 30.0f, 110.0f))
|
||||
{
|
||||
cam.fovDegrees = fov;
|
||||
}
|
||||
|
||||
WorldVec3 origin = sceneMgr->get_world_origin();
|
||||
glm::vec3 camLocal = sceneMgr->get_camera_local_position();
|
||||
ImGui::Text("Origin (world): (%.3f, %.3f, %.3f)", origin.x, origin.y, origin.z);
|
||||
ImGui::Text("Camera (local): (%.3f, %.3f, %.3f)", camLocal.x, camLocal.y, camLocal.z);
|
||||
|
||||
auto target_from_last_pick = [&](CameraTarget &target) -> bool {
|
||||
PickingSystem *picking = eng->picking();
|
||||
if (!picking) return false;
|
||||
const auto &pick = picking->last_pick();
|
||||
if (!pick.valid) return false;
|
||||
|
||||
if (pick.ownerType == RenderObject::OwnerType::MeshInstance)
|
||||
{
|
||||
target.type = CameraTargetType::MeshInstance;
|
||||
target.name = pick.ownerName;
|
||||
}
|
||||
else if (pick.ownerType == RenderObject::OwnerType::GLTFInstance)
|
||||
{
|
||||
target.type = CameraTargetType::GLTFInstance;
|
||||
target.name = pick.ownerName;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.type = CameraTargetType::WorldPoint;
|
||||
target.world_point = pick.worldPos;
|
||||
target.name.clear();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto draw_target = [&](const char *id, CameraTarget &target, char *name_buf, size_t name_buf_size) {
|
||||
ImGui::PushID(id);
|
||||
static const char *k_target_types[] = {"None", "WorldPoint", "MeshInstance", "GLTFInstance"};
|
||||
int type = static_cast<int>(target.type);
|
||||
if (ImGui::Combo("Target type", &type, k_target_types, IM_ARRAYSIZE(k_target_types)))
|
||||
{
|
||||
target.type = static_cast<CameraTargetType>(type);
|
||||
if (target.type != CameraTargetType::MeshInstance && target.type != CameraTargetType::GLTFInstance)
|
||||
{
|
||||
target.name.clear();
|
||||
if (name_buf_size > 0)
|
||||
{
|
||||
name_buf[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target.type == CameraTargetType::WorldPoint)
|
||||
{
|
||||
double p[3] = {target.world_point.x, target.world_point.y, target.world_point.z};
|
||||
if (ImGui::InputScalarN("World point", ImGuiDataType_Double, p, 3, nullptr, nullptr, "%.3f"))
|
||||
{
|
||||
target.world_point = WorldVec3(p[0], p[1], p[2]);
|
||||
}
|
||||
}
|
||||
else if (target.type == CameraTargetType::MeshInstance || target.type == CameraTargetType::GLTFInstance)
|
||||
{
|
||||
if (std::strncmp(name_buf, target.name.c_str(), name_buf_size) != 0)
|
||||
{
|
||||
std::snprintf(name_buf, name_buf_size, "%s", target.name.c_str());
|
||||
}
|
||||
ImGui::InputText("Target name", name_buf, name_buf_size);
|
||||
target.name = name_buf;
|
||||
}
|
||||
|
||||
WorldVec3 tpos{};
|
||||
glm::quat trot{};
|
||||
bool ok = rig.resolve_target(*sceneMgr, target, tpos, trot);
|
||||
ImGui::Text("Resolved: %s", ok ? "yes" : "no");
|
||||
if (ok)
|
||||
{
|
||||
ImGui::Text("Target world: (%.3f, %.3f, %.3f)", tpos.x, tpos.y, tpos.z);
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
|
||||
// Free
|
||||
if (ImGui::CollapsingHeader("Free", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
auto &s = rig.free_settings();
|
||||
ImGui::InputFloat("Move speed (u/s)", &s.move_speed);
|
||||
s.move_speed = std::clamp(s.move_speed, 0.06f, 300.0f);
|
||||
ImGui::InputFloat("Look sensitivity", &s.look_sensitivity);
|
||||
ImGui::InputFloat("Roll speed (rad/s)", &s.roll_speed);
|
||||
ImGui::TextUnformatted("Roll keys: Q/E");
|
||||
}
|
||||
|
||||
// Orbit
|
||||
if (ImGui::CollapsingHeader("Orbit"))
|
||||
{
|
||||
auto &s = rig.orbit_settings();
|
||||
static char orbitName[128] = "";
|
||||
draw_target("orbit_target", s.target, orbitName, IM_ARRAYSIZE(orbitName));
|
||||
if (ImGui::Button("Orbit target = Last Pick"))
|
||||
{
|
||||
target_from_last_pick(s.target);
|
||||
}
|
||||
ImGui::InputDouble("Distance", &s.distance, 0.1, 1.0, "%.3f");
|
||||
s.distance = std::clamp(s.distance, 0.2, 100000.0);
|
||||
float yawDeg = glm::degrees(s.yaw);
|
||||
float pitchDeg = glm::degrees(s.pitch);
|
||||
if (ImGui::SliderFloat("Yaw (deg)", &yawDeg, -180.0f, 180.0f))
|
||||
{
|
||||
s.yaw = glm::radians(yawDeg);
|
||||
}
|
||||
if (ImGui::SliderFloat("Pitch (deg)", &pitchDeg, -89.0f, 89.0f))
|
||||
{
|
||||
s.pitch = glm::radians(pitchDeg);
|
||||
}
|
||||
ImGui::InputFloat("Look sensitivity##orbit", &s.look_sensitivity);
|
||||
}
|
||||
|
||||
// Follow
|
||||
if (ImGui::CollapsingHeader("Follow"))
|
||||
{
|
||||
auto &s = rig.follow_settings();
|
||||
static char followName[128] = "";
|
||||
draw_target("follow_target", s.target, followName, IM_ARRAYSIZE(followName));
|
||||
if (ImGui::Button("Follow target = Last Pick"))
|
||||
{
|
||||
target_from_last_pick(s.target);
|
||||
}
|
||||
ImGui::InputFloat3("Position offset (local)", &s.position_offset_local.x);
|
||||
|
||||
glm::vec3 rotDeg = glm::degrees(glm::eulerAngles(s.rotation_offset));
|
||||
float r[3] = {rotDeg.x, rotDeg.y, rotDeg.z};
|
||||
if (ImGui::InputFloat3("Rotation offset (deg XYZ)", r))
|
||||
{
|
||||
glm::mat4 R = glm::eulerAngleXYZ(glm::radians(r[0]), glm::radians(r[1]), glm::radians(r[2]));
|
||||
s.rotation_offset = glm::quat_cast(R);
|
||||
}
|
||||
if (ImGui::Button("Reset rotation offset"))
|
||||
{
|
||||
s.rotation_offset = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Chase
|
||||
if (ImGui::CollapsingHeader("Chase"))
|
||||
{
|
||||
auto &s = rig.chase_settings();
|
||||
static char chaseName[128] = "";
|
||||
draw_target("chase_target", s.target, chaseName, IM_ARRAYSIZE(chaseName));
|
||||
if (ImGui::Button("Chase target = Last Pick"))
|
||||
{
|
||||
target_from_last_pick(s.target);
|
||||
}
|
||||
ImGui::InputFloat3("Position offset (local)##chase", &s.position_offset_local.x);
|
||||
|
||||
glm::vec3 rotDeg = glm::degrees(glm::eulerAngles(s.rotation_offset));
|
||||
float r[3] = {rotDeg.x, rotDeg.y, rotDeg.z};
|
||||
if (ImGui::InputFloat3("Rotation offset (deg XYZ)##chase", r))
|
||||
{
|
||||
glm::mat4 R = glm::eulerAngleXYZ(glm::radians(r[0]), glm::radians(r[1]), glm::radians(r[2]));
|
||||
s.rotation_offset = glm::quat_cast(R);
|
||||
}
|
||||
|
||||
ImGui::SliderFloat("Position lag (1/s)", &s.position_lag, 0.0f, 30.0f);
|
||||
ImGui::SliderFloat("Rotation lag (1/s)", &s.rotation_lag, 0.0f, 30.0f);
|
||||
}
|
||||
|
||||
// Fixed
|
||||
if (ImGui::CollapsingHeader("Fixed"))
|
||||
{
|
||||
ImGui::TextUnformatted("Fixed mode does not modify the camera automatically.");
|
||||
}
|
||||
}
|
||||
|
||||
// Texture streaming + budget UI
|
||||
static const char *stateName(uint8_t s)
|
||||
{
|
||||
@@ -2051,6 +2259,7 @@ namespace
|
||||
bool show_ibl{false};
|
||||
bool show_postfx{false};
|
||||
bool show_scene{false};
|
||||
bool show_camera{false};
|
||||
bool show_async_assets{false};
|
||||
bool show_textures{false};
|
||||
};
|
||||
@@ -2072,6 +2281,7 @@ void vk_engine_draw_debug_ui(VulkanEngine *eng)
|
||||
ImGui::MenuItem("Window", nullptr, &g_debug_windows.show_window);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Scene", nullptr, &g_debug_windows.show_scene);
|
||||
ImGui::MenuItem("Camera", nullptr, &g_debug_windows.show_camera);
|
||||
ImGui::MenuItem("Render Graph", nullptr, &g_debug_windows.show_render_graph);
|
||||
ImGui::MenuItem("Pipelines", nullptr, &g_debug_windows.show_pipelines);
|
||||
ImGui::Separator();
|
||||
@@ -2187,6 +2397,15 @@ void vk_engine_draw_debug_ui(VulkanEngine *eng)
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (g_debug_windows.show_camera)
|
||||
{
|
||||
if (ImGui::Begin("Camera", &g_debug_windows.show_camera))
|
||||
{
|
||||
ui_camera(eng);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (g_debug_windows.show_async_assets)
|
||||
{
|
||||
if (ImGui::Begin("Async Assets", &g_debug_windows.show_async_assets))
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/picking/picking_system.h"
|
||||
#include "scene/vk_scene.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/camera/camera_rig.h"
|
||||
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
@@ -1579,6 +1580,268 @@ void Engine::camera_look_at(const glm::dvec3& target)
|
||||
cam.orientation = glm::quat_cast(rot);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
::CameraMode to_internal_camera_mode(GameAPI::CameraMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GameAPI::CameraMode::Free: return ::CameraMode::Free;
|
||||
case GameAPI::CameraMode::Orbit: return ::CameraMode::Orbit;
|
||||
case GameAPI::CameraMode::Follow: return ::CameraMode::Follow;
|
||||
case GameAPI::CameraMode::Chase: return ::CameraMode::Chase;
|
||||
case GameAPI::CameraMode::Fixed: return ::CameraMode::Fixed;
|
||||
default: return ::CameraMode::Free;
|
||||
}
|
||||
}
|
||||
|
||||
GameAPI::CameraMode to_api_camera_mode(::CameraMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ::CameraMode::Free: return GameAPI::CameraMode::Free;
|
||||
case ::CameraMode::Orbit: return GameAPI::CameraMode::Orbit;
|
||||
case ::CameraMode::Follow: return GameAPI::CameraMode::Follow;
|
||||
case ::CameraMode::Chase: return GameAPI::CameraMode::Chase;
|
||||
case ::CameraMode::Fixed: return GameAPI::CameraMode::Fixed;
|
||||
default: return GameAPI::CameraMode::Free;
|
||||
}
|
||||
}
|
||||
|
||||
::CameraTargetType to_internal_target_type(GameAPI::CameraTargetType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GameAPI::CameraTargetType::None: return ::CameraTargetType::None;
|
||||
case GameAPI::CameraTargetType::WorldPoint: return ::CameraTargetType::WorldPoint;
|
||||
case GameAPI::CameraTargetType::MeshInstance: return ::CameraTargetType::MeshInstance;
|
||||
case GameAPI::CameraTargetType::GLTFInstance: return ::CameraTargetType::GLTFInstance;
|
||||
default: return ::CameraTargetType::None;
|
||||
}
|
||||
}
|
||||
|
||||
GameAPI::CameraTargetType to_api_target_type(::CameraTargetType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ::CameraTargetType::None: return GameAPI::CameraTargetType::None;
|
||||
case ::CameraTargetType::WorldPoint: return GameAPI::CameraTargetType::WorldPoint;
|
||||
case ::CameraTargetType::MeshInstance: return GameAPI::CameraTargetType::MeshInstance;
|
||||
case ::CameraTargetType::GLTFInstance: return GameAPI::CameraTargetType::GLTFInstance;
|
||||
default: return GameAPI::CameraTargetType::None;
|
||||
}
|
||||
}
|
||||
|
||||
::CameraTarget to_internal_target(const GameAPI::CameraTarget &target)
|
||||
{
|
||||
::CameraTarget t;
|
||||
t.type = to_internal_target_type(target.type);
|
||||
t.name = target.name;
|
||||
t.world_point = WorldVec3(target.worldPoint);
|
||||
return t;
|
||||
}
|
||||
|
||||
GameAPI::CameraTarget to_api_target(const ::CameraTarget &target)
|
||||
{
|
||||
GameAPI::CameraTarget t;
|
||||
t.type = to_api_target_type(target.type);
|
||||
t.name = target.name;
|
||||
t.worldPoint = glm::dvec3(target.world_point);
|
||||
return t;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Engine::set_camera_mode(CameraMode mode)
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SceneManager *scene = _engine->_sceneManager.get();
|
||||
Camera &cam = scene->getMainCamera();
|
||||
CameraRig &rig = scene->getCameraRig();
|
||||
|
||||
if (_engine->_input)
|
||||
{
|
||||
_engine->_input->set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
|
||||
rig.set_mode(to_internal_camera_mode(mode), *scene, cam);
|
||||
}
|
||||
|
||||
CameraMode Engine::get_camera_mode() const
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return CameraMode::Free;
|
||||
}
|
||||
return to_api_camera_mode(_engine->_sceneManager->getCameraRig().mode());
|
||||
}
|
||||
|
||||
void Engine::set_free_camera_settings(const FreeCameraSettings& settings)
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
::FreeCameraSettings &s = _engine->_sceneManager->getCameraRig().free_settings();
|
||||
s.move_speed = settings.moveSpeed;
|
||||
s.look_sensitivity = settings.lookSensitivity;
|
||||
s.roll_speed = settings.rollSpeed;
|
||||
}
|
||||
|
||||
FreeCameraSettings Engine::get_free_camera_settings() const
|
||||
{
|
||||
FreeCameraSettings out{};
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
const ::FreeCameraSettings &s = _engine->_sceneManager->getCameraRig().free_settings();
|
||||
out.moveSpeed = s.move_speed;
|
||||
out.lookSensitivity = s.look_sensitivity;
|
||||
out.rollSpeed = s.roll_speed;
|
||||
return out;
|
||||
}
|
||||
|
||||
void Engine::set_orbit_camera_settings(const OrbitCameraSettings& settings)
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
::OrbitCameraSettings &s = _engine->_sceneManager->getCameraRig().orbit_settings();
|
||||
s.target = to_internal_target(settings.target);
|
||||
s.distance = settings.distance;
|
||||
s.yaw = settings.yaw;
|
||||
s.pitch = settings.pitch;
|
||||
s.look_sensitivity = settings.lookSensitivity;
|
||||
}
|
||||
|
||||
OrbitCameraSettings Engine::get_orbit_camera_settings() const
|
||||
{
|
||||
OrbitCameraSettings out{};
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
const ::OrbitCameraSettings &s = _engine->_sceneManager->getCameraRig().orbit_settings();
|
||||
out.target = to_api_target(s.target);
|
||||
out.distance = s.distance;
|
||||
out.yaw = s.yaw;
|
||||
out.pitch = s.pitch;
|
||||
out.lookSensitivity = s.look_sensitivity;
|
||||
return out;
|
||||
}
|
||||
|
||||
void Engine::set_follow_camera_settings(const FollowCameraSettings& settings)
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
::FollowCameraSettings &s = _engine->_sceneManager->getCameraRig().follow_settings();
|
||||
s.target = to_internal_target(settings.target);
|
||||
s.position_offset_local = settings.positionOffsetLocal;
|
||||
s.rotation_offset = settings.rotationOffset;
|
||||
}
|
||||
|
||||
FollowCameraSettings Engine::get_follow_camera_settings() const
|
||||
{
|
||||
FollowCameraSettings out{};
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
const ::FollowCameraSettings &s = _engine->_sceneManager->getCameraRig().follow_settings();
|
||||
out.target = to_api_target(s.target);
|
||||
out.positionOffsetLocal = s.position_offset_local;
|
||||
out.rotationOffset = s.rotation_offset;
|
||||
return out;
|
||||
}
|
||||
|
||||
void Engine::set_chase_camera_settings(const ChaseCameraSettings& settings)
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
::ChaseCameraSettings &s = _engine->_sceneManager->getCameraRig().chase_settings();
|
||||
s.target = to_internal_target(settings.target);
|
||||
s.position_offset_local = settings.positionOffsetLocal;
|
||||
s.rotation_offset = settings.rotationOffset;
|
||||
s.position_lag = settings.positionLag;
|
||||
s.rotation_lag = settings.rotationLag;
|
||||
}
|
||||
|
||||
ChaseCameraSettings Engine::get_chase_camera_settings() const
|
||||
{
|
||||
ChaseCameraSettings out{};
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
const ::ChaseCameraSettings &s = _engine->_sceneManager->getCameraRig().chase_settings();
|
||||
out.target = to_api_target(s.target);
|
||||
out.positionOffsetLocal = s.position_offset_local;
|
||||
out.rotationOffset = s.rotation_offset;
|
||||
out.positionLag = s.position_lag;
|
||||
out.rotationLag = s.rotation_lag;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Engine::set_camera_target_from_last_pick()
|
||||
{
|
||||
if (!_engine || !_engine->_sceneManager)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const PickingSystem *picking = _engine->picking();
|
||||
if (!picking)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &pick = picking->last_pick();
|
||||
if (!pick.valid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
::CameraTarget t;
|
||||
if (pick.ownerType == RenderObject::OwnerType::MeshInstance)
|
||||
{
|
||||
t.type = ::CameraTargetType::MeshInstance;
|
||||
t.name = pick.ownerName;
|
||||
}
|
||||
else if (pick.ownerType == RenderObject::OwnerType::GLTFInstance)
|
||||
{
|
||||
t.type = ::CameraTargetType::GLTFInstance;
|
||||
t.name = pick.ownerName;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.type = ::CameraTargetType::WorldPoint;
|
||||
t.world_point = pick.worldPos;
|
||||
}
|
||||
|
||||
CameraRig &rig = _engine->_sceneManager->getCameraRig();
|
||||
rig.orbit_settings().target = t;
|
||||
rig.follow_settings().target = t;
|
||||
rig.chase_settings().target = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Rendering
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -304,6 +304,66 @@ struct Stats
|
||||
int drawCallCount{0};
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Camera Rig Types
|
||||
// ============================================================================
|
||||
|
||||
enum class CameraMode : uint8_t
|
||||
{
|
||||
Free = 0,
|
||||
Orbit = 1,
|
||||
Follow = 2,
|
||||
Chase = 3,
|
||||
Fixed = 4
|
||||
};
|
||||
|
||||
enum class CameraTargetType : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
WorldPoint = 1,
|
||||
MeshInstance = 2,
|
||||
GLTFInstance = 3
|
||||
};
|
||||
|
||||
struct CameraTarget
|
||||
{
|
||||
CameraTargetType type{CameraTargetType::None};
|
||||
std::string name{};
|
||||
glm::dvec3 worldPoint{0.0, 0.0, 0.0};
|
||||
};
|
||||
|
||||
struct FreeCameraSettings
|
||||
{
|
||||
float moveSpeed{1.8f}; // world units / second
|
||||
float lookSensitivity{0.0020f};
|
||||
float rollSpeed{1.0f}; // radians / second
|
||||
};
|
||||
|
||||
struct OrbitCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
double distance{10.0};
|
||||
float yaw{0.0f}; // radians
|
||||
float pitch{0.0f}; // radians
|
||||
float lookSensitivity{0.0020f};
|
||||
};
|
||||
|
||||
struct FollowCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
glm::vec3 positionOffsetLocal{0.0f, 2.0f, 6.0f};
|
||||
glm::quat rotationOffset{1.0f, 0.0f, 0.0f, 0.0f};
|
||||
};
|
||||
|
||||
struct ChaseCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
glm::vec3 positionOffsetLocal{0.0f, 2.0f, 6.0f};
|
||||
glm::quat rotationOffset{1.0f, 0.0f, 0.0f, 0.0f};
|
||||
float positionLag{8.0f}; // smoothing rate (1/sec), higher = snappier
|
||||
float rotationLag{10.0f}; // smoothing rate (1/sec)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Main API Class
|
||||
// ============================================================================
|
||||
@@ -651,6 +711,25 @@ public:
|
||||
void camera_look_at(const glm::vec3& target);
|
||||
void camera_look_at(const glm::dvec3& target);
|
||||
|
||||
// Camera mode and per-mode settings
|
||||
void set_camera_mode(CameraMode mode);
|
||||
CameraMode get_camera_mode() const;
|
||||
|
||||
void set_free_camera_settings(const FreeCameraSettings& settings);
|
||||
FreeCameraSettings get_free_camera_settings() const;
|
||||
|
||||
void set_orbit_camera_settings(const OrbitCameraSettings& settings);
|
||||
OrbitCameraSettings get_orbit_camera_settings() const;
|
||||
|
||||
void set_follow_camera_settings(const FollowCameraSettings& settings);
|
||||
FollowCameraSettings get_follow_camera_settings() const;
|
||||
|
||||
void set_chase_camera_settings(const ChaseCameraSettings& settings);
|
||||
ChaseCameraSettings get_chase_camera_settings() const;
|
||||
|
||||
// Convenience: set Orbit/Follow/Chase target from the engine's last pick.
|
||||
bool set_camera_target_from_last_pick();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Rendering
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace GameRuntime
|
||||
// --- Camera input (if not captured by UI) ---
|
||||
if (_renderer->_sceneManager && input)
|
||||
{
|
||||
_renderer->_sceneManager->getMainCamera().process_input(*input, ui_capture_keyboard, ui_capture_mouse);
|
||||
_renderer->_sceneManager->getCameraRig().process_input(*input, ui_capture_keyboard, ui_capture_mouse);
|
||||
}
|
||||
|
||||
// --- Throttle when minimized ---
|
||||
|
||||
@@ -1,102 +1,6 @@
|
||||
#include "camera.h"
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
void Camera::update()
|
||||
{
|
||||
glm::mat4 cameraRotation = getRotationMatrix();
|
||||
glm::vec3 delta = glm::vec3(cameraRotation * glm::vec4(velocity * moveSpeed, 0.f));
|
||||
position_world += glm::dvec3(delta);
|
||||
}
|
||||
|
||||
void Camera::process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 Camera::getViewMatrix(const glm::vec3 &position_local) const
|
||||
{
|
||||
|
||||
@@ -2,29 +2,15 @@
|
||||
|
||||
#include <core/types.h>
|
||||
|
||||
#include <core/input/input_system.h>
|
||||
|
||||
#include "glm/vec3.hpp"
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
glm::vec3 velocity{0.0f, 0.0f, 0.0f};
|
||||
glm::dvec3 position_world{0.0, 0.0, 0.0};
|
||||
// Orientation stored as a quaternion (local -> world).
|
||||
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// Movement/look tuning
|
||||
float moveSpeed { 0.03f };
|
||||
float lookSensitivity { 0.0020f };
|
||||
bool rmbDown { false };
|
||||
|
||||
// Field of view in degrees for projection
|
||||
float fovDegrees { 50.f };
|
||||
|
||||
glm::mat4 getViewMatrix(const glm::vec3 &position_local) const;
|
||||
glm::mat4 getRotationMatrix() const;
|
||||
|
||||
void process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse);
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
129
src/scene/camera/camera_rig.cpp
Normal file
129
src/scene/camera/camera_rig.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "camera_rig.h"
|
||||
|
||||
#include <scene/camera/mode_chase.h>
|
||||
#include <scene/camera/mode_fixed.h>
|
||||
#include <scene/camera/mode_follow.h>
|
||||
#include <scene/camera/mode_free.h>
|
||||
#include <scene/camera/mode_orbit.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/vk_scene.h>
|
||||
|
||||
#include <core/input/input_system.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<ICameraMode> make_mode(CameraMode mode,
|
||||
FreeCameraSettings &free_settings,
|
||||
OrbitCameraSettings &orbit_settings,
|
||||
FollowCameraSettings &follow_settings,
|
||||
ChaseCameraSettings &chase_settings,
|
||||
FixedCameraSettings &fixed_settings)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CameraMode::Free: return std::make_unique<FreeCameraMode>(free_settings);
|
||||
case CameraMode::Orbit: return std::make_unique<OrbitCameraMode>(orbit_settings);
|
||||
case CameraMode::Follow: return std::make_unique<FollowCameraMode>(follow_settings);
|
||||
case CameraMode::Chase: return std::make_unique<ChaseCameraMode>(chase_settings);
|
||||
case CameraMode::Fixed: return std::make_unique<FixedCameraMode>(fixed_settings);
|
||||
default: return std::make_unique<FreeCameraMode>(free_settings);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CameraRig::CameraRig() = default;
|
||||
CameraRig::~CameraRig() = default;
|
||||
|
||||
void CameraRig::init(SceneManager &scene, Camera &camera)
|
||||
{
|
||||
_scene = &scene;
|
||||
_camera = &camera;
|
||||
recreate_mode(scene, camera);
|
||||
}
|
||||
|
||||
void CameraRig::set_mode(CameraMode mode, SceneManager &scene, Camera &camera)
|
||||
{
|
||||
if (_mode == mode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_mode = mode;
|
||||
recreate_mode(scene, camera);
|
||||
}
|
||||
|
||||
const char *CameraRig::mode_name() const
|
||||
{
|
||||
if (_mode_impl)
|
||||
{
|
||||
return _mode_impl->name();
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
void CameraRig::process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse)
|
||||
{
|
||||
if (_mode_impl && _scene && _camera)
|
||||
{
|
||||
_mode_impl->process_input(*_scene, *_camera, input, ui_capture_keyboard, ui_capture_mouse);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraRig::update(SceneManager &scene, Camera &camera, float dt)
|
||||
{
|
||||
if (_mode_impl)
|
||||
{
|
||||
_mode_impl->update(scene, camera, dt);
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraRig::resolve_target(SceneManager &scene,
|
||||
const CameraTarget &target,
|
||||
WorldVec3 &out_position_world,
|
||||
glm::quat &out_rotation) const
|
||||
{
|
||||
out_rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
switch (target.type)
|
||||
{
|
||||
case CameraTargetType::WorldPoint:
|
||||
out_position_world = target.world_point;
|
||||
return true;
|
||||
case CameraTargetType::MeshInstance:
|
||||
{
|
||||
WorldVec3 t{};
|
||||
glm::quat r{};
|
||||
glm::vec3 s{};
|
||||
if (!scene.getMeshInstanceTRSWorld(target.name, t, r, s))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
out_position_world = t;
|
||||
out_rotation = r;
|
||||
return true;
|
||||
}
|
||||
case CameraTargetType::GLTFInstance:
|
||||
{
|
||||
WorldVec3 t{};
|
||||
glm::quat r{};
|
||||
glm::vec3 s{};
|
||||
if (!scene.getGLTFInstanceTRSWorld(target.name, t, r, s))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
out_position_world = t;
|
||||
out_rotation = r;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CameraRig::recreate_mode(SceneManager &scene, Camera &camera)
|
||||
{
|
||||
_mode_impl = make_mode(_mode, _free, _orbit, _follow, _chase, _fixed);
|
||||
if (_mode_impl)
|
||||
{
|
||||
_mode_impl->on_activate(scene, camera);
|
||||
}
|
||||
}
|
||||
124
src/scene/camera/camera_rig.h
Normal file
124
src/scene/camera/camera_rig.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/world.h>
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Camera;
|
||||
class InputSystem;
|
||||
class SceneManager;
|
||||
|
||||
enum class CameraMode : uint8_t
|
||||
{
|
||||
Free = 0,
|
||||
Orbit = 1,
|
||||
Follow = 2,
|
||||
Chase = 3,
|
||||
Fixed = 4
|
||||
};
|
||||
|
||||
enum class CameraTargetType : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
WorldPoint = 1,
|
||||
MeshInstance = 2,
|
||||
GLTFInstance = 3
|
||||
};
|
||||
|
||||
struct CameraTarget
|
||||
{
|
||||
CameraTargetType type{CameraTargetType::None};
|
||||
std::string name{};
|
||||
WorldVec3 world_point{0.0, 0.0, 0.0};
|
||||
};
|
||||
|
||||
struct FreeCameraSettings
|
||||
{
|
||||
float move_speed{1.8f}; // world units / second
|
||||
float look_sensitivity{0.0020f};
|
||||
float roll_speed{1.0f}; // radians / second
|
||||
};
|
||||
|
||||
struct OrbitCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
double distance{10.0};
|
||||
float yaw{0.0f}; // radians
|
||||
float pitch{0.0f}; // radians
|
||||
float look_sensitivity{0.0020f};
|
||||
};
|
||||
|
||||
struct FollowCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
glm::vec3 position_offset_local{0.0f, 2.0f, 6.0f};
|
||||
glm::quat rotation_offset{1.0f, 0.0f, 0.0f, 0.0f};
|
||||
};
|
||||
|
||||
struct ChaseCameraSettings
|
||||
{
|
||||
CameraTarget target{};
|
||||
glm::vec3 position_offset_local{0.0f, 2.0f, 6.0f};
|
||||
glm::quat rotation_offset{1.0f, 0.0f, 0.0f, 0.0f};
|
||||
float position_lag{8.0f}; // smoothing rate (1/sec), higher = snappier
|
||||
float rotation_lag{10.0f}; // smoothing rate (1/sec)
|
||||
};
|
||||
|
||||
struct FixedCameraSettings
|
||||
{
|
||||
};
|
||||
|
||||
class CameraRig
|
||||
{
|
||||
public:
|
||||
CameraRig();
|
||||
~CameraRig();
|
||||
|
||||
CameraRig(const CameraRig &) = delete;
|
||||
CameraRig &operator=(const CameraRig &) = delete;
|
||||
|
||||
void init(SceneManager &scene, Camera &camera);
|
||||
|
||||
CameraMode mode() const { return _mode; }
|
||||
void set_mode(CameraMode mode, SceneManager &scene, Camera &camera);
|
||||
|
||||
const char *mode_name() const;
|
||||
|
||||
FreeCameraSettings &free_settings() { return _free; }
|
||||
OrbitCameraSettings &orbit_settings() { return _orbit; }
|
||||
FollowCameraSettings &follow_settings() { return _follow; }
|
||||
ChaseCameraSettings &chase_settings() { return _chase; }
|
||||
FixedCameraSettings &fixed_settings() { return _fixed; }
|
||||
|
||||
const FreeCameraSettings &free_settings() const { return _free; }
|
||||
const OrbitCameraSettings &orbit_settings() const { return _orbit; }
|
||||
const FollowCameraSettings &follow_settings() const { return _follow; }
|
||||
const ChaseCameraSettings &chase_settings() const { return _chase; }
|
||||
const FixedCameraSettings &fixed_settings() const { return _fixed; }
|
||||
|
||||
void process_input(InputSystem &input, bool ui_capture_keyboard, bool ui_capture_mouse);
|
||||
void update(SceneManager &scene, Camera &camera, float dt);
|
||||
|
||||
bool resolve_target(SceneManager &scene,
|
||||
const CameraTarget &target,
|
||||
WorldVec3 &out_position_world,
|
||||
glm::quat &out_rotation) const;
|
||||
|
||||
private:
|
||||
void recreate_mode(SceneManager &scene, Camera &camera);
|
||||
|
||||
CameraMode _mode{CameraMode::Free};
|
||||
std::unique_ptr<class ICameraMode> _mode_impl;
|
||||
SceneManager *_scene = nullptr;
|
||||
Camera *_camera = nullptr;
|
||||
|
||||
FreeCameraSettings _free{};
|
||||
OrbitCameraSettings _orbit{};
|
||||
FollowCameraSettings _follow{};
|
||||
ChaseCameraSettings _chase{};
|
||||
FixedCameraSettings _fixed{};
|
||||
};
|
||||
20
src/scene/camera/icamera_mode.h
Normal file
20
src/scene/camera/icamera_mode.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
class Camera;
|
||||
class InputSystem;
|
||||
class SceneManager;
|
||||
|
||||
class ICameraMode
|
||||
{
|
||||
public:
|
||||
virtual ~ICameraMode() = default;
|
||||
|
||||
virtual const char *name() const = 0;
|
||||
virtual void on_activate(SceneManager &scene, Camera &camera) = 0;
|
||||
virtual void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) = 0;
|
||||
virtual void update(SceneManager &scene, Camera &camera, float dt) = 0;
|
||||
};
|
||||
74
src/scene/camera/mode_chase.cpp
Normal file
74
src/scene/camera/mode_chase.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "mode_chase.h"
|
||||
|
||||
#include <scene/camera/camera_rig.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/vk_scene.h>
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
ChaseCameraMode::ChaseCameraMode(ChaseCameraSettings &settings)
|
||||
: _settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void ChaseCameraMode::on_activate(SceneManager &scene, Camera &camera)
|
||||
{
|
||||
// If no target set, chase a point in front of the camera.
|
||||
if (_settings.target.type == CameraTargetType::None)
|
||||
{
|
||||
glm::vec3 forward = glm::rotate(camera.orientation, glm::vec3(0.0f, 0.0f, -1.0f));
|
||||
_settings.target.type = CameraTargetType::WorldPoint;
|
||||
_settings.target.world_point = camera.position_world + WorldVec3(forward) * 10.0;
|
||||
}
|
||||
|
||||
// Preserve current relative transform to the target when possible.
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glm::quat inv_target = glm::inverse(target_rot);
|
||||
glm::vec3 rel_pos = glm::vec3(camera.position_world - target_pos);
|
||||
_settings.position_offset_local = glm::rotate(inv_target, rel_pos);
|
||||
_settings.rotation_offset = glm::normalize(inv_target * camera.orientation);
|
||||
}
|
||||
|
||||
void ChaseCameraMode::process_input(SceneManager & /*scene*/,
|
||||
Camera & /*camera*/,
|
||||
InputSystem & /*input*/,
|
||||
bool /*ui_capture_keyboard*/,
|
||||
bool /*ui_capture_mouse*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ChaseCameraMode::update(SceneManager &scene, Camera &camera, float dt)
|
||||
{
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 offset_world = glm::rotate(target_rot, _settings.position_offset_local);
|
||||
WorldVec3 desired_pos = target_pos + WorldVec3(offset_world);
|
||||
glm::quat desired_rot = glm::normalize(target_rot * _settings.rotation_offset);
|
||||
|
||||
if (dt > 0.0f)
|
||||
{
|
||||
float pos_alpha = 1.0f - std::exp(-_settings.position_lag * dt);
|
||||
float rot_alpha = 1.0f - std::exp(-_settings.rotation_lag * dt);
|
||||
|
||||
camera.position_world += (desired_pos - camera.position_world) * static_cast<double>(pos_alpha);
|
||||
camera.orientation = glm::normalize(glm::slerp(camera.orientation, desired_rot, rot_alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
camera.position_world = desired_pos;
|
||||
camera.orientation = desired_rot;
|
||||
}
|
||||
}
|
||||
24
src/scene/camera/mode_chase.h
Normal file
24
src/scene/camera/mode_chase.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <scene/camera/icamera_mode.h>
|
||||
|
||||
struct ChaseCameraSettings;
|
||||
|
||||
class ChaseCameraMode : public ICameraMode
|
||||
{
|
||||
public:
|
||||
explicit ChaseCameraMode(ChaseCameraSettings &settings);
|
||||
~ChaseCameraMode() override = default;
|
||||
|
||||
const char *name() const override { return "Chase"; }
|
||||
void on_activate(SceneManager &scene, Camera &camera) override;
|
||||
void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) override;
|
||||
void update(SceneManager &scene, Camera &camera, float dt) override;
|
||||
|
||||
private:
|
||||
ChaseCameraSettings &_settings;
|
||||
};
|
||||
25
src/scene/camera/mode_fixed.cpp
Normal file
25
src/scene/camera/mode_fixed.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "mode_fixed.h"
|
||||
|
||||
#include <scene/camera/camera_rig.h>
|
||||
|
||||
FixedCameraMode::FixedCameraMode(FixedCameraSettings &settings)
|
||||
: _settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void FixedCameraMode::on_activate(SceneManager & /*scene*/, Camera & /*camera*/)
|
||||
{
|
||||
}
|
||||
|
||||
void FixedCameraMode::process_input(SceneManager & /*scene*/,
|
||||
Camera & /*camera*/,
|
||||
InputSystem & /*input*/,
|
||||
bool /*ui_capture_keyboard*/,
|
||||
bool /*ui_capture_mouse*/)
|
||||
{
|
||||
}
|
||||
|
||||
void FixedCameraMode::update(SceneManager & /*scene*/, Camera & /*camera*/, float /*dt*/)
|
||||
{
|
||||
(void)_settings;
|
||||
}
|
||||
24
src/scene/camera/mode_fixed.h
Normal file
24
src/scene/camera/mode_fixed.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <scene/camera/icamera_mode.h>
|
||||
|
||||
struct FixedCameraSettings;
|
||||
|
||||
class FixedCameraMode : public ICameraMode
|
||||
{
|
||||
public:
|
||||
explicit FixedCameraMode(FixedCameraSettings &settings);
|
||||
~FixedCameraMode() override = default;
|
||||
|
||||
const char *name() const override { return "Fixed"; }
|
||||
void on_activate(SceneManager &scene, Camera &camera) override;
|
||||
void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) override;
|
||||
void update(SceneManager &scene, Camera &camera, float dt) override;
|
||||
|
||||
private:
|
||||
FixedCameraSettings &_settings;
|
||||
};
|
||||
58
src/scene/camera/mode_follow.cpp
Normal file
58
src/scene/camera/mode_follow.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "mode_follow.h"
|
||||
|
||||
#include <scene/camera/camera_rig.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/vk_scene.h>
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
FollowCameraMode::FollowCameraMode(FollowCameraSettings &settings)
|
||||
: _settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void FollowCameraMode::on_activate(SceneManager &scene, Camera &camera)
|
||||
{
|
||||
// If no target set, follow a point in front of the camera.
|
||||
if (_settings.target.type == CameraTargetType::None)
|
||||
{
|
||||
glm::vec3 forward = glm::rotate(camera.orientation, glm::vec3(0.0f, 0.0f, -1.0f));
|
||||
_settings.target.type = CameraTargetType::WorldPoint;
|
||||
_settings.target.world_point = camera.position_world + WorldVec3(forward) * 10.0;
|
||||
}
|
||||
|
||||
// Preserve current relative transform to the target when possible.
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glm::quat inv_target = glm::inverse(target_rot);
|
||||
glm::vec3 rel_pos = glm::vec3(camera.position_world - target_pos);
|
||||
_settings.position_offset_local = glm::rotate(inv_target, rel_pos);
|
||||
_settings.rotation_offset = glm::normalize(inv_target * camera.orientation);
|
||||
}
|
||||
|
||||
void FollowCameraMode::process_input(SceneManager & /*scene*/,
|
||||
Camera & /*camera*/,
|
||||
InputSystem & /*input*/,
|
||||
bool /*ui_capture_keyboard*/,
|
||||
bool /*ui_capture_mouse*/)
|
||||
{
|
||||
}
|
||||
|
||||
void FollowCameraMode::update(SceneManager &scene, Camera &camera, float /*dt*/)
|
||||
{
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 offset_world = glm::rotate(target_rot, _settings.position_offset_local);
|
||||
camera.position_world = target_pos + WorldVec3(offset_world);
|
||||
camera.orientation = glm::normalize(target_rot * _settings.rotation_offset);
|
||||
}
|
||||
24
src/scene/camera/mode_follow.h
Normal file
24
src/scene/camera/mode_follow.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <scene/camera/icamera_mode.h>
|
||||
|
||||
struct FollowCameraSettings;
|
||||
|
||||
class FollowCameraMode : public ICameraMode
|
||||
{
|
||||
public:
|
||||
explicit FollowCameraMode(FollowCameraSettings &settings);
|
||||
~FollowCameraMode() override = default;
|
||||
|
||||
const char *name() const override { return "Follow"; }
|
||||
void on_activate(SceneManager &scene, Camera &camera) override;
|
||||
void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) override;
|
||||
void update(SceneManager &scene, Camera &camera, float dt) override;
|
||||
|
||||
private:
|
||||
FollowCameraSettings &_settings;
|
||||
};
|
||||
147
src/scene/camera/mode_free.cpp
Normal file
147
src/scene/camera/mode_free.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "mode_free.h"
|
||||
|
||||
#include <scene/camera/camera_rig.h>
|
||||
#include <scene/camera.h>
|
||||
|
||||
#include <core/input/input_system.h>
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
FreeCameraMode::FreeCameraMode(FreeCameraSettings &settings)
|
||||
: _settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void FreeCameraMode::on_activate(SceneManager & /*scene*/, Camera & /*camera*/)
|
||||
{
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_roll_dir = 0.0f;
|
||||
_rmb_down = false;
|
||||
}
|
||||
|
||||
void FreeCameraMode::process_input(SceneManager & /*scene*/,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse)
|
||||
{
|
||||
const InputState &st = input.state();
|
||||
|
||||
// Movement is state-based so simultaneous keys work naturally.
|
||||
if (ui_capture_keyboard)
|
||||
{
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_roll_dir = 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;
|
||||
|
||||
float roll = 0.0f;
|
||||
if (st.key_down(Key::Q)) { roll -= 1.0f; }
|
||||
if (st.key_down(Key::E)) { roll += 1.0f; }
|
||||
_roll_dir = roll;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
_rmb_down = true;
|
||||
input.set_cursor_mode(CursorMode::Relative);
|
||||
}
|
||||
else if (e.type == InputEvent::Type::MouseButtonUp && e.mouse_button == MouseButton::Right)
|
||||
{
|
||||
_rmb_down = false;
|
||||
input.set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
else if (e.type == InputEvent::Type::MouseMove && _rmb_down)
|
||||
{
|
||||
// Convert mouse motion to incremental yaw/pitch angles.
|
||||
float dx = e.mouse_delta.x * _settings.look_sensitivity;
|
||||
float dy = e.mouse_delta.y * _settings.look_sensitivity;
|
||||
|
||||
// Mouse right (xrel > 0) turns view right with -Z-forward: yaw around +Y.
|
||||
glm::quat yaw_rotation = glm::angleAxis(dx, glm::vec3{0.f, 1.f, 0.f});
|
||||
|
||||
// Mouse up (yrel < 0) looks up with -Z-forward: negative dy.
|
||||
float pitch_delta = -dy;
|
||||
// Pitch around the camera's local X (right) axis in world space.
|
||||
glm::vec3 right = glm::rotate(camera.orientation, glm::vec3{1.f, 0.f, 0.f});
|
||||
glm::quat pitch_rotation = glm::angleAxis(pitch_delta, glm::vec3(right));
|
||||
|
||||
// Apply yaw, then pitch, to the current orientation.
|
||||
camera.orientation = glm::normalize(pitch_rotation * yaw_rotation * camera.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)
|
||||
camera.fovDegrees -= steps * 2.0f;
|
||||
camera.fovDegrees = std::clamp(camera.fovDegrees, 30.0f, 110.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exponential scale for pleasant feel
|
||||
float factor = std::pow(1.15f, steps);
|
||||
_settings.move_speed = std::clamp(_settings.move_speed * factor, 0.06f, 300.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: if mouse state shows RMB is no longer down, release relative mode.
|
||||
if (_rmb_down && !st.mouse_down(MouseButton::Right))
|
||||
{
|
||||
_rmb_down = false;
|
||||
input.set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
}
|
||||
|
||||
void FreeCameraMode::update(SceneManager & /*scene*/, Camera &camera, float dt)
|
||||
{
|
||||
if (dt <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Roll around the camera's forward axis (world-space axis).
|
||||
if (_roll_dir != 0.0f && _settings.roll_speed > 0.0f)
|
||||
{
|
||||
glm::vec3 forward = glm::rotate(camera.orientation, glm::vec3{0.0f, 0.0f, -1.0f});
|
||||
float angle = _roll_dir * _settings.roll_speed * dt;
|
||||
glm::quat roll_rotation = glm::angleAxis(angle, glm::normalize(forward));
|
||||
camera.orientation = glm::normalize(roll_rotation * camera.orientation);
|
||||
}
|
||||
|
||||
// Move in camera-local space.
|
||||
if (_velocity.x != 0.0f || _velocity.y != 0.0f || _velocity.z != 0.0f)
|
||||
{
|
||||
glm::vec3 local_delta = _velocity * (_settings.move_speed * dt);
|
||||
glm::mat4 camera_rotation = camera.getRotationMatrix();
|
||||
glm::vec3 world_delta = glm::vec3(camera_rotation * glm::vec4(local_delta, 0.0f));
|
||||
camera.position_world += glm::dvec3(world_delta);
|
||||
}
|
||||
}
|
||||
27
src/scene/camera/mode_free.h
Normal file
27
src/scene/camera/mode_free.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <scene/camera/icamera_mode.h>
|
||||
|
||||
struct FreeCameraSettings;
|
||||
|
||||
class FreeCameraMode : public ICameraMode
|
||||
{
|
||||
public:
|
||||
explicit FreeCameraMode(FreeCameraSettings &settings);
|
||||
~FreeCameraMode() override = default;
|
||||
|
||||
const char *name() const override { return "Free"; }
|
||||
void on_activate(SceneManager &scene, Camera &camera) override;
|
||||
void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) override;
|
||||
void update(SceneManager &scene, Camera &camera, float dt) override;
|
||||
|
||||
private:
|
||||
FreeCameraSettings &_settings;
|
||||
glm::vec3 _velocity{0.0f, 0.0f, 0.0f};
|
||||
float _roll_dir = 0.0f;
|
||||
bool _rmb_down = false;
|
||||
};
|
||||
163
src/scene/camera/mode_orbit.cpp
Normal file
163
src/scene/camera/mode_orbit.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "mode_orbit.h"
|
||||
|
||||
#include <scene/camera/camera_rig.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/vk_scene.h>
|
||||
|
||||
#include <core/input/input_system.h>
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
OrbitCameraMode::OrbitCameraMode(OrbitCameraSettings &settings)
|
||||
: _settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
void OrbitCameraMode::on_activate(SceneManager &scene, Camera &camera)
|
||||
{
|
||||
_rmb_down = false;
|
||||
|
||||
// If no target set, orbit around a point in front of the camera.
|
||||
if (_settings.target.type == CameraTargetType::None)
|
||||
{
|
||||
glm::vec3 forward = glm::rotate(camera.orientation, glm::vec3(0.0f, 0.0f, -1.0f));
|
||||
_settings.target.type = CameraTargetType::WorldPoint;
|
||||
_settings.target.world_point = camera.position_world + WorldVec3(forward) * _settings.distance;
|
||||
}
|
||||
|
||||
// Derive yaw/pitch/distance from current camera pose to avoid snapping.
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WorldVec3 to_cam = camera.position_world - target_pos;
|
||||
double dist = glm::length(to_cam);
|
||||
if (!std::isfinite(dist) || dist < 0.001)
|
||||
{
|
||||
dist = _settings.distance;
|
||||
}
|
||||
_settings.distance = dist;
|
||||
|
||||
glm::vec3 dir = glm::normalize(glm::vec3(to_cam / dist)); // target -> camera
|
||||
_settings.yaw = std::atan2(dir.x, dir.z);
|
||||
_settings.pitch = std::asin(std::clamp(-dir.y, -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
void OrbitCameraMode::process_input(SceneManager & /*scene*/,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool /*ui_capture_keyboard*/,
|
||||
bool ui_capture_mouse)
|
||||
{
|
||||
const InputState &st = input.state();
|
||||
|
||||
for (const InputEvent &e : input.events())
|
||||
{
|
||||
if (ui_capture_mouse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type == InputEvent::Type::MouseButtonDown && e.mouse_button == MouseButton::Right)
|
||||
{
|
||||
_rmb_down = true;
|
||||
input.set_cursor_mode(CursorMode::Relative);
|
||||
}
|
||||
else if (e.type == InputEvent::Type::MouseButtonUp && e.mouse_button == MouseButton::Right)
|
||||
{
|
||||
_rmb_down = false;
|
||||
input.set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
else if (e.type == InputEvent::Type::MouseMove && _rmb_down)
|
||||
{
|
||||
float dx = e.mouse_delta.x * _settings.look_sensitivity;
|
||||
float dy = e.mouse_delta.y * _settings.look_sensitivity;
|
||||
|
||||
_settings.yaw += dx;
|
||||
_settings.pitch += dy;
|
||||
|
||||
const float limit = glm::half_pi<float>() - 0.01f;
|
||||
_settings.pitch = std::clamp(_settings.pitch, -limit, limit);
|
||||
}
|
||||
else if (e.type == InputEvent::Type::MouseWheel)
|
||||
{
|
||||
const float steps = e.wheel_delta.y; // positive = wheel up
|
||||
if (std::abs(steps) < 0.001f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.mods.ctrl)
|
||||
{
|
||||
camera.fovDegrees -= steps * 2.0f;
|
||||
camera.fovDegrees = std::clamp(camera.fovDegrees, 30.0f, 110.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double factor = std::pow(1.15, -static_cast<double>(steps));
|
||||
_settings.distance = std::clamp(_settings.distance * factor, 0.2, 100000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_rmb_down && !st.mouse_down(MouseButton::Right))
|
||||
{
|
||||
_rmb_down = false;
|
||||
input.set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
}
|
||||
|
||||
void OrbitCameraMode::update(SceneManager &scene, Camera &camera, float /*dt*/)
|
||||
{
|
||||
WorldVec3 target_pos{};
|
||||
glm::quat target_rot{};
|
||||
if (!scene.getCameraRig().resolve_target(scene, _settings.target, target_pos, target_rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto wrap_pi = [](float a) -> float {
|
||||
// Wrap angle to [-pi, pi] to avoid precision issues over long play sessions.
|
||||
constexpr float two_pi = glm::two_pi<float>();
|
||||
a = std::fmod(a + glm::pi<float>(), two_pi);
|
||||
if (a < 0.0f) a += two_pi;
|
||||
return a - glm::pi<float>();
|
||||
};
|
||||
|
||||
float yaw = wrap_pi(_settings.yaw);
|
||||
float pitch = std::clamp(_settings.pitch,
|
||||
-glm::half_pi<float>() + 0.01f,
|
||||
glm::half_pi<float>() - 0.01f);
|
||||
_settings.yaw = yaw;
|
||||
_settings.pitch = pitch;
|
||||
double dist = std::max(0.2, _settings.distance);
|
||||
|
||||
glm::quat yaw_q = glm::angleAxis(yaw, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::vec3 right = glm::rotate(yaw_q, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::quat pitch_q = glm::angleAxis(pitch, right);
|
||||
glm::quat orbit_q = glm::normalize(pitch_q * yaw_q);
|
||||
|
||||
// Place the camera on its local +Z axis relative to the target so the camera's
|
||||
// -Z forward axis points toward the target.
|
||||
const double yaw_d = static_cast<double>(yaw);
|
||||
const double pitch_d = static_cast<double>(pitch);
|
||||
const double cos_pitch = std::cos(pitch_d);
|
||||
const double sin_pitch = std::sin(pitch_d);
|
||||
const double sin_yaw = std::sin(yaw_d);
|
||||
const double cos_yaw = std::cos(yaw_d);
|
||||
|
||||
const glm::dvec3 dir_target_to_camera(
|
||||
sin_yaw * cos_pitch,
|
||||
-sin_pitch,
|
||||
cos_yaw * cos_pitch);
|
||||
|
||||
camera.position_world = target_pos + dir_target_to_camera * dist;
|
||||
camera.orientation = orbit_q;
|
||||
}
|
||||
25
src/scene/camera/mode_orbit.h
Normal file
25
src/scene/camera/mode_orbit.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <scene/camera/icamera_mode.h>
|
||||
|
||||
struct OrbitCameraSettings;
|
||||
|
||||
class OrbitCameraMode : public ICameraMode
|
||||
{
|
||||
public:
|
||||
explicit OrbitCameraMode(OrbitCameraSettings &settings);
|
||||
~OrbitCameraMode() override = default;
|
||||
|
||||
const char *name() const override { return "Orbit"; }
|
||||
void on_activate(SceneManager &scene, Camera &camera) override;
|
||||
void process_input(SceneManager &scene,
|
||||
Camera &camera,
|
||||
InputSystem &input,
|
||||
bool ui_capture_keyboard,
|
||||
bool ui_capture_mouse) override;
|
||||
void update(SceneManager &scene, Camera &camera, float dt) override;
|
||||
|
||||
private:
|
||||
OrbitCameraSettings &_settings;
|
||||
bool _rmb_down = false;
|
||||
};
|
||||
@@ -111,7 +111,6 @@ void SceneManager::init(EngineContext *context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
mainCamera.velocity = glm::vec3(0.f);
|
||||
mainCamera.position_world = WorldVec3(30.0, 0.0, 85.0);
|
||||
mainCamera.orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
@@ -119,6 +118,8 @@ void SceneManager::init(EngineContext *context)
|
||||
sceneData.sunlightDirection = glm::vec4(-0.2f, -1.0f, -0.3f, 1.0f);
|
||||
sceneData.sunlightColor = glm::vec4(1.0f, 1.0f, 1.0f, 3.0f);
|
||||
|
||||
cameraRig.init(*this, mainCamera);
|
||||
|
||||
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
|
||||
}
|
||||
|
||||
@@ -151,7 +152,25 @@ void SceneManager::update_scene()
|
||||
mainDrawContext.nextID = 1;
|
||||
mainDrawContext.gltfNodeLocalOverrides = nullptr;
|
||||
|
||||
mainCamera.update();
|
||||
// Simple per-frame dt (seconds) for animations and camera behavior.
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (_lastFrameTime.time_since_epoch().count() == 0)
|
||||
{
|
||||
_lastFrameTime = now;
|
||||
}
|
||||
float dt = std::chrono::duration<float>(now - _lastFrameTime).count();
|
||||
_lastFrameTime = now;
|
||||
if (dt < 0.f)
|
||||
{
|
||||
dt = 0.f;
|
||||
}
|
||||
if (dt > 0.1f)
|
||||
{
|
||||
dt = 0.1f;
|
||||
}
|
||||
_deltaTime = dt;
|
||||
|
||||
cameraRig.update(*this, mainCamera, dt);
|
||||
|
||||
// Floating origin: keep render-local coordinates near (0,0,0) by shifting the origin
|
||||
// when the camera drifts too far in world space.
|
||||
@@ -171,24 +190,6 @@ void SceneManager::update_scene()
|
||||
|
||||
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
|
||||
|
||||
// Simple per-frame dt (seconds) for animations
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (_lastFrameTime.time_since_epoch().count() == 0)
|
||||
{
|
||||
_lastFrameTime = now;
|
||||
}
|
||||
float dt = std::chrono::duration<float>(now - _lastFrameTime).count();
|
||||
_lastFrameTime = now;
|
||||
if (dt < 0.f)
|
||||
{
|
||||
dt = 0.f;
|
||||
}
|
||||
if (dt > 0.1f)
|
||||
{
|
||||
dt = 0.1f;
|
||||
}
|
||||
_deltaTime = dt;
|
||||
|
||||
auto tagOwner = [&](RenderObject::OwnerType type, const std::string &name,
|
||||
size_t opaqueBegin, size_t transpBegin)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <core/types.h>
|
||||
#include <core/world.h>
|
||||
#include <scene/camera.h>
|
||||
#include <scene/camera/camera_rig.h>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -68,6 +69,8 @@ public:
|
||||
void update_scene();
|
||||
|
||||
Camera &getMainCamera() { return mainCamera; }
|
||||
CameraRig &getCameraRig() { return cameraRig; }
|
||||
const CameraRig &getCameraRig() const { return cameraRig; }
|
||||
|
||||
WorldVec3 get_world_origin() const { return _origin_world; }
|
||||
glm::vec3 get_camera_local_position() const { return _camera_position_local; }
|
||||
@@ -227,6 +230,7 @@ private:
|
||||
EngineContext *_context = nullptr;
|
||||
|
||||
Camera mainCamera = {};
|
||||
CameraRig cameraRig{};
|
||||
GPUSceneData sceneData = {};
|
||||
DrawContext mainDrawContext;
|
||||
std::vector<PointLight> pointLights;
|
||||
|
||||
Reference in New Issue
Block a user