Implementación de sistema de 2 logos superpuestos con animaciones completamente independientes: **Nuevas características:** - Dos logos superpuestos (logo1.png + logo2.png) con animaciones independientes - 4 tipos de animación: ZOOM_ONLY, ELASTIC_STICK, ROTATE_SPIRAL, BOUNCE_SQUASH - Aleatorización independiente para entrada y salida de cada logo - 256 combinaciones posibles (4×4 entrada × 4×4 salida) **Ajuste de tamaño y posición:** - Nueva constante APPLOGO_HEIGHT_PERCENT (40%) - altura del logo respecto a pantalla - Nueva constante APPLOGO_PADDING_PERCENT (10%) - padding desde esquina inferior-derecha - Logo anclado a esquina en lugar de centrado en cuadrante - Valores fácilmente ajustables mediante constantes en defines.h **Cambios técnicos:** - Variables duplicadas para logo1 y logo2 (scale, squash, stretch, rotation) - Variables compartidas para sincronización (state, timer, alpha) - renderWithGeometry() acepta parámetro logo_index (1 o 2) - Logo1 renderizado primero (fondo), Logo2 encima (overlay) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
636 lines
27 KiB
C++
636 lines
27 KiB
C++
#include "app_logo.h"
|
|
|
|
#include <SDL3/SDL_render.h> // for SDL_SCALEMODE_LINEAR, SDL_RenderGeometry
|
|
#include <cmath> // for powf, sinf, cosf
|
|
|
|
#include "external/sprite.h" // for Sprite
|
|
#include "external/texture.h" // for Texture
|
|
|
|
bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) {
|
|
renderer_ = renderer;
|
|
screen_width_ = screen_width;
|
|
screen_height_ = screen_height;
|
|
|
|
std::string resources_dir = getResourcesDirectory();
|
|
|
|
// ========================================================================
|
|
// Cargar LOGO1 desde data/logo/logo.png
|
|
// ========================================================================
|
|
std::string logo1_path = resources_dir + "/data/logo/logo.png";
|
|
logo1_texture_ = std::make_shared<Texture>(renderer, logo1_path);
|
|
if (logo1_texture_->getWidth() == 0 || logo1_texture_->getHeight() == 0) {
|
|
// Error al cargar textura logo1
|
|
return false;
|
|
}
|
|
|
|
// Configurar filtrado LINEAR para suavizado
|
|
logo1_texture_->setScaleMode(SDL_SCALEMODE_LINEAR);
|
|
|
|
// Crear sprite con la textura
|
|
logo1_sprite_ = std::make_unique<Sprite>(logo1_texture_);
|
|
|
|
// Configurar el clip para que use toda la textura
|
|
float logo1_width = static_cast<float>(logo1_texture_->getWidth());
|
|
float logo1_height = static_cast<float>(logo1_texture_->getHeight());
|
|
logo1_sprite_->setClip({0.0f, 0.0f, logo1_width, logo1_height});
|
|
|
|
// ========================================================================
|
|
// Cargar LOGO2 desde data/logo/logo2.png
|
|
// ========================================================================
|
|
std::string logo2_path = resources_dir + "/data/logo/logo2.png";
|
|
logo2_texture_ = std::make_shared<Texture>(renderer, logo2_path);
|
|
if (logo2_texture_->getWidth() == 0 || logo2_texture_->getHeight() == 0) {
|
|
// Error al cargar textura logo2
|
|
return false;
|
|
}
|
|
|
|
// Configurar filtrado LINEAR para suavizado
|
|
logo2_texture_->setScaleMode(SDL_SCALEMODE_LINEAR);
|
|
|
|
// Crear sprite con la textura
|
|
logo2_sprite_ = std::make_unique<Sprite>(logo2_texture_);
|
|
|
|
// Configurar el clip para que use toda la textura
|
|
float logo2_width = static_cast<float>(logo2_texture_->getWidth());
|
|
float logo2_height = static_cast<float>(logo2_texture_->getHeight());
|
|
logo2_sprite_->setClip({0.0f, 0.0f, logo2_width, logo2_height});
|
|
|
|
// ========================================================================
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
// 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, animaciones independientes para logo1 y logo2
|
|
{
|
|
float fade_progress = timer_ / APPLOGO_FADE_DURATION;
|
|
if (fade_progress >= 1.0f) {
|
|
// Fade in completado
|
|
state_ = AppLogoState::VISIBLE;
|
|
timer_ = 0.0f;
|
|
current_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 linealmente (0 → 255) - compartido
|
|
current_alpha_ = static_cast<int>(fade_progress * 255.0f);
|
|
|
|
// ================================================================
|
|
// 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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
// 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, animaciones independientes para logo1 y logo2
|
|
{
|
|
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;
|
|
// 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) - compartido
|
|
current_alpha_ = static_cast<int>((1.0f - fade_progress) * 255.0f);
|
|
|
|
// ================================================================
|
|
// 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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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 a ambos logos (compartido - sincronizado)
|
|
if (logo1_texture_) {
|
|
logo1_texture_->setAlpha(current_alpha_);
|
|
}
|
|
if (logo2_texture_) {
|
|
logo2_texture_->setAlpha(current_alpha_);
|
|
}
|
|
|
|
// 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) {
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppLogo::updateScreenSize(int screen_width, int screen_height) {
|
|
screen_width_ = screen_width;
|
|
screen_height_ = screen_height;
|
|
|
|
// 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<float>(logo1_texture_->getWidth());
|
|
float logo_height = static_cast<float>(logo1_texture_->getHeight());
|
|
|
|
// 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_ = target_height; // = logo_height * scale
|
|
|
|
// 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);
|
|
}
|
|
|
|
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() {
|
|
// Calcular padding desde bordes derecho e inferior
|
|
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
|
float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT;
|
|
|
|
// 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});
|
|
}
|
|
|
|
// 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
|
|
// ============================================================================
|
|
|
|
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 deformada (para animación ELASTIC_STICK)
|
|
// ============================================================================
|
|
|
|
void AppLogo::renderWithGeometry(int logo_index) {
|
|
if (!renderer_) return;
|
|
|
|
// Seleccionar variables según el logo_index (1 = logo1, 2 = logo2)
|
|
std::shared_ptr<Texture> 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_ * 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->getSDLTexture(), vertices, 4, indices, 6);
|
|
}
|