Compare commits

5 Commits

Author SHA1 Message Date
db8acf0331 clean: Eliminar logging debug + fix: Centro fijo para animación ZOOM
**1. Eliminado logging de debug del FADE_OUT:**
- Removido log de timer/delta_time/progress (FADE_OUT inicial)
- Removido log de alpha1/alpha2
- Removido log de animaciones (ZOOM, ELASTIC, SPIRAL, BOUNCE)
- Removido log de completado de FADE_OUT
- Consola limpia en modo producción

**2. Fix centro del logo en animación ZOOM_ONLY:**

**Problema:**
- Centro del logo se calculaba basándose en width/height escalados
- Cuando scale cambiaba (1.2 → 1.0), corner_x/corner_y se movían
- Resultado: logo se desplazaba lateralmente durante zoom

**Solución:**
- Calcular esquina BASE (sin escala): corner_x_base, corner_y_base
- Calcular centro FIJO basándose en base_width/base_height
- Calcular width/height escalados DESPUÉS (solo para vértices)
- Resultado: centro permanece fijo, zoom crece/decrece alrededor del centro

**Archivos modificados:**
- source/app_logo.cpp:
  - Líneas 343-347: Eliminado log FADE_OUT inicial
  - Línea 347: Eliminado log completado
  - Líneas 365-366: Eliminado log alphas
  - Líneas 381-383: Eliminado log ZOOM
  - Líneas 396-398: Eliminado log ELASTIC
  - Líneas 414-417: Eliminado log SPIRAL
  - Líneas 444-446: Eliminado log BOUNCE
  - Líneas 609-625: Reordenado cálculo de centro (FIJO) y tamaño (ESCALADO)

**Resultado esperado:**
- Sin spam en consola
- Animación ZOOM perfectamente centrada en esquina inferior derecha
- Logo crece/decrece sin desplazamiento lateral

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:40:53 +02:00
5a35cc1abf fix: Aplicar alpha del logo a través de vértices en lugar de textura
**Problema:**
- SDL_SetTextureAlphaMod() no funciona correctamente con SDL_RenderGeometry()
- El alpha de los vértices (hardcodeado a 1.0) overrideaba el alpha de textura
- Resultado: fade invisible o instantáneo a pesar de valores correctos

**Solución:**
- Eliminar SDL_SetTextureAlphaMod()
- Convertir alpha de 0-255 a 0.0-1.0 (alpha_normalized)
- Aplicar alpha_normalized directamente al canal alpha de los 4 vértices
- Ahora SDL_RenderGeometry respeta el fade correctamente

**Archivos modificados:**
- source/app_logo.cpp:
  - Línea 630: Crear alpha_normalized en lugar de SetTextureAlphaMod
  - Líneas 669, 680, 691, 702: Aplicar alpha_normalized a vértices

**Resultado esperado:**
- Fade visible y suave durante 2 segundos completos
- Logo 2 con retraso de 0.25s como esperado
- Sincronización perfecta entre animación y fade

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:31:28 +02:00
d30a4fd440 debug: Agregar logging detallado a FADE_OUT de AppLogo
- Log de timer_, delta_time, progress en cada frame
- Log de alpha1/alpha2 calculados
- Log de valores de animación por tipo (ZOOM/ELASTIC/SPIRAL/BOUNCE)
- Log de ease_t1 en ROTATE_SPIRAL para diagnosticar desincronización
- Log cuando FADE_OUT se completa

Propósito: Diagnosticar por qué el fade parece instantáneo
y desincronizado con la animación (serie en lugar de paralelo).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:26:14 +02:00
97c0683f6e fix: Restaurar fade lineal del alpha + renombrar constante
Restaura fade lineal para el alpha de los logos, eliminando el easing
que hacía la transición casi imperceptible. Renombra constante para
mayor claridad sobre lo que controla.

Problema identificado:
- El alpha usaba easeInOutQuad, acelerando la transición en el medio
- Con 2 segundos, el easing hacía que el logo pareciera aparecer/
  desaparecer instantáneamente sin fade visible
- El usuario reportó "el logo termina y desaparece directamente"

Solución implementada:

1. **Fade lineal restaurado**:
   - FADE_IN: alpha aumenta linealmente 0→255
   - FADE_OUT: alpha disminuye linealmente 255→0
   - Progreso visible y constante durante toda la duración

2. **Constante renombrada**:
   - `APPLOGO_FADE_DURATION` → `APPLOGO_ANIMATION_DURATION`
   - Nombre más claro: controla duración de toda la animación
   - Actualizado valor a 2.0 segundos (configurable por usuario)

3. **Animaciones mantienen easing**:
   - Zoom, rotación, squash, etc. siguen usando sus easings
   - Solo el alpha es lineal para fade visible

