diff --git a/data/logo/logo.png b/data/logo/logo.png index 7f87466..91114a5 100644 Binary files a/data/logo/logo.png and b/data/logo/logo.png differ diff --git a/data/logo/logo2.png b/data/logo/logo2.png new file mode 100644 index 0000000..b4486df Binary files /dev/null and b/data/logo/logo2.png differ diff --git a/source/app_logo.cpp b/source/app_logo.cpp index 7f8d50e..986a1cc 100644 --- a/source/app_logo.cpp +++ b/source/app_logo.cpp @@ -11,44 +11,65 @@ bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_he 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 + // ======================================================================== + // Cargar LOGO1 desde data/logo/logo.png + // ======================================================================== + std::string logo1_path = resources_dir + "/data/logo/logo.png"; + logo1_texture_ = std::make_shared(renderer, logo1_path); + if (logo1_texture_->getWidth() == 0 || logo1_texture_->getHeight() == 0) { + // Error al cargar textura logo1 return false; } - // Configurar filtrado LINEAR para suavizado (mejor para logos escalados) - logo_texture_->setScaleMode(SDL_SCALEMODE_LINEAR); + // Configurar filtrado LINEAR para suavizado + logo1_texture_->setScaleMode(SDL_SCALEMODE_LINEAR); // Crear sprite con la textura - logo_sprite_ = std::make_unique(logo_texture_); + logo1_sprite_ = std::make_unique(logo1_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}); + // Configurar el clip para que use toda la textura + float logo1_width = static_cast(logo1_texture_->getWidth()); + float logo1_height = static_cast(logo1_texture_->getHeight()); + logo1_sprite_->setClip({0.0f, 0.0f, logo1_width, logo1_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; + // ======================================================================== + // Cargar LOGO2 desde data/logo/logo2.png + // ======================================================================== + std::string logo2_path = resources_dir + "/data/logo/logo2.png"; + logo2_texture_ = std::make_shared(renderer, logo2_path); + if (logo2_texture_->getWidth() == 0 || logo2_texture_->getHeight() == 0) { + // Error al cargar textura logo2 + return false; + } - float scale_x = quadrant_width / logo_width; - float scale_y = quadrant_height / logo_height; - float scale = (scale_x < scale_y) ? scale_x : scale_y; + // Configurar filtrado LINEAR para suavizado + logo2_texture_->setScaleMode(SDL_SCALEMODE_LINEAR); - // Calcular tamaño base (guardarlo para animaciones de zoom) - base_width_ = logo_width * scale; - base_height_ = logo_height * scale; + // Crear sprite con la textura + logo2_sprite_ = std::make_unique(logo2_texture_); - // Aplicar escala inicial - logo_sprite_->setSize(base_width_, base_height_); + // Configurar el clip para que use toda la textura + float logo2_width = static_cast(logo2_texture_->getWidth()); + float logo2_height = static_cast(logo2_texture_->getHeight()); + logo2_sprite_->setClip({0.0f, 0.0f, logo2_width, logo2_height}); - // Posicionar logo en el centro del cuadrante inferior derecho + // ======================================================================== + // Calcular tamaño base (asumimos mismo tamaño para ambos logos) + // El logo debe tener una altura de APPLOGO_HEIGHT_PERCENT (40%) de la pantalla + // ======================================================================== + float target_height = screen_height_ * APPLOGO_HEIGHT_PERCENT; + float scale = target_height / logo1_height; + + base_width_ = logo1_width * scale; + base_height_ = target_height; // = logo1_height * scale + + // Aplicar escala inicial a ambos sprites + logo1_sprite_->setSize(base_width_, base_height_); + logo2_sprite_->setSize(base_width_, base_height_); + + // Posicionar ambos logos en el centro del cuadrante inferior derecho (superpuestos) updateLogoPosition(); return true; @@ -73,11 +94,14 @@ void AppLogo::update(float delta_time, AppMode current_mode) { state_ = AppLogoState::FADE_IN; timer_ = 0.0f; current_alpha_ = 0; + // Elegir animaciones de entrada aleatorias (independientes para cada logo) + logo1_entry_animation_ = getRandomAnimation(); + logo2_entry_animation_ = getRandomAnimation(); } break; case AppLogoState::FADE_IN: - // Fade in: alpha de 0 a 255, scale de 120% a 100% + // Fade in: alpha de 0 a 255, animaciones independientes para logo1 y logo2 { float fade_progress = timer_ / APPLOGO_FADE_DURATION; if (fade_progress >= 1.0f) { @@ -85,36 +109,105 @@ void AppLogo::update(float delta_time, AppMode current_mode) { state_ = AppLogoState::VISIBLE; timer_ = 0.0f; current_alpha_ = 255; - current_scale_ = 1.0f; - squash_y_ = 1.0f; - stretch_x_ = 1.0f; - rotation_ = 0.0f; + // Resetear variables de ambos logos + logo1_scale_ = 1.0f; + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + logo1_rotation_ = 0.0f; + logo2_scale_ = 1.0f; + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + logo2_rotation_ = 0.0f; } else { - // Interpolar alpha linealmente (0 → 255) + // Interpolar alpha linealmente (0 → 255) - compartido current_alpha_ = static_cast(fade_progress * 255.0f); - if (animation_type_ == AppLogoAnimationType::ELASTIC_STICK) { - // Animación elástica tipo "pegatina" - // Usar easing elástico para el scale (bounce al final) - float elastic_t = easeOutElastic(fade_progress); - current_scale_ = 1.2f - (elastic_t * 0.2f); + // ================================================================ + // Aplicar animación de LOGO1 según logo1_entry_animation_ + // ================================================================ + switch (logo1_entry_animation_) { + case AppLogoAnimationType::ZOOM_ONLY: + logo1_scale_ = 1.2f - (fade_progress * 0.2f); + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + logo1_rotation_ = 0.0f; + break; - // Squash vertical al principio, luego se normaliza con overshoot - // Empieza aplastado (0.6), termina normal (1.0) con bounce - float squash_t = easeOutBack(fade_progress); - squash_y_ = 0.6f + (squash_t * 0.4f); + case AppLogoAnimationType::ELASTIC_STICK: + { + float elastic_t = easeOutElastic(fade_progress); + logo1_scale_ = 1.2f - (elastic_t * 0.2f); + float squash_t = easeOutBack(fade_progress); + logo1_squash_y_ = 0.6f + (squash_t * 0.4f); + logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f; + logo1_rotation_ = 0.0f; + } + break; - // Compensar squash con stretch horizontal (conservación de área) - stretch_x_ = 1.0f + (1.0f - squash_y_) * 0.5f; + case AppLogoAnimationType::ROTATE_SPIRAL: + { + float ease_t = easeInOutQuad(fade_progress); + logo1_scale_ = 0.3f + (ease_t * 0.7f); + logo1_rotation_ = (1.0f - fade_progress) * 6.28f; + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + } + break; - // Sin rotación en fade in - rotation_ = 0.0f; - } else { - // Animación simple (solo zoom) - current_scale_ = 1.2f - (fade_progress * 0.2f); - squash_y_ = 1.0f; - stretch_x_ = 1.0f; - rotation_ = 0.0f; + case AppLogoAnimationType::BOUNCE_SQUASH: + { + float bounce_t = easeOutBounce(fade_progress); + logo1_scale_ = 1.0f; + float squash_amount = (1.0f - bounce_t) * 0.3f; + logo1_squash_y_ = 1.0f - squash_amount; + logo1_stretch_x_ = 1.0f + squash_amount * 0.5f; + logo1_rotation_ = 0.0f; + } + break; + } + + // ================================================================ + // Aplicar animación de LOGO2 según logo2_entry_animation_ + // ================================================================ + switch (logo2_entry_animation_) { + case AppLogoAnimationType::ZOOM_ONLY: + logo2_scale_ = 1.2f - (fade_progress * 0.2f); + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + logo2_rotation_ = 0.0f; + break; + + case AppLogoAnimationType::ELASTIC_STICK: + { + float elastic_t = easeOutElastic(fade_progress); + logo2_scale_ = 1.2f - (elastic_t * 0.2f); + float squash_t = easeOutBack(fade_progress); + logo2_squash_y_ = 0.6f + (squash_t * 0.4f); + logo2_stretch_x_ = 1.0f + (1.0f - logo2_squash_y_) * 0.5f; + logo2_rotation_ = 0.0f; + } + break; + + case AppLogoAnimationType::ROTATE_SPIRAL: + { + float ease_t = easeInOutQuad(fade_progress); + logo2_scale_ = 0.3f + (ease_t * 0.7f); + logo2_rotation_ = (1.0f - fade_progress) * 6.28f; + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + } + break; + + case AppLogoAnimationType::BOUNCE_SQUASH: + { + float bounce_t = easeOutBounce(fade_progress); + logo2_scale_ = 1.0f; + float squash_amount = (1.0f - bounce_t) * 0.3f; + logo2_squash_y_ = 1.0f - squash_amount; + logo2_stretch_x_ = 1.0f + squash_amount * 0.5f; + logo2_rotation_ = 0.0f; + } + break; } } } @@ -126,11 +219,14 @@ void AppLogo::update(float delta_time, AppMode current_mode) { state_ = AppLogoState::FADE_OUT; timer_ = 0.0f; current_alpha_ = 255; + // Elegir animaciones de salida aleatorias (independientes para cada logo) + logo1_exit_animation_ = getRandomAnimation(); + logo2_exit_animation_ = getRandomAnimation(); } break; case AppLogoState::FADE_OUT: - // Fade out: alpha de 255 a 0, scale de 100% a 120% + // Fade out: alpha de 255 a 0, animaciones independientes para logo1 y logo2 { float fade_progress = timer_ / APPLOGO_FADE_DURATION; if (fade_progress >= 1.0f) { @@ -138,64 +234,165 @@ void AppLogo::update(float delta_time, AppMode current_mode) { state_ = AppLogoState::HIDDEN; timer_ = 0.0f; current_alpha_ = 0; - current_scale_ = 1.0f; - squash_y_ = 1.0f; - stretch_x_ = 1.0f; - rotation_ = 0.0f; + // Resetear variables de ambos logos + logo1_scale_ = 1.0f; + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + logo1_rotation_ = 0.0f; + logo2_scale_ = 1.0f; + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + logo2_rotation_ = 0.0f; } else { - // Interpolar alpha linealmente (255 → 0) + // Interpolar alpha linealmente (255 → 0) - compartido current_alpha_ = static_cast((1.0f - fade_progress) * 255.0f); - if (animation_type_ == AppLogoAnimationType::ELASTIC_STICK) { - // Animación elástica tipo "despegar pegatina" - // Scale crece con easing out (más rápido al principio) - current_scale_ = 1.0f + (fade_progress * fade_progress * 0.2f); + // ================================================================ + // Aplicar animación de LOGO1 según logo1_exit_animation_ + // ================================================================ + switch (logo1_exit_animation_) { + case AppLogoAnimationType::ZOOM_ONLY: + logo1_scale_ = 1.0f + (fade_progress * 0.2f); + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + logo1_rotation_ = 0.0f; + break; - // Stretch vertical (estiramiento al despegarse) - squash_y_ = 1.0f + (fade_progress * 0.3f); + case AppLogoAnimationType::ELASTIC_STICK: + logo1_scale_ = 1.0f + (fade_progress * fade_progress * 0.2f); + logo1_squash_y_ = 1.0f + (fade_progress * 0.3f); + logo1_stretch_x_ = 1.0f - (fade_progress * 0.2f); + logo1_rotation_ = fade_progress * 0.1f; + break; - // Squash horizontal (se comprime al estirarse verticalmente) - stretch_x_ = 1.0f - (fade_progress * 0.2f); + case AppLogoAnimationType::ROTATE_SPIRAL: + { + float ease_t = easeInOutQuad(fade_progress); + logo1_scale_ = 1.0f - (ease_t * 0.7f); + logo1_rotation_ = fade_progress * 6.28f; + logo1_squash_y_ = 1.0f; + logo1_stretch_x_ = 1.0f; + } + break; - // Rotación sutil al despegarse (parece que se tuerce) - rotation_ = fade_progress * 0.1f; // ~5.7 grados máximo - } else { - // Animación simple (solo zoom) - current_scale_ = 1.0f + (fade_progress * 0.2f); - squash_y_ = 1.0f; - stretch_x_ = 1.0f; - rotation_ = 0.0f; + case AppLogoAnimationType::BOUNCE_SQUASH: + { + if (fade_progress < 0.2f) { + float squash_t = fade_progress / 0.2f; + logo1_squash_y_ = 1.0f - (squash_t * 0.3f); + logo1_stretch_x_ = 1.0f + (squash_t * 0.2f); + } else { + float jump_t = (fade_progress - 0.2f) / 0.8f; + logo1_squash_y_ = 0.7f + (jump_t * 0.5f); + logo1_stretch_x_ = 1.2f - (jump_t * 0.2f); + } + logo1_scale_ = 1.0f + (fade_progress * 0.3f); + logo1_rotation_ = 0.0f; + } + break; + } + + // ================================================================ + // Aplicar animación de LOGO2 según logo2_exit_animation_ + // ================================================================ + switch (logo2_exit_animation_) { + case AppLogoAnimationType::ZOOM_ONLY: + logo2_scale_ = 1.0f + (fade_progress * 0.2f); + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + logo2_rotation_ = 0.0f; + break; + + case AppLogoAnimationType::ELASTIC_STICK: + logo2_scale_ = 1.0f + (fade_progress * fade_progress * 0.2f); + logo2_squash_y_ = 1.0f + (fade_progress * 0.3f); + logo2_stretch_x_ = 1.0f - (fade_progress * 0.2f); + logo2_rotation_ = fade_progress * 0.1f; + break; + + case AppLogoAnimationType::ROTATE_SPIRAL: + { + float ease_t = easeInOutQuad(fade_progress); + logo2_scale_ = 1.0f - (ease_t * 0.7f); + logo2_rotation_ = fade_progress * 6.28f; + logo2_squash_y_ = 1.0f; + logo2_stretch_x_ = 1.0f; + } + break; + + case AppLogoAnimationType::BOUNCE_SQUASH: + { + if (fade_progress < 0.2f) { + float squash_t = fade_progress / 0.2f; + logo2_squash_y_ = 1.0f - (squash_t * 0.3f); + logo2_stretch_x_ = 1.0f + (squash_t * 0.2f); + } else { + float jump_t = (fade_progress - 0.2f) / 0.8f; + logo2_squash_y_ = 0.7f + (jump_t * 0.5f); + logo2_stretch_x_ = 1.2f - (jump_t * 0.2f); + } + logo2_scale_ = 1.0f + (fade_progress * 0.3f); + logo2_rotation_ = 0.0f; + } + break; } } } break; } - // Aplicar alpha y scale al logo - if (logo_texture_) { - logo_texture_->setAlpha(current_alpha_); + // Aplicar alpha a ambos logos (compartido - sincronizado) + if (logo1_texture_) { + logo1_texture_->setAlpha(current_alpha_); + } + if (logo2_texture_) { + logo2_texture_->setAlpha(current_alpha_); } - if (logo_sprite_) { - // Aplicar escala animada al tamaño - float scaled_width = base_width_ * current_scale_; - float scaled_height = base_height_ * current_scale_; - logo_sprite_->setSize(scaled_width, scaled_height); - - // Recentrar con el nuevo tamaño (importante para que el zoom sea desde el centro) - updateLogoPosition(); + // Aplicar escala animada INDEPENDIENTE a cada logo + if (logo1_sprite_) { + float scaled_width = base_width_ * logo1_scale_; + float scaled_height = base_height_ * logo1_scale_; + logo1_sprite_->setSize(scaled_width, scaled_height); } + + if (logo2_sprite_) { + float scaled_width = base_width_ * logo2_scale_; + float scaled_height = base_height_ * logo2_scale_; + logo2_sprite_->setSize(scaled_width, scaled_height); + } + + // Recentrar ambos logos (están superpuestos, misma posición) + updateLogoPosition(); } void AppLogo::render() { // Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT) if (state_ != AppLogoState::HIDDEN) { - if (animation_type_ == AppLogoAnimationType::ELASTIC_STICK) { - // Usar renderizado con geometría para deformaciones - renderWithGeometry(); - } else if (logo_sprite_) { - // Usar renderizado simple con Sprite - logo_sprite_->render(); + // Determinar animaciones actuales para cada logo + AppLogoAnimationType logo1_anim = (state_ == AppLogoState::FADE_IN) ? logo1_entry_animation_ : logo1_exit_animation_; + AppLogoAnimationType logo2_anim = (state_ == AppLogoState::FADE_IN) ? logo2_entry_animation_ : logo2_exit_animation_; + + // ==================================================================== + // Renderizar LOGO1 primero (fondo) + // ==================================================================== + if (logo1_anim != AppLogoAnimationType::ZOOM_ONLY) { + // Usar renderizado con geometría para deformaciones/rotación + renderWithGeometry(1); + } else if (logo1_sprite_) { + // Usar renderizado simple con Sprite (solo ZOOM_ONLY) + logo1_sprite_->render(); + } + + // ==================================================================== + // Renderizar LOGO2 después (encima de logo1) + // ==================================================================== + if (logo2_anim != AppLogoAnimationType::ZOOM_ONLY) { + // Usar renderizado con geometría para deformaciones/rotación + renderWithGeometry(2); + } else if (logo2_sprite_) { + // Usar renderizado simple con Sprite (solo ZOOM_ONLY) + logo2_sprite_->render(); } } } @@ -204,55 +401,63 @@ void AppLogo::updateScreenSize(int screen_width, int screen_height) { screen_width_ = screen_width; screen_height_ = screen_height; - // Recalcular tamaño base del logo para la nueva resolución - if (logo_sprite_ && logo_texture_) { - float logo_width = static_cast(logo_texture_->getWidth()); - float logo_height = static_cast(logo_texture_->getHeight()); + // Recalcular tamaño base para la nueva resolución (asumimos mismo tamaño para ambos logos) + if (logo1_sprite_ && logo1_texture_) { + float logo_width = static_cast(logo1_texture_->getWidth()); + float logo_height = static_cast(logo1_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; + // El logo debe tener una altura de APPLOGO_HEIGHT_PERCENT (40%) de la pantalla + float target_height = screen_height_ * APPLOGO_HEIGHT_PERCENT; + float scale = target_height / logo_height; // Recalcular tamaño base base_width_ = logo_width * scale; - base_height_ = logo_height * scale; + base_height_ = target_height; // = logo_height * scale - // Aplicar escala actual (respeta la animación en curso) - float scaled_width = base_width_ * current_scale_; - float scaled_height = base_height_ * current_scale_; - logo_sprite_->setSize(scaled_width, scaled_height); + // Aplicar escala actual a AMBOS logos (respeta la animación en curso) + if (logo1_sprite_) { + float scaled_width = base_width_ * logo1_scale_; + float scaled_height = base_height_ * logo1_scale_; + logo1_sprite_->setSize(scaled_width, scaled_height); + } - // Reposicionar logo + if (logo2_sprite_) { + float scaled_width = base_width_ * logo2_scale_; + float scaled_height = base_height_ * logo2_scale_; + logo2_sprite_->setSize(scaled_width, scaled_height); + } + + // Reposicionar ambos logos updateLogoPosition(); } } void AppLogo::updateLogoPosition() { - if (!logo_sprite_) return; + // Calcular padding desde bordes derecho e inferior + float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT; + float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT; - // Usar el tamaño actual del logo (base_width/height * current_scale_) - float current_width = base_width_ * current_scale_; - float current_height = base_height_ * current_scale_; + // Posicionar LOGO1 (anclado a esquina inferior derecha con padding) + if (logo1_sprite_) { + float logo1_width = base_width_ * logo1_scale_; + float logo1_height = base_height_ * logo1_scale_; + float pos_x = screen_width_ - logo1_width - padding_x; + float pos_y = screen_height_ - logo1_height - padding_y; + logo1_sprite_->setPos({pos_x, pos_y}); + } - // 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 - (current_width / 2.0f); - float pos_y = quadrant_center_y - (current_height / 2.0f); - - logo_sprite_->setPos({pos_x, pos_y}); + // Posicionar LOGO2 (anclado a esquina inferior derecha con padding, superpuesto a logo1) + if (logo2_sprite_) { + float logo2_width = base_width_ * logo2_scale_; + float logo2_height = base_height_ * logo2_scale_; + float pos_x = screen_width_ - logo2_width - padding_x; + float pos_y = screen_height_ - logo2_height - padding_y; + logo2_sprite_->setPos({pos_x, pos_y}); + } } // ============================================================================ -// Funciones de easing para animaciones elásticas +// Funciones de easing para animaciones // ============================================================================ float AppLogo::easeOutElastic(float t) { @@ -273,28 +478,103 @@ float AppLogo::easeOutBack(float t) { return 1.0f + c3 * powf(t - 1.0f, 3.0f) + c1 * powf(t - 1.0f, 2.0f); } +float AppLogo::easeOutBounce(float t) { + // Bounce easing out: rebotes decrecientes (para BOUNCE_SQUASH) + const float n1 = 7.5625f; + const float d1 = 2.75f; + + if (t < 1.0f / d1) { + return n1 * t * t; + } else if (t < 2.0f / d1) { + t -= 1.5f / d1; + return n1 * t * t + 0.75f; + } else if (t < 2.5f / d1) { + t -= 2.25f / d1; + return n1 * t * t + 0.9375f; + } else { + t -= 2.625f / d1; + return n1 * t * t + 0.984375f; + } +} + +float AppLogo::easeInOutQuad(float t) { + // Quadratic easing in/out: aceleración suave (para ROTATE_SPIRAL) + if (t < 0.5f) { + return 2.0f * t * t; + } else { + return 1.0f - powf(-2.0f * t + 2.0f, 2.0f) / 2.0f; + } +} + +// ============================================================================ +// Función auxiliar para aleatorización +// ============================================================================ + +AppLogoAnimationType AppLogo::getRandomAnimation() { + // Generar número aleatorio entre 0 y 3 (4 tipos de animación) + int random_value = rand() % 4; + + switch (random_value) { + case 0: + return AppLogoAnimationType::ZOOM_ONLY; + case 1: + return AppLogoAnimationType::ELASTIC_STICK; + case 2: + return AppLogoAnimationType::ROTATE_SPIRAL; + case 3: + default: + return AppLogoAnimationType::BOUNCE_SQUASH; + } +} + // ============================================================================ // Renderizado con geometría deformada (para animación ELASTIC_STICK) // ============================================================================ -void AppLogo::renderWithGeometry() { - if (!logo_texture_ || !renderer_) return; +void AppLogo::renderWithGeometry(int logo_index) { + if (!renderer_) return; + + // Seleccionar variables según el logo_index (1 = logo1, 2 = logo2) + std::shared_ptr texture; + float scale, squash_y, stretch_x, rotation; + + if (logo_index == 1) { + if (!logo1_texture_) return; + texture = logo1_texture_; + scale = logo1_scale_; + squash_y = logo1_squash_y_; + stretch_x = logo1_stretch_x_; + rotation = logo1_rotation_; + } else if (logo_index == 2) { + if (!logo2_texture_) return; + texture = logo2_texture_; + scale = logo2_scale_; + squash_y = logo2_squash_y_; + stretch_x = logo2_stretch_x_; + rotation = logo2_rotation_; + } else { + return; // Índice inválido + } // Calcular tamaño con escala y deformaciones aplicadas - float width = base_width_ * current_scale_ * stretch_x_; - float height = base_height_ * current_scale_ * squash_y_; + float width = base_width_ * scale * stretch_x; + float height = base_height_ * scale * squash_y; - // Centro del cuadrante inferior derecho (mismo que updateLogoPosition) - float quadrant_center_x = screen_width_ * 0.75f; - float quadrant_center_y = screen_height_ * 0.75f; + // Calcular padding desde bordes derecho e inferior + float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT; + float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT; - // Calcular posición centrada - float center_x = quadrant_center_x; - float center_y = quadrant_center_y; + // Calcular esquina del logo (anclado a esquina inferior derecha con padding) + float corner_x = screen_width_ - width - padding_x; + float corner_y = screen_height_ - height - padding_y; + + // Centro del logo (para rotación) = esquina + mitad del tamaño + float center_x = corner_x + (width / 2.0f); + float center_y = corner_y + (height / 2.0f); // Pre-calcular seno y coseno de rotación - float cos_rot = cosf(rotation_); - float sin_rot = sinf(rotation_); + float cos_rot = cosf(rotation); + float sin_rot = sinf(rotation); // Crear 4 vértices del quad (centrado en center_x, center_y) SDL_Vertex vertices[4]; @@ -350,6 +630,6 @@ void AppLogo::renderWithGeometry() { // Índices para 2 triángulos int indices[6] = {0, 1, 2, 2, 3, 0}; - // Renderizar con la textura del logo - SDL_RenderGeometry(renderer_, logo_texture_->getSDLTexture(), vertices, 4, indices, 6); + // Renderizar con la textura del logo correspondiente + SDL_RenderGeometry(renderer_, texture->getSDLTexture(), vertices, 4, indices, 6); } diff --git a/source/app_logo.h b/source/app_logo.h index 0648496..52840a5 100644 --- a/source/app_logo.h +++ b/source/app_logo.h @@ -19,8 +19,10 @@ enum class AppLogoState { // Tipo de animación de entrada/salida enum class AppLogoAnimationType { - ZOOM_ONLY, // Solo zoom simple (120% → 100% → 120%) - ELASTIC_STICK // Zoom + deformación elástica tipo "pegatina" + ZOOM_ONLY, // A: Solo zoom simple (120% → 100% → 120%) + ELASTIC_STICK, // B: Zoom + deformación elástica tipo "pegatina" + ROTATE_SPIRAL, // C: Rotación en espiral (entra girando, sale girando) + BOUNCE_SQUASH // D: Rebote con aplastamiento (cae rebotando, salta) }; class AppLogo { @@ -41,21 +43,34 @@ class AppLogo { 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 + // Texturas y sprites (x2 - logo1 y logo2 superpuestos) + std::shared_ptr logo1_texture_; // Textura del logo1 (data/logo/logo.png) + std::unique_ptr logo1_sprite_; // Sprite para renderizar logo1 + std::shared_ptr logo2_texture_; // Textura del logo2 (data/logo/logo2.png) + std::unique_ptr logo2_sprite_; // Sprite para renderizar logo2 + // Variables COMPARTIDAS (sincronización de ambos logos) 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) - float current_scale_ = 1.0f; // Escala actual (1.0 = 100%) - // Tipo de animación (FIJO para testing: ELASTIC_STICK) - AppLogoAnimationType animation_type_ = AppLogoAnimationType::ELASTIC_STICK; + // Animaciones INDEPENDIENTES para cada logo + AppLogoAnimationType logo1_entry_animation_ = AppLogoAnimationType::ZOOM_ONLY; + AppLogoAnimationType logo1_exit_animation_ = AppLogoAnimationType::ZOOM_ONLY; + AppLogoAnimationType logo2_entry_animation_ = AppLogoAnimationType::ZOOM_ONLY; + AppLogoAnimationType logo2_exit_animation_ = AppLogoAnimationType::ZOOM_ONLY; - // Variables de deformación elástica (para ELASTIC_STICK) - float squash_y_ = 1.0f; // Factor de aplastamiento vertical (1.0 = normal) - float stretch_x_ = 1.0f; // Factor de estiramiento horizontal (1.0 = normal) - float rotation_ = 0.0f; // Rotación en radianes (para efecto despegar) + // Variables de deformación INDEPENDIENTES para logo1 + float logo1_scale_ = 1.0f; // Escala actual de logo1 (1.0 = 100%) + float logo1_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo1 + float logo1_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo1 + float logo1_rotation_ = 0.0f; // Rotación en radianes logo1 + + // Variables de deformación INDEPENDIENTES para logo2 + float logo2_scale_ = 1.0f; // Escala actual de logo2 (1.0 = 100%) + float logo2_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo2 + float logo2_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo2 + float logo2_rotation_ = 0.0f; // Rotación en radianes logo2 int screen_width_ = 0; // Ancho de pantalla (para centrar) int screen_height_ = 0; // Alto de pantalla (para centrar) @@ -68,10 +83,15 @@ class AppLogo { SDL_Renderer* renderer_ = nullptr; // Métodos privados auxiliares - void updateLogoPosition(); // Centrar logo en pantalla - void renderWithGeometry(); // Renderizar con vértices deformados + void updateLogoPosition(); // Centrar ambos logos en pantalla (superpuestos) + void renderWithGeometry(int logo_index); // Renderizar logo con vértices deformados (1 o 2) // Funciones de easing float easeOutElastic(float t); // Elastic bounce out float easeOutBack(float t); // Overshoot out + float easeOutBounce(float t); // Bounce easing (para BOUNCE_SQUASH) + float easeInOutQuad(float t); // Quadratic easing (para ROTATE_SPIRAL) + + // Función auxiliar para elegir animación aleatoria + AppLogoAnimationType getRandomAnimation(); }; diff --git a/source/defines.h b/source/defines.h index bac3bda..8c71740 100644 --- a/source/defines.h +++ b/source/defines.h @@ -289,9 +289,11 @@ constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progres 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) +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) +constexpr float APPLOGO_HEIGHT_PERCENT = 0.4f; // Altura del logo = 40% de la altura de pantalla +constexpr float APPLOGO_PADDING_PERCENT = 0.1f; // Padding desde esquina inferior-derecha = 10% // Configuración de Modo BOIDS (comportamiento de enjambre) // TIME-BASED CONVERSION (frame-based → time-based):