aurora-rendering-engine/src/scene/camera.cpp

201 lines
4.9 KiB
C++

/**
* @file camera.cpp
* @brief Implementation of Camera class
*/
#include <are/scene/camera.h>
#include <are/core/logger.h>
#include <are/core/profiler.h>
#include <are/utils/math_utils.h>
#include <glm/gtc/matrix_transform.hpp>
namespace are {
Camera::Camera()
: position_(0.0f, 0.0f, 5.0f)
, target_(0.0f, 0.0f, 0.0f)
, up_(0.0f, 1.0f, 0.0f)
, fov_(45.0f)
, aspect_ratio_(16.0f / 9.0f)
, near_plane_(0.1f)
, far_plane_(1000.0f)
, view_matrix_(1.0f)
, projection_matrix_(1.0f)
, view_dirty_(true)
, projection_dirty_(true)
, dirty_(true) {
}
Camera::Camera(const Vec3& position, const Vec3& target, const Vec3& up)
: position_(position)
, target_(target)
, up_(glm::normalize(up))
, fov_(45.0f)
, aspect_ratio_(16.0f / 9.0f)
, near_plane_(0.1f)
, far_plane_(1000.0f)
, view_matrix_(1.0f)
, projection_matrix_(1.0f)
, view_dirty_(true)
, projection_dirty_(true)
, dirty_(true) {
}
void Camera::set_position(const Vec3& position) {
position_ = position;
view_dirty_ = true;
dirty_ = true;
}
void Camera::set_target(const Vec3& target) {
target_ = target;
view_dirty_ = true;
dirty_ = true;
}
void Camera::set_up(const Vec3& up) {
Real length = glm::length(up);
if (length < are_epsilon) {
ARE_LOG_WARN("Camera: Invalid up vector (zero length), using default");
up_ = Vec3(0.0f, 1.0f, 0.0f);
} else {
up_ = up / length;
}
view_dirty_ = true;
dirty_ = true;
}
void Camera::look_at(const Vec3& position, const Vec3& target, const Vec3& up) {
position_ = position;
target_ = target;
set_up(up);
}
void Camera::set_fov(Real fov_degrees) {
// Clamp FOV to reasonable range
fov_ = clamp(fov_degrees, 1.0f, 179.0f);
projection_dirty_ = true;
dirty_ = true;
}
void Camera::set_aspect_ratio(Real aspect) {
if (aspect <= 0.0f) {
ARE_LOG_ERROR("Camera: Invalid aspect ratio (must be positive)");
return;
}
aspect_ratio_ = aspect;
projection_dirty_ = true;
dirty_ = true;
}
void Camera::set_near_plane(Real near) {
if (near <= 0.0f) {
ARE_LOG_ERROR("Camera: Invalid near plane (must be positive)");
return;
}
near_plane_ = near;
projection_dirty_ = true;
dirty_ = true;
}
void Camera::set_far_plane(Real far) {
if (far <= near_plane_) {
ARE_LOG_ERROR("Camera: Invalid far plane (must be greater than near plane)");
return;
}
far_plane_ = far;
projection_dirty_ = true;
dirty_ = true;
}
void Camera::set_perspective(Real fov_degrees, Real aspect, Real near, Real far) {
set_fov(fov_degrees);
set_aspect_ratio(aspect);
set_near_plane(near);
set_far_plane(far);
}
Vec3 Camera::get_forward() const {
return glm::normalize(target_ - position_);
}
Vec3 Camera::get_right() const {
return glm::normalize(glm::cross(get_forward(), up_));
}
const Mat4& Camera::get_view_matrix() const {
if (view_dirty_) {
update_view_matrix();
}
return view_matrix_;
}
const Mat4& Camera::get_projection_matrix() const {
if (projection_dirty_) {
update_projection_matrix();
}
return projection_matrix_;
}
Mat4 Camera::get_view_projection_matrix() const {
return get_projection_matrix() * get_view_matrix();
}
void Camera::generate_ray(Real u, Real v, Vec3& origin, Vec3& direction) const {
ARE_PROFILE_FUNCTION();
// Ray origin is camera position
origin = position_;
// Calculate ray direction in camera space
// u, v are in [0, 1], convert to [-1, 1]
Real x = 2.0f * u - 1.0f;
Real y = 1.0f - 2.0f * v; // Flip y for screen coordinates
// Calculate direction based on FOV and aspect ratio
Real tan_half_fov = std::tan(degrees_to_radians(fov_ * 0.5f));
Real viewport_height = 2.0f * tan_half_fov;
Real viewport_width = viewport_height * aspect_ratio_;
// Get camera basis vectors
Vec3 forward = get_forward();
Vec3 right = get_right();
Vec3 up = glm::normalize(glm::cross(right, forward));
// Calculate ray direction
direction = glm::normalize(
forward +
right * (x * viewport_width * 0.5f) +
up * (y * viewport_height * 0.5f)
);
}
void Camera::update_view_matrix() const {
ARE_PROFILE_FUNCTION();
// Check if camera is looking at itself
if (glm::length(target_ - position_) < are_epsilon) {
ARE_LOG_WARN("Camera: Position and target are too close, using default view");
view_matrix_ = Mat4(1.0f);
} else {
view_matrix_ = glm::lookAt(position_, target_, up_);
}
view_dirty_ = false;
}
void Camera::update_projection_matrix() const {
ARE_PROFILE_FUNCTION();
projection_matrix_ = glm::perspective(
degrees_to_radians(fov_),
aspect_ratio_,
near_plane_,
far_plane_
);
projection_dirty_ = false;
}
} // namespace are