// starfield.cpp - Implementació del sistema de estrelles de fons // © 2025 Orni Attack #include "core/graphics/starfield.hpp" #include #include #include #include "core/defaults.hpp" #include "core/graphics/shape_loader.hpp" #include "core/rendering/shape_renderer.hpp" namespace Graphics { // Constructor Starfield::Starfield(SDL_Renderer* renderer, const Vec2& punt_fuga, const SDL_FRect& area, int densitat) : renderer_(renderer), punt_fuga_(punt_fuga), area_(area), densitat_(densitat) { // Carregar shape de estrella con ShapeLoader shape_estrella_ = ShapeLoader::load("star.shp"); if (!shape_estrella_ || !shape_estrella_->isValid()) { std::cerr << "ERROR: No s'ha pogut load star.shp" << '\n'; return; } // 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}); // Capa 1: Profunditat mitjana capes_.push_back({40.0F, 0.5F, 1.2F, densitat / 3}); // Capa 2: Primer pla (ràpida, grande) capes_.push_back({80.0F, 0.8F, 2.0F, densitat / 3}); // 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(rand()) / 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(rand()) / 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); } } } // Inicialitzar una estrella (nueva o regenerada) void Starfield::inicialitzar_estrella(Estrella& estrella) const { // Angle aleatori des del point de fuga hacia fuera estrella.angle = (static_cast(rand()) / RAND_MAX) * 2.0F * Defaults::Math::PI; // Distancia inicial pequeña (5% del radi màxim) - neix prop del centro estrella.distancia_centre = 0.05F; // 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)); } // Verificar si una estrella está fuera de l'àrea bool Starfield::fora_area(const Estrella& estrella) const { 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); } // Calcular scale dinàmica segons distancia del centro float Starfield::calcular_escala(const Estrella& estrella) const { 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 float Starfield::calcular_brightness(const Estrella& estrella) const { // 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 (fora_area(estrella)) { inicialitzar_estrella(estrella); } } } // Establir multiplicador de brightness void Starfield::set_brightness(float multiplier) { multiplicador_brightness_ = std::max(0.0F, multiplier); // Evitar valors negatius } // Dibuixar todas las estrelles void Starfield::draw() { if (!shape_estrella_->isValid()) { return; } for (const auto& estrella : estrelles_) { // Calcular scale i brightness dinàmicament float scale = calcular_escala(estrella); float brightness = calcular_brightness(estrella); // Renderizar estrella sin rotación Rendering::render_shape( 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 ); } } } // namespace Graphics