100 lines
3.0 KiB
C++
100 lines
3.0 KiB
C++
#include "editor_camera.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace {
|
|
|
|
constexpr float ORBIT_RADIANS_PER_PIXEL = 0.005f;
|
|
const float MAX_PITCH = glm::radians(89.f);
|
|
// Each wheel step moves this fraction of the way towards the pivot.
|
|
constexpr float ZOOM_STEP = 0.1f;
|
|
constexpr float MIN_DISTANCE = 0.05f;
|
|
constexpr float MIN_ORTHO_HEIGHT = 0.01f;
|
|
// Fly speed per unit of distance from the pivot, so it suits any scale.
|
|
constexpr float FLY_SPEED = 1.5f;
|
|
|
|
} // namespace
|
|
|
|
void EditorCamera::orbit(glm::vec2 pixels) {
|
|
if (mode != Mode::Scene3D) {
|
|
return;
|
|
}
|
|
yaw -= pixels.x * ORBIT_RADIANS_PER_PIXEL;
|
|
pitch = std::clamp(pitch - pixels.y * ORBIT_RADIANS_PER_PIXEL, -MAX_PITCH,
|
|
MAX_PITCH);
|
|
}
|
|
|
|
void EditorCamera::pan(glm::vec2 pixels, float viewportHeight) {
|
|
if (viewportHeight <= 0.f) {
|
|
return;
|
|
}
|
|
|
|
// World units one pixel covers at the pivot's depth.
|
|
float unitsPerPixel =
|
|
mode == Mode::Scene3D
|
|
? 2.f * distance * std::tan(fov / 2.f) / viewportHeight
|
|
: orthoHeight / viewportHeight;
|
|
|
|
// Screen y runs down, world y up. Dragging moves the scene with the mouse,
|
|
// so the camera moves the other way.
|
|
glm::vec3 offset{-pixels.x * unitsPerPixel, pixels.y * unitsPerPixel, 0.f};
|
|
pivot += mode == Mode::Scene3D ? getRotation() * offset : offset;
|
|
}
|
|
|
|
void EditorCamera::zoom(float steps) {
|
|
float scale = std::pow(1.f - ZOOM_STEP, steps);
|
|
if (mode == Mode::Scene3D) {
|
|
distance = std::max(distance * scale, MIN_DISTANCE);
|
|
} else {
|
|
orthoHeight = std::max(orthoHeight * scale, MIN_ORTHO_HEIGHT);
|
|
}
|
|
}
|
|
|
|
void EditorCamera::fly(glm::vec3 direction, float deltaTime) {
|
|
if (mode != Mode::Scene3D || direction == glm::vec3{0.f}) {
|
|
return;
|
|
}
|
|
float speed = FLY_SPEED * std::max(distance, 1.f);
|
|
pivot += getRotation() * glm::normalize(direction) * speed * deltaTime;
|
|
}
|
|
|
|
void EditorCamera::frame(glm::vec3 target, float radius) {
|
|
pivot = target;
|
|
radius = std::max(radius, 0.1f);
|
|
// Far enough that the sphere fits in the vertical field of view.
|
|
distance = radius / std::sin(fov / 2.f);
|
|
orthoHeight = radius * 2.5f;
|
|
}
|
|
|
|
sophia::TransformComponent EditorCamera::getTransform() const {
|
|
sophia::TransformComponent transform;
|
|
if (mode == Mode::Scene3D) {
|
|
transform.rotation = getRotation();
|
|
transform.position =
|
|
pivot + transform.rotation * glm::vec3{0.f, 0.f, distance};
|
|
} else {
|
|
transform.position = pivot;
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
sophia::CameraComponent EditorCamera::getCamera() const {
|
|
sophia::CameraComponent camera;
|
|
if (mode == Mode::Scene3D) {
|
|
camera.projection = sophia::CameraComponent::Projection::Perspective;
|
|
camera.perspective.fov = fov;
|
|
// Room to zoom far out without the scene clipping.
|
|
camera.perspective.farPlane = 1000.f;
|
|
} else {
|
|
camera.projection = sophia::CameraComponent::Projection::Orthographic;
|
|
camera.ortho.height = orthoHeight;
|
|
}
|
|
return camera;
|
|
}
|
|
|
|
glm::quat EditorCamera::getRotation() const {
|
|
return glm::angleAxis(yaw, glm::vec3{0.f, 1.f, 0.f}) *
|
|
glm::angleAxis(pitch, glm::vec3{1.f, 0.f, 0.f});
|
|
}
|