refactor(title): la 3D és l'única — elimina backup 2D i renomena als noms canònics
This commit is contained in:
@@ -1,168 +1,105 @@
|
||||
// starfield.cpp - Implementació del sistema de estrelles de fons
|
||||
// starfield.cpp - Implementació del starfield 3D
|
||||
// © 2026 JailDesigner
|
||||
|
||||
#include "core/graphics/starfield.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/graphics/shape_loader.hpp"
|
||||
#include "core/rendering/shape_renderer.hpp"
|
||||
|
||||
namespace Graphics {
|
||||
|
||||
// Constructor
|
||||
Starfield::Starfield(Rendering::Renderer* renderer,
|
||||
const Vec2& punt_fuga,
|
||||
const SDL_FRect& area,
|
||||
int densitat)
|
||||
: shape_estrella_(ShapeLoader::load("star.shp")),
|
||||
renderer_(renderer),
|
||||
punt_fuga_(punt_fuga),
|
||||
area_(area) {
|
||||
if (!shape_estrella_ || !shape_estrella_->isValid()) {
|
||||
std::cerr << "ERROR: No s'ha pogut load star.shp" << '\n';
|
||||
return;
|
||||
}
|
||||
namespace {
|
||||
|
||||
// Configurar 3 capes con diferents velocitats i escales
|
||||
// Capa 0: Fons llunyà (lenta, pequeña)
|
||||
capes_.push_back({20.0F, 0.3F, 0.8F, densitat / 3});
|
||||
// Helper: número aleatori en [0, 1) usant rand()/RAND_MAX (mateixa convenció
|
||||
// que la resta del joc — veure starfield.cpp).
|
||||
auto randFloat01() -> float {
|
||||
return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
}
|
||||
|
||||
// Capa 1: Profunditat mitjana
|
||||
capes_.push_back({40.0F, 0.5F, 1.2F, densitat / 3});
|
||||
auto randRange(float lo, float hi) -> float {
|
||||
return lo + (randFloat01() * (hi - lo));
|
||||
}
|
||||
|
||||
// Capa 2: Primer pla (ràpida, grande)
|
||||
capes_.push_back({80.0F, 0.8F, 2.0F, densitat / 3});
|
||||
} // namespace
|
||||
|
||||
// Calcular radi màxim (distancia del centro al racó més llunyà)
|
||||
float dx = std::max(punt_fuga_.x, area_.w - punt_fuga_.x);
|
||||
float dy = std::max(punt_fuga_.y, area_.h - punt_fuga_.y);
|
||||
radi_max_ = std::sqrt((dx * dx) + (dy * dy));
|
||||
|
||||
// Inicialitzar estrelles con posicions distribuïdes (pre-omplir pantalla)
|
||||
for (int capa_idx = 0; capa_idx < 3; capa_idx++) {
|
||||
int num = capes_[capa_idx].num_estrelles;
|
||||
for (int i = 0; i < num; i++) {
|
||||
Estrella estrella;
|
||||
estrella.capa = capa_idx;
|
||||
|
||||
// Angle aleatori
|
||||
estrella.angle = (static_cast<float>(rand()) / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
|
||||
|
||||
// Distancia aleatòria (0.0 a 1.0) per omplir toda la pantalla
|
||||
estrella.distancia_centre = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
|
||||
// Calcular posición desde la distancia
|
||||
float radi = estrella.distancia_centre * radi_max_;
|
||||
estrella.position.x = punt_fuga_.x + (radi * std::cos(estrella.angle));
|
||||
estrella.position.y = punt_fuga_.y + (radi * std::sin(estrella.angle));
|
||||
|
||||
estrelles_.push_back(estrella);
|
||||
Starfield::Starfield(Rendering::Renderer* renderer, const Camera3D* camera, int density)
|
||||
: renderer_(renderer),
|
||||
camera_(camera),
|
||||
octahedron_(makeOctahedron()) {
|
||||
stars_.resize(static_cast<std::size_t>(std::max(0, density)));
|
||||
for (auto& star : stars_) {
|
||||
// Pre-omplir amb estrelles distribuïdes per tot el rang Z, no només al far.
|
||||
initStar(star, /*spawn_at_far=*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inicialitzar una estrella (nueva o regenerada)
|
||||
void Starfield::initStar(Estrella& estrella) const {
|
||||
// Angle aleatori des del point de fuga hacia fuera
|
||||
estrella.angle = (static_cast<float>(rand()) / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
|
||||
void Starfield::initStar(Star& star, bool spawn_at_far) {
|
||||
star.position.x = randRange(-HALF_SPAWN_X, HALF_SPAWN_X);
|
||||
star.position.y = randRange(-HALF_SPAWN_Y, HALF_SPAWN_Y);
|
||||
star.position.z = spawn_at_far
|
||||
? Z_FAR_SPAWN
|
||||
: randRange(Z_NEAR_RESPAWN, Z_FAR_SPAWN);
|
||||
|
||||
// Distancia inicial pequeña (5% del radi màxim) - neix prop del centro
|
||||
estrella.distancia_centre = 0.05F;
|
||||
star.velocity_z = -randRange(MIN_VELOCITY_Z, MAX_VELOCITY_Z);
|
||||
star.rot_phase_y = randFloat01() * Defaults::Math::PI * 2.0F;
|
||||
star.rot_phase_x = randFloat01() * Defaults::Math::PI * 2.0F;
|
||||
star.rot_speed_y = randRange(MIN_ROT_SPEED, MAX_ROT_SPEED);
|
||||
star.rot_speed_x = randRange(MIN_ROT_SPEED, MAX_ROT_SPEED);
|
||||
star.scale = STAR_BASE_SCALE + (randRange(-1.0F, 1.0F) * STAR_SCALE_JITTER);
|
||||
}
|
||||
|
||||
// Posición inicial: mucho prop del point de fuga
|
||||
float radi = estrella.distancia_centre * radi_max_;
|
||||
estrella.position.x = punt_fuga_.x + (radi * std::cos(estrella.angle));
|
||||
estrella.position.y = punt_fuga_.y + (radi * std::sin(estrella.angle));
|
||||
}
|
||||
auto Starfield::computeBrightness(const Star& star) const -> float {
|
||||
// Lerp segons distància Z normalitzada [Z_NEAR_RESPAWN .. Z_FAR_SPAWN].
|
||||
const float SPAN = Z_FAR_SPAWN - Z_NEAR_RESPAWN;
|
||||
const float T_RAW = (star.position.z - Z_NEAR_RESPAWN) / SPAN;
|
||||
const float T = std::clamp(T_RAW, 0.0F, 1.0F);
|
||||
const float BRIGHTNESS = BRIGHTNESS_NEAR + (T * (BRIGHTNESS_FAR - BRIGHTNESS_NEAR));
|
||||
return std::clamp(BRIGHTNESS * brightness_mult_, 0.0F, 1.0F);
|
||||
}
|
||||
|
||||
// Verificar si una estrella está fuera de l'àrea
|
||||
auto Starfield::isOutsideArea(const Estrella& estrella) const -> bool {
|
||||
return (estrella.position.x < area_.x ||
|
||||
estrella.position.x > area_.x + area_.w ||
|
||||
estrella.position.y < area_.y ||
|
||||
estrella.position.y > area_.y + area_.h);
|
||||
}
|
||||
void Starfield::update(float delta_time) {
|
||||
for (auto& star : stars_) {
|
||||
star.position.z += star.velocity_z * delta_time;
|
||||
star.rot_phase_y += star.rot_speed_y * delta_time;
|
||||
star.rot_phase_x += star.rot_speed_x * delta_time;
|
||||
|
||||
// Calcular scale dinàmica segons distancia del centro
|
||||
auto Starfield::computeScale(const Estrella& estrella) const -> float {
|
||||
const CapaConfig& capa = capes_[estrella.capa];
|
||||
|
||||
// Interpolació lineal basada en distancia del centro
|
||||
// distancia_centre: 0.0 (centro) → 1.0 (vora)
|
||||
return capa.escala_min +
|
||||
((capa.escala_max - capa.escala_min) * estrella.distancia_centre);
|
||||
}
|
||||
|
||||
// Calcular brightness dinàmica segons distancia del centro
|
||||
auto Starfield::computeBrightness(const Estrella& estrella) const -> float {
|
||||
// Interpolació lineal: estrelles properes (vora) més brillants
|
||||
// distancia_centre: 0.0 (centro, llunyanes) → 1.0 (vora, properes)
|
||||
float brightness_base = Defaults::Brightness::STARFIELD_MIN +
|
||||
((Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) *
|
||||
estrella.distancia_centre);
|
||||
|
||||
// Aplicar multiplicador i limitar a 1.0
|
||||
return std::min(1.0F, brightness_base * multiplicador_brightness_);
|
||||
}
|
||||
|
||||
// Actualitzar posicions de las estrelles
|
||||
void Starfield::update(float delta_time) {
|
||||
for (auto& estrella : estrelles_) {
|
||||
// Obtenir configuración de la capa
|
||||
const CapaConfig& capa = capes_[estrella.capa];
|
||||
|
||||
// Moure hacia fuera des del centro
|
||||
float velocity = capa.velocitat_base;
|
||||
float dx = velocity * std::cos(estrella.angle) * delta_time;
|
||||
float dy = velocity * std::sin(estrella.angle) * delta_time;
|
||||
|
||||
estrella.position.x += dx;
|
||||
estrella.position.y += dy;
|
||||
|
||||
// Actualitzar distancia del centro
|
||||
float dx_centre = estrella.position.x - punt_fuga_.x;
|
||||
float dy_centre = estrella.position.y - punt_fuga_.y;
|
||||
float dist_px = std::sqrt((dx_centre * dx_centre) + (dy_centre * dy_centre));
|
||||
estrella.distancia_centre = dist_px / radi_max_;
|
||||
|
||||
// Si ha sortit de l'àrea, regenerar-la
|
||||
if (isOutsideArea(estrella)) {
|
||||
initStar(estrella);
|
||||
if (star.position.z < Z_NEAR_RESPAWN) {
|
||||
initStar(star, /*spawn_at_far=*/true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Establir multiplicador de brightness
|
||||
void Starfield::setBrightness(float multiplier) {
|
||||
multiplicador_brightness_ = std::max(0.0F, multiplier); // Evitar valors negatius
|
||||
}
|
||||
void Starfield::draw() const {
|
||||
if (camera_ == nullptr || renderer_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
// Ordena de més lluny a més a prop perquè el blending additiu acumule
|
||||
// brightness sense que els elements de davant queden tapats pels de
|
||||
// darrere. Còpia d'índexs per no modificar l'ordre intern de stars_.
|
||||
std::vector<std::size_t> order(stars_.size());
|
||||
for (std::size_t i = 0; i < order.size(); ++i) {
|
||||
order[i] = i;
|
||||
}
|
||||
std::ranges::sort(order, [&](std::size_t a, std::size_t b) {
|
||||
return stars_[a].position.z > stars_[b].position.z;
|
||||
});
|
||||
|
||||
// Dibuixar todas las estrelles
|
||||
void Starfield::draw() {
|
||||
if (!shape_estrella_->isValid()) {
|
||||
return;
|
||||
for (std::size_t idx : order) {
|
||||
const Star& star = stars_[idx];
|
||||
const Transform3D TRANSFORM{
|
||||
.position = star.position,
|
||||
.rotation_euler = Vec3{.x = star.rot_phase_x, .y = star.rot_phase_y, .z = 0.0F},
|
||||
.scale = star.scale,
|
||||
};
|
||||
drawWireframe(renderer_, *camera_, octahedron_, TRANSFORM, computeBrightness(star));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& estrella : estrelles_) {
|
||||
// Calcular scale i brightness dinàmicament
|
||||
float scale = computeScale(estrella);
|
||||
float brightness = computeBrightness(estrella);
|
||||
|
||||
// Renderizar estrella sin rotación
|
||||
Rendering::renderShape(
|
||||
renderer_,
|
||||
shape_estrella_,
|
||||
estrella.position,
|
||||
0.0F, // angle (las estrelles no giren)
|
||||
scale, // scale dinàmica
|
||||
1.0F, // progress (siempre visible)
|
||||
brightness // brightness dinàmica
|
||||
);
|
||||
void Starfield::setBrightness(float multiplier) {
|
||||
brightness_mult_ = std::max(0.0F, multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Graphics
|
||||
|
||||
Reference in New Issue
Block a user