Compare commits
9 Commits
c4ca49b006
...
f0baa51415
| Author | SHA1 | Date | |
|---|---|---|---|
| f0baa51415 | |||
| db3d4d6630 | |||
| d030d4270e | |||
| fbd09b3201 | |||
| a04c1cba13 | |||
| 757bb9c525 | |||
| 723bb6d198 | |||
| 1e5c9f8f9d | |||
| e24f06ed90 |
91
ROADMAP.md
91
ROADMAP.md
@@ -30,33 +30,24 @@
|
|||||||
|
|
||||||
## Mejoras de Presentación 🎨
|
## Mejoras de Presentación 🎨
|
||||||
|
|
||||||
### 1. ⏳ Mejorar Animaciones de Figuras 3D
|
### 1. ✅ Mejorar Animaciones de Figuras 3D
|
||||||
**Descripción:** Añadir movimientos más dinámicos e interesantes a algunas figuras
|
**Descripción:** Añadir movimientos más dinámicos e interesantes a algunas figuras
|
||||||
**Prioridad:** Media
|
**Prioridad:** Media
|
||||||
**Estimación:** 1.5 horas
|
**Estado:** ✅ COMPLETADO
|
||||||
**Detalles:**
|
**Detalles:**
|
||||||
|
|
||||||
#### CYLINDER (Y):
|
#### CYLINDER (Y):
|
||||||
- **Rotación actual:** Solo eje Y (spin horizontal continuo)
|
- ✅ **Rotación principal en eje Y** (spin horizontal continuo)
|
||||||
- **Mejora propuesta:** Rotaciones multi-eje con cambios periódicos
|
- ✅ **Tumbling ocasional en eje X** cada 3-5 segundos
|
||||||
- Rotación principal en eje Y (como ahora)
|
- ✅ Transiciones suaves con ease-in-out (1.5s duración)
|
||||||
- Cada 3-5 segundos: tumbling en eje X o Z durante 1-2 segundos
|
- ✅ Efecto visual: cilindro "se da una vuelta" ocasionalmente
|
||||||
- Efecto visual: "dar una vuelta" sobre otro eje ocasionalmente
|
|
||||||
- Transiciones suaves con aceleración/desaceleración
|
|
||||||
|
|
||||||
#### WAVE_GRID (W):
|
#### WAVE_GRID (W):
|
||||||
- **Rotación actual:** XY girando como esfera (confuso)
|
- ✅ **Vista frontal paralela a pantalla** (sin rotación confusa)
|
||||||
- **Mejora propuesta:** Vista frontal con pivoteo sutil
|
- ✅ **Pivoteo sutil en ejes X e Y**
|
||||||
- **Vista principal:** Paralela a pantalla (mirando de frente)
|
- ✅ Esquinas se mueven adelante/atrás según posición
|
||||||
- **Movimiento:** Pivoteo en centro con esquinas hacia adelante/atrás
|
- ✅ Movimiento ondulatorio + pivoteo = efecto "océano"
|
||||||
- Ejemplo: esquina superior-derecha se aleja (Z-), inferior-izquierda se acerca (Z+)
|
- ✅ Velocidades lentas (0.3-0.5 rad/s) para organicidad
|
||||||
- Movimiento ondulatorio sincronizado: olas + pivoteo crea efecto "océano"
|
|
||||||
- **Opcional:** Rotación completa ocasional (cada 10-15s) como transición
|
|
||||||
|
|
||||||
**Implementación técnica:**
|
|
||||||
- Añadir `rotation_mode_` y `mode_timer_` a cada shape
|
|
||||||
- Estados: NORMAL, TRANSITION, ALTERNATIVE
|
|
||||||
- Interpolación suave entre modos de rotación
|
|
||||||
|
|
||||||
### 2. ✅ Modo DEMO (Auto-play)
|
### 2. ✅ Modo DEMO (Auto-play)
|
||||||
**Descripción:** Modo demostración automática con acciones aleatorias
|
**Descripción:** Modo demostración automática con acciones aleatorias
|
||||||
@@ -83,7 +74,57 @@
|
|||||||
- ✅ Help text con `--help`
|
- ✅ Help text con `--help`
|
||||||
- Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f`
|
- Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f`
|
||||||
|
|
||||||
### 4. 🐛 Corregir Escalado de Pelotas en Reposo
|
### 4. 🎯 Implementar Modo Logo (Easter Egg)
|
||||||
|
**Descripción:** Modo especial que muestra el logo JAILGAMES como "marca de agua"
|
||||||
|
**Prioridad:** Alta (característica distintiva)
|
||||||
|
**Estimación:** 2 horas
|
||||||
|
**Detalles:**
|
||||||
|
|
||||||
|
#### Configuración Modo Logo:
|
||||||
|
- **Figura:** Solo PNG_SHAPE (logo JAILGAMES)
|
||||||
|
- **Textura:** Siempre "tiny" (pelota más pequeña)
|
||||||
|
- **Tema:** Siempre MONOCHROME (blanco puro)
|
||||||
|
- **Escala:** 120% (figuras más grandes que normal)
|
||||||
|
- **Pelotas mínimas:** 500
|
||||||
|
- **Tecla manual:** K (activa/desactiva modo logo)
|
||||||
|
|
||||||
|
#### Comportamiento en Modo Logo:
|
||||||
|
- Alterna entre modo SHAPE y modo PHYSICS (como DEMO)
|
||||||
|
- Mantiene configuración fija (no cambia tema/textura/escala)
|
||||||
|
- Es como un "DEMO específico del logo"
|
||||||
|
|
||||||
|
#### Integración con DEMO LITE:
|
||||||
|
- **Requisitos para salto automático:**
|
||||||
|
- Mínimo 500 pelotas
|
||||||
|
- Tema MONOCHROME activo
|
||||||
|
- Si se cumplen → cambia automáticamente textura a "tiny" y escala a 120%
|
||||||
|
- **Duración:** Menos tiempo que DEMO normal (es un "recordatorio")
|
||||||
|
- **Después:** Vuelve a DEMO LITE normal
|
||||||
|
|
||||||
|
#### Integración con DEMO:
|
||||||
|
- **Requisitos:** Mínimo 500 pelotas
|
||||||
|
- **Acción:** Cambia automáticamente a: MONOCHROME + tiny + escala 120%
|
||||||
|
- **Duración:** Menos tiempo que acciones normales
|
||||||
|
- **Después:** Vuelve a DEMO normal
|
||||||
|
|
||||||
|
#### Proporción temporal sugerida:
|
||||||
|
- DEMO/DEMO_LITE normal: 80-90% del tiempo
|
||||||
|
- Modo Logo: 10-20% del tiempo (aparición ocasional como "easter egg")
|
||||||
|
|
||||||
|
### 5. ⏳ Mejorar Sistema de Vértices PNG_SHAPE
|
||||||
|
**Descripción:** Con 50 pelotas no activa modo vértices correctamente
|
||||||
|
**Prioridad:** Baja (mejora visual)
|
||||||
|
**Estimación:** 1 hora
|
||||||
|
**Detalles:**
|
||||||
|
- **Comportamiento actual:** Con 50 pelotas usa filas alternas en bordes
|
||||||
|
- **Comportamiento deseado:** Activar modo VÉRTICES (extremos izq/der de cada fila)
|
||||||
|
- **Problema:** Condición `num_points < 150` no es suficientemente agresiva
|
||||||
|
- **Solución propuesta:**
|
||||||
|
- Ajustar umbrales de activación de vértices
|
||||||
|
- Mejorar algoritmo extractCornerVertices() para detectar puntos clave
|
||||||
|
- Considerar densidad de píxeles en decisión (no solo cantidad absoluta)
|
||||||
|
|
||||||
|
### 5. 🐛 Corregir Escalado de Pelotas en Reposo
|
||||||
**Descripción:** Las pelotas cambian de tamaño cuando están quietas (bug visual)
|
**Descripción:** Las pelotas cambian de tamaño cuando están quietas (bug visual)
|
||||||
**Prioridad:** Alta (bug visible)
|
**Prioridad:** Alta (bug visible)
|
||||||
**Estimación:** 30 minutos
|
**Estimación:** 30 minutos
|
||||||
@@ -96,7 +137,7 @@
|
|||||||
- **Investigar:** Ball::render(), scale calculations, depth brightness
|
- **Investigar:** Ball::render(), scale calculations, depth brightness
|
||||||
- **Solución esperada:** Tamaño constante independiente de velocidad
|
- **Solución esperada:** Tamaño constante independiente de velocidad
|
||||||
|
|
||||||
### 5. ⏳ Sistema de Release
|
### 6. ⏳ Sistema de Release
|
||||||
**Descripción:** Empaquetado para distribución standalone
|
**Descripción:** Empaquetado para distribución standalone
|
||||||
**Prioridad:** Baja
|
**Prioridad:** Baja
|
||||||
**Estimación:** 30 minutos
|
**Estimación:** 30 minutos
|
||||||
@@ -165,13 +206,15 @@
|
|||||||
- ✅ Resolución configurable (mínimo 640x480)
|
- ✅ Resolución configurable (mínimo 640x480)
|
||||||
- 📝 Preparado para voxelización 3D (Enfoque B) en futuro
|
- 📝 Preparado para voxelización 3D (Enfoque B) en futuro
|
||||||
|
|
||||||
### 2025-10-04 (Sesión 4) - Modo DEMO
|
### 2025-10-04 (Sesión 4) - Modo DEMO + Mejoras Animaciones
|
||||||
- ✅ **Implementado Modo DEMO (auto-play)** - Tecla D para toggle
|
- ✅ **Implementado Modo DEMO (auto-play)** - Tecla D para toggle
|
||||||
- ✅ Sistema de acciones aleatorias cada 3-8 segundos (configurable)
|
- ✅ Sistema de acciones aleatorias cada 3-8 segundos (configurable)
|
||||||
- ✅ 8 tipos de acciones con pesos de probabilidad ajustables
|
- ✅ 8 tipos de acciones con pesos de probabilidad ajustables
|
||||||
- ✅ Totalmente interactivo - usuario puede seguir controlando
|
- ✅ Totalmente interactivo - usuario puede seguir controlando
|
||||||
- ✅ Display visual "DEMO MODE" centrado en naranja
|
- ✅ Display visual "DEMO MODE" centrado en naranja
|
||||||
- ✅ Mejoras animaciones 3D: tumbling en cilindro + pivoteo en wave grid
|
- ✅ **Mejoras animaciones 3D**: tumbling en cilindro + pivoteo en wave grid
|
||||||
|
- ✅ CYLINDER: tumbling ocasional en eje X cada 3-5s con ease-in-out
|
||||||
|
- ✅ WAVE_GRID: pivoteo sutil paralelo a pantalla (efecto océano)
|
||||||
- ❌ Eliminado sistema auto-restart antiguo (ya no necesario)
|
- ❌ Eliminado sistema auto-restart antiguo (ya no necesario)
|
||||||
|
|
||||||
### 2025-10-04 (Sesión 3)
|
### 2025-10-04 (Sesión 3)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 720 B After Width: | Height: | Size: 618 B |
166
source/defines.h
166
source/defines.h
@@ -5,10 +5,10 @@
|
|||||||
// Configuración de ventana y pantalla
|
// Configuración de ventana y pantalla
|
||||||
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
||||||
|
|
||||||
constexpr int SCREEN_WIDTH = 320; // Ancho de la pantalla lógica (píxeles)
|
// Resolución por defecto (usada si no se especifica en CLI)
|
||||||
constexpr int SCREEN_HEIGHT = 240; // Alto de la pantalla lógica (píxeles)
|
constexpr int DEFAULT_SCREEN_WIDTH = 320; // Ancho lógico por defecto (si no hay -w)
|
||||||
constexpr int WINDOW_ZOOM = 3; // Zoom inicial de la ventana
|
constexpr int DEFAULT_SCREEN_HEIGHT = 240; // Alto lógico por defecto (si no hay -h)
|
||||||
// BALL_SIZE eliminado: ahora se obtiene dinámicamente desde texture_->getWidth()
|
constexpr int DEFAULT_WINDOW_ZOOM = 3; // Zoom inicial de ventana (1x = sin zoom)
|
||||||
|
|
||||||
// Configuración de zoom dinámico de ventana
|
// Configuración de zoom dinámico de ventana
|
||||||
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
||||||
@@ -38,6 +38,9 @@ constexpr float GRAVITY_CHANGE_LATERAL_MAX = 0.08f; // Velocidad lateral máxim
|
|||||||
// Configuración de spawn inicial de pelotas
|
// Configuración de spawn inicial de pelotas
|
||||||
constexpr float BALL_SPAWN_MARGIN = 0.15f; // Margen lateral para spawn (0.25 = 25% a cada lado)
|
constexpr float BALL_SPAWN_MARGIN = 0.15f; // Margen lateral para spawn (0.25 = 25% a cada lado)
|
||||||
|
|
||||||
|
// Escenarios de número de pelotas (teclas 1-8)
|
||||||
|
constexpr int BALL_COUNT_SCENARIOS[8] = {10, 50, 100, 500, 1000, 5000, 10000, 50000};
|
||||||
|
|
||||||
// Estructura para representar colores RGB
|
// Estructura para representar colores RGB
|
||||||
struct Color {
|
struct Color {
|
||||||
int r, g, b; // Componentes rojo, verde, azul (0-255)
|
int r, g, b; // Componentes rojo, verde, azul (0-255)
|
||||||
@@ -63,22 +66,22 @@ enum class ColorTheme {
|
|||||||
|
|
||||||
// Enum para tipo de figura 3D
|
// Enum para tipo de figura 3D
|
||||||
enum class ShapeType {
|
enum class ShapeType {
|
||||||
NONE, // Sin figura (modo física pura)
|
NONE, // Sin figura (modo física pura)
|
||||||
SPHERE, // Esfera Fibonacci (antiguo RotoBall)
|
SPHERE, // Esfera Fibonacci (antiguo RotoBall)
|
||||||
CUBE, // Cubo rotante
|
CUBE, // Cubo rotante
|
||||||
HELIX, // Espiral 3D
|
HELIX, // Espiral 3D
|
||||||
TORUS, // Toroide/donut
|
TORUS, // Toroide/donut
|
||||||
WAVE_GRID, // Malla ondeante
|
WAVE_GRID, // Malla ondeante
|
||||||
CYLINDER, // Cilindro rotante
|
CYLINDER, // Cilindro rotante
|
||||||
ICOSAHEDRON, // Icosaedro D20
|
ICOSAHEDRON, // Icosaedro D20
|
||||||
ATOM, // Átomo con órbitas
|
ATOM, // Átomo con órbitas
|
||||||
PNG_SHAPE // Forma cargada desde PNG 1-bit
|
PNG_SHAPE // Forma cargada desde PNG 1-bit
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enum para modo de simulación
|
// Enum para modo de simulación
|
||||||
enum class SimulationMode {
|
enum class SimulationMode {
|
||||||
PHYSICS, // Modo física normal con gravedad
|
PHYSICS, // Modo física normal con gravedad
|
||||||
SHAPE // Modo figura 3D (Shape polimórfico)
|
SHAPE // Modo figura 3D (Shape polimórfico)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enum para modo de escalado en fullscreen (F5)
|
// Enum para modo de escalado en fullscreen (F5)
|
||||||
@@ -98,45 +101,45 @@ constexpr int ROTOBALL_MAX_BRIGHTNESS = 255; // Brillo máximo (frente, 0-
|
|||||||
|
|
||||||
// Física de atracción para figuras 3D (sistema de resorte)
|
// Física de atracción para figuras 3D (sistema de resorte)
|
||||||
// ROTOBALL: Figura esfera rotante especial (modo C)
|
// ROTOBALL: Figura esfera rotante especial (modo C)
|
||||||
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
|
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
|
||||||
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
|
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
|
||||||
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
|
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
|
||||||
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
|
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
|
||||||
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
|
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
|
||||||
|
|
||||||
// SHAPE: Figuras 3D normales (Q/W/E/R/T/Y/U/I/O) - Mayor pegajosidad
|
// SHAPE: Figuras 3D normales (Q/W/E/R/T/Y/U/I/O) - Mayor pegajosidad
|
||||||
constexpr float SHAPE_SPRING_K = 800.0f; // Rigidez alta (pelotas más "pegadas")
|
constexpr float SHAPE_SPRING_K = 800.0f; // Rigidez alta (pelotas más "pegadas")
|
||||||
constexpr float SHAPE_DAMPING_BASE = 60.0f; // Amortiguación alta (menos rebote)
|
constexpr float SHAPE_DAMPING_BASE = 60.0f; // Amortiguación alta (menos rebote)
|
||||||
constexpr float SHAPE_DAMPING_NEAR = 150.0f; // Absorción muy rápida al llegar
|
constexpr float SHAPE_DAMPING_NEAR = 150.0f; // Absorción muy rápida al llegar
|
||||||
constexpr float SHAPE_NEAR_THRESHOLD = 8.0f; // Umbral "cerca" más amplio
|
constexpr float SHAPE_NEAR_THRESHOLD = 8.0f; // Umbral "cerca" más amplio
|
||||||
constexpr float SHAPE_MAX_FORCE = 2000.0f; // Permite fuerzas más fuertes
|
constexpr float SHAPE_MAX_FORCE = 2000.0f; // Permite fuerzas más fuertes
|
||||||
|
|
||||||
// Configuración del Cubo (cubo 3D rotante)
|
// Configuración del Cubo (cubo 3D rotante)
|
||||||
constexpr float CUBE_SIZE_FACTOR = 0.25f; // Tamaño como proporción de altura (60/240 = 0.25)
|
constexpr float CUBE_SIZE_FACTOR = 0.25f; // Tamaño como proporción de altura (60/240 = 0.25)
|
||||||
constexpr float CUBE_ROTATION_SPEED_X = 0.5f; // Velocidad rotación eje X (rad/s)
|
constexpr float CUBE_ROTATION_SPEED_X = 0.5f; // Velocidad rotación eje X (rad/s)
|
||||||
constexpr float CUBE_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
constexpr float CUBE_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
||||||
constexpr float CUBE_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
constexpr float CUBE_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
||||||
|
|
||||||
// Configuración de Helix (espiral helicoidal 3D)
|
// Configuración de Helix (espiral helicoidal 3D)
|
||||||
constexpr float HELIX_RADIUS_FACTOR = 0.25f; // Radio de la espiral (proporción de altura)
|
constexpr float HELIX_RADIUS_FACTOR = 0.25f; // Radio de la espiral (proporción de altura)
|
||||||
constexpr float HELIX_PITCH_FACTOR = 0.25f; // Separación vertical entre vueltas (proporción de altura)
|
constexpr float HELIX_PITCH_FACTOR = 0.25f; // Separación vertical entre vueltas (proporción de altura)
|
||||||
constexpr float HELIX_NUM_TURNS = 3.0f; // Número de vueltas completas (1-5)
|
constexpr float HELIX_NUM_TURNS = 3.0f; // Número de vueltas completas (1-5)
|
||||||
constexpr float HELIX_ROTATION_SPEED_Y = 1.2f; // Velocidad rotación eje Y (rad/s)
|
constexpr float HELIX_ROTATION_SPEED_Y = 1.2f; // Velocidad rotación eje Y (rad/s)
|
||||||
constexpr float HELIX_PHASE_SPEED = 0.5f; // Velocidad de animación vertical (rad/s)
|
constexpr float HELIX_PHASE_SPEED = 0.5f; // Velocidad de animación vertical (rad/s)
|
||||||
|
|
||||||
// Configuración de Wave Grid (malla ondeante 3D)
|
// Configuración de Wave Grid (malla ondeante 3D)
|
||||||
constexpr float WAVE_GRID_SIZE_FACTOR = 0.35f; // Tamaño del grid (proporción de altura)
|
constexpr float WAVE_GRID_SIZE_FACTOR = 0.35f; // Tamaño del grid (proporción de altura)
|
||||||
constexpr float WAVE_GRID_AMPLITUDE = 0.15f; // Amplitud de las ondas (proporción de altura)
|
constexpr float WAVE_GRID_AMPLITUDE = 0.15f; // Amplitud de las ondas (proporción de altura)
|
||||||
constexpr float WAVE_GRID_FREQUENCY = 3.0f; // Frecuencia de ondas (ciclos por grid)
|
constexpr float WAVE_GRID_FREQUENCY = 3.0f; // Frecuencia de ondas (ciclos por grid)
|
||||||
constexpr float WAVE_GRID_PHASE_SPEED = 2.0f; // Velocidad de animación de ondas (rad/s)
|
constexpr float WAVE_GRID_PHASE_SPEED = 2.0f; // Velocidad de animación de ondas (rad/s)
|
||||||
constexpr float WAVE_GRID_ROTATION_SPEED_Y = 0.4f; // Velocidad rotación eje Y (rad/s)
|
constexpr float WAVE_GRID_ROTATION_SPEED_Y = 0.4f; // Velocidad rotación eje Y (rad/s)
|
||||||
|
|
||||||
// Configuración de Torus (toroide/donut 3D)
|
// Configuración de Torus (toroide/donut 3D)
|
||||||
constexpr float TORUS_MAJOR_RADIUS_FACTOR = 0.25f; // Radio mayor R (centro torus a centro tubo)
|
constexpr float TORUS_MAJOR_RADIUS_FACTOR = 0.25f; // Radio mayor R (centro torus a centro tubo)
|
||||||
constexpr float TORUS_MINOR_RADIUS_FACTOR = 0.12f; // Radio menor r (grosor del tubo)
|
constexpr float TORUS_MINOR_RADIUS_FACTOR = 0.12f; // Radio menor r (grosor del tubo)
|
||||||
constexpr float TORUS_ROTATION_SPEED_X = 0.6f; // Velocidad rotación eje X (rad/s)
|
constexpr float TORUS_ROTATION_SPEED_X = 0.6f; // Velocidad rotación eje X (rad/s)
|
||||||
constexpr float TORUS_ROTATION_SPEED_Y = 0.9f; // Velocidad rotación eje Y (rad/s)
|
constexpr float TORUS_ROTATION_SPEED_Y = 0.9f; // Velocidad rotación eje Y (rad/s)
|
||||||
constexpr float TORUS_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
constexpr float TORUS_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
||||||
|
|
||||||
// Configuración de Cylinder (cilindro 3D)
|
// Configuración de Cylinder (cilindro 3D)
|
||||||
constexpr float CYLINDER_RADIUS_FACTOR = 0.25f; // Radio del cilindro (proporción de altura)
|
constexpr float CYLINDER_RADIUS_FACTOR = 0.25f; // Radio del cilindro (proporción de altura)
|
||||||
@@ -144,10 +147,10 @@ constexpr float CYLINDER_HEIGHT_FACTOR = 0.5f; // Altura del cilindro (propo
|
|||||||
constexpr float CYLINDER_ROTATION_SPEED_Y = 1.0f; // Velocidad rotación eje Y (rad/s)
|
constexpr float CYLINDER_ROTATION_SPEED_Y = 1.0f; // Velocidad rotación eje Y (rad/s)
|
||||||
|
|
||||||
// Configuración de Icosahedron (icosaedro D20)
|
// Configuración de Icosahedron (icosaedro D20)
|
||||||
constexpr float ICOSAHEDRON_RADIUS_FACTOR = 0.30f; // Radio de la esfera circunscrita
|
constexpr float ICOSAHEDRON_RADIUS_FACTOR = 0.30f; // Radio de la esfera circunscrita
|
||||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_X = 0.4f; // Velocidad rotación eje X (rad/s)
|
constexpr float ICOSAHEDRON_ROTATION_SPEED_X = 0.4f; // Velocidad rotación eje X (rad/s)
|
||||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
constexpr float ICOSAHEDRON_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
||||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_Z = 0.2f; // Velocidad rotación eje Z (rad/s)
|
constexpr float ICOSAHEDRON_ROTATION_SPEED_Z = 0.2f; // Velocidad rotación eje Z (rad/s)
|
||||||
|
|
||||||
// Configuración de Atom (núcleo con órbitas electrónicas)
|
// Configuración de Atom (núcleo con órbitas electrónicas)
|
||||||
constexpr float ATOM_NUCLEUS_RADIUS_FACTOR = 0.08f; // Radio del núcleo central
|
constexpr float ATOM_NUCLEUS_RADIUS_FACTOR = 0.08f; // Radio del núcleo central
|
||||||
@@ -162,33 +165,33 @@ constexpr float PNG_EXTRUSION_DEPTH_FACTOR = 0.12f; // Profundidad de extrusió
|
|||||||
constexpr int PNG_NUM_EXTRUSION_LAYERS = 15; // Capas de extrusión (más capas = más pegajosidad)
|
constexpr int PNG_NUM_EXTRUSION_LAYERS = 15; // Capas de extrusión (más capas = más pegajosidad)
|
||||||
constexpr bool PNG_USE_EDGES_ONLY = false; // true = solo bordes, false = relleno completo
|
constexpr bool PNG_USE_EDGES_ONLY = false; // true = solo bordes, false = relleno completo
|
||||||
// Rotación "legible" (texto de frente con volteretas ocasionales)
|
// Rotación "legible" (texto de frente con volteretas ocasionales)
|
||||||
constexpr float PNG_IDLE_TIME_MIN = 3.0f; // Tiempo mínimo de frente (segundos)
|
constexpr float PNG_IDLE_TIME_MIN = 3.0f; // Tiempo mínimo de frente (segundos)
|
||||||
constexpr float PNG_IDLE_TIME_MAX = 8.0f; // Tiempo máximo de frente (segundos)
|
constexpr float PNG_IDLE_TIME_MAX = 8.0f; // Tiempo máximo de frente (segundos)
|
||||||
constexpr float PNG_FLIP_SPEED = 3.0f; // Velocidad voltereta (rad/s)
|
constexpr float PNG_FLIP_SPEED = 3.0f; // Velocidad voltereta (rad/s)
|
||||||
constexpr float PNG_FLIP_DURATION = 1.5f; // Duración voltereta (segundos)
|
constexpr float PNG_FLIP_DURATION = 1.5f; // Duración voltereta (segundos)
|
||||||
|
|
||||||
// Control manual de escala de figuras 3D (Numpad +/-)
|
// Control manual de escala de figuras 3D (Numpad +/-)
|
||||||
constexpr float SHAPE_SCALE_MIN = 0.3f; // Escala mínima (30%)
|
constexpr float SHAPE_SCALE_MIN = 0.3f; // Escala mínima (30%)
|
||||||
constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
|
constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
|
||||||
constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación
|
constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación
|
||||||
constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%)
|
constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%)
|
||||||
|
|
||||||
// Configuración de Modo DEMO (auto-play completo)
|
// Configuración de Modo DEMO (auto-play completo)
|
||||||
constexpr float DEMO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre acciones (segundos)
|
constexpr float DEMO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre acciones (segundos)
|
||||||
constexpr float DEMO_ACTION_INTERVAL_MAX = 6.0f; // Tiempo máximo entre acciones (segundos)
|
constexpr float DEMO_ACTION_INTERVAL_MAX = 6.0f; // Tiempo máximo entre acciones (segundos)
|
||||||
|
|
||||||
// Pesos de probabilidad DEMO MODE (valores relativos, se normalizan)
|
// Pesos de probabilidad DEMO MODE (valores relativos, se normalizan)
|
||||||
constexpr int DEMO_WEIGHT_GRAVITY_DIR = 12; // Cambiar dirección gravedad (12%)
|
constexpr int DEMO_WEIGHT_GRAVITY_DIR = 12; // Cambiar dirección gravedad (12%)
|
||||||
constexpr int DEMO_WEIGHT_GRAVITY_TOGGLE = 15; // Toggle gravedad ON/OFF (15%) - ¡Ver caer pelotas!
|
constexpr int DEMO_WEIGHT_GRAVITY_TOGGLE = 15; // Toggle gravedad ON/OFF (15%) - ¡Ver caer pelotas!
|
||||||
constexpr int DEMO_WEIGHT_SHAPE = 22; // Activar figura 3D (22%) - Construir figuras
|
constexpr int DEMO_WEIGHT_SHAPE = 22; // Activar figura 3D (22%) - Construir figuras
|
||||||
constexpr int DEMO_WEIGHT_TOGGLE_PHYSICS = 18; // Toggle física ↔ figura (18%) - ¡Destruir figuras!
|
constexpr int DEMO_WEIGHT_TOGGLE_PHYSICS = 18; // Toggle física ↔ figura (18%) - ¡Destruir figuras!
|
||||||
constexpr int DEMO_WEIGHT_REGENERATE_SHAPE = 10; // Re-generar misma figura (10%) - Reconstruir
|
constexpr int DEMO_WEIGHT_REGENERATE_SHAPE = 10; // Re-generar misma figura (10%) - Reconstruir
|
||||||
constexpr int DEMO_WEIGHT_THEME = 12; // Cambiar tema de colores (12%)
|
constexpr int DEMO_WEIGHT_THEME = 12; // Cambiar tema de colores (12%)
|
||||||
constexpr int DEMO_WEIGHT_SCENARIO = 2; // Cambiar número de pelotas (2%) - MUY OCASIONAL
|
constexpr int DEMO_WEIGHT_SCENARIO = 2; // Cambiar número de pelotas (2%) - MUY OCASIONAL
|
||||||
constexpr int DEMO_WEIGHT_IMPULSE = 6; // Aplicar impulso (SPACE) (6%)
|
constexpr int DEMO_WEIGHT_IMPULSE = 6; // Aplicar impulso (SPACE) (6%)
|
||||||
constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 1; // Toggle profundidad (1%)
|
constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 1; // Toggle profundidad (1%)
|
||||||
constexpr int DEMO_WEIGHT_SHAPE_SCALE = 1; // Cambiar escala figura (1%)
|
constexpr int DEMO_WEIGHT_SHAPE_SCALE = 1; // Cambiar escala figura (1%)
|
||||||
constexpr int DEMO_WEIGHT_SPRITE = 1; // Cambiar sprite (1%)
|
constexpr int DEMO_WEIGHT_SPRITE = 1; // Cambiar sprite (1%)
|
||||||
// TOTAL: 100
|
// TOTAL: 100
|
||||||
|
|
||||||
// Configuración de Modo DEMO LITE (solo física/figuras)
|
// Configuración de Modo DEMO LITE (solo física/figuras)
|
||||||
@@ -196,11 +199,22 @@ constexpr float DEMO_LITE_ACTION_INTERVAL_MIN = 1.5f; // Más rápido que demo
|
|||||||
constexpr float DEMO_LITE_ACTION_INTERVAL_MAX = 4.0f;
|
constexpr float DEMO_LITE_ACTION_INTERVAL_MAX = 4.0f;
|
||||||
|
|
||||||
// Pesos de probabilidad DEMO LITE (solo física/figuras, sin cambios de escenario/tema)
|
// Pesos de probabilidad DEMO LITE (solo física/figuras, sin cambios de escenario/tema)
|
||||||
constexpr int DEMO_LITE_WEIGHT_GRAVITY_DIR = 25; // Cambiar dirección gravedad (25%)
|
constexpr int DEMO_LITE_WEIGHT_GRAVITY_DIR = 25; // Cambiar dirección gravedad (25%)
|
||||||
constexpr int DEMO_LITE_WEIGHT_GRAVITY_TOGGLE = 20;// Toggle gravedad ON/OFF (20%)
|
constexpr int DEMO_LITE_WEIGHT_GRAVITY_TOGGLE = 20; // Toggle gravedad ON/OFF (20%)
|
||||||
constexpr int DEMO_LITE_WEIGHT_SHAPE = 25; // Activar figura 3D (25%)
|
constexpr int DEMO_LITE_WEIGHT_SHAPE = 25; // Activar figura 3D (25%)
|
||||||
constexpr int DEMO_LITE_WEIGHT_TOGGLE_PHYSICS = 20;// Toggle física ↔ figura (20%)
|
constexpr int DEMO_LITE_WEIGHT_TOGGLE_PHYSICS = 20; // Toggle física ↔ figura (20%)
|
||||||
constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%)
|
constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%)
|
||||||
// TOTAL: 100
|
// TOTAL: 100
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
// Configuración de Modo LOGO (easter egg - "marca de agua")
|
||||||
|
constexpr int LOGO_MODE_MIN_BALLS = 500; // Mínimo de pelotas para activar modo logo
|
||||||
|
constexpr float LOGO_MODE_SHAPE_SCALE = 1.2f; // Escala de figura en modo logo (120%)
|
||||||
|
constexpr float LOGO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre alternancia SHAPE/PHYSICS
|
||||||
|
constexpr float LOGO_ACTION_INTERVAL_MAX = 5.0f; // Tiempo máximo entre alternancia SHAPE/PHYSICS
|
||||||
|
constexpr int LOGO_WEIGHT_TOGGLE_PHYSICS = 100; // Único peso: alternar SHAPE ↔ PHYSICS (100%)
|
||||||
|
|
||||||
|
// Probabilidad de salto a Logo Mode desde DEMO/DEMO_LITE (%)
|
||||||
|
constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO = 15; // 15% probabilidad en DEMO normal
|
||||||
|
constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE = 10; // 10% probabilidad en DEMO LITE
|
||||||
|
|
||||||
|
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||||
@@ -8,31 +8,31 @@
|
|||||||
#include <SDL3/SDL_timer.h> // for SDL_GetTicks
|
#include <SDL3/SDL_timer.h> // for SDL_GetTicks
|
||||||
#include <SDL3/SDL_video.h> // for SDL_CreateWindow, SDL_DestroyWindow, SDL_GetDisplayBounds
|
#include <SDL3/SDL_video.h> // for SDL_CreateWindow, SDL_DestroyWindow, SDL_GetDisplayBounds
|
||||||
|
|
||||||
#include <algorithm> // for std::min, std::max, std::sort
|
#include <algorithm> // for std::min, std::max, std::sort
|
||||||
#include <cmath> // for sqrtf, acosf, cosf, sinf (funciones matemáticas)
|
#include <cmath> // for sqrtf, acosf, cosf, sinf (funciones matemáticas)
|
||||||
#include <cstdlib> // for rand, srand
|
#include <cstdlib> // for rand, srand
|
||||||
#include <cstring> // for strlen
|
#include <cstring> // for strlen
|
||||||
#include <ctime> // for time
|
#include <ctime> // for time
|
||||||
#include <iostream> // for cout
|
#include <filesystem> // for path operations
|
||||||
#include <string> // for string
|
#include <iostream> // for cout
|
||||||
#include <filesystem> // for path operations
|
#include <string> // for string
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h> // for GetModuleFileName
|
#include <windows.h> // for GetModuleFileName
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
#include "shapes/sphere_shape.h" // for SphereShape
|
#include "shapes/atom_shape.h" // for AtomShape
|
||||||
#include "shapes/cube_shape.h" // for CubeShape
|
#include "shapes/cube_shape.h" // for CubeShape
|
||||||
#include "shapes/helix_shape.h" // for HelixShape
|
#include "shapes/cylinder_shape.h" // for CylinderShape
|
||||||
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
#include "shapes/helix_shape.h" // for HelixShape
|
||||||
#include "shapes/torus_shape.h" // for TorusShape
|
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
||||||
#include "shapes/cylinder_shape.h" // for CylinderShape
|
#include "shapes/png_shape.h" // for PNGShape
|
||||||
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
#include "shapes/sphere_shape.h" // for SphereShape
|
||||||
#include "shapes/atom_shape.h" // for AtomShape
|
#include "shapes/torus_shape.h" // for TorusShape
|
||||||
#include "shapes/png_shape.h" // for PNGShape
|
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
||||||
|
|
||||||
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
||||||
std::string getExecutableDirectory() {
|
std::string getExecutableDirectory() {
|
||||||
@@ -48,19 +48,64 @@ std::string getExecutableDirectory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implementación de métodos públicos
|
// Implementación de métodos públicos
|
||||||
bool Engine::initialize(int width, int height, bool fullscreen) {
|
bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
// Usar parámetros o valores por defecto
|
// Obtener resolución de pantalla para validación
|
||||||
int window_width = (width > 0) ? width : SCREEN_WIDTH * WINDOW_ZOOM;
|
|
||||||
int window_height = (height > 0) ? height : SCREEN_HEIGHT * WINDOW_ZOOM;
|
|
||||||
int logical_width = (width > 0) ? width : SCREEN_WIDTH;
|
|
||||||
int logical_height = (height > 0) ? height : SCREEN_HEIGHT;
|
|
||||||
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||||
std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl;
|
std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl;
|
||||||
success = false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
int num_displays = 0;
|
||||||
|
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
||||||
|
const auto *dm = (displays && num_displays > 0) ? SDL_GetCurrentDisplayMode(displays[0]) : nullptr;
|
||||||
|
|
||||||
|
int screen_w = dm ? dm->w : 1920; // Fallback si falla
|
||||||
|
int screen_h = dm ? dm->h - WINDOW_DECORATION_HEIGHT : 1080;
|
||||||
|
|
||||||
|
if (displays) SDL_free(displays);
|
||||||
|
|
||||||
|
// Usar parámetros o valores por defecto
|
||||||
|
int logical_width = (width > 0) ? width : DEFAULT_SCREEN_WIDTH;
|
||||||
|
int logical_height = (height > 0) ? height : DEFAULT_SCREEN_HEIGHT;
|
||||||
|
int window_zoom = (zoom > 0) ? zoom : DEFAULT_WINDOW_ZOOM;
|
||||||
|
|
||||||
|
// VALIDACIÓN 1: Si resolución > pantalla → reset a default
|
||||||
|
if (logical_width > screen_w || logical_height > screen_h) {
|
||||||
|
std::cout << "Advertencia: Resolución " << logical_width << "x" << logical_height
|
||||||
|
<< " excede pantalla " << screen_w << "x" << screen_h
|
||||||
|
<< ". Usando default " << DEFAULT_SCREEN_WIDTH << "x" << DEFAULT_SCREEN_HEIGHT << "\n";
|
||||||
|
logical_width = DEFAULT_SCREEN_WIDTH;
|
||||||
|
logical_height = DEFAULT_SCREEN_HEIGHT;
|
||||||
|
window_zoom = DEFAULT_WINDOW_ZOOM; // Reset zoom también
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDACIÓN 2: Calcular max_zoom y ajustar si es necesario
|
||||||
|
int max_zoom = std::min(screen_w / logical_width, screen_h / logical_height);
|
||||||
|
if (window_zoom > max_zoom) {
|
||||||
|
std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom
|
||||||
|
<< " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n";
|
||||||
|
window_zoom = max_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si se especificaron parámetros CLI y zoom no se especificó, usar zoom=1
|
||||||
|
if ((width > 0 || height > 0) && zoom == 0) {
|
||||||
|
window_zoom = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular tamaño de ventana
|
||||||
|
int window_width = logical_width * window_zoom;
|
||||||
|
int window_height = logical_height * window_zoom;
|
||||||
|
|
||||||
|
// Guardar resolución base (configurada por CLI o default)
|
||||||
|
base_screen_width_ = logical_width;
|
||||||
|
base_screen_height_ = logical_height;
|
||||||
|
current_screen_width_ = logical_width;
|
||||||
|
current_screen_height_ = logical_height;
|
||||||
|
|
||||||
|
// SDL ya inicializado arriba para validación
|
||||||
|
{
|
||||||
// Crear ventana principal (fullscreen si se especifica)
|
// Crear ventana principal (fullscreen si se especifica)
|
||||||
Uint32 window_flags = SDL_WINDOW_OPENGL;
|
Uint32 window_flags = SDL_WINDOW_OPENGL;
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
@@ -191,7 +236,7 @@ void Engine::calculateDeltaTime() {
|
|||||||
last_frame_time_ = current_time;
|
last_frame_time_ = current_time;
|
||||||
|
|
||||||
// Limitar delta time para evitar saltos grandes (pausa larga, depuración, etc.)
|
// Limitar delta time para evitar saltos grandes (pausa larga, depuración, etc.)
|
||||||
if (delta_time_ > 0.05f) { // Máximo 50ms (20 FPS mínimo)
|
if (delta_time_ > 0.05f) { // Máximo 50ms (20 FPS mínimo)
|
||||||
delta_time_ = 1.0f / 60.0f; // Fallback a 60 FPS
|
delta_time_ = 1.0f / 60.0f; // Fallback a 60 FPS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,7 +256,7 @@ void Engine::update() {
|
|||||||
// Bifurcar actualización según modo activo
|
// Bifurcar actualización según modo activo
|
||||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||||
// Modo física normal: actualizar física de cada pelota
|
// Modo física normal: actualizar física de cada pelota
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->update(delta_time_); // Pasar delta time a cada pelota
|
ball->update(delta_time_); // Pasar delta time a cada pelota
|
||||||
}
|
}
|
||||||
} else if (current_mode_ == SimulationMode::SHAPE) {
|
} else if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
@@ -607,10 +652,9 @@ void Engine::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
||||||
std::sort(render_order.begin(), render_order.end(),
|
std::sort(render_order.begin(), render_order.end(), [this](size_t a, size_t b) {
|
||||||
[this](size_t a, size_t b) {
|
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
||||||
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Renderizar en orden de profundidad (fondo → frente)
|
// Renderizar en orden de profundidad (fondo → frente)
|
||||||
for (size_t idx : render_order) {
|
for (size_t idx : render_order) {
|
||||||
@@ -620,8 +664,7 @@ void Engine::render() {
|
|||||||
float depth_scale = balls_[idx]->getDepthScale();
|
float depth_scale = balls_[idx]->getDepthScale();
|
||||||
|
|
||||||
// Mapear brightness de 0-1 a rango MIN-MAX
|
// Mapear brightness de 0-1 a rango MIN-MAX
|
||||||
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness *
|
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
||||||
(ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
|
||||||
|
|
||||||
// Aplicar factor de brillo al color
|
// Aplicar factor de brillo al color
|
||||||
int r_mod = static_cast<int>(color.r * brightness_factor);
|
int r_mod = static_cast<int>(color.r * brightness_factor);
|
||||||
@@ -633,7 +676,7 @@ void Engine::render() {
|
|||||||
} else {
|
} else {
|
||||||
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
SDL_FRect pos = ball->getPosition();
|
SDL_FRect pos = ball->getPosition();
|
||||||
Color color = getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
Color color = getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||||
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f);
|
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f);
|
||||||
@@ -659,7 +702,7 @@ void Engine::render() {
|
|||||||
const ThemeColors& target = themes_[static_cast<int>(target_theme_)];
|
const ThemeColors& target = themes_[static_cast<int>(target_theme_)];
|
||||||
if (text_ != current.name_es && text_ != target.name_es) {
|
if (text_ != current.name_es && text_ != target.name_es) {
|
||||||
int theme_text_width = static_cast<int>(strlen(current.name_es) * 8); // 8 píxeles por carácter
|
int theme_text_width = static_cast<int>(strlen(current.name_es) * 8); // 8 píxeles por carácter
|
||||||
int theme_x = (current_screen_width_ - theme_text_width) / 2; // Centrar horizontalmente
|
int theme_x = (current_screen_width_ - theme_text_width) / 2; // Centrar horizontalmente
|
||||||
|
|
||||||
// Texto del nombre del tema con el mismo color
|
// Texto del nombre del tema con el mismo color
|
||||||
dbg_print(theme_x, 24, current.name_es, current.text_color_r, current.text_color_g, current.text_color_b);
|
dbg_print(theme_x, 24, current.name_es, current.text_color_r, current.text_color_g, current.text_color_b);
|
||||||
@@ -670,7 +713,7 @@ void Engine::render() {
|
|||||||
if (show_debug_) {
|
if (show_debug_) {
|
||||||
// Mostrar contador de FPS en esquina superior derecha
|
// Mostrar contador de FPS en esquina superior derecha
|
||||||
int fps_text_width = static_cast<int>(fps_text_.length() * 8); // 8 píxeles por carácter
|
int fps_text_width = static_cast<int>(fps_text_.length() * 8); // 8 píxeles por carácter
|
||||||
int fps_x = current_screen_width_ - fps_text_width - 8; // 8 píxeles de margen
|
int fps_x = current_screen_width_ - fps_text_width - 8; // 8 píxeles de margen
|
||||||
dbg_print(fps_x, 8, fps_text_.c_str(), 255, 255, 0); // Amarillo para distinguir
|
dbg_print(fps_x, 8, fps_text_.c_str(), 255, 255, 0); // Amarillo para distinguir
|
||||||
|
|
||||||
// Mostrar estado V-Sync en esquina superior izquierda
|
// Mostrar estado V-Sync en esquina superior izquierda
|
||||||
@@ -695,7 +738,7 @@ void Engine::render() {
|
|||||||
// Línea 4: Coeficiente de rebote (loss)
|
// Línea 4: Coeficiente de rebote (loss)
|
||||||
float loss_val = balls_[0]->getLossCoefficient();
|
float loss_val = balls_[0]->getLossCoefficient();
|
||||||
std::string loss_text = "LOSS " + std::to_string(loss_val).substr(0, 4); // Solo 2 decimales
|
std::string loss_text = "LOSS " + std::to_string(loss_val).substr(0, 4); // Solo 2 decimales
|
||||||
dbg_print(8, 48, loss_text.c_str(), 255, 0, 255); // Magenta para debug
|
dbg_print(8, 48, loss_text.c_str(), 255, 0, 255); // Magenta para debug
|
||||||
|
|
||||||
// Línea 5: Dirección de gravedad
|
// Línea 5: Dirección de gravedad
|
||||||
std::string gravity_dir_text = "GRAVITY " + gravityDirectionToString(current_gravity_);
|
std::string gravity_dir_text = "GRAVITY " + gravityDirectionToString(current_gravity_);
|
||||||
@@ -735,16 +778,16 @@ void Engine::initBalls(int value) {
|
|||||||
changeGravityDirection(GravityDirection::DOWN);
|
changeGravityDirection(GravityDirection::DOWN);
|
||||||
|
|
||||||
// Crear las bolas según el escenario
|
// Crear las bolas según el escenario
|
||||||
for (int i = 0; i < test_.at(value); ++i) {
|
for (int i = 0; i < BALL_COUNT_SCENARIOS[value]; ++i) {
|
||||||
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
|
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
|
||||||
// Calcular spawn zone: margen a cada lado, zona central para spawn
|
// Calcular spawn zone: margen a cada lado, zona central para spawn
|
||||||
const int margin = static_cast<int>(current_screen_width_ * BALL_SPAWN_MARGIN);
|
const int margin = static_cast<int>(current_screen_width_ * BALL_SPAWN_MARGIN);
|
||||||
const int spawn_zone_width = current_screen_width_ - (2 * margin);
|
const int spawn_zone_width = current_screen_width_ - (2 * margin);
|
||||||
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
|
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
|
||||||
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
|
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
|
||||||
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
|
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
|
||||||
// Seleccionar color de la paleta del tema actual
|
// Seleccionar color de la paleta del tema actual
|
||||||
ThemeColors &theme = themes_[static_cast<int>(current_theme_)];
|
ThemeColors& theme = themes_[static_cast<int>(current_theme_)];
|
||||||
int color_index = rand() % theme.ball_colors.size(); // Cantidad variable de colores por tema
|
int color_index = rand() % theme.ball_colors.size(); // Cantidad variable de colores por tema
|
||||||
const Color COLOR = theme.ball_colors[color_index];
|
const Color COLOR = theme.ball_colors[color_index];
|
||||||
// Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada)
|
// Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada)
|
||||||
@@ -758,7 +801,7 @@ void Engine::setText() {
|
|||||||
// Suprimir textos durante modos demo
|
// Suprimir textos durante modos demo
|
||||||
if (demo_mode_enabled_ || demo_lite_enabled_) return;
|
if (demo_mode_enabled_ || demo_lite_enabled_) return;
|
||||||
|
|
||||||
int num_balls = test_.at(scenario_);
|
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
|
||||||
if (num_balls == 1) {
|
if (num_balls == 1) {
|
||||||
text_ = "1 PELOTA";
|
text_ = "1 PELOTA";
|
||||||
} else {
|
} else {
|
||||||
@@ -770,7 +813,7 @@ void Engine::setText() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::pushBallsAwayFromGravity() {
|
void Engine::pushBallsAwayFromGravity() {
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
const int SIGNO = ((rand() % 2) * 2) - 1;
|
const int SIGNO = ((rand() % 2) * 2) - 1;
|
||||||
const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO;
|
const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO;
|
||||||
const float MAIN = ((rand() % 40) * 0.1f) + 5;
|
const float MAIN = ((rand() % 40) * 0.1f) + 5;
|
||||||
@@ -781,7 +824,7 @@ void Engine::pushBallsAwayFromGravity() {
|
|||||||
vx = LATERAL;
|
vx = LATERAL;
|
||||||
vy = -MAIN;
|
vy = -MAIN;
|
||||||
break;
|
break;
|
||||||
case GravityDirection::UP: // Impulsar ABAJO
|
case GravityDirection::UP: // Impulsar ABAJO
|
||||||
vx = LATERAL;
|
vx = LATERAL;
|
||||||
vy = MAIN;
|
vy = MAIN;
|
||||||
break;
|
break;
|
||||||
@@ -789,7 +832,7 @@ void Engine::pushBallsAwayFromGravity() {
|
|||||||
vx = MAIN;
|
vx = MAIN;
|
||||||
vy = LATERAL;
|
vy = LATERAL;
|
||||||
break;
|
break;
|
||||||
case GravityDirection::RIGHT: // Impulsar IZQUIERDA
|
case GravityDirection::RIGHT: // Impulsar IZQUIERDA
|
||||||
vx = -MAIN;
|
vx = -MAIN;
|
||||||
vy = LATERAL;
|
vy = LATERAL;
|
||||||
break;
|
break;
|
||||||
@@ -799,32 +842,32 @@ void Engine::pushBallsAwayFromGravity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::switchBallsGravity() {
|
void Engine::switchBallsGravity() {
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->switchGravity();
|
ball->switchGravity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::enableBallsGravityIfDisabled() {
|
void Engine::enableBallsGravityIfDisabled() {
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->enableGravityIfDisabled();
|
ball->enableGravityIfDisabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::forceBallsGravityOn() {
|
void Engine::forceBallsGravityOn() {
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->forceGravityOn();
|
ball->forceGravityOn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::forceBallsGravityOff() {
|
void Engine::forceBallsGravityOff() {
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->forceGravityOff();
|
ball->forceGravityOff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::changeGravityDirection(GravityDirection direction) {
|
void Engine::changeGravityDirection(GravityDirection direction) {
|
||||||
current_gravity_ = direction;
|
current_gravity_ = direction;
|
||||||
for (auto &ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
ball->setGravityDirection(direction);
|
ball->setGravityDirection(direction);
|
||||||
ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio
|
ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio
|
||||||
}
|
}
|
||||||
@@ -867,9 +910,9 @@ void Engine::toggleRealFullscreen() {
|
|||||||
if (real_fullscreen_enabled_) {
|
if (real_fullscreen_enabled_) {
|
||||||
// Obtener resolución del escritorio
|
// Obtener resolución del escritorio
|
||||||
int num_displays = 0;
|
int num_displays = 0;
|
||||||
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
||||||
if (displays != nullptr && num_displays > 0) {
|
if (displays != nullptr && num_displays > 0) {
|
||||||
const auto *dm = SDL_GetCurrentDisplayMode(displays[0]);
|
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
||||||
if (dm != nullptr) {
|
if (dm != nullptr) {
|
||||||
// Cambiar a resolución nativa del escritorio
|
// Cambiar a resolución nativa del escritorio
|
||||||
current_screen_width_ = dm->w;
|
current_screen_width_ = dm->w;
|
||||||
@@ -891,16 +934,16 @@ void Engine::toggleRealFullscreen() {
|
|||||||
// Ocultar cursor en real fullscreen
|
// Ocultar cursor en real fullscreen
|
||||||
SDL_HideCursor();
|
SDL_HideCursor();
|
||||||
} else {
|
} else {
|
||||||
// Volver a resolución original
|
// Volver a resolución base (configurada por CLI o default)
|
||||||
current_screen_width_ = SCREEN_WIDTH;
|
current_screen_width_ = base_screen_width_;
|
||||||
current_screen_height_ = SCREEN_HEIGHT;
|
current_screen_height_ = base_screen_height_;
|
||||||
|
|
||||||
// Restaurar ventana normal
|
// Restaurar ventana normal
|
||||||
SDL_SetWindowFullscreen(window_, false);
|
SDL_SetWindowFullscreen(window_, false);
|
||||||
SDL_SetWindowSize(window_, SCREEN_WIDTH * WINDOW_ZOOM, SCREEN_HEIGHT * WINDOW_ZOOM);
|
SDL_SetWindowSize(window_, base_screen_width_ * DEFAULT_WINDOW_ZOOM, base_screen_height_ * DEFAULT_WINDOW_ZOOM);
|
||||||
|
|
||||||
// Restaurar presentación lógica original
|
// Restaurar presentación lógica base
|
||||||
SDL_SetRenderLogicalPresentation(renderer_, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
SDL_SetRenderLogicalPresentation(renderer_, base_screen_width_, base_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
||||||
|
|
||||||
// Reinicar la escena con resolución original
|
// Reinicar la escena con resolución original
|
||||||
initBalls(scenario_);
|
initBalls(scenario_);
|
||||||
@@ -948,7 +991,7 @@ void Engine::toggleIntegerScaling() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetRenderLogicalPresentation(renderer_, SCREEN_WIDTH, SCREEN_HEIGHT, presentation);
|
SDL_SetRenderLogicalPresentation(renderer_, current_screen_width_, current_screen_height_, presentation);
|
||||||
|
|
||||||
// Mostrar texto informativo
|
// Mostrar texto informativo
|
||||||
text_ = "SCALING: ";
|
text_ = "SCALING: ";
|
||||||
@@ -960,11 +1003,16 @@ void Engine::toggleIntegerScaling() {
|
|||||||
|
|
||||||
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
|
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case GravityDirection::DOWN: return "DOWN";
|
case GravityDirection::DOWN:
|
||||||
case GravityDirection::UP: return "UP";
|
return "DOWN";
|
||||||
case GravityDirection::LEFT: return "LEFT";
|
case GravityDirection::UP:
|
||||||
case GravityDirection::RIGHT: return "RIGHT";
|
return "UP";
|
||||||
default: return "UNKNOWN";
|
case GravityDirection::LEFT:
|
||||||
|
return "LEFT";
|
||||||
|
case GravityDirection::RIGHT:
|
||||||
|
return "RIGHT";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,8 +1025,8 @@ void Engine::renderGradientBackground() {
|
|||||||
|
|
||||||
if (transitioning_) {
|
if (transitioning_) {
|
||||||
// Interpolar entre tema actual y tema destino
|
// Interpolar entre tema actual y tema destino
|
||||||
ThemeColors ¤t = themes_[static_cast<int>(current_theme_)];
|
ThemeColors& current = themes_[static_cast<int>(current_theme_)];
|
||||||
ThemeColors &target = themes_[static_cast<int>(target_theme_)];
|
ThemeColors& target = themes_[static_cast<int>(target_theme_)];
|
||||||
|
|
||||||
top_r = lerp(current.bg_top_r, target.bg_top_r, transition_progress_);
|
top_r = lerp(current.bg_top_r, target.bg_top_r, transition_progress_);
|
||||||
top_g = lerp(current.bg_top_g, target.bg_top_g, transition_progress_);
|
top_g = lerp(current.bg_top_g, target.bg_top_g, transition_progress_);
|
||||||
@@ -989,7 +1037,7 @@ void Engine::renderGradientBackground() {
|
|||||||
bottom_b = lerp(current.bg_bottom_b, target.bg_bottom_b, transition_progress_);
|
bottom_b = lerp(current.bg_bottom_b, target.bg_bottom_b, transition_progress_);
|
||||||
} else {
|
} else {
|
||||||
// Sin transición: usar tema actual directamente
|
// Sin transición: usar tema actual directamente
|
||||||
ThemeColors &theme = themes_[static_cast<int>(current_theme_)];
|
ThemeColors& theme = themes_[static_cast<int>(current_theme_)];
|
||||||
top_r = theme.bg_top_r;
|
top_r = theme.bg_top_r;
|
||||||
top_g = theme.bg_top_g;
|
top_g = theme.bg_top_g;
|
||||||
top_b = theme.bg_top_b;
|
top_b = theme.bg_top_b;
|
||||||
@@ -1081,20 +1129,20 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g,
|
|||||||
int Engine::calculateMaxWindowZoom() const {
|
int Engine::calculateMaxWindowZoom() const {
|
||||||
// Obtener información del display usando el método de Coffee Crisis
|
// Obtener información del display usando el método de Coffee Crisis
|
||||||
int num_displays = 0;
|
int num_displays = 0;
|
||||||
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
||||||
if (displays == nullptr || num_displays == 0) {
|
if (displays == nullptr || num_displays == 0) {
|
||||||
return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener
|
return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener el modo de display actual
|
// Obtener el modo de display actual
|
||||||
const auto *dm = SDL_GetCurrentDisplayMode(displays[0]);
|
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
||||||
if (dm == nullptr) {
|
if (dm == nullptr) {
|
||||||
SDL_free(displays);
|
SDL_free(displays);
|
||||||
return WINDOW_ZOOM_MIN;
|
return WINDOW_ZOOM_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular zoom máximo usando la fórmula de Coffee Crisis
|
// Calcular zoom máximo usando la fórmula de Coffee Crisis
|
||||||
const int MAX_ZOOM = std::min(dm->w / SCREEN_WIDTH, (dm->h - WINDOW_DECORATION_HEIGHT) / SCREEN_HEIGHT);
|
const int MAX_ZOOM = std::min(dm->w / base_screen_width_, (dm->h - WINDOW_DECORATION_HEIGHT) / base_screen_height_);
|
||||||
|
|
||||||
SDL_free(displays);
|
SDL_free(displays);
|
||||||
|
|
||||||
@@ -1114,12 +1162,12 @@ void Engine::setWindowZoom(int new_zoom) {
|
|||||||
// Obtener posición actual del centro de la ventana
|
// Obtener posición actual del centro de la ventana
|
||||||
int current_x, current_y;
|
int current_x, current_y;
|
||||||
SDL_GetWindowPosition(window_, ¤t_x, ¤t_y);
|
SDL_GetWindowPosition(window_, ¤t_x, ¤t_y);
|
||||||
int current_center_x = current_x + (SCREEN_WIDTH * current_window_zoom_) / 2;
|
int current_center_x = current_x + (base_screen_width_ * current_window_zoom_) / 2;
|
||||||
int current_center_y = current_y + (SCREEN_HEIGHT * current_window_zoom_) / 2;
|
int current_center_y = current_y + (base_screen_height_ * current_window_zoom_) / 2;
|
||||||
|
|
||||||
// Calcular nuevo tamaño
|
// Calcular nuevo tamaño
|
||||||
int new_width = SCREEN_WIDTH * new_zoom;
|
int new_width = base_screen_width_ * new_zoom;
|
||||||
int new_height = SCREEN_HEIGHT * new_zoom;
|
int new_height = base_screen_height_ * new_zoom;
|
||||||
|
|
||||||
// Calcular nueva posición (centrada en el punto actual)
|
// Calcular nueva posición (centrada en el punto actual)
|
||||||
int new_x = current_center_x - new_width / 2;
|
int new_x = current_center_x - new_width / 2;
|
||||||
@@ -1156,80 +1204,117 @@ void Engine::zoomOut() {
|
|||||||
void Engine::initializeThemes() {
|
void Engine::initializeThemes() {
|
||||||
// SUNSET: Naranjas, rojos, amarillos, rosas (8 colores)
|
// SUNSET: Naranjas, rojos, amarillos, rosas (8 colores)
|
||||||
themes_[0] = {
|
themes_[0] = {
|
||||||
"SUNSET", "ATARDECER", // Nombres (inglés, español)
|
"SUNSET",
|
||||||
255, 140, 60, // Color texto: naranja cálido
|
"ATARDECER", // Nombres (inglés, español)
|
||||||
180.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo superior (naranja suave)
|
255,
|
||||||
40.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior (púrpura oscuro)
|
140,
|
||||||
{{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}}
|
60, // Color texto: naranja cálido
|
||||||
};
|
180.0f / 255.0f,
|
||||||
|
140.0f / 255.0f,
|
||||||
|
100.0f / 255.0f, // Fondo superior (naranja suave)
|
||||||
|
40.0f / 255.0f,
|
||||||
|
20.0f / 255.0f,
|
||||||
|
60.0f / 255.0f, // Fondo inferior (púrpura oscuro)
|
||||||
|
{{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}}};
|
||||||
|
|
||||||
// OCEAN: Azules, turquesas, blancos (8 colores)
|
// OCEAN: Azules, turquesas, blancos (8 colores)
|
||||||
themes_[1] = {
|
themes_[1] = {
|
||||||
"OCEAN", "OCEANO", // Nombres (inglés, español)
|
"OCEAN",
|
||||||
80, 200, 255, // Color texto: azul océano
|
"OCEANO", // Nombres (inglés, español)
|
||||||
100.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo superior (azul cielo)
|
80,
|
||||||
20.0f / 255.0f, 40.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior (azul marino)
|
200,
|
||||||
{{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}}
|
255, // Color texto: azul océano
|
||||||
};
|
100.0f / 255.0f,
|
||||||
|
150.0f / 255.0f,
|
||||||
|
200.0f / 255.0f, // Fondo superior (azul cielo)
|
||||||
|
20.0f / 255.0f,
|
||||||
|
40.0f / 255.0f,
|
||||||
|
80.0f / 255.0f, // Fondo inferior (azul marino)
|
||||||
|
{{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}}};
|
||||||
|
|
||||||
// NEON: Cian, magenta, verde lima, amarillo vibrante (8 colores)
|
// NEON: Cian, magenta, verde lima, amarillo vibrante (8 colores)
|
||||||
themes_[2] = {
|
themes_[2] = {
|
||||||
"NEON", "NEON", // Nombres (inglés, español)
|
"NEON",
|
||||||
255, 60, 255, // Color texto: magenta brillante
|
"NEON", // Nombres (inglés, español)
|
||||||
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior (negro azulado)
|
255,
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro)
|
60,
|
||||||
{{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}}
|
255, // Color texto: magenta brillante
|
||||||
};
|
20.0f / 255.0f,
|
||||||
|
20.0f / 255.0f,
|
||||||
|
40.0f / 255.0f, // Fondo superior (negro azulado)
|
||||||
|
0.0f / 255.0f,
|
||||||
|
0.0f / 255.0f,
|
||||||
|
0.0f / 255.0f, // Fondo inferior (negro)
|
||||||
|
{{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}}};
|
||||||
|
|
||||||
// FOREST: Verdes, marrones, amarillos otoño (8 colores)
|
// FOREST: Verdes, marrones, amarillos otoño (8 colores)
|
||||||
themes_[3] = {
|
themes_[3] = {
|
||||||
"FOREST", "BOSQUE", // Nombres (inglés, español)
|
"FOREST",
|
||||||
100, 255, 100, // Color texto: verde natural
|
"BOSQUE", // Nombres (inglés, español)
|
||||||
144.0f / 255.0f, 238.0f / 255.0f, 144.0f / 255.0f, // Fondo superior (verde claro)
|
100,
|
||||||
101.0f / 255.0f, 67.0f / 255.0f, 33.0f / 255.0f, // Fondo inferior (marrón tierra)
|
255,
|
||||||
{{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}}
|
100, // Color texto: verde natural
|
||||||
};
|
144.0f / 255.0f,
|
||||||
|
238.0f / 255.0f,
|
||||||
|
144.0f / 255.0f, // Fondo superior (verde claro)
|
||||||
|
101.0f / 255.0f,
|
||||||
|
67.0f / 255.0f,
|
||||||
|
33.0f / 255.0f, // Fondo inferior (marrón tierra)
|
||||||
|
{{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}}};
|
||||||
|
|
||||||
// RGB: Círculo cromático con 24 puntos (cada 15°) - Ultra precisión matemática
|
// RGB: Círculo cromático con 24 puntos (cada 15°) - Ultra precisión matemática
|
||||||
themes_[4] = {
|
themes_[4] = {
|
||||||
"RGB", "RGB", // Nombres (inglés, español)
|
"RGB",
|
||||||
100, 100, 100, // Color texto: gris oscuro (contraste con fondo blanco)
|
"RGB", // Nombres (inglés, español)
|
||||||
1.0f, 1.0f, 1.0f, // Fondo superior (blanco puro)
|
100,
|
||||||
1.0f, 1.0f, 1.0f, // Fondo inferior (blanco puro) - sin degradado
|
100,
|
||||||
|
100, // Color texto: gris oscuro (contraste con fondo blanco)
|
||||||
|
1.0f,
|
||||||
|
1.0f,
|
||||||
|
1.0f, // Fondo superior (blanco puro)
|
||||||
|
1.0f,
|
||||||
|
1.0f,
|
||||||
|
1.0f, // Fondo inferior (blanco puro) - sin degradado
|
||||||
{
|
{
|
||||||
{255, 0, 0}, // 0° - Rojo puro
|
{255, 0, 0}, // 0° - Rojo puro
|
||||||
{255, 64, 0}, // 15° - Rojo-Naranja
|
{255, 64, 0}, // 15° - Rojo-Naranja
|
||||||
{255, 128, 0}, // 30° - Naranja
|
{255, 128, 0}, // 30° - Naranja
|
||||||
{255, 191, 0}, // 45° - Naranja-Amarillo
|
{255, 191, 0}, // 45° - Naranja-Amarillo
|
||||||
{255, 255, 0}, // 60° - Amarillo puro
|
{255, 255, 0}, // 60° - Amarillo puro
|
||||||
{191, 255, 0}, // 75° - Amarillo-Verde claro
|
{191, 255, 0}, // 75° - Amarillo-Verde claro
|
||||||
{128, 255, 0}, // 90° - Verde-Amarillo
|
{128, 255, 0}, // 90° - Verde-Amarillo
|
||||||
{64, 255, 0}, // 105° - Verde claro-Amarillo
|
{64, 255, 0}, // 105° - Verde claro-Amarillo
|
||||||
{0, 255, 0}, // 120° - Verde puro
|
{0, 255, 0}, // 120° - Verde puro
|
||||||
{0, 255, 64}, // 135° - Verde-Cian claro
|
{0, 255, 64}, // 135° - Verde-Cian claro
|
||||||
{0, 255, 128}, // 150° - Verde-Cian
|
{0, 255, 128}, // 150° - Verde-Cian
|
||||||
{0, 255, 191}, // 165° - Verde claro-Cian
|
{0, 255, 191}, // 165° - Verde claro-Cian
|
||||||
{0, 255, 255}, // 180° - Cian puro
|
{0, 255, 255}, // 180° - Cian puro
|
||||||
{0, 191, 255}, // 195° - Cian-Azul claro
|
{0, 191, 255}, // 195° - Cian-Azul claro
|
||||||
{0, 128, 255}, // 210° - Azul-Cian
|
{0, 128, 255}, // 210° - Azul-Cian
|
||||||
{0, 64, 255}, // 225° - Azul claro-Cian
|
{0, 64, 255}, // 225° - Azul claro-Cian
|
||||||
{0, 0, 255}, // 240° - Azul puro
|
{0, 0, 255}, // 240° - Azul puro
|
||||||
{64, 0, 255}, // 255° - Azul-Magenta claro
|
{64, 0, 255}, // 255° - Azul-Magenta claro
|
||||||
{128, 0, 255}, // 270° - Azul-Magenta
|
{128, 0, 255}, // 270° - Azul-Magenta
|
||||||
{191, 0, 255}, // 285° - Azul claro-Magenta
|
{191, 0, 255}, // 285° - Azul claro-Magenta
|
||||||
{255, 0, 255}, // 300° - Magenta puro
|
{255, 0, 255}, // 300° - Magenta puro
|
||||||
{255, 0, 191}, // 315° - Magenta-Rojo claro
|
{255, 0, 191}, // 315° - Magenta-Rojo claro
|
||||||
{255, 0, 128}, // 330° - Magenta-Rojo
|
{255, 0, 128}, // 330° - Magenta-Rojo
|
||||||
{255, 0, 64} // 345° - Magenta claro-Rojo
|
{255, 0, 64} // 345° - Magenta claro-Rojo
|
||||||
}
|
}};
|
||||||
};
|
|
||||||
|
|
||||||
// MONOCHROME: Fondo negro degradado, sprites blancos monocromáticos (8 tonos grises)
|
// MONOCHROME: Fondo negro degradado, sprites blancos monocromáticos (8 tonos grises)
|
||||||
themes_[5] = {
|
themes_[5] = {
|
||||||
"MONOCHROME", "MONOCROMO", // Nombres (inglés, español)
|
"MONOCHROME",
|
||||||
200, 200, 200, // Color texto: gris claro
|
"MONOCROMO", // Nombres (inglés, español)
|
||||||
20.0f / 255.0f, 20.0f / 255.0f, 20.0f / 255.0f, // Fondo superior (gris muy oscuro)
|
200,
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro)
|
200,
|
||||||
|
200, // Color texto: gris claro
|
||||||
|
20.0f / 255.0f,
|
||||||
|
20.0f / 255.0f,
|
||||||
|
20.0f / 255.0f, // Fondo superior (gris muy oscuro)
|
||||||
|
0.0f / 255.0f,
|
||||||
|
0.0f / 255.0f,
|
||||||
|
0.0f / 255.0f, // Fondo inferior (negro)
|
||||||
{
|
{
|
||||||
{255, 255, 255}, // Blanco puro - todas las pelotas del mismo color
|
{255, 255, 255}, // Blanco puro - todas las pelotas del mismo color
|
||||||
{255, 255, 255},
|
{255, 255, 255},
|
||||||
@@ -1238,9 +1323,7 @@ void Engine::initializeThemes() {
|
|||||||
{255, 255, 255},
|
{255, 255, 255},
|
||||||
{255, 255, 255},
|
{255, 255, 255},
|
||||||
{255, 255, 255},
|
{255, 255, 255},
|
||||||
{255, 255, 255}
|
{255, 255, 255}}};
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::startThemeTransition(ColorTheme new_theme) {
|
void Engine::startThemeTransition(ColorTheme new_theme) {
|
||||||
@@ -1278,8 +1361,7 @@ Color Engine::getInterpolatedColor(size_t ball_index) const {
|
|||||||
return {
|
return {
|
||||||
static_cast<Uint8>(lerp(static_cast<float>(current_color.r), static_cast<float>(target_color.r), transition_progress_)),
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.r), static_cast<float>(target_color.r), transition_progress_)),
|
||||||
static_cast<Uint8>(lerp(static_cast<float>(current_color.g), static_cast<float>(target_color.g), transition_progress_)),
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.g), static_cast<float>(target_color.g), transition_progress_)),
|
||||||
static_cast<Uint8>(lerp(static_cast<float>(current_color.b), static_cast<float>(target_color.b), transition_progress_))
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.b), static_cast<float>(target_color.b), transition_progress_))};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sistema de Modo DEMO (auto-play)
|
// Sistema de Modo DEMO (auto-play)
|
||||||
@@ -1315,9 +1397,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
|
|
||||||
if (is_lite) {
|
if (is_lite) {
|
||||||
// DEMO LITE: Solo física/figuras
|
// DEMO LITE: Solo física/figuras
|
||||||
TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE
|
TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE + DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS + DEMO_LITE_WEIGHT_IMPULSE;
|
||||||
+ DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS
|
|
||||||
+ DEMO_LITE_WEIGHT_IMPULSE;
|
|
||||||
random_value = rand() % TOTAL_WEIGHT;
|
random_value = rand() % TOTAL_WEIGHT;
|
||||||
|
|
||||||
// Cambiar dirección gravedad (25%)
|
// Cambiar dirección gravedad (25%)
|
||||||
@@ -1338,9 +1418,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
// Activar figura 3D (25%)
|
// Activar figura 3D (25%)
|
||||||
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
||||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
||||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
||||||
int shape_index = rand() % 9;
|
int shape_index = rand() % 9;
|
||||||
activateShape(shapes[shape_index]);
|
activateShape(shapes[shape_index]);
|
||||||
return;
|
return;
|
||||||
@@ -1362,10 +1440,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// DEMO COMPLETO: Todas las acciones
|
// DEMO COMPLETO: Todas las acciones
|
||||||
TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE
|
TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE + DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
||||||
+ DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME
|
|
||||||
+ DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM
|
|
||||||
+ DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
|
||||||
random_value = rand() % TOTAL_WEIGHT;
|
random_value = rand() % TOTAL_WEIGHT;
|
||||||
|
|
||||||
// Cambiar dirección gravedad (10%)
|
// Cambiar dirección gravedad (10%)
|
||||||
@@ -1386,9 +1461,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|||||||
// Activar figura 3D (20%)
|
// Activar figura 3D (20%)
|
||||||
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
||||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
||||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
||||||
int shape_index = rand() % 9;
|
int shape_index = rand() % 9;
|
||||||
activateShape(shapes[shape_index]);
|
activateShape(shapes[shape_index]);
|
||||||
return;
|
return;
|
||||||
@@ -1483,9 +1556,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
||||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
||||||
activateShape(shapes[rand() % 8]);
|
activateShape(shapes[rand() % 8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1521,9 +1592,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
||||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
||||||
activateShape(shapes[rand() % 8]);
|
activateShape(shapes[rand() % 8]);
|
||||||
|
|
||||||
// 5. Profundidad (solo si estamos en figura)
|
// 5. Profundidad (solo si estamos en figura)
|
||||||
@@ -1774,9 +1843,7 @@ void Engine::updateShape() {
|
|||||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||||
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
||||||
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
||||||
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_,
|
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_, SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
||||||
SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR,
|
|
||||||
SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
|
||||||
|
|
||||||
// Calcular brillo según profundidad Z para renderizado
|
// Calcular brillo según profundidad Z para renderizado
|
||||||
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
||||||
|
|||||||
280
source/engine.h
280
source/engine.h
@@ -1,162 +1,176 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL3/SDL_events.h> // for SDL_Event
|
#include <SDL3/SDL_events.h> // for SDL_Event
|
||||||
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
||||||
#include <SDL3/SDL_stdinc.h> // for Uint64
|
#include <SDL3/SDL_stdinc.h> // for Uint64
|
||||||
#include <SDL3/SDL_video.h> // for SDL_Window
|
#include <SDL3/SDL_video.h> // for SDL_Window
|
||||||
|
|
||||||
#include <array> // for array
|
#include <array> // for array
|
||||||
#include <memory> // for unique_ptr, shared_ptr
|
#include <memory> // for unique_ptr, shared_ptr
|
||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
#include "ball.h" // for Ball
|
||||||
#include "ball.h" // for Ball
|
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
public:
|
public:
|
||||||
// Interfaz pública
|
// Interfaz pública
|
||||||
bool initialize(int width = 0, int height = 0, bool fullscreen = false);
|
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false);
|
||||||
void run();
|
void run();
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Recursos SDL
|
// Recursos SDL
|
||||||
SDL_Window* window_ = nullptr;
|
SDL_Window* window_ = nullptr;
|
||||||
SDL_Renderer* renderer_ = nullptr;
|
SDL_Renderer* renderer_ = nullptr;
|
||||||
std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
|
std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
|
||||||
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
|
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
|
||||||
std::vector<std::string> texture_names_; // Nombres de texturas (sin extensión)
|
std::vector<std::string> texture_names_; // Nombres de texturas (sin extensión)
|
||||||
size_t current_texture_index_ = 0; // Índice de textura activa
|
size_t current_texture_index_ = 0; // Índice de textura activa
|
||||||
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture)
|
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture)
|
||||||
|
|
||||||
// Estado del simulador
|
// Estado del simulador
|
||||||
std::vector<std::unique_ptr<Ball>> balls_;
|
std::vector<std::unique_ptr<Ball>> balls_;
|
||||||
std::array<int, 8> test_ = {1, 10, 100, 500, 1000, 10000, 50000, 100000};
|
GravityDirection current_gravity_ = GravityDirection::DOWN;
|
||||||
GravityDirection current_gravity_ = GravityDirection::DOWN;
|
int scenario_ = 0;
|
||||||
int scenario_ = 0;
|
bool should_exit_ = false;
|
||||||
bool should_exit_ = false;
|
|
||||||
|
|
||||||
// Sistema de timing
|
// Sistema de timing
|
||||||
Uint64 last_frame_time_ = 0;
|
Uint64 last_frame_time_ = 0;
|
||||||
float delta_time_ = 0.0f;
|
float delta_time_ = 0.0f;
|
||||||
|
|
||||||
// UI y debug
|
// UI y debug
|
||||||
bool show_debug_ = false;
|
bool show_debug_ = false;
|
||||||
bool show_text_ = true;
|
bool show_text_ = true;
|
||||||
|
|
||||||
// Sistema de zoom dinámico
|
// Sistema de zoom dinámico
|
||||||
int current_window_zoom_ = WINDOW_ZOOM;
|
int current_window_zoom_ = DEFAULT_WINDOW_ZOOM;
|
||||||
std::string text_;
|
std::string text_;
|
||||||
int text_pos_ = 0;
|
int text_pos_ = 0;
|
||||||
Uint64 text_init_time_ = 0;
|
Uint64 text_init_time_ = 0;
|
||||||
|
|
||||||
// FPS y V-Sync
|
// FPS y V-Sync
|
||||||
Uint64 fps_last_time_ = 0;
|
Uint64 fps_last_time_ = 0;
|
||||||
int fps_frame_count_ = 0;
|
int fps_frame_count_ = 0;
|
||||||
int fps_current_ = 0;
|
int fps_current_ = 0;
|
||||||
std::string fps_text_ = "FPS: 0";
|
std::string fps_text_ = "FPS: 0";
|
||||||
bool vsync_enabled_ = true;
|
bool vsync_enabled_ = true;
|
||||||
std::string vsync_text_ = "VSYNC ON";
|
std::string vsync_text_ = "VSYNC ON";
|
||||||
bool fullscreen_enabled_ = false;
|
bool fullscreen_enabled_ = false;
|
||||||
bool real_fullscreen_enabled_ = false;
|
bool real_fullscreen_enabled_ = false;
|
||||||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
||||||
|
|
||||||
// Resolución dinámica para modo real fullscreen
|
// Resolución base (configurada por CLI o default)
|
||||||
int current_screen_width_ = SCREEN_WIDTH;
|
int base_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||||
int current_screen_height_ = SCREEN_HEIGHT;
|
int base_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||||
|
|
||||||
// Sistema de temas
|
// Resolución dinámica actual (cambia en fullscreen real)
|
||||||
ColorTheme current_theme_ = ColorTheme::SUNSET;
|
int current_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||||
ColorTheme target_theme_ = ColorTheme::SUNSET; // Tema destino para transición
|
int current_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||||
bool transitioning_ = false; // ¿Estamos en transición?
|
|
||||||
float transition_progress_ = 0.0f; // Progreso de 0.0 a 1.0
|
|
||||||
float transition_duration_ = 0.5f; // Duración en segundos
|
|
||||||
|
|
||||||
// Estructura de tema de colores
|
// Sistema de temas
|
||||||
struct ThemeColors {
|
ColorTheme current_theme_ = ColorTheme::SUNSET;
|
||||||
const char* name_en; // Nombre en inglés (para debug)
|
ColorTheme target_theme_ = ColorTheme::SUNSET; // Tema destino para transición
|
||||||
const char* name_es; // Nombre en español (para display)
|
bool transitioning_ = false; // ¿Estamos en transición?
|
||||||
int text_color_r, text_color_g, text_color_b; // Color del texto del tema
|
float transition_progress_ = 0.0f; // Progreso de 0.0 a 1.0
|
||||||
float bg_top_r, bg_top_g, bg_top_b;
|
float transition_duration_ = 0.5f; // Duración en segundos
|
||||||
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
|
||||||
std::vector<Color> ball_colors;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Temas de colores definidos
|
// Estructura de tema de colores
|
||||||
ThemeColors themes_[6]; // 6 temas: SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME
|
struct ThemeColors {
|
||||||
|
const char* name_en; // Nombre en inglés (para debug)
|
||||||
|
const char* name_es; // Nombre en español (para display)
|
||||||
|
int text_color_r, text_color_g, text_color_b; // Color del texto del tema
|
||||||
|
float bg_top_r, bg_top_g, bg_top_b;
|
||||||
|
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
||||||
|
std::vector<Color> ball_colors;
|
||||||
|
};
|
||||||
|
|
||||||
// Sistema de Figuras 3D (polimórfico)
|
// Temas de colores definidos
|
||||||
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
ThemeColors themes_[6]; // 6 temas: SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME
|
||||||
ShapeType current_shape_type_ = ShapeType::SPHERE; // Tipo de figura actual
|
|
||||||
ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F
|
|
||||||
std::unique_ptr<Shape> active_shape_; // Puntero polimórfico a figura activa
|
|
||||||
float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-)
|
|
||||||
bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado
|
|
||||||
|
|
||||||
// Sistema de Modo DEMO (auto-play)
|
// Sistema de Figuras 3D (polimórfico)
|
||||||
bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo?
|
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
||||||
bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite?
|
ShapeType current_shape_type_ = ShapeType::SPHERE; // Tipo de figura actual
|
||||||
float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción
|
ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F
|
||||||
float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos)
|
std::unique_ptr<Shape> active_shape_; // Puntero polimórfico a figura activa
|
||||||
|
float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-)
|
||||||
|
bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado
|
||||||
|
|
||||||
// Batch rendering
|
// Sistema de Modo DEMO (auto-play)
|
||||||
std::vector<SDL_Vertex> batch_vertices_;
|
bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo?
|
||||||
std::vector<int> batch_indices_;
|
bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite?
|
||||||
|
bool logo_mode_enabled_ = false; // ¿Está activo el modo logo (easter egg)?
|
||||||
|
float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción
|
||||||
|
float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos)
|
||||||
|
|
||||||
// Métodos principales del loop
|
// Estado previo antes de entrar a Logo Mode (para restaurar al salir)
|
||||||
void calculateDeltaTime();
|
ColorTheme logo_previous_theme_ = ColorTheme::SUNSET;
|
||||||
void update();
|
size_t logo_previous_texture_index_ = 0;
|
||||||
void handleEvents();
|
float logo_previous_shape_scale_ = 1.0f;
|
||||||
void render();
|
|
||||||
|
|
||||||
// Métodos auxiliares
|
// Batch rendering
|
||||||
void initBalls(int value);
|
std::vector<SDL_Vertex> batch_vertices_;
|
||||||
void setText();
|
std::vector<int> batch_indices_;
|
||||||
void pushBallsAwayFromGravity();
|
|
||||||
void switchBallsGravity();
|
|
||||||
void enableBallsGravityIfDisabled();
|
|
||||||
void forceBallsGravityOn();
|
|
||||||
void forceBallsGravityOff();
|
|
||||||
void changeGravityDirection(GravityDirection direction);
|
|
||||||
void toggleVSync();
|
|
||||||
void toggleFullscreen();
|
|
||||||
void toggleRealFullscreen();
|
|
||||||
void toggleIntegerScaling();
|
|
||||||
std::string gravityDirectionToString(GravityDirection direction) const;
|
|
||||||
void initializeThemes();
|
|
||||||
|
|
||||||
// Sistema de Modo DEMO
|
// Métodos principales del loop
|
||||||
void updateDemoMode();
|
void calculateDeltaTime();
|
||||||
void performDemoAction(bool is_lite);
|
void update();
|
||||||
void randomizeOnDemoStart(bool is_lite);
|
void handleEvents();
|
||||||
void toggleGravityOnOff();
|
void render();
|
||||||
|
|
||||||
// Sistema de transiciones LERP
|
// Métodos auxiliares
|
||||||
float lerp(float a, float b, float t) const { return a + (b - a) * t; }
|
void initBalls(int value);
|
||||||
Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición
|
void setText();
|
||||||
void startThemeTransition(ColorTheme new_theme);
|
void pushBallsAwayFromGravity();
|
||||||
|
void switchBallsGravity();
|
||||||
|
void enableBallsGravityIfDisabled();
|
||||||
|
void forceBallsGravityOn();
|
||||||
|
void forceBallsGravityOff();
|
||||||
|
void changeGravityDirection(GravityDirection direction);
|
||||||
|
void toggleVSync();
|
||||||
|
void toggleFullscreen();
|
||||||
|
void toggleRealFullscreen();
|
||||||
|
void toggleIntegerScaling();
|
||||||
|
std::string gravityDirectionToString(GravityDirection direction) const;
|
||||||
|
void initializeThemes();
|
||||||
|
|
||||||
// Sistema de cambio de sprites dinámico
|
// Sistema de Modo DEMO
|
||||||
void switchTexture(); // Cambia a siguiente textura disponible
|
void updateDemoMode();
|
||||||
void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño
|
void performDemoAction(bool is_lite);
|
||||||
|
void randomizeOnDemoStart(bool is_lite);
|
||||||
|
void toggleGravityOnOff();
|
||||||
|
|
||||||
// Sistema de zoom dinámico
|
// Sistema de Modo Logo (easter egg)
|
||||||
int calculateMaxWindowZoom() const;
|
void toggleLogoMode(); // Activar/desactivar modo logo manual (tecla K)
|
||||||
void setWindowZoom(int new_zoom);
|
void enterLogoMode(bool from_demo = false); // Entrar al modo logo (manual o automático)
|
||||||
void zoomIn();
|
void exitLogoMode(bool return_to_demo = false); // Salir del modo logo
|
||||||
void zoomOut();
|
|
||||||
|
|
||||||
// Rendering
|
// Sistema de transiciones LERP
|
||||||
void renderGradientBackground();
|
float lerp(float a, float b, float t) const { return a + (b - a) * t; }
|
||||||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f);
|
Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición
|
||||||
|
void startThemeTransition(ColorTheme new_theme);
|
||||||
|
|
||||||
// Sistema de Figuras 3D
|
// Sistema de cambio de sprites dinámico
|
||||||
void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F)
|
void switchTexture(); // Cambia a siguiente textura disponible
|
||||||
void activateShape(ShapeType type); // Activar figura específica (teclas Q/W/E/R/Y/U/I)
|
void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño
|
||||||
void updateShape(); // Actualizar figura activa
|
|
||||||
void generateShape(); // Generar puntos de figura activa
|
// Sistema de zoom dinámico
|
||||||
void clampShapeScale(); // Limitar escala para evitar clipping
|
int calculateMaxWindowZoom() const;
|
||||||
|
void setWindowZoom(int new_zoom);
|
||||||
|
void zoomIn();
|
||||||
|
void zoomOut();
|
||||||
|
|
||||||
|
// Rendering
|
||||||
|
void renderGradientBackground();
|
||||||
|
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f);
|
||||||
|
|
||||||
|
// Sistema de Figuras 3D
|
||||||
|
void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F)
|
||||||
|
void activateShape(ShapeType type); // Activar figura específica (teclas Q/W/E/R/Y/U/I)
|
||||||
|
void updateShape(); // Actualizar figura activa
|
||||||
|
void generateShape(); // Generar puntos de figura activa
|
||||||
|
void clampShapeScale(); // Limitar escala para evitar clipping
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,19 +6,23 @@ void printHelp() {
|
|||||||
std::cout << "ViBe3 Physics - Simulador de físicas avanzadas\n";
|
std::cout << "ViBe3 Physics - Simulador de físicas avanzadas\n";
|
||||||
std::cout << "\nUso: vibe3_physics [opciones]\n\n";
|
std::cout << "\nUso: vibe3_physics [opciones]\n\n";
|
||||||
std::cout << "Opciones:\n";
|
std::cout << "Opciones:\n";
|
||||||
std::cout << " -w, --width <px> Ancho de resolución (default: 1280)\n";
|
std::cout << " -w, --width <px> Ancho de resolución (default: 320)\n";
|
||||||
std::cout << " -h, --height <px> Alto de resolución (default: 720)\n";
|
std::cout << " -h, --height <px> Alto de resolución (default: 240)\n";
|
||||||
|
std::cout << " -z, --zoom <n> Zoom de ventana (default: 3)\n";
|
||||||
std::cout << " -f, --fullscreen Modo pantalla completa\n";
|
std::cout << " -f, --fullscreen Modo pantalla completa\n";
|
||||||
std::cout << " --help Mostrar esta ayuda\n\n";
|
std::cout << " --help Mostrar esta ayuda\n\n";
|
||||||
std::cout << "Ejemplos:\n";
|
std::cout << "Ejemplos:\n";
|
||||||
std::cout << " vibe3_physics # 1280x720 ventana\n";
|
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
|
||||||
std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 ventana\n";
|
std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 (auto)\n";
|
||||||
std::cout << " vibe3_physics -w 1920 -h 1080 -f # 1920x1080 fullscreen\n";
|
std::cout << " vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n";
|
||||||
|
std::cout << " vibe3_physics -w 1920 -h 1080 -f # 1920x1080 fullscreen\n\n";
|
||||||
|
std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
int zoom = 0;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
|
|
||||||
// Parsear argumentos
|
// Parsear argumentos
|
||||||
@@ -48,6 +52,17 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cerr << "Error: -h/--height requiere un valor\n";
|
std::cerr << "Error: -h/--height requiere un valor\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--zoom") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
zoom = atoi(argv[++i]);
|
||||||
|
if (zoom < 1) {
|
||||||
|
std::cerr << "Error: Zoom mínimo es 1\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: -z/--zoom requiere un valor\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) {
|
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) {
|
||||||
fullscreen = true;
|
fullscreen = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -59,7 +74,7 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
Engine engine;
|
Engine engine;
|
||||||
|
|
||||||
if (!engine.initialize(width, height, fullscreen)) {
|
if (!engine.initialize(width, height, zoom, fullscreen)) {
|
||||||
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
PNGShape::PNGShape(const char* png_path) {
|
PNGShape::PNGShape(const char* png_path) {
|
||||||
// Cargar PNG desde path
|
// Cargar PNG desde path
|
||||||
@@ -98,14 +99,118 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
|
|||||||
extrusion_depth_ = screen_height * PNG_EXTRUSION_DEPTH_FACTOR;
|
extrusion_depth_ = screen_height * PNG_EXTRUSION_DEPTH_FACTOR;
|
||||||
num_layers_ = PNG_NUM_EXTRUSION_LAYERS;
|
num_layers_ = PNG_NUM_EXTRUSION_LAYERS;
|
||||||
|
|
||||||
// Generar puntos según el enfoque
|
// Generar AMBOS conjuntos de puntos (relleno Y bordes)
|
||||||
generateExtrudedPoints();
|
floodFill(); // Generar filled_points_
|
||||||
|
detectEdges(); // Generar edge_points_
|
||||||
|
|
||||||
// Debug: mostrar cantidad de puntos 2D detectados
|
// Guardar copias originales (las funciones de filtrado modifican los vectores)
|
||||||
size_t num_2d_points = PNG_USE_EDGES_ONLY ? edge_points_.size() : filled_points_.size();
|
std::vector<Point2D> filled_points_original = filled_points_;
|
||||||
size_t total_3d_points = num_2d_points * num_layers_;
|
std::vector<Point2D> edge_points_original = edge_points_;
|
||||||
std::cout << "[PNG_SHAPE] Detectados " << num_2d_points << " puntos 2D × "
|
|
||||||
<< num_layers_ << " capas = " << total_3d_points << " puntos 3D totales\n";
|
// Conjunto de puntos ACTIVO (será modificado por filtros)
|
||||||
|
std::vector<Point2D> active_points_data;
|
||||||
|
std::string mode_name = "";
|
||||||
|
|
||||||
|
// === SISTEMA DE DISTRIBUCIÓN ADAPTATIVA ===
|
||||||
|
// Estrategia: Optimizar según número de pelotas disponibles
|
||||||
|
// Objetivo: SIEMPRE intentar usar relleno primero, solo bordes si es necesario
|
||||||
|
|
||||||
|
size_t num_2d_points = 0;
|
||||||
|
size_t total_3d_points = 0;
|
||||||
|
|
||||||
|
// NIVEL 1: Decidir punto de partida (relleno o bordes por configuración)
|
||||||
|
if (PNG_USE_EDGES_ONLY) {
|
||||||
|
active_points_data = edge_points_original;
|
||||||
|
mode_name = "BORDES (config)";
|
||||||
|
} else {
|
||||||
|
active_points_data = filled_points_original;
|
||||||
|
mode_name = "RELLENO";
|
||||||
|
}
|
||||||
|
|
||||||
|
num_2d_points = active_points_data.size();
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 1: Modo inicial " << mode_name
|
||||||
|
<< " (puntos 2D: " << num_2d_points << ", capas: " << num_layers_
|
||||||
|
<< ", total 3D: " << total_3d_points << ")\n";
|
||||||
|
std::cout << "[PNG_SHAPE] Pelotas disponibles: " << num_points << "\n";
|
||||||
|
|
||||||
|
// NIVEL 2: Reducir capas AGRESIVAMENTE hasta 1 (priorizar calidad 2D sobre profundidad 3D)
|
||||||
|
// Objetivo: Llenar bien el texto en 2D antes de reducir píxeles
|
||||||
|
while (num_layers_ > 1 && num_points < static_cast<int>(total_3d_points)) {
|
||||||
|
num_layers_ = std::max(1, num_layers_ / 2);
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 2: Reduciendo capas a " << num_layers_
|
||||||
|
<< " (total 3D: " << total_3d_points << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NIVEL 3: Filas alternas en RELLENO (solo si 1 capa no alcanza)
|
||||||
|
// Esto permite usar relleno incluso con pocas pelotas
|
||||||
|
int row_skip = 1;
|
||||||
|
if (!PNG_USE_EDGES_ONLY) { // Solo si empezamos con relleno
|
||||||
|
while (row_skip < 5 && num_points < static_cast<int>(total_3d_points)) {
|
||||||
|
row_skip++;
|
||||||
|
// ✅ CLAVE: Recalcular desde el ORIGINAL cada vez (no desde el filtrado previo)
|
||||||
|
active_points_data = extractAlternateRows(filled_points_original, row_skip);
|
||||||
|
num_2d_points = active_points_data.size();
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 3: Filas alternas RELLENO (cada " << row_skip
|
||||||
|
<< " filas, puntos 2D: " << num_2d_points << ", total 3D: " << total_3d_points << ")\n";
|
||||||
|
mode_name = "RELLENO + FILAS/" + std::to_string(row_skip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NIVEL 4: Cambiar a BORDES (solo si relleno con filas alternas no alcanza)
|
||||||
|
if (!PNG_USE_EDGES_ONLY && num_points < static_cast<int>(total_3d_points)) {
|
||||||
|
active_points_data = edge_points_original;
|
||||||
|
mode_name = "BORDES (auto)";
|
||||||
|
num_2d_points = active_points_data.size();
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
row_skip = 1; // Reset row_skip para bordes
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 4: Cambiando a BORDES (pelotas: " << num_points
|
||||||
|
<< ", necesarias: " << total_3d_points << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NIVEL 5: Filas alternas en BORDES (si aún no alcanza)
|
||||||
|
while (row_skip < 8 && num_points < static_cast<int>(total_3d_points)) {
|
||||||
|
row_skip++;
|
||||||
|
// ✅ CLAVE: Recalcular desde edge_points_original cada vez
|
||||||
|
active_points_data = extractAlternateRows(edge_points_original, row_skip);
|
||||||
|
num_2d_points = active_points_data.size();
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 5: Filas alternas BORDES (cada " << row_skip
|
||||||
|
<< " filas, puntos 2D: " << num_2d_points << ", total 3D: " << total_3d_points << ")\n";
|
||||||
|
if (mode_name.find("FILAS") == std::string::npos) {
|
||||||
|
mode_name += " + FILAS/" + std::to_string(row_skip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NIVEL 6: Vértices/esquinas (último recurso, muy pocas pelotas)
|
||||||
|
if (num_points < static_cast<int>(total_3d_points) && num_points < 150) {
|
||||||
|
// Determinar desde qué conjunto extraer vértices (el que esté activo actualmente)
|
||||||
|
const std::vector<Point2D>& source_for_vertices = (mode_name.find("BORDES") != std::string::npos)
|
||||||
|
? edge_points_original
|
||||||
|
: filled_points_original;
|
||||||
|
|
||||||
|
std::vector<Point2D> vertices = extractCornerVertices(source_for_vertices);
|
||||||
|
if (!vertices.empty() && vertices.size() < active_points_data.size()) {
|
||||||
|
active_points_data = vertices;
|
||||||
|
num_2d_points = active_points_data.size();
|
||||||
|
total_3d_points = num_2d_points * num_layers_;
|
||||||
|
mode_name = "VÉRTICES";
|
||||||
|
std::cout << "[PNG_SHAPE] Nivel 6: Solo vértices (puntos 2D: " << num_2d_points << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CLAVE: Guardar el conjunto de puntos optimizado final en optimized_points_ (usado por getPoint3D)
|
||||||
|
optimized_points_ = active_points_data;
|
||||||
|
|
||||||
|
// Debug: mostrar configuración final
|
||||||
|
std::cout << "[PNG_SHAPE] === CONFIGURACIÓN FINAL ===\n";
|
||||||
|
std::cout << "[PNG_SHAPE] Modo: " << mode_name << "\n";
|
||||||
|
std::cout << "[PNG_SHAPE] Píxeles 2D: " << num_2d_points << "\n";
|
||||||
|
std::cout << "[PNG_SHAPE] Capas extrusión: " << num_layers_ << "\n";
|
||||||
|
std::cout << "[PNG_SHAPE] Total puntos 3D: " << total_3d_points << "\n";
|
||||||
std::cout << "[PNG_SHAPE] Pelotas disponibles: " << num_points << "\n";
|
std::cout << "[PNG_SHAPE] Pelotas disponibles: " << num_points << "\n";
|
||||||
std::cout << "[PNG_SHAPE] Ratio: " << (float)num_points / (float)total_3d_points << " pelotas/punto\n";
|
std::cout << "[PNG_SHAPE] Ratio: " << (float)num_points / (float)total_3d_points << " pelotas/punto\n";
|
||||||
|
|
||||||
@@ -118,6 +223,68 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
|
|||||||
center_offset_y_ = image_height_ * 0.5f;
|
center_offset_y_ = image_height_ * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extraer filas alternas de puntos (FUNCIÓN PURA: no modifica parámetros)
|
||||||
|
// Recibe vector original y devuelve nuevo vector filtrado
|
||||||
|
std::vector<PNGShape::Point2D> PNGShape::extractAlternateRows(const std::vector<Point2D>& source, int row_skip) {
|
||||||
|
std::vector<Point2D> result;
|
||||||
|
|
||||||
|
if (row_skip <= 1 || source.empty()) {
|
||||||
|
return source; // Sin filtrado, devolver copia del original
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organizar puntos por fila (Y)
|
||||||
|
std::map<int, std::vector<Point2D>> rows;
|
||||||
|
for (const auto& p : source) {
|
||||||
|
int row = static_cast<int>(p.y);
|
||||||
|
rows[row].push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tomar solo cada N filas
|
||||||
|
int row_counter = 0;
|
||||||
|
for (const auto& [row_y, row_points] : rows) {
|
||||||
|
if (row_counter % row_skip == 0) {
|
||||||
|
result.insert(result.end(), row_points.begin(), row_points.end());
|
||||||
|
}
|
||||||
|
row_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extraer vértices y esquinas (FUNCIÓN PURA: devuelve nuevo vector)
|
||||||
|
std::vector<PNGShape::Point2D> PNGShape::extractCornerVertices(const std::vector<Point2D>& source) {
|
||||||
|
std::vector<Point2D> result;
|
||||||
|
|
||||||
|
if (source.empty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrategia simple: tomar bordes extremos de cada fila
|
||||||
|
// Esto da el "esqueleto" mínimo de las letras
|
||||||
|
|
||||||
|
std::map<int, std::pair<float, float>> row_extremes; // Y -> (min_x, max_x)
|
||||||
|
|
||||||
|
for (const auto& p : source) {
|
||||||
|
int row = static_cast<int>(p.y);
|
||||||
|
if (row_extremes.find(row) == row_extremes.end()) {
|
||||||
|
row_extremes[row] = {p.x, p.x};
|
||||||
|
} else {
|
||||||
|
row_extremes[row].first = std::min(row_extremes[row].first, p.x);
|
||||||
|
row_extremes[row].second = std::max(row_extremes[row].second, p.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generar puntos en extremos de cada fila
|
||||||
|
for (const auto& [row_y, extremes] : row_extremes) {
|
||||||
|
result.push_back({extremes.first, static_cast<float>(row_y)}); // Extremo izquierdo
|
||||||
|
if (extremes.second != extremes.first) { // Solo añadir derecho si es diferente
|
||||||
|
result.push_back({extremes.second, static_cast<float>(row_y)}); // Extremo derecho
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void PNGShape::update(float delta_time, float screen_width, float screen_height) {
|
void PNGShape::update(float delta_time, float screen_width, float screen_height) {
|
||||||
if (!is_flipping_) {
|
if (!is_flipping_) {
|
||||||
// Estado IDLE: texto de frente
|
// Estado IDLE: texto de frente
|
||||||
@@ -159,8 +326,8 @@ void PNGShape::update(float delta_time, float screen_width, float screen_height)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||||
// Seleccionar puntos según configuración
|
// Usar SIEMPRE el vector optimizado (resultado final de generatePoints)
|
||||||
const std::vector<Point2D>& points = PNG_USE_EDGES_ONLY ? edge_points_ : filled_points_;
|
const std::vector<Point2D>& points = optimized_points_;
|
||||||
|
|
||||||
if (points.empty()) {
|
if (points.empty()) {
|
||||||
x = y = z = 0.0f;
|
x = y = z = 0.0f;
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ private:
|
|||||||
struct Point2D {
|
struct Point2D {
|
||||||
float x, y;
|
float x, y;
|
||||||
};
|
};
|
||||||
std::vector<Point2D> edge_points_; // Contorno (solo bordes)
|
std::vector<Point2D> edge_points_; // Contorno (solo bordes) - ORIGINAL sin optimizar
|
||||||
std::vector<Point2D> filled_points_; // Relleno completo (para Enfoque B)
|
std::vector<Point2D> filled_points_; // Relleno completo - ORIGINAL sin optimizar
|
||||||
|
std::vector<Point2D> optimized_points_; // Puntos finales optimizados (usado por getPoint3D)
|
||||||
|
|
||||||
// Parámetros de extrusión
|
// Parámetros de extrusión
|
||||||
float extrusion_depth_ = 0.0f; // Profundidad de extrusión en Z
|
float extrusion_depth_ = 0.0f; // Profundidad de extrusión en Z
|
||||||
@@ -46,6 +47,10 @@ private:
|
|||||||
void floodFill(); // Rellenar interior (Enfoque B - futuro)
|
void floodFill(); // Rellenar interior (Enfoque B - futuro)
|
||||||
void generateExtrudedPoints(); // Generar puntos con extrusión 2D
|
void generateExtrudedPoints(); // Generar puntos con extrusión 2D
|
||||||
|
|
||||||
|
// Métodos de distribución adaptativa (funciones puras, no modifican parámetros)
|
||||||
|
std::vector<Point2D> extractAlternateRows(const std::vector<Point2D>& source, int row_skip); // Extraer filas alternas
|
||||||
|
std::vector<Point2D> extractCornerVertices(const std::vector<Point2D>& source); // Extraer vértices/esquinas
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor: recibe path relativo al PNG
|
// Constructor: recibe path relativo al PNG
|
||||||
PNGShape(const char* png_path = "data/shapes/jailgames.png");
|
PNGShape(const char* png_path = "data/shapes/jailgames.png");
|
||||||
|
|||||||
Reference in New Issue
Block a user