diff --git a/data/logo/logo.png b/data/logo/logo.png new file mode 100644 index 0000000..7f87466 Binary files /dev/null and b/data/logo/logo.png differ diff --git a/source/app_logo.cpp b/source/app_logo.cpp new file mode 100644 index 0000000..6905b0e --- /dev/null +++ b/source/app_logo.cpp @@ -0,0 +1,185 @@ +#include "app_logo.h" + +#include // for SDL_SCALEMODE_LINEAR + +#include "external/sprite.h" // for Sprite +#include "external/texture.h" // for Texture + +bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) { + screen_width_ = screen_width; + screen_height_ = screen_height; + + // Cargar textura del logo desde data/logo/logo.png + std::string resources_dir = getResourcesDirectory(); + std::string logo_path = resources_dir + "/data/logo/logo.png"; + + logo_texture_ = std::make_shared(renderer, logo_path); + if (logo_texture_->getWidth() == 0 || logo_texture_->getHeight() == 0) { + // Error al cargar textura + return false; + } + + // Configurar filtrado LINEAR para suavizado (mejor para logos escalados) + logo_texture_->setScaleMode(SDL_SCALEMODE_LINEAR); + + // Crear sprite con la textura + logo_sprite_ = std::make_unique(logo_texture_); + + // IMPORTANTE: Configurar el clip para que use toda la textura + float logo_width = static_cast(logo_texture_->getWidth()); + float logo_height = static_cast(logo_texture_->getHeight()); + logo_sprite_->setClip({0.0f, 0.0f, logo_width, logo_height}); + + // Calcular factor de escala para que el logo ocupe 1/4 de la pantalla (un cuadrante) + // El logo debe caber en width/2 x height/2 + float quadrant_width = screen_width_ / 2.0f; + float quadrant_height = screen_height_ / 2.0f; + + float scale_x = quadrant_width / logo_width; + float scale_y = quadrant_height / logo_height; + float scale = (scale_x < scale_y) ? scale_x : scale_y; + + // Aplicar escala + float scaled_width = logo_width * scale; + float scaled_height = logo_height * scale; + logo_sprite_->setSize(scaled_width, scaled_height); + + // Posicionar logo en el centro del cuadrante inferior derecho + updateLogoPosition(); + + return true; +} + +void AppLogo::update(float delta_time, AppMode current_mode) { + // Si estamos en SANDBOX, resetear y no hacer nada (logo desactivado) + if (current_mode == AppMode::SANDBOX) { + state_ = AppLogoState::HIDDEN; + timer_ = 0.0f; + current_alpha_ = 0; + return; + } + + // Máquina de estados con fade in/out + timer_ += delta_time; + + switch (state_) { + case AppLogoState::HIDDEN: + // Esperando el intervalo de espera + if (timer_ >= APPLOGO_DISPLAY_INTERVAL) { + state_ = AppLogoState::FADE_IN; + timer_ = 0.0f; + current_alpha_ = 0; + } + break; + + case AppLogoState::FADE_IN: + // Fade in: alpha de 0 a 255 + { + float fade_progress = timer_ / APPLOGO_FADE_DURATION; + if (fade_progress >= 1.0f) { + // Fade in completado + state_ = AppLogoState::VISIBLE; + timer_ = 0.0f; + current_alpha_ = 255; + } else { + // Interpolar alpha linealmente + current_alpha_ = static_cast(fade_progress * 255.0f); + } + } + break; + + case AppLogoState::VISIBLE: + // Logo completamente visible, esperando duración + if (timer_ >= APPLOGO_DISPLAY_DURATION) { + state_ = AppLogoState::FADE_OUT; + timer_ = 0.0f; + current_alpha_ = 255; + } + break; + + case AppLogoState::FADE_OUT: + // Fade out: alpha de 255 a 0 + { + float fade_progress = timer_ / APPLOGO_FADE_DURATION; + if (fade_progress >= 1.0f) { + // Fade out completado, volver a HIDDEN + state_ = AppLogoState::HIDDEN; + timer_ = 0.0f; + current_alpha_ = 0; + } else { + // Interpolar alpha linealmente (inverso) + current_alpha_ = static_cast((1.0f - fade_progress) * 255.0f); + } + } + break; + } + + // Aplicar alpha a la textura + if (logo_texture_) { + logo_texture_->setAlpha(current_alpha_); + } +} + +void AppLogo::render() { + // Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT) + if (state_ != AppLogoState::HIDDEN && logo_sprite_) { + logo_sprite_->render(); + } +} + +void AppLogo::updateScreenSize(int screen_width, int screen_height) { + screen_width_ = screen_width; + screen_height_ = screen_height; + + // Recalcular escala y posición del logo + if (logo_sprite_ && logo_texture_) { + float logo_width = static_cast(logo_texture_->getWidth()); + float logo_height = static_cast(logo_texture_->getHeight()); + + // Calcular factor de escala para que el logo ocupe 1/4 de la pantalla + float quadrant_width = screen_width_ / 2.0f; + float quadrant_height = screen_height_ / 2.0f; + + float scale_x = quadrant_width / logo_width; + float scale_y = quadrant_height / logo_height; + float scale = (scale_x < scale_y) ? scale_x : scale_y; + + // Aplicar escala + float scaled_width = logo_width * scale; + float scaled_height = logo_height * scale; + logo_sprite_->setSize(scaled_width, scaled_height); + + // Posicionar logo + updateLogoPosition(); + } +} + +void AppLogo::updateLogoPosition() { + if (!logo_sprite_ || !logo_texture_) return; + + // Calcular tamaño escalado del logo (ya configurado en setSize) + float logo_width = static_cast(logo_texture_->getWidth()); + float logo_height = static_cast(logo_texture_->getHeight()); + + float quadrant_width = screen_width_ / 2.0f; + float quadrant_height = screen_height_ / 2.0f; + + float scale_x = quadrant_width / logo_width; + float scale_y = quadrant_height / logo_height; + float scale = (scale_x < scale_y) ? scale_x : scale_y; + + float scaled_width = logo_width * scale; + float scaled_height = logo_height * scale; + + // Centro del cuadrante inferior derecho + // Cuadrante inferior derecho va de (width/2, height/2) a (width, height) + // Su centro está en (3/4 * width, 3/4 * height) + float quadrant_center_x = screen_width_ * 0.75f; + float quadrant_center_y = screen_height_ * 0.75f; + + // Centrar el logo en ese punto (sprite se posiciona por esquina superior izquierda) + float pos_x = quadrant_center_x - (scaled_width / 2.0f); + float pos_y = quadrant_center_y - (scaled_height / 2.0f); + + logo_sprite_->setPos({pos_x, pos_y}); +} diff --git a/source/app_logo.h b/source/app_logo.h new file mode 100644 index 0000000..6f6ffd3 --- /dev/null +++ b/source/app_logo.h @@ -0,0 +1,50 @@ +#pragma once + +#include // for SDL_Renderer + +#include // for unique_ptr, shared_ptr + +#include "defines.h" // for AppMode + +class Texture; +class Sprite; + +// Estados de la máquina de estados del logo +enum class AppLogoState { + HIDDEN, // Logo oculto, esperando APPLOGO_DISPLAY_INTERVAL + FADE_IN, // Apareciendo (alpha 0 → 255) + VISIBLE, // Completamente visible, esperando APPLOGO_DISPLAY_DURATION + FADE_OUT // Desapareciendo (alpha 255 → 0) +}; + +class AppLogo { + public: + AppLogo() = default; + ~AppLogo() = default; + + // Inicializar textura y sprite del logo + bool initialize(SDL_Renderer* renderer, int screen_width, int screen_height); + + // Actualizar temporizadores y estado de visibilidad + void update(float delta_time, AppMode current_mode); + + // Renderizar logo si está visible + void render(); + + // Actualizar tamaño de pantalla (reposicionar logo) + void updateScreenSize(int screen_width, int screen_height); + + private: + std::shared_ptr logo_texture_; // Textura del logo + std::unique_ptr logo_sprite_; // Sprite para renderizar + + AppLogoState state_ = AppLogoState::HIDDEN; // Estado actual de la máquina de estados + float timer_ = 0.0f; // Contador de tiempo para estado actual + int current_alpha_ = 0; // Alpha actual (0-255) + + int screen_width_ = 0; // Ancho de pantalla (para centrar) + int screen_height_ = 0; // Alto de pantalla (para centrar) + + // Métodos privados auxiliares + void updateLogoPosition(); // Centrar logo en pantalla +}; diff --git a/source/defines.h b/source/defines.h index aa1a75e..bac3bda 100644 --- a/source/defines.h +++ b/source/defines.h @@ -288,6 +288,11 @@ constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progres constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip" +// Configuración de AppLogo (logo periódico en pantalla) +constexpr float APPLOGO_DISPLAY_INTERVAL = 20.0f; // Intervalo entre apariciones del logo (segundos) +constexpr float APPLOGO_DISPLAY_DURATION = 5.0f; // Duración de visibilidad del logo (segundos) +constexpr float APPLOGO_FADE_DURATION = 0.5f; // Duración del fade in/out (segundos) + // Configuración de Modo BOIDS (comportamiento de enjambre) // TIME-BASED CONVERSION (frame-based → time-based): // - Radios: sin cambios (píxeles) diff --git a/source/engine.cpp b/source/engine.cpp index a135982..b810c7f 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -254,6 +254,14 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) { boid_manager_ = std::make_unique(); boid_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(), current_screen_width_, current_screen_height_); + + // Inicializar AppLogo (logo periódico en pantalla) + app_logo_ = std::make_unique(); + if (!app_logo_->initialize(renderer_, current_screen_width_, current_screen_height_)) { + std::cerr << "Advertencia: No se pudo inicializar AppLogo (logo periódico)" << std::endl; + // No es crítico, continuar sin logo + app_logo_.reset(); + } } return success; @@ -334,6 +342,11 @@ void Engine::update() { // Actualizar transiciones de temas (delegado a ThemeManager) theme_manager_->update(delta_time_); + + // Actualizar AppLogo (logo periódico) + if (app_logo_) { + app_logo_->update(delta_time_, state_manager_->getCurrentMode()); + } } // === IMPLEMENTACIÓN DE MÉTODOS PÚBLICOS PARA INPUT HANDLER === @@ -716,6 +729,11 @@ void Engine::render() { active_shape_.get(), shape_convergence_, physical_window_width_, physical_window_height_, current_screen_width_); + // Renderizar AppLogo (logo periódico) - después de UI, antes de present + if (app_logo_) { + app_logo_->render(); + } + SDL_RenderPresent(renderer_); } @@ -797,6 +815,11 @@ void Engine::toggleRealFullscreen() { // Actualizar tamaño de pantalla para boids (wrapping boundaries) boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_); + // Actualizar AppLogo con nueva resolución + if (app_logo_) { + app_logo_->updateScreenSize(current_screen_width_, current_screen_height_); + } + // Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones if (current_mode_ == SimulationMode::SHAPE) { generateShape(); // Regenerar figura con nuevas dimensiones de pantalla @@ -830,6 +853,11 @@ void Engine::toggleRealFullscreen() { scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_); scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_); + // Actualizar AppLogo con resolución restaurada + if (app_logo_) { + app_logo_->updateScreenSize(current_screen_width_, current_screen_height_); + } + // Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones if (current_mode_ == SimulationMode::SHAPE) { generateShape(); // Regenerar figura con nuevas dimensiones de pantalla diff --git a/source/engine.h b/source/engine.h index c25d1b6..8ce5dad 100644 --- a/source/engine.h +++ b/source/engine.h @@ -10,6 +10,7 @@ #include // for string #include // for vector +#include "app_logo.h" // for AppLogo #include "ball.h" // for Ball #include "boids_mgr/boid_manager.h" // for BoidManager #include "defines.h" // for GravityDirection, ColorTheme, ShapeType @@ -105,6 +106,7 @@ class Engine { std::unique_ptr boid_manager_; // Gestión de comportamiento boids std::unique_ptr state_manager_; // Gestión de estados (DEMO/LOGO) std::unique_ptr ui_manager_; // Gestión de UI (HUD, FPS, notificaciones) + std::unique_ptr app_logo_; // Gestión de logo periódico en pantalla // Recursos SDL SDL_Window* window_ = nullptr; @@ -160,7 +162,6 @@ class Engine { // Sistema de Modo DEMO (auto-play) y LOGO // NOTA: Engine mantiene estado de implementación para callbacks performLogoAction() // StateManager coordina los triggers y timers, Engine ejecuta las acciones - AppMode previous_app_mode_ = AppMode::SANDBOX; // Modo previo antes de entrar a LOGO float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos) diff --git a/source/external/texture.cpp b/source/external/texture.cpp index 5f1b87f..0d8df3e 100644 --- a/source/external/texture.cpp +++ b/source/external/texture.cpp @@ -128,6 +128,9 @@ bool Texture::loadFromFile(const std::string &file_path) { // Configurar filtro nearest neighbor para píxel perfect SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST); + + // Habilitar alpha blending para transparencias + SDL_SetTextureBlendMode(new_texture, SDL_BLENDMODE_BLEND); } // Destruye la superficie cargada @@ -169,3 +172,17 @@ int Texture::getHeight() { void Texture::setColor(int r, int g, int b) { SDL_SetTextureColorMod(texture_, r, g, b); } + +// Modula el alpha de la textura +void Texture::setAlpha(int alpha) { + if (texture_ != nullptr) { + SDL_SetTextureAlphaMod(texture_, static_cast(alpha)); + } +} + +// Configurar modo de escalado +void Texture::setScaleMode(SDL_ScaleMode mode) { + if (texture_ != nullptr) { + SDL_SetTextureScaleMode(texture_, mode); + } +} diff --git a/source/external/texture.h b/source/external/texture.h index 6ea5eb7..dc91634 100644 --- a/source/external/texture.h +++ b/source/external/texture.h @@ -44,6 +44,12 @@ class Texture { // Modula el color de la textura void setColor(int r, int g, int b); + // Modula el alpha (transparencia) de la textura + void setAlpha(int alpha); + + // Configurar modo de escalado (NEAREST para pixel art, LINEAR para suavizado) + void setScaleMode(SDL_ScaleMode mode); + // Getter para batch rendering SDL_Texture *getSDLTexture() const { return texture_; } };