Confirmaciones:
 Sistema time-based: usa delta_time correctamente
 Blend mode configurado: SDL_BLENDMODE_BLEND en todas las texturas
 Alpha se aplica: SDL_SetTextureAlphaMod en renderizado

Resultado con APPLOGO_ANIMATION_DURATION = 2.0s:
- t=0.0s → Alpha=0 (invisible)
- t=0.5s → Alpha=64 (25% visible)
- t=1.0s → Alpha=127 (50% visible)
- t=1.5s → Alpha=191 (75% visible)
- t=2.0s → Alpha=255 (100% visible)

Nota: El logo solo se muestra en modos DEMO/DEMO_LITE/LOGO,
no en SANDBOX. Para probar: ./vibe3_physics --mode demo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:16:33 +02:00
c3d24cc07d feat: Argumentos CLI para establecer AppMode inicial
Permite arrancar directamente en modo DEMO, DEMO_LITE, LOGO o SANDBOX
mediante argumentos de línea de comandos, eliminando necesidad de
cambiar manualmente el modo después del arranque.

Nuevos argumentos:
- `-m, --mode <mode>` - Establece modo inicial
  - `sandbox` - Control manual (default)
  - `demo` - Auto-play completo (figuras + temas + colisiones)
  - `demo-lite` - Auto-play simple (solo física/figuras)
  - `logo` - Modo logo (easter egg con convergencia)

Ejemplos de uso:
```bash
# Arrancar en modo DEMO
./vibe3_physics --mode demo
./vibe3_physics -m demo

# Arrancar en DEMO_LITE (solo física)
./vibe3_physics -m demo-lite

# Arrancar directo en LOGO
./vibe3_physics --mode logo

# Combinar con otros argumentos
./vibe3_physics -w 1920 -h 1080 --mode demo
./vibe3_physics -F -m demo-lite  # Fullscreen + DEMO_LITE
```

Implementación:
1. main.cpp: Parsing de argumento --mode con validación
2. engine.h: Nuevo parámetro `initial_mode` en initialize()
3. engine.cpp: Aplicación del modo vía StateManager::setState()

Si no se especifica --mode, se usa SANDBOX (comportamiento actual).
El modo se aplica después de inicializar StateManager, garantizando
que todos los componentes estén listos antes del cambio de estado.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:00:16 +02:00
6 changed files with 69 additions and 40 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -222,8 +222,8 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
// 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);
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
// Verificar si fade in completado (cuando logo2 también termina)
if (fade_progress_logo2 >= 1.0f) {
@@ -242,11 +242,9 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
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);
// Interpolar alpha con retraso de forma LINEAL (sin easing)
logo1_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo1) * 255.0f);
logo2_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo2) * 255.0f);
// ================================================================
// Aplicar MISMA animación (current_animation_) a ambos logos
@@ -342,8 +340,8 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
// 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);
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
// Verificar si fade out completado (cuando logo2 también termina)
if (fade_progress_logo2 >= 1.0f) {
@@ -362,11 +360,9 @@ void AppLogo::update(float delta_time, AppMode current_mode) {
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);
// Interpolar alpha con retraso de forma LINEAL (255 → 0, sin easing)
logo1_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo1)) * 255.0f);
logo2_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo2)) * 255.0f);
// ================================================================
// Aplicar MISMA animación (current_animation_) de forma invertida
@@ -607,24 +603,26 @@ void AppLogo::renderWithGeometry(int logo_index) {
// 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;
float alpha_normalized = static_cast<float>(alpha) / 255.0f; // Convertir 0-255 → 0.0-1.0
// NO usar SDL_SetTextureAlphaMod - aplicar alpha directamente a vértices
// 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;
// Calcular esquina BASE (sin escala) para obtener centro FIJO
// Esto asegura que el centro no se mueva cuando cambia scale/squash/stretch
float corner_x_base = screen_width_ - base_width - padding_x;
float corner_y_base = screen_height_ - base_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);
// Centro FIJO del logo (no cambia con scale/squash/stretch)
float center_x = corner_x_base + (base_width / 2.0f);
float center_y = corner_y_base + (base_height / 2.0f);
// Calcular tamaño ESCALADO (para vértices)
// (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;
// Pre-calcular seno y coseno de rotación
float cos_rot = cosf(rotation);
@@ -645,7 +643,7 @@ void AppLogo::renderWithGeometry(int logo_index) {
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)
vertices[0].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
}
// Vértice superior derecho (rotado)
@@ -656,7 +654,7 @@ void AppLogo::renderWithGeometry(int logo_index) {
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};
vertices[1].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
}
// Vértice inferior derecho (rotado)
@@ -667,7 +665,7 @@ void AppLogo::renderWithGeometry(int logo_index) {
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};
vertices[2].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
}
// Vértice inferior izquierdo (rotado)
@@ -678,7 +676,7 @@ void AppLogo::renderWithGeometry(int logo_index) {
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};
vertices[3].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
}
// Índices para 2 triángulos

