Files

93 lines
2.8 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// camera3d.cpp - Implementació de la càmera 3D amb projecció en CPU
// © 2026 JailDesigner
#include "core/graphics/camera3d.hpp"
#include <cmath>
namespace Graphics {
Camera3D::Camera3D(const Vec3& position, const Vec3& target, const Vec3& up_world, float fov_y_rad, float viewport_w, float viewport_h, float near_plane, float far_plane)
: position_(position),
target_(target),
up_world_(up_world),
fov_y_rad_(fov_y_rad),
viewport_w_(viewport_w),
viewport_h_(viewport_h),
near_(near_plane),
far_(far_plane) {
recomputeBasis();
recomputeFocal();
}
void Camera3D::setPosition(const Vec3& p) {
position_ = p;
recomputeBasis();
}
void Camera3D::setTarget(const Vec3& t) {
target_ = t;
recomputeBasis();
}
void Camera3D::setUpWorld(const Vec3& u) {
up_world_ = u;
recomputeBasis();
}
void Camera3D::setViewport(float w, float h) {
viewport_w_ = w;
viewport_h_ = h;
recomputeFocal();
}
void Camera3D::setFovY(float fov_y_rad) {
fov_y_rad_ = fov_y_rad;
recomputeFocal();
}
void Camera3D::recomputeBasis() {
// Forward = del position cap al target.
forward_ = (target_ - position_).normalized();
// Right = up_world × forward (convenció right-handed amb Y up,
// mirant cap a +Z → right cau a +X). L'invers (forward × up_world)
// donava la base mirall i invertia l'eix X de la projecció.
right_ = up_world_.cross(forward_).normalized();
// Up ortogonal real = forward × right (manté la mà dreta).
up_ = forward_.cross(right_).normalized();
}
void Camera3D::recomputeFocal() {
// Focal length en píxels: (viewport_height / 2) / tan(fov_y / 2).
// Assumeix píxels quadrats (focal_x == focal_y).
const float HALF_FOV = fov_y_rad_ * 0.5F;
const float TAN_HALF = std::tan(HALF_FOV);
focal_ = (TAN_HALF > 0.0F) ? ((viewport_h_ * 0.5F) / TAN_HALF) : 0.0F;
centre_x_ = viewport_w_ * 0.5F;
centre_y_ = viewport_h_ * 0.5F;
}
auto Camera3D::project(const Vec3& world) const -> std::optional<ProjectedPoint> {
const Vec3 REL = world - position_;
const float CX = REL.dot(right_);
const float CY = REL.dot(up_);
const float CZ = REL.dot(forward_);
if (CZ <= near_) {
return std::nullopt;
}
const float SCALE = focal_ / CZ;
return ProjectedPoint{
.screen = Vec2{
.x = centre_x_ + (CX * SCALE),
// Flip Y: en pantalla Y creix cap avall.
.y = centre_y_ - (CY * SCALE),
},
.scale = SCALE,
.depth = CZ,
};
}
} // namespace Graphics