// camera3d.cpp - Implementació de la càmera 3D amb projecció en CPU // © 2026 JailDesigner #include "core/graphics/camera3d.hpp" #include 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 = forward × up_world. Cau a (1,0,0) si forward ≈ up_world. right_ = forward_.cross(up_world_).normalized(); // Up real ortogonal = right × forward. up_ = right_.cross(forward_).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 { 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