ADD: planet camera fix
This commit is contained in:
BIN
bin/libImGuizmo.a
LFS
BIN
bin/libImGuizmo.a
LFS
Binary file not shown.
BIN
bin/libfastgltf.a
LFS
BIN
bin/libfastgltf.a
LFS
Binary file not shown.
BIN
bin/libfmtd.a
LFS
BIN
bin/libfmtd.a
LFS
Binary file not shown.
BIN
bin/libimgui.a
LFS
BIN
bin/libimgui.a
LFS
Binary file not shown.
BIN
bin/libmikktspace.a
LFS
BIN
bin/libmikktspace.a
LFS
Binary file not shown.
BIN
bin/libvkbootstrap.a
LFS
BIN
bin/libvkbootstrap.a
LFS
Binary file not shown.
BIN
bin/vulkan_engine
LFS
BIN
bin/vulkan_engine
LFS
Binary file not shown.
BIN
bin/vulkan_engine.exe
LFS
BIN
bin/vulkan_engine.exe
LFS
Binary file not shown.
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user