refactor: eliminar Rotation3D i el seu camí de codi (codi mort)

L'struct Rotation3D, la funció apply3dRotation i el paràmetre opcional
rotation_3d de renderShape mai s'activaven en cap caller:
- Ship, Enemy i Bullet passaven explícitament nullptr.
- Title scene, logo scene, starfield, vector_text i ship_animator
  usaven el default nullptr (set els 7 callers).

CLAUDE.md descriu un sistema 3D del title screen que ja no està viu —
el comentari en ship_animator.cpp aclareix que la perspectiva s'ha
bakeat dins de la shape, així que la rotació dinàmica era residu
històric.

Esborrats: struct Rotation3D + ctors + hasRotation(), apply3dRotation(),
la branca rotation_3d a transformPoint() i el seu paràmetre, el
paràmetre rotation_3d de renderShape, i els arguments nullptr als
3 callers d'entitats.

Hallazgo #16 de CODE_REVIEW.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 16:50:03 +02:00
parent 682c27c07c
commit 707fd29b97
5 changed files with 117 additions and 183 deletions
+14 -58
View File
@@ -9,76 +9,34 @@
namespace Rendering { namespace Rendering {
// Helper: aplicar rotación 3D a un point 2D (assumeix Z=0) // Helper: transformar un point con rotación, scale i traslación
static auto apply3dRotation(float x, float y, const Rotation3D& rot) -> Vec2 { static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
float z = 0.0F; // Todos los points 2D comencen a Z=0
// Pitch (rotación eix X): cabeceo arriba/baix
float cos_pitch = std::cos(rot.pitch);
float sin_pitch = std::sin(rot.pitch);
float y1 = (y * cos_pitch) - (z * sin_pitch);
float z1 = (y * sin_pitch) + (z * cos_pitch);
// Yaw (rotación eix Y): guiñada izquierda/derecha
float cos_yaw = std::cos(rot.yaw);
float sin_yaw = std::sin(rot.yaw);
float x2 = (x * cos_yaw) + (z1 * sin_yaw);
float z2 = (-x * sin_yaw) + (z1 * cos_yaw);
// Roll (rotación eix Z): alabeo lateral
float cos_roll = std::cos(rot.roll);
float sin_roll = std::sin(rot.roll);
float x3 = (x2 * cos_roll) - (y1 * sin_roll);
float y3 = (x2 * sin_roll) + (y1 * cos_roll);
// Proyecció perspectiva (Z-divide simple)
// Naves quieren hacia el point de fuga (320, 240) a "infinit" (Z → +∞)
// Z més grande = més lluny = més pequeño a pantalla
constexpr float PERSPECTIVE_FACTOR = 500.0F;
float scale_factor = PERSPECTIVE_FACTOR / (PERSPECTIVE_FACTOR + z2);
return {.x = x3 * scale_factor, .y = y3 * scale_factor};
}
// Helper: transformar un point con rotación, scale i traslación
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale, const Rotation3D* rotation_3d) -> Vec2 {
// 1. Centrar el point respecte al centro de la shape // 1. Centrar el point respecte al centro de la shape
float centered_x = point.x - shape_centre.x; float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y; float centered_y = point.y - shape_centre.y;
// 2. Aplicar rotación 3D (si es proporciona) // 2. Aplicar scale al point
if ((rotation_3d != nullptr) && rotation_3d->hasRotation()) {
Vec2 rotated_3d = apply3dRotation(centered_x, centered_y, *rotation_3d);
centered_x = rotated_3d.x;
centered_y = rotated_3d.y;
}
// 3. Aplicar scale al point (después de rotación 3D)
float scaled_x = centered_x * scale; float scaled_x = centered_x * scale;
float scaled_y = centered_y * scale; float scaled_y = centered_y * scale;
// 4. Aplicar rotación 2D (Z-axis, tradicional) // 3. Aplicar rotación 2D (Z-axis)
// IMPORTANT: En el sistema original, angle=0 apunta AMUNT (no derecha)
// Per això usem (angle - PI/2) per compensar
// Pero aquí angle ya ve en el sistema correcte del juego
float cos_a = std::cos(angle); float cos_a = std::cos(angle);
float sin_a = std::sin(angle); float sin_a = std::sin(angle);
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a); float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a); float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
// 5. Aplicar traslación a posición mundial // 4. Aplicar traslación a posición mundial
return {.x = rotated_x + position.x, .y = rotated_y + position.y}; return {.x = rotated_x + position.x, .y = rotated_y + position.y};
} }
void renderShape(Rendering::Renderer* renderer, void renderShape(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape, const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& position, const Vec2& position,
float angle, float angle,
float scale, float scale,
float progress, float progress,
float brightness, float brightness,
const Rotation3D* rotation_3d,
SDL_Color color) { SDL_Color color) {
if (!shape || !shape->isValid()) { if (!shape || !shape->isValid()) {
return; return;
@@ -93,18 +51,16 @@ void renderShape(Rendering::Renderer* renderer,
if (primitive.type == Graphics::PrimitiveType::POLYLINE) { if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// POLYLINE: conectar puntos consecutivos. // POLYLINE: conectar puntos consecutivos.
for (size_t i = 0; i < primitive.points.size() - 1; i++) { for (size_t i = 0; i < primitive.points.size() - 1; i++) {
const Vec2 P1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale, rotation_3d); const Vec2 P1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale);
const Vec2 P2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale, rotation_3d); const Vec2 P2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale);
linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
} }
} else if (primitive.points.size() >= 2) { // LINE } else if (primitive.points.size() >= 2) { // LINE
const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale, rotation_3d); const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale);
const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale, rotation_3d); const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale);
linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color); }
} }
} }
}
} // namespace Rendering } // namespace Rendering
+10 -32
View File
@@ -3,53 +3,31 @@
#pragma once #pragma once
#include "core/rendering/render_context.hpp"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <memory> #include <memory>
#include "core/graphics/shape.hpp" #include "core/graphics/shape.hpp"
#include "core/rendering/render_context.hpp"
#include "core/types.hpp" #include "core/types.hpp"
namespace Rendering { namespace Rendering {
// Estructura per rotacions 3D (pitch, yaw, roll) // Renderizar shape con transformacions
struct Rotation3D { // - renderer: SDL renderer
float pitch; // Rotación eix X (cabeceo arriba/baix) // - shape: shape vectorial a draw
float yaw; // Rotación eix Y (guiñada izquierda/derecha) // - position: posición del centro en coordenades mundials
float roll; // Rotación eix Z (alabeo lateral) // - angle: rotación en radians (0 = amunt, sentit horari)
// - scale: factor de scale (1.0 = mida original)
Rotation3D() // - progress: progrés de l'animación (0.0-1.0, default 1.0 = tot visible)
: pitch(0.0F), // - brightness: factor de brightness (0.0-1.0, default 1.0 = màxima brightness)
yaw(0.0F), void renderShape(Rendering::Renderer* renderer,
roll(0.0F) {}
Rotation3D(float p, float y, float r)
: pitch(p),
yaw(y),
roll(r) {}
[[nodiscard]] auto hasRotation() const -> bool {
return pitch != 0.0F || yaw != 0.0F || roll != 0.0F;
}
};
// Renderizar shape con transformacions
// - renderer: SDL renderer
// - shape: shape vectorial a draw
// - position: posición del centro en coordenades mundials
// - angle: rotación en radians (0 = amunt, sentit horari)
// - scale: factor de scale (1.0 = mida original)
// - progress: progrés de l'animación (0.0-1.0, default 1.0 = tot visible)
// - brightness: factor de brightness (0.0-1.0, default 1.0 = màxima brightness)
void renderShape(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape, const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& position, const Vec2& position,
float angle, float angle,
float scale = 1.0F, float scale = 1.0F,
float progress = 1.0F, float progress = 1.0F,
float brightness = 1.0F, float brightness = 1.0F,
const Rotation3D* rotation_3d = nullptr,
SDL_Color color = {0, 0, 0, 0}); // alpha==0 → usa global oscilador SDL_Color color = {0, 0, 0, 0}); // alpha==0 → usa global oscilador
} // namespace Rendering } // namespace Rendering
+5 -7
View File
@@ -17,14 +17,13 @@
#include "game/constants.hpp" #include "game/constants.hpp"
namespace { namespace {
// Velocidad escalar de las balas (px/s). Conserva el feel del Pascal original // Velocidad escalar de las balas (px/s). Conserva el feel del Pascal original
// (7 px/frame × 20 FPS = 140 px/s). // (7 px/frame × 20 FPS = 140 px/s).
constexpr float BULLET_SPEED = 140.0F; constexpr float BULLET_SPEED = 140.0F;
} // namespace } // namespace
Bullet::Bullet(Rendering::Renderer* renderer) Bullet::Bullet(Rendering::Renderer* renderer)
: Entity(renderer) : Entity(renderer) {
{
// Brightness específico para balas // Brightness específico para balas
brightness_ = Defaults::Brightness::BALA; brightness_ = Defaults::Brightness::BALA;
@@ -134,7 +133,6 @@ void Bullet::desactivar() {
void Bullet::draw() const { void Bullet::draw() const {
if (is_active_ && shape_) { if (is_active_ && shape_) {
// Les bales roten segons l'angle de trayectòria (estático tras disparo) // Les bales roten segons l'angle de trayectòria (estático tras disparo)
Rendering::renderShape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_, Rendering::renderShape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_, Defaults::Palette::BULLET);
/*rotation_3d=*/nullptr, Defaults::Palette::BULLET);
} }
} }
+19 -15
View File
@@ -17,32 +17,31 @@
namespace { namespace {
// Velocidad inicial vectorial a partir de un ángulo (rad). // Velocidad inicial vectorial a partir de un ángulo (rad).
// angle=0 apunta hacia arriba (eje Y negativo SDL), como el resto del juego. // angle=0 apunta hacia arriba (eje Y negativo SDL), como el resto del juego.
auto angleToDirection(float angle) -> Vec2 { auto angleToDirection(float angle) -> Vec2 {
return Vec2{ return Vec2{
.x = std::cos(angle - (Constants::PI / 2.0F)), .x = std::cos(angle - (Constants::PI / 2.0F)),
.y = std::sin(angle - (Constants::PI / 2.0F)), .y = std::sin(angle - (Constants::PI / 2.0F)),
}; };
} }
// Recupera el "ángulo equivalente" de un body en movimiento (para zigzag). // Recupera el "ángulo equivalente" de un body en movimiento (para zigzag).
// Si está parado, devuelve 0. // Si está parado, devuelve 0.
auto velocityToAngle(const Vec2& velocity) -> float { auto velocityToAngle(const Vec2& velocity) -> float {
if (velocity.lengthSquared() < 0.0001F) { if (velocity.lengthSquared() < 0.0001F) {
return 0.0F; return 0.0F;
} }
// El movimiento (vx, vy) corresponde a angle - PI/2; invertimos. // El movimiento (vx, vy) corresponde a angle - PI/2; invertimos.
return std::atan2(velocity.y, velocity.x) + (Constants::PI / 2.0F); return std::atan2(velocity.y, velocity.x) + (Constants::PI / 2.0F);
} }
} // namespace } // namespace
Enemy::Enemy(Rendering::Renderer* renderer) Enemy::Enemy(Rendering::Renderer* renderer)
: Entity(renderer), : Entity(renderer),
tracking_strength_(0.5F) tracking_strength_(0.5F) {
{
brightness_ = Defaults::Brightness::ENEMIC; brightness_ = Defaults::Brightness::ENEMIC;
// Configuración del cuerpo físico — defaults para enemy genérico. // Configuración del cuerpo físico — defaults para enemy genérico.
@@ -225,12 +224,17 @@ void Enemy::draw() const {
const float SCALE = computeCurrentScale(); const float SCALE = computeCurrentScale();
SDL_Color color{}; SDL_Color color{};
switch (type_) { switch (type_) {
case EnemyType::PENTAGON: color = Defaults::Palette::PENTAGON; break; case EnemyType::PENTAGON:
case EnemyType::QUADRAT: color = Defaults::Palette::QUADRAT; break; color = Defaults::Palette::PENTAGON;
case EnemyType::MOLINILLO: color = Defaults::Palette::MOLINILLO; break; break;
case EnemyType::QUADRAT:
color = Defaults::Palette::QUADRAT;
break;
case EnemyType::MOLINILLO:
color = Defaults::Palette::MOLINILLO;
break;
} }
Rendering::renderShape(renderer_, shape_, center_, rotacio_, SCALE, 1.0F, brightness_, Rendering::renderShape(renderer_, shape_, center_, rotacio_, SCALE, 1.0F, brightness_, color);
/*rotation_3d=*/nullptr, color);
} }
void Enemy::destruir() { void Enemy::destruir() {
+2 -4
View File
@@ -20,8 +20,7 @@
#include "game/constants.hpp" #include "game/constants.hpp"
Ship::Ship(Rendering::Renderer* renderer, const char* shape_file) Ship::Ship(Rendering::Renderer* renderer, const char* shape_file)
: Entity(renderer) : Entity(renderer) {
{
// Brightness específico para naves // Brightness específico para naves
brightness_ = Defaults::Brightness::NAU; brightness_ = Defaults::Brightness::NAU;
@@ -158,6 +157,5 @@ void Ship::draw() const {
const float VISUAL_PUSH = SPEED / 33.33F; const float VISUAL_PUSH = SPEED / 33.33F;
const float SCALE = 1.0F + (VISUAL_PUSH / 12.0F); const float SCALE = 1.0F + (VISUAL_PUSH / 12.0F);
Rendering::renderShape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_, Rendering::renderShape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_, Defaults::Palette::SHIP);
/*rotation_3d=*/nullptr, Defaults::Palette::SHIP);
} }