View File

@@ -289,12 +289,12 @@ 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_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%
constexpr float APPLOGO_LOGO2_DELAY = 0.25f; // Retraso de Logo 2 respecto a Logo 1 (segundos)
constexpr float APPLOGO_DISPLAY_INTERVAL = 2.0f; // Intervalo entre apariciones del logo (segundos)
constexpr float APPLOGO_DISPLAY_DURATION = 4.0f; // Duración de visibilidad del logo (segundos)
constexpr float APPLOGO_ANIMATION_DURATION = 0.5f; // Duración de animación entrada/salida (segundos)
constexpr float APPLOGO_HEIGHT_PERCENT = 0.4f; // Altura del logo = 40% de la altura de pantalla
constexpr float APPLOGO_PADDING_PERCENT = 0.05f; // Padding desde esquina inferior-derecha = 10%
constexpr float APPLOGO_LOGO2_DELAY = 0.25f; // Retraso de Logo 2 respecto a Logo 1 (segundos)
// Configuración de Modo BOIDS (comportamiento de enjambre)
// TIME-BASED CONVERSION (frame-based → time-based):

View File

@@ -37,7 +37,7 @@
// getExecutableDirectory() ya está definido en defines.h como inline
// Implementación de métodos públicos
bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMode initial_mode) {
bool success = true;
// Obtener resolución de pantalla para validación
@@ -246,6 +246,11 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
state_manager_ = std::make_unique<StateManager>();
state_manager_->initialize(this); // Callback al Engine
// Establecer modo inicial si no es SANDBOX (default)
if (initial_mode != AppMode::SANDBOX) {
state_manager_->setState(initial_mode, current_screen_width_, current_screen_height_);
}
// Actualizar ShapeManager con StateManager (dependencia circular - StateManager debe existir primero)
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
current_screen_width_, current_screen_height_);

View File

@@ -26,7 +26,7 @@
class Engine {
public:
// Interfaz pública principal
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false);
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
void run();
void shutdown();

View File

@@ -1,6 +1,8 @@
#include <iostream>
#include <cstring>
#include <string>
#include "engine.h"
#include "defines.h"
// getExecutableDirectory() ya está definido en defines.h como inline
@@ -13,13 +15,17 @@ void printHelp() {
std::cout << " -z, --zoom <n> Zoom de ventana (default: 3)\n";
std::cout << " -f, --fullscreen Modo pantalla completa (F3 - letterbox)\n";
std::cout << " -F, --real-fullscreen Modo pantalla completa real (F4 - nativo)\n";
std::cout << " -m, --mode <mode> Modo inicial: sandbox, demo, demo-lite, logo (default: sandbox)\n";
std::cout << " --help Mostrar esta ayuda\n\n";
std::cout << "Ejemplos:\n";
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 (auto)\n";
std::cout << " vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n";
std::cout << " vibe3_physics -f # Fullscreen letterbox (F3)\n";
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n\n";
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n";
std::cout << " vibe3_physics --mode demo # Arrancar en modo DEMO (auto-play)\n";
std::cout << " vibe3_physics -m demo-lite # Arrancar en modo DEMO_LITE (solo física)\n";
std::cout << " vibe3_physics -F --mode logo # Fullscreen + modo LOGO (easter egg)\n\n";
std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n";
}
@@ -29,6 +35,7 @@ int main(int argc, char* argv[]) {
int zoom = 0;
bool fullscreen = false;
bool real_fullscreen = false;
AppMode initial_mode = AppMode::SANDBOX; // Modo inicial (default: SANDBOX)
// Parsear argumentos
for (int i = 1; i < argc; i++) {
@@ -72,6 +79,25 @@ int main(int argc, char* argv[]) {
fullscreen = true;
} else if (strcmp(argv[i], "-F") == 0 || strcmp(argv[i], "--real-fullscreen") == 0) {
real_fullscreen = true;
} else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--mode") == 0) {
if (i + 1 < argc) {
std::string mode_str = argv[++i];
if (mode_str == "sandbox") {
initial_mode = AppMode::SANDBOX;
} else if (mode_str == "demo") {
initial_mode = AppMode::DEMO;
} else if (mode_str == "demo-lite") {
initial_mode = AppMode::DEMO_LITE;
} else if (mode_str == "logo") {
initial_mode = AppMode::LOGO;
} else {
std::cerr << "Error: Modo '" << mode_str << "' no válido. Usa: sandbox, demo, demo-lite, logo\n";
return -1;
}
} else {
std::cerr << "Error: -m/--mode requiere un valor\n";
return -1;
}
} else {
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
printHelp();
@@ -86,7 +112,7 @@ int main(int argc, char* argv[]) {
Engine engine;
if (!engine.initialize(width, height, zoom, fullscreen)) {
if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) {
std::cout << "¡Error al inicializar el engine!" << std::endl;
return -1;
}