From 719a5445ce4e4860186389beffe0bcb42bc74e54 Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Tue, 30 Dec 2025 17:56:16 +0900 Subject: [PATCH] ADD: planet camera fix --- bin/libImGuizmo.a | 4 +- bin/libfastgltf.a | 4 +- bin/libfmtd.a | 4 +- bin/libimgui.a | 4 +- bin/libmikktspace.a | 4 +- bin/libvkbootstrap.a | 4 +- bin/vulkan_engine | 4 +- bin/vulkan_engine.exe | 2 +- src/core/engine_ui.cpp | 38 ++++++++++++- src/core/game_api.cpp | 32 ++++++++++- src/core/picking/picking_system.cpp | 88 +++++++++++++++++++++++++++++ src/scene/camera/camera_rig.h | 3 + src/scene/camera/mode_free.cpp | 15 ++--- src/scene/camera/mode_orbit.cpp | 6 +- src/scene/planet/planet_system.cpp | 13 +++++ src/scene/planet/planet_system.h | 2 + 16 files changed, 198 insertions(+), 29 deletions(-) diff --git a/bin/libImGuizmo.a b/bin/libImGuizmo.a index 67b4d1f..48ad76e 100644 --- a/bin/libImGuizmo.a +++ b/bin/libImGuizmo.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88a91b4b4e8bf28f150277cb33d1ced8b1bde278cca592b4f20a1b68257e34fd -size 2871024 +oid sha256:4f1f66f7934e239f61520dd8fa055da163a0866ea4c17ffc6264ab7cd2404b80 +size 2871104 diff --git a/bin/libfastgltf.a b/bin/libfastgltf.a index 123088b..e441136 100644 --- a/bin/libfastgltf.a +++ b/bin/libfastgltf.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dc52c8bfaff2492e642bafc07d12f5de65b3db6b905460103cb30cfe7c608db -size 7889466 +oid sha256:5e85bcba09f84f90ca848c2945d02395d405679bd8074383b6325e71eff7b06a +size 7889498 diff --git a/bin/libfmtd.a b/bin/libfmtd.a index e0586ca..3ec2325 100644 --- a/bin/libfmtd.a +++ b/bin/libfmtd.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b109671228719b12bc33d97f6691722801700b7296aacb57be82bc8f29d6e749 -size 2209582 +oid sha256:5355c0e39b26dabb22c49ae9873b030ee95e98f407a6c54d9ef9ca5941aaae79 +size 2209614 diff --git a/bin/libimgui.a b/bin/libimgui.a index 279b760..45e27f1 100644 --- a/bin/libimgui.a +++ b/bin/libimgui.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52d16fdeb0c33e696307c60c5600169f38ed34c518f8697d32d7842aa0f5d7cd -size 6704290 +oid sha256:89e9f8d0f7dbccea792a7062c0db7a1dfbeb54edf217dbffd28010c90b180f7d +size 6704410 diff --git a/bin/libmikktspace.a b/bin/libmikktspace.a index dccefb5..daf5299 100644 --- a/bin/libmikktspace.a +++ b/bin/libmikktspace.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9ef2e11a55c8b19b66ef4d3e807507ae54d13429267dc6c3f38b07cfd3cd749 -size 88374 +oid sha256:bfec5e4be49a3fa4bff403fc9f71ae67836a704bb1321cd0886042836429758a +size 88390 diff --git a/bin/libvkbootstrap.a b/bin/libvkbootstrap.a index ca6404b..d4e11ae 100644 --- a/bin/libvkbootstrap.a +++ b/bin/libvkbootstrap.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fe491028ecf1e943109d949e1fa8f05eda89c3aacc519acab167b38d7030771 -size 4230074 +oid sha256:9344a949ab1b360e3032136efdd0225f12515edc495e2cdc595376cda54206c8 +size 4230090 diff --git a/bin/vulkan_engine b/bin/vulkan_engine index 8fad223..27a4c84 100644 --- a/bin/vulkan_engine +++ b/bin/vulkan_engine @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8298adbe6895ae3a2da058a6ebb78426e430666e9c97098e33f84e5d55222902 -size 138285088 +oid sha256:352726dfaaffdb56be6a2ff227813c1be842ba44254bd6fe30335444c1fb04a9 +size 138286752 diff --git a/bin/vulkan_engine.exe b/bin/vulkan_engine.exe index 57fdab4..ebe0c41 100644 --- a/bin/vulkan_engine.exe +++ b/bin/vulkan_engine.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4222a45944c5b74aaed404176b8741dd4706c4bf3360df72b937c0dbdc4aaae0 +oid sha256:ec1e72df61df2e27f6b4e7b2bc0d8870b204c66206134829f1cea76c8077c30c size 2819072 diff --git a/src/core/engine_ui.cpp b/src/core/engine_ui.cpp index 6166009..19124b0 100644 --- a/src/core/engine_ui.cpp +++ b/src/core/engine_ui.cpp @@ -835,8 +835,38 @@ namespace if (pick.ownerType == RenderObject::OwnerType::MeshInstance) { - target.type = CameraTargetType::MeshInstance; - target.name = pick.ownerName; + // Many procedural objects (planets etc.) tag draws as "MeshInstance" for picking, + // but they don't exist in SceneManager::dynamicMeshInstances. Only use a + // MeshInstance camera target if it resolves. + WorldVec3 t{}; + glm::quat r{}; + glm::vec3 s{}; + if (sceneMgr->getMeshInstanceTRSWorld(pick.ownerName, t, r, s)) + { + target.type = CameraTargetType::MeshInstance; + target.name = pick.ownerName; + } + else if (PlanetSystem *planets = sceneMgr->get_planet_system()) + { + if (PlanetSystem::PlanetBody *body = planets->find_body_by_name(pick.ownerName)) + { + target.type = CameraTargetType::WorldPoint; + target.world_point = body->center_world; + target.name = body->name; + } + else + { + target.type = CameraTargetType::WorldPoint; + target.world_point = pick.worldPos; + target.name.clear(); + } + } + else + { + target.type = CameraTargetType::WorldPoint; + target.world_point = pick.worldPos; + target.name.clear(); + } } else if (pick.ownerType == RenderObject::OwnerType::GLTFInstance) { @@ -920,7 +950,9 @@ namespace 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); + s.distance = std::clamp(s.distance, + OrbitCameraSettings::kMinDistance, + OrbitCameraSettings::kMaxDistance); float yawDeg = glm::degrees(s.yaw); float pitchDeg = glm::degrees(s.pitch); if (ImGui::SliderFloat("Yaw (deg)", &yawDeg, -180.0f, 180.0f)) diff --git a/src/core/game_api.cpp b/src/core/game_api.cpp index 0f31d7a..a33cbb3 100644 --- a/src/core/game_api.cpp +++ b/src/core/game_api.cpp @@ -14,6 +14,7 @@ #include "scene/vk_scene.h" #include "scene/camera.h" #include "scene/camera/camera_rig.h" +#include "scene/planet/planet_system.h" #include #include @@ -1821,8 +1822,35 @@ bool Engine::set_camera_target_from_last_pick() ::CameraTarget t; if (pick.ownerType == RenderObject::OwnerType::MeshInstance) { - t.type = ::CameraTargetType::MeshInstance; - t.name = pick.ownerName; + // RenderObject::OwnerType::MeshInstance is also used for some procedural objects + // (planets etc.) which don't exist in SceneManager::dynamicMeshInstances. + WorldVec3 inst_t{}; + glm::quat inst_r{}; + glm::vec3 inst_s{}; + if (_engine->_sceneManager->getMeshInstanceTRSWorld(pick.ownerName, inst_t, inst_r, inst_s)) + { + t.type = ::CameraTargetType::MeshInstance; + t.name = pick.ownerName; + } + else if (PlanetSystem *planets = _engine->_sceneManager->get_planet_system()) + { + if (PlanetSystem::PlanetBody *body = planets->find_body_by_name(pick.ownerName)) + { + t.type = ::CameraTargetType::WorldPoint; + t.name = body->name; + t.world_point = body->center_world; + } + else + { + t.type = ::CameraTargetType::WorldPoint; + t.world_point = pick.worldPos; + } + } + else + { + t.type = ::CameraTargetType::WorldPoint; + t.world_point = pick.worldPos; + } } else if (pick.ownerType == RenderObject::OwnerType::GLTFInstance) { diff --git a/src/core/picking/picking_system.cpp b/src/core/picking/picking_system.cpp index 12b885c..578dd6e 100644 --- a/src/core/picking/picking_system.cpp +++ b/src/core/picking/picking_system.cpp @@ -5,6 +5,7 @@ #include "core/device/images.h" #include "core/device/swapchain.h" #include "render/graph/graph.h" +#include "scene/planet/planet_system.h" #include "SDL2/SDL.h" #include "SDL2/SDL_vulkan.h" @@ -12,6 +13,91 @@ #include #include +namespace +{ + bool try_orbit_camera_to_planet(EngineContext *context, const PickingSystem::PickInfo &pick) + { + if (!context || !context->scene) + { + return false; + } + + if (pick.ownerType != RenderObject::OwnerType::MeshInstance) + { + return false; + } + + SceneManager &scene = *context->scene; + + // If this is a real dynamic MeshInstance, leave it alone (user might want + // to orbit other things separately). + { + WorldVec3 t{}; + glm::quat r{}; + glm::vec3 s{}; + if (scene.getMeshInstanceTRSWorld(pick.ownerName, t, r, s)) + { + return false; + } + } + + PlanetSystem *planets = scene.get_planet_system(); + if (!planets || !planets->enabled()) + { + return false; + } + + PlanetSystem::PlanetBody *body = planets->find_body_by_name(pick.ownerName); + if (!body || !body->visible) + { + return false; + } + + CameraRig &rig = scene.getCameraRig(); + Camera &cam = scene.getMainCamera(); + + CameraTarget target{}; + target.type = CameraTargetType::WorldPoint; + target.name = body->name; + target.world_point = body->center_world; + + rig.orbit_settings().target = target; + rig.follow_settings().target = target; + rig.chase_settings().target = target; + + rig.set_mode(CameraMode::Orbit, scene, cam); + + const WorldVec3 to_cam = cam.position_world - body->center_world; + double dist = glm::length(to_cam); + + // If we're inside the planet (or very close), pop out to a sane viewing altitude. + // Clamp altitude to [10 km, 1000 km] and scale with body radius. + const double min_alt_m = std::clamp(body->radius_m * 0.05, 1.0e4, 1.0e6); + const double min_dist = body->radius_m + min_alt_m; + + if (!std::isfinite(dist) || dist < min_dist) + { + dist = min_dist; + } + + glm::dvec3 dir = glm::dvec3(to_cam); + if (glm::dot(dir, dir) < 1e-12) + { + dir = glm::dvec3(0.0, 0.0, 1.0); + } + else + { + dir = glm::normalize(dir); + } + + OrbitCameraSettings &orbit = rig.orbit_settings(); + orbit.distance = std::clamp(dist, OrbitCameraSettings::kMinDistance, OrbitCameraSettings::kMaxDistance); + orbit.yaw = static_cast(std::atan2(dir.x, dir.z)); + orbit.pitch = static_cast(std::asin(std::clamp(-dir.y, -1.0, 1.0))); + return true; + } +} // namespace + void PickingSystem::init(EngineContext *context) { _context = context; @@ -121,6 +207,7 @@ void PickingSystem::process_event(const SDL_Event &event, bool ui_want_capture_m { set_pick_from_hit(hit_object, hit_pos, _last_pick); _last_pick_object_id = hit_object.objectID; + try_orbit_camera_to_planet(_context, _last_pick); } else { @@ -223,6 +310,7 @@ void PickingSystem::begin_frame() glm::vec3 fallback_local = glm::vec3(picked.transform[3]); WorldVec3 fallback_pos = local_to_world(fallback_local, _context->scene->get_world_origin()); set_pick_from_hit(picked, fallback_pos, _last_pick); + try_orbit_camera_to_planet(_context, _last_pick); } else { diff --git a/src/scene/camera/camera_rig.h b/src/scene/camera/camera_rig.h index 28db6ae..87eae97 100644 --- a/src/scene/camera/camera_rig.h +++ b/src/scene/camera/camera_rig.h @@ -45,6 +45,9 @@ struct FreeCameraSettings struct OrbitCameraSettings { + static constexpr double kMinDistance = 0.2; + static constexpr double kMaxDistance = 1.0e12; + CameraTarget target{}; double distance{10.0}; float yaw{0.0f}; // radians diff --git a/src/scene/camera/mode_free.cpp b/src/scene/camera/mode_free.cpp index 3c5195e..bfefe00 100644 --- a/src/scene/camera/mode_free.cpp +++ b/src/scene/camera/mode_free.cpp @@ -76,17 +76,18 @@ void FreeCameraMode::process_input(SceneManager & /*scene*/, 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 right (xrel > 0) turns view right with -Z-forward: yaw around the + // camera's local +Y (up) axis, so yaw remains intuitive when rolled. + glm::vec3 up = glm::rotate(camera.orientation, glm::vec3{0.f, 1.f, 0.f}); + glm::quat yaw_rotation = glm::angleAxis(dx, glm::normalize(up)); + camera.orientation = glm::normalize(yaw_rotation * camera.orientation); // 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. + // Pitch around the camera's local +X (right) axis (after yaw is applied). 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); + glm::quat pitch_rotation = glm::angleAxis(pitch_delta, glm::normalize(right)); + camera.orientation = glm::normalize(pitch_rotation * camera.orientation); } else if (e.type == InputEvent::Type::MouseWheel) { diff --git a/src/scene/camera/mode_orbit.cpp b/src/scene/camera/mode_orbit.cpp index 80810c8..bf99b77 100644 --- a/src/scene/camera/mode_orbit.cpp +++ b/src/scene/camera/mode_orbit.cpp @@ -102,7 +102,9 @@ void OrbitCameraMode::process_input(SceneManager & /*scene*/, else { const double factor = std::pow(1.15, -static_cast(steps)); - _settings.distance = std::clamp(_settings.distance * factor, 0.2, 100000.0); + _settings.distance = std::clamp(_settings.distance * factor, + OrbitCameraSettings::kMinDistance, + OrbitCameraSettings::kMaxDistance); } } } @@ -137,7 +139,7 @@ void OrbitCameraMode::update(SceneManager &scene, Camera &camera, float /*dt*/) glm::half_pi() - 0.01f); _settings.yaw = yaw; _settings.pitch = pitch; - double dist = std::max(0.2, _settings.distance); + double dist = std::max(OrbitCameraSettings::kMinDistance, _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)); diff --git a/src/scene/planet/planet_system.cpp b/src/scene/planet/planet_system.cpp index 6630b5f..064bc91 100644 --- a/src/scene/planet/planet_system.cpp +++ b/src/scene/planet/planet_system.cpp @@ -182,6 +182,19 @@ PlanetSystem::PlanetBody *PlanetSystem::get_body(BodyID id) return &_bodies[i]; } +PlanetSystem::PlanetBody *PlanetSystem::find_body_by_name(std::string_view name) +{ + ensure_bodies_created(); + for (PlanetBody &b : _bodies) + { + if (b.name == name) + { + return &b; + } + } + return nullptr; +} + void PlanetSystem::ensure_bodies_created() { if (!_bodies.empty()) diff --git a/src/scene/planet/planet_system.h b/src/scene/planet/planet_system.h index 2837fc3..dc7d301 100644 --- a/src/scene/planet/planet_system.h +++ b/src/scene/planet/planet_system.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ public: const PlanetBody *get_body(BodyID id) const; PlanetBody *get_body(BodyID id); + PlanetBody *find_body_by_name(std::string_view name); const std::vector &bodies() const { return _bodies; } const planet::PlanetQuadtree::Settings &earth_quadtree_settings() const { return _earth_quadtree_settings; }