ADD: planet camera fix

This commit is contained in:
2025-12-30 17:56:16 +09:00
parent 3ff457d498
commit 719a5445ce
16 changed files with 198 additions and 29 deletions

Binary file not shown.

Binary file not shown.

BIN
bin/libfmtd.a LFS

Binary file not shown.

BIN
bin/libimgui.a LFS

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -834,10 +834,40 @@ namespace
if (!pick.valid) return false;
if (pick.ownerType == RenderObject::OwnerType::MeshInstance)
{
// 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)
{
target.type = CameraTargetType::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))

View File

@@ -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 <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/quaternion.hpp>
@@ -1820,10 +1821,37 @@ bool Engine::set_camera_target_from_last_pick()
::CameraTarget t;
if (pick.ownerType == RenderObject::OwnerType::MeshInstance)
{
// 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)
{
t.type = ::CameraTargetType::GLTFInstance;

View File

@@ -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 <algorithm>
#include <cmath>
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<float>(std::atan2(dir.x, dir.z));
orbit.pitch = static_cast<float>(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
{

View File

@@ -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

View File

@@ -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)
{

View File

@@ -102,7 +102,9 @@ void OrbitCameraMode::process_input(SceneManager & /*scene*/,
else
{
const double factor = std::pow(1.15, -static_cast<double>(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<float>() - 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));

View File

@@ -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())

View File

@@ -9,6 +9,7 @@
#include <list>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
@@ -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<PlanetBody> &bodies() const { return _bodies; }
const planet::PlanetQuadtree::Settings &earth_quadtree_settings() const { return _earth_quadtree_settings; }