Implementa sistema de sincronización elegante entre logos con efecto de "seguimiento" y fade suavizado para eliminar desincronización visual. Cambios principales: 1. **Animación sincronizada**: Ambos logos usan la MISMA animación - Eliminadas 4 variables independientes (logo1/logo2 × entry/exit) - Una sola variable `current_animation_` compartida - Misma animación para entrada y salida (simetría) 2. **Retraso de Logo 2**: 0.25 segundos detrás de Logo 1 - Logo 1 empieza en t=0.00s - Logo 2 empieza en t=0.25s - Efecto visual de "eco" o "seguimiento" 3. **Alpha independiente con retraso**: - `logo1_alpha_` y `logo2_alpha_` separados - Logo 2 aparece/desaparece más tarde visualmente 4. **Easing en alpha** (NUEVO): - Aplicado `easeInOutQuad()` al fade de alpha - Elimina problema de "logo deformado esperando a desvanecerse" - Sincronización visual perfecta entre animación y fade - Curva suave: lento al inicio, rápido en medio, lento al final Comportamiento resultante: FADE_IN: - t=0.00s: Logo 1 empieza (alpha con easing) - t=0.25s: Logo 2 empieza (alpha con easing + retraso) - t=0.50s: Logo 1 completamente visible - t=0.75s: Logo 2 completamente visible FADE_OUT: - t=0.00s: Logo 1 empieza a desaparecer (misma animación) - t=0.25s: Logo 2 empieza a desaparecer - t=0.50s: Logo 1 completamente invisible - t=0.75s: Logo 2 completamente invisible Archivos modificados: - source/defines.h: +APPLOGO_LOGO2_DELAY - source/app_logo.h: Reestructuración de variables de animación/alpha - source/app_logo.cpp: Implementación de retraso + easing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
690 lines
29 KiB
C++
690 lines
29 KiB
C++
#include "app_logo.h"
|
|
|
|
#include <SDL3/SDL_render.h> // for SDL_DestroyTexture, SDL_RenderGeometry, SDL_SetTextureAlphaMod
|
|
#include <cmath> // for powf, sinf, cosf
|
|
#include <cstdlib> // for free()
|
|
#include <iostream> // for std::cout
|
|
|
|
#include "logo_scaler.h" // for LogoScaler
|
|
#include "defines.h" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
|
|
|
|
// ============================================================================
|
|
// Destructor - Liberar las 4 texturas SDL
|
|
// ============================================================================
|
|
|
|
AppLogo::~AppLogo() {
|
|
if (logo1_base_texture_) {
|
|
SDL_DestroyTexture(logo1_base_texture_);
|
|
logo1_base_texture_ = nullptr;
|
|
}
|
|
if (logo1_native_texture_) {
|
|
SDL_DestroyTexture(logo1_native_texture_);
|
|
logo1_native_texture_ = nullptr;
|
|
}
|
|
if (logo2_base_texture_) {
|
|
SDL_DestroyTexture(logo2_base_texture_);
|
|
logo2_base_texture_ = nullptr;
|
|
}
|
|
if (logo2_native_texture_) {
|
|
SDL_DestroyTexture(logo2_native_texture_);
|
|
logo2_native_texture_ = nullptr;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Inicialización - Pre-escalar logos a 2 resoluciones (base y nativa)
|
|
// ============================================================================
|
|
|
|
bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) {
|
|
renderer_ = renderer;
|
|
base_screen_width_ = screen_width;
|
|
base_screen_height_ = screen_height;
|
|
screen_width_ = screen_width;
|
|
screen_height_ = screen_height;
|
|
|
|
std::string resources_dir = getResourcesDirectory();
|
|
|
|
// ========================================================================
|
|
// 1. Detectar resolución nativa del monitor
|
|
// ========================================================================
|
|
if (!LogoScaler::detectNativeResolution(native_screen_width_, native_screen_height_)) {
|
|
std::cout << "No se pudo detectar resolución nativa, usando solo base" << std::endl;
|
|
// Fallback: usar resolución base como nativa
|
|
native_screen_width_ = screen_width;
|
|
native_screen_height_ = screen_height;
|
|
}
|
|
|
|
// ========================================================================
|
|
// 2. Calcular alturas finales para ambas resoluciones
|
|
// ========================================================================
|
|
int logo_base_target_height = static_cast<int>(base_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
|
int logo_native_target_height = static_cast<int>(native_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
|
|
|
std::cout << "Pre-escalando logos:" << std::endl;
|
|
std::cout << " Base: " << base_screen_width_ << "x" << base_screen_height_
|
|
<< " (altura logo: " << logo_base_target_height << "px)" << std::endl;
|
|
std::cout << " Nativa: " << native_screen_width_ << "x" << native_screen_height_
|
|
<< " (altura logo: " << logo_native_target_height << "px)" << std::endl;
|
|
|
|
// ========================================================================
|
|
// 3. Cargar y escalar LOGO1 (data/logo/logo.png) a 2 versiones
|
|
// ========================================================================
|
|
std::string logo1_path = resources_dir + "/data/logo/logo.png";
|
|
|
|
// 3a. Versión BASE de logo1
|
|
unsigned char* logo1_base_data = LogoScaler::loadAndScale(
|
|
logo1_path,
|
|
0, // width calculado automáticamente por aspect ratio
|
|
logo_base_target_height,
|
|
logo1_base_width_,
|
|
logo1_base_height_
|
|
);
|
|
if (logo1_base_data == nullptr) {
|
|
std::cout << "Error: No se pudo escalar logo1 (base)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
logo1_base_texture_ = LogoScaler::createTextureFromBuffer(
|
|
renderer, logo1_base_data, logo1_base_width_, logo1_base_height_
|
|
);
|
|
free(logo1_base_data); // Liberar buffer temporal
|
|
|
|
if (logo1_base_texture_ == nullptr) {
|
|
std::cout << "Error: No se pudo crear textura logo1 (base)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Habilitar alpha blending
|
|
SDL_SetTextureBlendMode(logo1_base_texture_, SDL_BLENDMODE_BLEND);
|
|
|
|
// 3b. Versión NATIVA de logo1
|
|
unsigned char* logo1_native_data = LogoScaler::loadAndScale(
|
|
logo1_path,
|
|
0, // width calculado automáticamente
|
|
logo_native_target_height,
|
|
logo1_native_width_,
|
|
logo1_native_height_
|
|
);
|
|
if (logo1_native_data == nullptr) {
|
|
std::cout << "Error: No se pudo escalar logo1 (nativa)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
logo1_native_texture_ = LogoScaler::createTextureFromBuffer(
|
|
renderer, logo1_native_data, logo1_native_width_, logo1_native_height_
|
|
);
|
|
free(logo1_native_data);
|
|
|
|
if (logo1_native_texture_ == nullptr) {
|
|
std::cout << "Error: No se pudo crear textura logo1 (nativa)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
SDL_SetTextureBlendMode(logo1_native_texture_, SDL_BLENDMODE_BLEND);
|
|
|
|
// ========================================================================
|
|
// 4. Cargar y escalar LOGO2 (data/logo/logo2.png) a 2 versiones
|
|
// ========================================================================
|
|
std::string logo2_path = resources_dir + "/data/logo/logo2.png";
|
|
|
|
// 4a. Versión BASE de logo2
|
|
unsigned char* logo2_base_data = LogoScaler::loadAndScale(
|
|
logo2_path,
|
|
0,
|
|
logo_base_target_height,
|
|
logo2_base_width_,
|
|
logo2_base_height_
|
|
);
|
|
if (logo2_base_data == nullptr) {
|
|
std::cout << "Error: No se pudo escalar logo2 (base)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
logo2_base_texture_ = LogoScaler::createTextureFromBuffer(
|
|
renderer, logo2_base_data, logo2_base_width_, logo2_base_height_
|
|
);
|
|
free(logo2_base_data);
|
|
|
|
if (logo2_base_texture_ == nullptr) {
|
|
std::cout << "Error: No se pudo crear textura logo2 (base)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
SDL_SetTextureBlendMode(logo2_base_texture_, SDL_BLENDMODE_BLEND);
|
|
|
|
// 4b. Versión NATIVA de logo2
|
|
unsigned char* logo2_native_data = LogoScaler::loadAndScale(
|
|
logo2_path,
|
|
0,
|
|
logo_native_target_height,
|
|
logo2_native_width_,
|
|
logo2_native_height_
|
|
);
|
|
if (logo2_native_data == nullptr) {
|
|
std::cout << "Error: No se pudo escalar logo2 (nativa)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
logo2_native_texture_ = LogoScaler::createTextureFromBuffer(
|
|
renderer, logo2_native_data, logo2_native_width_, logo2_native_height_
|
|
);
|
|
free(logo2_native_data);
|
|
|
|
if (logo2_native_texture_ == nullptr) {
|
|
std::cout << "Error: No se pudo crear textura logo2 (nativa)" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
SDL_SetTextureBlendMode(logo2_native_texture_, SDL_BLENDMODE_BLEND);
|
|
|
|
// ========================================================================
|
|
// 5. Inicialmente usar texturas BASE (la resolución de inicio)
|
|
// ========================================================================
|
|
logo1_current_texture_ = logo1_base_texture_;
|
|
logo1_current_width_ = logo1_base_width_;
|
|
logo1_current_height_ = logo1_base_height_;
|
|
|
|
logo2_current_texture_ = logo2_base_texture_;
|
|
logo2_current_width_ = logo2_base_width_;
|
|
logo2_current_height_ = logo2_base_height_;
|
|
|
|
std::cout << "Logos pre-escalados exitosamente (4 texturas creadas)" << std::endl;
|
|
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;
|
|
logo1_alpha_ = 0;
|
|
logo2_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;
|
|
logo1_alpha_ = 0;
|
|
logo2_alpha_ = 0;
|
|
// Elegir UNA animación aleatoria (misma para ambos logos, misma entrada y salida)
|
|
current_animation_ = getRandomAnimation();
|
|
}
|
|
break;
|
|
|
|
case AppLogoState::FADE_IN:
|
|
// Fade in: alpha de 0 a 255, con Logo 2 retrasado 0.25s
|
|
{
|
|
// Calcular progreso de cada logo (Logo 2 con retraso)
|
|
float fade_progress_logo1 = timer_ / APPLOGO_FADE_DURATION;
|
|
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_FADE_DURATION);
|
|
|
|
// Verificar si fade in completado (cuando logo2 también termina)
|
|
if (fade_progress_logo2 >= 1.0f) {
|
|
// Fade in completado para ambos logos
|
|
state_ = AppLogoState::VISIBLE;
|
|
timer_ = 0.0f;
|
|
logo1_alpha_ = 255;
|
|
logo2_alpha_ = 255;
|
|
// 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 con retraso + easing para suavidad
|
|
float eased_prog1 = easeInOutQuad(std::min(1.0f, fade_progress_logo1));
|
|
float eased_prog2 = easeInOutQuad(std::min(1.0f, fade_progress_logo2));
|
|
logo1_alpha_ = static_cast<int>(eased_prog1 * 255.0f);
|
|
logo2_alpha_ = static_cast<int>(eased_prog2 * 255.0f);
|
|
|
|
// ================================================================
|
|
// Aplicar MISMA animación (current_animation_) a ambos logos
|
|
// con sus respectivos progresos
|
|
// ================================================================
|
|
switch (current_animation_) {
|
|
case AppLogoAnimationType::ZOOM_ONLY:
|
|
logo1_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
|
logo1_squash_y_ = 1.0f;
|
|
logo1_stretch_x_ = 1.0f;
|
|
logo1_rotation_ = 0.0f;
|
|
|
|
logo2_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
|
logo2_squash_y_ = 1.0f;
|
|
logo2_stretch_x_ = 1.0f;
|
|
logo2_rotation_ = 0.0f;
|
|
break;
|
|
|
|
case AppLogoAnimationType::ELASTIC_STICK:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
float elastic_t1 = easeOutElastic(prog1);
|
|
logo1_scale_ = 1.2f - (elastic_t1 * 0.2f);
|
|
float squash_t1 = easeOutBack(prog1);
|
|
logo1_squash_y_ = 0.6f + (squash_t1 * 0.4f);
|
|
logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f;
|
|
logo1_rotation_ = 0.0f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
float elastic_t2 = easeOutElastic(prog2);
|
|
logo2_scale_ = 1.2f - (elastic_t2 * 0.2f);
|
|
float squash_t2 = easeOutBack(prog2);
|
|
logo2_squash_y_ = 0.6f + (squash_t2 * 0.4f);
|
|
logo2_stretch_x_ = 1.0f + (1.0f - logo2_squash_y_) * 0.5f;
|
|
logo2_rotation_ = 0.0f;
|
|
}
|
|
break;
|
|
|
|
case AppLogoAnimationType::ROTATE_SPIRAL:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
float ease_t1 = easeInOutQuad(prog1);
|
|
logo1_scale_ = 0.3f + (ease_t1 * 0.7f);
|
|
logo1_rotation_ = (1.0f - prog1) * 6.28f;
|
|
logo1_squash_y_ = 1.0f;
|
|
logo1_stretch_x_ = 1.0f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
float ease_t2 = easeInOutQuad(prog2);
|
|
logo2_scale_ = 0.3f + (ease_t2 * 0.7f);
|
|
logo2_rotation_ = (1.0f - prog2) * 6.28f;
|
|
logo2_squash_y_ = 1.0f;
|
|
logo2_stretch_x_ = 1.0f;
|
|
}
|
|
break;
|
|
|
|
case AppLogoAnimationType::BOUNCE_SQUASH:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
float bounce_t1 = easeOutBounce(prog1);
|
|
logo1_scale_ = 1.0f;
|
|
float squash_amount1 = (1.0f - bounce_t1) * 0.3f;
|
|
logo1_squash_y_ = 1.0f - squash_amount1;
|
|
logo1_stretch_x_ = 1.0f + squash_amount1 * 0.5f;
|
|
logo1_rotation_ = 0.0f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
float bounce_t2 = easeOutBounce(prog2);
|
|
logo2_scale_ = 1.0f;
|
|
float squash_amount2 = (1.0f - bounce_t2) * 0.3f;
|
|
logo2_squash_y_ = 1.0f - squash_amount2;
|
|
logo2_stretch_x_ = 1.0f + squash_amount2 * 0.5f;
|
|
logo2_rotation_ = 0.0f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AppLogoState::VISIBLE:
|
|
// Logo completamente visible, esperando duración
|
|
if (timer_ >= APPLOGO_DISPLAY_DURATION) {
|
|
state_ = AppLogoState::FADE_OUT;
|
|
timer_ = 0.0f;
|
|
logo1_alpha_ = 255;
|
|
logo2_alpha_ = 255;
|
|
// NO elegir nueva animación - reutilizar current_animation_ (simetría entrada/salida)
|
|
}
|
|
break;
|
|
|
|
case AppLogoState::FADE_OUT:
|
|
// Fade out: alpha de 255 a 0, con Logo 2 retrasado 0.25s (misma animación que entrada)
|
|
{
|
|
// Calcular progreso de cada logo (Logo 2 con retraso)
|
|
float fade_progress_logo1 = timer_ / APPLOGO_FADE_DURATION;
|
|
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_FADE_DURATION);
|
|
|
|
// Verificar si fade out completado (cuando logo2 también termina)
|
|
if (fade_progress_logo2 >= 1.0f) {
|
|
// Fade out completado, volver a HIDDEN
|
|
state_ = AppLogoState::HIDDEN;
|
|
timer_ = 0.0f;
|
|
logo1_alpha_ = 0;
|
|
logo2_alpha_ = 0;
|
|
// 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 con retraso + easing para suavidad (255 → 0)
|
|
float eased_prog1 = easeInOutQuad(std::min(1.0f, fade_progress_logo1));
|
|
float eased_prog2 = easeInOutQuad(std::min(1.0f, fade_progress_logo2));
|
|
logo1_alpha_ = static_cast<int>((1.0f - eased_prog1) * 255.0f);
|
|
logo2_alpha_ = static_cast<int>((1.0f - eased_prog2) * 255.0f);
|
|
|
|
// ================================================================
|
|
// Aplicar MISMA animación (current_animation_) de forma invertida
|
|
// ================================================================
|
|
switch (current_animation_) {
|
|
case AppLogoAnimationType::ZOOM_ONLY:
|
|
logo1_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
|
logo1_squash_y_ = 1.0f;
|
|
logo1_stretch_x_ = 1.0f;
|
|
logo1_rotation_ = 0.0f;
|
|
|
|
logo2_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
|
logo2_squash_y_ = 1.0f;
|
|
logo2_stretch_x_ = 1.0f;
|
|
logo2_rotation_ = 0.0f;
|
|
break;
|
|
|
|
case AppLogoAnimationType::ELASTIC_STICK:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
logo1_scale_ = 1.0f + (prog1 * prog1 * 0.2f);
|
|
logo1_squash_y_ = 1.0f + (prog1 * 0.3f);
|
|
logo1_stretch_x_ = 1.0f - (prog1 * 0.2f);
|
|
logo1_rotation_ = prog1 * 0.1f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
logo2_scale_ = 1.0f + (prog2 * prog2 * 0.2f);
|
|
logo2_squash_y_ = 1.0f + (prog2 * 0.3f);
|
|
logo2_stretch_x_ = 1.0f - (prog2 * 0.2f);
|
|
logo2_rotation_ = prog2 * 0.1f;
|
|
}
|
|
break;
|
|
|
|
case AppLogoAnimationType::ROTATE_SPIRAL:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
float ease_t1 = easeInOutQuad(prog1);
|
|
logo1_scale_ = 1.0f - (ease_t1 * 0.7f);
|
|
logo1_rotation_ = prog1 * 6.28f;
|
|
logo1_squash_y_ = 1.0f;
|
|
logo1_stretch_x_ = 1.0f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
float ease_t2 = easeInOutQuad(prog2);
|
|
logo2_scale_ = 1.0f - (ease_t2 * 0.7f);
|
|
logo2_rotation_ = prog2 * 6.28f;
|
|
logo2_squash_y_ = 1.0f;
|
|
logo2_stretch_x_ = 1.0f;
|
|
}
|
|
break;
|
|
|
|
case AppLogoAnimationType::BOUNCE_SQUASH:
|
|
{
|
|
float prog1 = std::min(1.0f, fade_progress_logo1);
|
|
if (prog1 < 0.2f) {
|
|
float squash_t = prog1 / 0.2f;
|
|
logo1_squash_y_ = 1.0f - (squash_t * 0.3f);
|
|
logo1_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
|
} else {
|
|
float jump_t = (prog1 - 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 + (prog1 * 0.3f);
|
|
logo1_rotation_ = 0.0f;
|
|
|
|
float prog2 = std::min(1.0f, fade_progress_logo2);
|
|
if (prog2 < 0.2f) {
|
|
float squash_t = prog2 / 0.2f;
|
|
logo2_squash_y_ = 1.0f - (squash_t * 0.3f);
|
|
logo2_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
|
} else {
|
|
float jump_t = (prog2 - 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 + (prog2 * 0.3f);
|
|
logo2_rotation_ = 0.0f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AppLogo::render() {
|
|
// Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT)
|
|
if (state_ != AppLogoState::HIDDEN) {
|
|
// Renderizar LOGO1 primero (fondo), luego LOGO2 (encima)
|
|
renderWithGeometry(1);
|
|
renderWithGeometry(2);
|
|
}
|
|
}
|
|
|
|
void AppLogo::updateScreenSize(int screen_width, int screen_height) {
|
|
screen_width_ = screen_width;
|
|
screen_height_ = screen_height;
|
|
|
|
// ========================================================================
|
|
// Detectar si coincide con resolución nativa o base, cambiar texturas
|
|
// ========================================================================
|
|
bool is_native = (screen_width == native_screen_width_ && screen_height == native_screen_height_);
|
|
|
|
if (is_native) {
|
|
// Cambiar a texturas nativas (F4 fullscreen)
|
|
logo1_current_texture_ = logo1_native_texture_;
|
|
logo1_current_width_ = logo1_native_width_;
|
|
logo1_current_height_ = logo1_native_height_;
|
|
|
|
logo2_current_texture_ = logo2_native_texture_;
|
|
logo2_current_width_ = logo2_native_width_;
|
|
logo2_current_height_ = logo2_native_height_;
|
|
|
|
std::cout << "AppLogo: Cambiado a texturas NATIVAS" << std::endl;
|
|
} else {
|
|
// Cambiar a texturas base (ventana redimensionable)
|
|
logo1_current_texture_ = logo1_base_texture_;
|
|
logo1_current_width_ = logo1_base_width_;
|
|
logo1_current_height_ = logo1_base_height_;
|
|
|
|
logo2_current_texture_ = logo2_base_texture_;
|
|
logo2_current_width_ = logo2_base_width_;
|
|
logo2_current_height_ = logo2_base_height_;
|
|
|
|
std::cout << "AppLogo: Cambiado a texturas BASE" << std::endl;
|
|
}
|
|
|
|
// Nota: No es necesario recalcular escalas porque las texturas están pre-escaladas
|
|
// al tamaño exacto de pantalla. Solo renderizamos al 100% (o con deformaciones de animación).
|
|
}
|
|
|
|
// ============================================================================
|
|
// Funciones de easing para animaciones
|
|
// ============================================================================
|
|
|
|
float AppLogo::easeOutElastic(float t) {
|
|
// Elastic easing out: bounce elástico al final
|
|
const float c4 = (2.0f * 3.14159f) / 3.0f;
|
|
|
|
if (t == 0.0f) return 0.0f;
|
|
if (t == 1.0f) return 1.0f;
|
|
|
|
return powf(2.0f, -10.0f * t) * sinf((t * 10.0f - 0.75f) * c4) + 1.0f;
|
|
}
|
|
|
|
float AppLogo::easeOutBack(float t) {
|
|
// Back easing out: overshoot suave al final
|
|
const float c1 = 1.70158f;
|
|
const float c3 = c1 + 1.0f;
|
|
|
|
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 (para todos los logos, con deformaciones)
|
|
// ============================================================================
|
|
|
|
void AppLogo::renderWithGeometry(int logo_index) {
|
|
if (!renderer_) return;
|
|
|
|
// Seleccionar variables según el logo_index (1 = logo1, 2 = logo2)
|
|
SDL_Texture* texture;
|
|
int base_width, base_height;
|
|
float scale, squash_y, stretch_x, rotation;
|
|
|
|
if (logo_index == 1) {
|
|
if (!logo1_current_texture_) return;
|
|
texture = logo1_current_texture_;
|
|
base_width = logo1_current_width_;
|
|
base_height = logo1_current_height_;
|
|
scale = logo1_scale_;
|
|
squash_y = logo1_squash_y_;
|
|
stretch_x = logo1_stretch_x_;
|
|
rotation = logo1_rotation_;
|
|
} else if (logo_index == 2) {
|
|
if (!logo2_current_texture_) return;
|
|
texture = logo2_current_texture_;
|
|
base_width = logo2_current_width_;
|
|
base_height = logo2_current_height_;
|
|
scale = logo2_scale_;
|
|
squash_y = logo2_squash_y_;
|
|
stretch_x = logo2_stretch_x_;
|
|
rotation = logo2_rotation_;
|
|
} else {
|
|
return; // Índice inválido
|
|
}
|
|
|
|
// Aplicar alpha específico de cada logo (con retraso para logo2)
|
|
int alpha = (logo_index == 1) ? logo1_alpha_ : logo2_alpha_;
|
|
SDL_SetTextureAlphaMod(texture, static_cast<Uint8>(alpha));
|
|
|
|
// Calcular tamaño con escala y deformaciones aplicadas
|
|
// (base_width y base_height ya están pre-escalados al tamaño correcto de pantalla)
|
|
float width = base_width * scale * stretch_x;
|
|
float height = base_height * scale * squash_y;
|
|
|
|
// Calcular padding desde bordes derecho e inferior
|
|
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
|
float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT;
|
|
|
|
// 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);
|
|
|
|
// Crear 4 vértices del quad (centrado en center_x, center_y)
|
|
SDL_Vertex vertices[4];
|
|
|
|
// Offset desde el centro
|
|
float half_w = width / 2.0f;
|
|
float half_h = height / 2.0f;
|
|
|
|
// Vértice superior izquierdo (rotado)
|
|
{
|
|
float local_x = -half_w;
|
|
float local_y = -half_h;
|
|
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
|
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
|
vertices[0].position = {center_x + rotated_x, center_y + rotated_y};
|
|
vertices[0].tex_coord = {0.0f, 0.0f};
|
|
vertices[0].color = {1.0f, 1.0f, 1.0f, 1.0f}; // Color blanco (textura se modula con alpha)
|
|
}
|
|
|
|
// Vértice superior derecho (rotado)
|
|
{
|
|
float local_x = half_w;
|
|
float local_y = -half_h;
|
|
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
|
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
|
vertices[1].position = {center_x + rotated_x, center_y + rotated_y};
|
|
vertices[1].tex_coord = {1.0f, 0.0f};
|
|
vertices[1].color = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
}
|
|
|
|
// Vértice inferior derecho (rotado)
|
|
{
|
|
float local_x = half_w;
|
|
float local_y = half_h;
|
|
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
|
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
|
vertices[2].position = {center_x + rotated_x, center_y + rotated_y};
|
|
vertices[2].tex_coord = {1.0f, 1.0f};
|
|
vertices[2].color = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
}
|
|
|
|
// Vértice inferior izquierdo (rotado)
|
|
{
|
|
float local_x = -half_w;
|
|
float local_y = half_h;
|
|
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
|
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
|
vertices[3].position = {center_x + rotated_x, center_y + rotated_y};
|
|
vertices[3].tex_coord = {0.0f, 1.0f};
|
|
vertices[3].color = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
}
|
|
|
|
// Índices para 2 triángulos
|
|
int indices[6] = {0, 1, 2, 2, 3, 0};
|
|
|
|
// Renderizar con la textura del logo correspondiente
|
|
SDL_RenderGeometry(renderer_, texture, vertices, 4, indices, 6);
|
|
}
|