Compare commits
85 Commits
5f89299444
...
2026-03-18
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aa4a1227e | |||
| 02fdcd4113 | |||
| 7db9e46f95 | |||
| ff6aaef7c6 | |||
| 8e2e681b2c | |||
| f06123feff | |||
| cbe6dc9744 | |||
| dfbd8a430b | |||
| ea27a771ab | |||
| 09303537a4 | |||
| df17e85a8a | |||
| ce5c4681b8 | |||
| b79f1c3424 | |||
| a65544e8b3 | |||
| b9264c96a1 | |||
| fa285519b2 | |||
| 8285a8fafe | |||
| 1a555e03f7 | |||
| af3ed6c2b3 | |||
| a9d7b66e83 | |||
| a929df6b73 | |||
| 3f027d953c | |||
| 1354ed82d2 | |||
| 2fd6d99a61 | |||
| 2fa1684f01 | |||
| 41c76316ef | |||
| ce50a29019 | |||
| f25cb96a91 | |||
| d73781be9f | |||
| 288e4813e8 | |||
| 4d3ddec14e | |||
| ec1700b439 | |||
| 8aa2a112b4 | |||
| dfebd8ece4 | |||
| 827d9f0e76 | |||
| df93d5080d | |||
| 0da4b45fef | |||
| db8acf0331 | |||
| 5a35cc1abf | |||
| d30a4fd440 | |||
| 97c0683f6e | |||
| c3d24cc07d | |||
| 7609b9ef5c | |||
| ad3f5a00e4 | |||
| c91cb1ca56 | |||
| 8d608357b4 | |||
| f73a133756 | |||
| de23327861 | |||
| f6402084eb | |||
| 9909d4c12d | |||
| a929346463 | |||
| c4075f68db | |||
| 399650f8da | |||
| 9b8afa1219 | |||
| 5b674c8ea6 | |||
| 7fac103c51 | |||
| bcceb94c9e | |||
| 1b3d32ba84 | |||
| 7c0a60f140 | |||
| 250b1a640d | |||
| 795fa33e50 | |||
| e7dc8f6d13 | |||
| 9cabbd867f | |||
| 8c2a8857fc | |||
| 3d26bfc6fa | |||
| adfa315a43 | |||
| 18a8812ad7 | |||
| 35f29340db | |||
| abbda0f30b | |||
| 6aacb86d6a | |||
| 0873d80765 | |||
| b73e77e9bc | |||
| 1bb8807060 | |||
| 39c0a24a45 | |||
| 01d1ebd2a3 | |||
| 83ea03fda3 | |||
| d62b8e5f52 | |||
| 0fe2efc051 | |||
| 1c38ab2009 | |||
| 8be4c5586d | |||
| e4636c8e82 | |||
| e2a60e4f87 | |||
| e655c643a5 | |||
| f93879b803 | |||
| b8d3c60e58 |
22
.gitignore
vendored
22
.gitignore
vendored
@@ -12,7 +12,6 @@ vibe3_physics.exe
|
||||
*.lib
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Archivos de compilación y enlazado
|
||||
*.d
|
||||
@@ -26,6 +25,7 @@ Build/
|
||||
BUILD/
|
||||
cmake-build-*/
|
||||
.cmake/
|
||||
.cache/
|
||||
|
||||
# Archivos generados por CMake
|
||||
CMakeFiles/
|
||||
@@ -57,7 +57,6 @@ Makefile
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
*.qm
|
||||
.qmake.stash
|
||||
|
||||
@@ -94,4 +93,21 @@ Thumbs.db
|
||||
*.temp
|
||||
|
||||
# Claude Code
|
||||
.claude/
|
||||
.claude/
|
||||
|
||||
# Archivos de recursos empaquetados
|
||||
resources.pack
|
||||
|
||||
# Archivos de distribución (resultados de release)
|
||||
*.zip
|
||||
*.dmg
|
||||
*.tar.gz
|
||||
*.AppImage
|
||||
|
||||
# Carpetas temporales de empaquetado
|
||||
vibe3_release/
|
||||
Frameworks/
|
||||
|
||||
# Binarios de herramientas
|
||||
tools/pack_resources
|
||||
tools/*.exe
|
||||
577
CLAUDE.md
577
CLAUDE.md
@@ -1,577 +0,0 @@
|
||||
# Claude Code Session - ViBe3 Physics
|
||||
|
||||
## Estado del Proyecto
|
||||
|
||||
**Proyecto:** ViBe3 Physics - Simulador de sprites con físicas avanzadas
|
||||
**Objetivo:** Implementar nuevas físicas experimentales expandiendo sobre el sistema de delta time
|
||||
**Base:** Migrado desde vibe1_delta con sistema delta time ya implementado
|
||||
|
||||
## Progreso Actual
|
||||
|
||||
### ✅ Completado
|
||||
|
||||
#### 1. **Migración y Setup Inicial**
|
||||
- ✅ Renombrado vibe1_delta → vibe3_physics en todos los archivos
|
||||
- ✅ Carpeta resources → data
|
||||
- ✅ Actualizado CMakeLists.txt, .gitignore, defines.h, README.md
|
||||
- ✅ Añadido .claude/ al .gitignore
|
||||
- ✅ Sistema de compilación CMake funcionando
|
||||
|
||||
#### 2. **Sistema de Físicas Base (Heredado)**
|
||||
- ✅ **Delta time implementado** - Física independiente del framerate
|
||||
- ✅ Contador FPS en tiempo real (esquina superior derecha, amarillo)
|
||||
- ✅ Control V-Sync dinámico con tecla "V" (ON/OFF)
|
||||
- ✅ Display V-Sync (esquina superior izquierda, cian)
|
||||
- ✅ **Sistema de temas visuales** - 15 temas (9 estáticos + 6 dinámicos con animación)
|
||||
- ✅ **Batch rendering optimizado** - Maneja hasta 100,000 sprites
|
||||
|
||||
#### 3. **NUEVA CARACTERÍSTICA: Gravedad Direccional** 🎯
|
||||
- ✅ **Enum GravityDirection** (UP/DOWN/LEFT/RIGHT) en defines.h
|
||||
- ✅ **Ball class actualizada** para física multi-direccional
|
||||
- ✅ **Detección de superficie inteligente** - Adaptada a cada dirección
|
||||
- ✅ **Fricción direccional** - Se aplica en la superficie correcta
|
||||
- ✅ **Controles de cursor** - Cambio dinámico de gravedad
|
||||
- ✅ **Debug display actualizado** - Muestra dirección actual
|
||||
|
||||
#### 4. **NUEVA CARACTERÍSTICA: Coeficientes de Rebote Variables** ⚡
|
||||
- ✅ **Rango ampliado** - De 0.60-0.89 a 0.30-0.95 (+120% variabilidad)
|
||||
- ✅ **Comportamientos diversos** - Desde pelotas super rebotonas a muy amortiguadas
|
||||
- ✅ **Debug display** - Muestra coeficiente LOSS de primera pelota
|
||||
- ✅ **Física realista** - Elimina sincronización entre pelotas
|
||||
|
||||
#### 5. **🎯 NUEVA CARACTERÍSTICA: Modo RotoBall (Esfera 3D Rotante)** 🌐
|
||||
- ✅ **Fibonacci Sphere Algorithm** - Distribución uniforme de puntos en esfera 3D
|
||||
- ✅ **Rotación dual (X/Y)** - Efecto visual dinámico estilo demoscene
|
||||
- ✅ **Profundidad Z simulada** - Color mod según distancia (oscuro=lejos, brillante=cerca)
|
||||
- ✅ **Física de atracción con resorte** - Sistema de fuerzas con conservación de momento
|
||||
- ✅ **Transición física realista** - Pelotas atraídas a esfera rotante con aceleración
|
||||
- ✅ **Amortiguación variable** - Mayor damping cerca del punto (estabilización)
|
||||
- ✅ **Sin sprites adicionales** - Usa SDL_SetTextureColorMod para profundidad
|
||||
- ✅ **Proyección ortográfica** - Coordenadas 3D → 2D en tiempo real
|
||||
- ✅ **Conservación de inercia** - Al salir mantienen velocidad tangencial
|
||||
- ✅ **Compatible con temas** - Mantiene paleta de colores activa
|
||||
- ✅ **Performance optimizado** - Funciona con 1-100,000 pelotas
|
||||
|
||||
### 📋 Controles Actuales
|
||||
|
||||
| Tecla | Acción |
|
||||
|-------|--------|
|
||||
| **↑** | **Gravedad hacia ARRIBA** |
|
||||
| **↓** | **Gravedad hacia ABAJO** |
|
||||
| **←** | **Gravedad hacia IZQUIERDA** |
|
||||
| **→** | **Gravedad hacia DERECHA** |
|
||||
| **C** | **🌐 MODO ROTOBALL - Toggle esfera 3D rotante** |
|
||||
| V | Alternar V-Sync ON/OFF |
|
||||
| H | **Toggle debug display (FPS, V-Sync, física, gravedad, modo)** |
|
||||
| **Numpad Enter** | **Toggle página de temas (Página 1 ↔ Página 2)** |
|
||||
| **Numpad 1-9, 0** | **Acceso directo a temas según página activa** (ver tablas abajo) |
|
||||
| B | Ciclar entre TODOS los temas de colores (15 temas) - Adelante |
|
||||
| Shift+B | Ciclar entre TODOS los temas de colores - Atrás |
|
||||
| 1-8 | Cambiar número de pelotas (1 a 100,000) |
|
||||
| ESPACIO | Impulsar pelotas hacia arriba |
|
||||
| G | Alternar gravedad ON/OFF (mantiene dirección) |
|
||||
| ESC | Salir |
|
||||
|
||||
### 🎨 Temas de Colores (15 Temas Disponibles - Sistema de 2 Páginas)
|
||||
|
||||
**IMPORTANTE:** Usa **Numpad Enter** para cambiar entre Página 1 y Página 2
|
||||
|
||||
#### **Página 1** (Temas Estáticos + 1 Dinámico)
|
||||
| Tecla | Tema | Tipo | Descripción |
|
||||
|-------|------|------|-------------|
|
||||
| Numpad 1 | ATARDECER | Estático | Naranjas, rojos, amarillos, rosas |
|
||||
| Numpad 2 | OCÉANO | Estático | Azules, turquesas, blancos |
|
||||
| Numpad 3 | NEÓN | Estático | Cian, magenta, verde lima, amarillo vibrante |
|
||||
| Numpad 4 | BOSQUE | Estático | Verdes, marrones, amarillos otoño |
|
||||
| Numpad 5 | RGB | Estático | Círculo cromático 24 colores (fondo blanco) |
|
||||
| Numpad 6 | MONOCROMO | Estático | Fondo negro degradado, sprites blancos |
|
||||
| Numpad 7 | LAVANDA | Estático | Degradado violeta-azul, pelotas amarillo dorado |
|
||||
| Numpad 8 | CARMESÍ | Estático | Fondo negro-rojo, pelotas rojas uniformes |
|
||||
| Numpad 9 | ESMERALDA | Estático | Fondo negro-verde, pelotas verdes uniformes |
|
||||
| Numpad 0 | AMANECER | **Dinámico** | Noche → Alba → Día (loop 12s) |
|
||||
|
||||
#### **Página 2** (Temas Dinámicos Animados)
|
||||
| Tecla | Tema | Tipo | Descripción |
|
||||
|-------|------|------|-------------|
|
||||
| Numpad 1 | OLAS OCEÁNICAS | **Dinámico** | Azul oscuro ↔ Turquesa (loop 8s) |
|
||||
| Numpad 2 | PULSO NEÓN | **Dinámico** | Negro ↔ Neón brillante (ping-pong 3s) |
|
||||
| Numpad 3 | FUEGO | **Dinámico** | Brasas → Llamas → Inferno (loop 10s) |
|
||||
| Numpad 4 | AURORA | **Dinámico** | Verde → Violeta → Cian (loop 14s) |
|
||||
| Numpad 5 | VOLCÁN | **Dinámico** | Ceniza → Erupción → Lava (loop 12s) |
|
||||
| Numpad 6-9, 0 | (sin asignar) | - | Sin función en Página 2 |
|
||||
|
||||
### 🎯 Debug Display (Tecla H)
|
||||
|
||||
Cuando está activado muestra:
|
||||
```
|
||||
FPS: 75 # Esquina superior derecha (amarillo)
|
||||
VSYNC ON # Esquina superior izquierda (cian)
|
||||
GRAV 720 # Magnitud gravedad (magenta)
|
||||
VY -145 # Velocidad Y primera pelota (magenta)
|
||||
SURFACE YES # En superficie (magenta)
|
||||
LOSS 0.73 # Coeficiente rebote primera pelota (magenta)
|
||||
GRAVITY DOWN # Dirección actual (amarillo)
|
||||
THEME SUNSET # Tema activo (amarillo claro)
|
||||
MODE PHYSICS # Modo simulación actual (verde claro) - PHYSICS/ROTOBALL
|
||||
```
|
||||
|
||||
## Arquitectura Actual
|
||||
|
||||
```
|
||||
vibe3_physics/
|
||||
├── source/
|
||||
│ ├── main.cpp # Bucle principal + controles + debug
|
||||
│ ├── ball.h/.cpp # Clase Ball con física direccional
|
||||
│ ├── defines.h # Constantes + enum GravityDirection
|
||||
│ └── external/ # Utilidades externas
|
||||
│ ├── texture.h/.cpp # Gestión texturas + nearest filter
|
||||
│ ├── sprite.h/.cpp # Sistema sprites
|
||||
│ ├── dbgtxt.h # Debug text + nearest filter
|
||||
│ └── stb_image.h # Carga imágenes
|
||||
├── data/ # Recursos (antes resources/)
|
||||
│ └── ball.png # Textura pelota 10x10px
|
||||
├── CMakeLists.txt # Build system
|
||||
└── CLAUDE.md # Este archivo de seguimiento
|
||||
```
|
||||
|
||||
## Sistema de Gravedad Direccional
|
||||
|
||||
### 🔧 Implementación Técnica
|
||||
|
||||
#### Enum y Estados
|
||||
```cpp
|
||||
enum class GravityDirection {
|
||||
DOWN, // ↓ Gravedad hacia abajo (por defecto)
|
||||
UP, // ↑ Gravedad hacia arriba
|
||||
LEFT, // ← Gravedad hacia la izquierda
|
||||
RIGHT // → Gravedad hacia la derecha
|
||||
};
|
||||
```
|
||||
|
||||
#### Lógica de Física por Dirección
|
||||
- **DOWN**: Pelotas caen hacia abajo, fricción en suelo inferior
|
||||
- **UP**: Pelotas "caen" hacia arriba, fricción en techo
|
||||
- **LEFT**: Pelotas "caen" hacia izquierda, fricción en pared izquierda
|
||||
- **RIGHT**: Pelotas "caen" hacia derecha, fricción en pared derecha
|
||||
|
||||
#### Cambios en Ball Class
|
||||
- `on_floor_` → `on_surface_` (más genérico)
|
||||
- `gravity_direction_` (nuevo miembro)
|
||||
- `setGravityDirection()` (nueva función)
|
||||
- `update()` completamente reescrito para lógica direccional
|
||||
|
||||
## Lecciones Aprendidas de ViBe2 Modules
|
||||
|
||||
### ✅ Éxitos de Modularización
|
||||
- **C++20 modules** son viables para código propio
|
||||
- **CMake + Ninja** funciona bien para modules
|
||||
- **Separación clara** de responsabilidades mejora arquitectura
|
||||
|
||||
### ❌ Limitaciones Encontradas
|
||||
- **SDL3 + modules** generan conflictos irresolubles
|
||||
- **Bibliotecas externas** requieren includes tradicionales
|
||||
- **Enfoque híbrido** (modules propios + includes externos) es más práctico
|
||||
|
||||
### 🎯 Decisión para ViBe3 Physics
|
||||
- **Headers tradicionales** (.h/.cpp) por compatibilidad
|
||||
- **Enfoque en características** antes que arquitectura
|
||||
- **Organización por clases** en lugar de modules inicialmente
|
||||
|
||||
## Sistema de Coeficientes de Rebote Variables
|
||||
|
||||
### 🔧 Implementación Técnica
|
||||
|
||||
#### Problema Anterior
|
||||
```cpp
|
||||
// Sistema ANTIGUO - Poca variabilidad
|
||||
loss_ = ((rand() % 30) * 0.01f) + 0.6f; // 0.60 - 0.89 (diferencia: 0.29)
|
||||
```
|
||||
|
||||
**Resultado**: Pelotas con comportamientos muy similares → Sincronización visible
|
||||
|
||||
#### Solución Implementada
|
||||
```cpp
|
||||
// Sistema NUEVO - Alta variabilidad
|
||||
loss_ = ((rand() % 66) * 0.01f) + 0.30f; // 0.30 - 0.95 (diferencia: 0.65)
|
||||
```
|
||||
|
||||
### 🎯 Tipos de Comportamiento
|
||||
|
||||
#### Categorías de Materiales
|
||||
- **🏀 Super Rebotona** (0.85-0.95): Casi no pierde energía, rebota muchas veces
|
||||
- **⚽ Normal** (0.65-0.85): Comportamiento estándar equilibrado
|
||||
- **🎾 Amortiguada** (0.45-0.65): Pierde energía moderada, se estabiliza
|
||||
- **🏐 Muy Amortiguada** (0.30-0.45): Se para rápidamente, pocas rebotes
|
||||
|
||||
### ✅ Beneficios Conseguidos
|
||||
- **+120% variabilidad** en coeficientes de rebote
|
||||
- **Eliminación de sincronización** entre pelotas
|
||||
- **Comportamientos diversos** visibles inmediatamente
|
||||
- **Física más realista** con materiales diferentes
|
||||
- **Debug display** para monitoreo en tiempo real
|
||||
|
||||
## 🚀 Próximos Pasos - Físicas Avanzadas
|
||||
|
||||
### Ideas Pendientes de Implementación
|
||||
|
||||
#### 1. **Colisiones Entre Partículas**
|
||||
- Detección de colisión ball-to-ball
|
||||
- Física de rebotes entre pelotas
|
||||
- Conservación de momentum
|
||||
|
||||
#### 2. **Materiales y Propiedades**
|
||||
- Diferentes coeficientes de rebote por pelota
|
||||
- Fricción variable por material
|
||||
- Densidad y masa como propiedades
|
||||
|
||||
#### 3. **Fuerzas Externas**
|
||||
- **Viento** - Fuerza horizontal constante
|
||||
- **Campos magnéticos** - Atracción/repulsión a puntos
|
||||
- **Turbulencia** - Fuerzas aleatorias localizadas
|
||||
|
||||
#### 4. **Interactividad Avanzada**
|
||||
- Click para aplicar fuerzas puntuales
|
||||
- Arrastrar para crear campos de fuerza
|
||||
- Herramientas de "pincel" de física
|
||||
|
||||
#### 5. **Visualización Avanzada**
|
||||
- **Trails** - Estelas de movimiento
|
||||
- **Heatmaps** - Visualización de velocidad/energía
|
||||
- **Vectores de fuerza** - Visualizar gravedad y fuerzas
|
||||
|
||||
#### 6. **Optimizaciones**
|
||||
- Spatial partitioning para colisiones
|
||||
- Level-of-detail para muchas partículas
|
||||
- GPU compute shaders para física masiva
|
||||
|
||||
### 🎮 Controles Futuros Sugeridos
|
||||
```
|
||||
Mouse Click: Aplicar fuerza puntual
|
||||
Mouse Drag: Crear campo de fuerza
|
||||
Mouse Wheel: Ajustar intensidad
|
||||
R: Reset todas las pelotas
|
||||
P: Pausa/Resume física
|
||||
M: Modo materiales
|
||||
W: Toggle viento
|
||||
```
|
||||
|
||||
## 🌐 Implementación Técnica: Modo RotoBall
|
||||
|
||||
### Algoritmo Fibonacci Sphere
|
||||
|
||||
Distribución uniforme de puntos en una esfera usando la secuencia de Fibonacci:
|
||||
|
||||
```cpp
|
||||
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
||||
const float angle_increment = PI * 2.0f * golden_ratio;
|
||||
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
float t = static_cast<float>(i) / static_cast<float>(num_points);
|
||||
float phi = acosf(1.0f - 2.0f * t); // Latitud: 0 a π
|
||||
float theta = angle_increment * i; // Longitud: 0 a 2π * golden_ratio
|
||||
|
||||
// Coordenadas esféricas → cartesianas
|
||||
float x = cosf(theta) * sinf(phi) * radius;
|
||||
float y = sinf(theta) * sinf(phi) * radius;
|
||||
float z = cosf(phi) * radius;
|
||||
}
|
||||
```
|
||||
|
||||
**Ventajas:**
|
||||
- Distribución uniforme sin clustering en polos
|
||||
- O(1) cálculo por punto (no requiere iteraciones)
|
||||
- Visualmente perfecto para demoscene effects
|
||||
|
||||
### Rotación 3D (Matrices de Rotación)
|
||||
|
||||
```cpp
|
||||
// Rotación en eje Y (horizontal)
|
||||
float cos_y = cosf(angle_y);
|
||||
float sin_y = sinf(angle_y);
|
||||
float x_rot = x * cos_y - z * sin_y;
|
||||
float z_rot = x * sin_y + z * cos_y;
|
||||
|
||||
// Rotación en eje X (vertical)
|
||||
float cos_x = cosf(angle_x);
|
||||
float sin_x = sinf(angle_x);
|
||||
float y_rot = y * cos_x - z_rot * sin_x;
|
||||
float z_final = y * sin_x + z_rot * cos_x;
|
||||
```
|
||||
|
||||
**Velocidades:**
|
||||
- Eje Y: 1.5 rad/s (rotación principal horizontal)
|
||||
- Eje X: 0.8 rad/s (rotación secundaria vertical)
|
||||
- Ratio Y/X ≈ 2:1 para efecto visual dinámico
|
||||
|
||||
### Proyección 3D → 2D
|
||||
|
||||
**Proyección Ortográfica:**
|
||||
```cpp
|
||||
float screen_x = center_x + x_rotated;
|
||||
float screen_y = center_y + y_rotated;
|
||||
```
|
||||
|
||||
**Profundidad Z (Color Modulation):**
|
||||
```cpp
|
||||
// Normalizar Z de [-radius, +radius] a [0, 1]
|
||||
float z_normalized = (z_final + radius) / (2.0f * radius);
|
||||
|
||||
// Mapear a rango de brillo [MIN_BRIGHTNESS, MAX_BRIGHTNESS]
|
||||
float brightness_factor = (MIN + z_normalized * (MAX - MIN)) / 255.0f;
|
||||
|
||||
// Aplicar a color RGB
|
||||
int r_mod = color.r * brightness_factor;
|
||||
int g_mod = color.g * brightness_factor;
|
||||
int b_mod = color.b * brightness_factor;
|
||||
```
|
||||
|
||||
**Efecto visual:**
|
||||
- Z cerca (+radius): Brillo máximo (255) → Color original
|
||||
- Z lejos (-radius): Brillo mínimo (50) → Color oscuro
|
||||
- Simula profundidad sin sprites adicionales
|
||||
|
||||
### Transición Suave (Interpolación)
|
||||
|
||||
```cpp
|
||||
// Progress de 0.0 a 1.0 en ROTOBALL_TRANSITION_TIME (1.5s)
|
||||
transition_progress += delta_time / ROTOBALL_TRANSITION_TIME;
|
||||
|
||||
// Lerp desde posición actual a posición de esfera
|
||||
float lerp_x = current_x + (target_sphere_x - current_x) * progress;
|
||||
float lerp_y = current_y + (target_sphere_y - current_y) * progress;
|
||||
```
|
||||
|
||||
**Características:**
|
||||
- Independiente del framerate (usa delta_time)
|
||||
- Suave y orgánico
|
||||
- Sin pop visual
|
||||
|
||||
### Performance
|
||||
|
||||
- **Batch rendering**: Una sola llamada `SDL_RenderGeometry` para todos los puntos
|
||||
- **Recalculación**: Fibonacci sphere recalculada cada frame (O(n) predecible)
|
||||
- **Sin malloc**: Usa datos ya almacenados en Ball objects
|
||||
- **Color mod**: CPU-side, sin overhead GPU adicional
|
||||
|
||||
**Rendimiento medido:**
|
||||
- 100 pelotas: >300 FPS
|
||||
- 1,000 pelotas: >200 FPS
|
||||
- 10,000 pelotas: >100 FPS
|
||||
- 100,000 pelotas: >60 FPS (mismo que modo física)
|
||||
|
||||
---
|
||||
|
||||
## 🔬 Sistema de Física con Atracción (Spring Force)
|
||||
|
||||
### Mejora Implementada: Transición Física Realista
|
||||
|
||||
**Problema anterior:** Interpolación lineal artificial (lerp) sin física real
|
||||
**Solución:** Sistema de resorte (Hooke's Law) con conservación de momento
|
||||
|
||||
### Ecuaciones Implementadas
|
||||
|
||||
#### Fuerza de Resorte (Ley de Hooke)
|
||||
```cpp
|
||||
F_spring = k * (target - position)
|
||||
```
|
||||
- `k = 300.0`: Constante de rigidez del resorte (N/m)
|
||||
- Mayor k = atracción más fuerte
|
||||
|
||||
#### Fuerza de Amortiguación (Damping)
|
||||
```cpp
|
||||
F_damping = c * velocity
|
||||
F_total = F_spring - F_damping
|
||||
```
|
||||
- `c_base = 15.0`: Amortiguación lejos del punto
|
||||
- `c_near = 50.0`: Amortiguación cerca (estabilización)
|
||||
- Evita oscilaciones infinitas
|
||||
|
||||
#### Aplicación de Fuerzas
|
||||
```cpp
|
||||
acceleration = F_total / mass // Asumiendo mass = 1
|
||||
velocity += acceleration * deltaTime
|
||||
position += velocity * deltaTime
|
||||
```
|
||||
|
||||
### Comportamiento Físico
|
||||
|
||||
**Al activar RotoBall (tecla C):**
|
||||
1. Esfera comienza a rotar inmediatamente
|
||||
2. Cada pelota mantiene su velocidad actual (`vx`, `vy`)
|
||||
3. Se aplica fuerza de atracción hacia punto móvil en esfera
|
||||
4. Las pelotas se aceleran hacia sus destinos
|
||||
5. Amortiguación las estabiliza al llegar
|
||||
|
||||
**Durante RotoBall:**
|
||||
- Punto destino rota constantemente (actualización cada frame)
|
||||
- Fuerza se recalcula hacia posición rotada
|
||||
- Pelotas "persiguen" su punto mientras este se mueve
|
||||
- Efecto: Convergencia con ligera oscilación orbital
|
||||
|
||||
**Al desactivar RotoBall (tecla C):**
|
||||
1. Atracción se desactiva (`enableRotoBallAttraction(false)`)
|
||||
2. Pelotas conservan velocidad tangencial actual
|
||||
3. Gravedad vuelve a aplicarse
|
||||
4. Transición suave a física normal
|
||||
|
||||
### Constantes Físicas Ajustables
|
||||
|
||||
```cpp
|
||||
// En defines.h (VALORES ACTUALES - Amortiguamiento crítico)
|
||||
ROTOBALL_SPRING_K = 300.0f; // Rigidez resorte
|
||||
ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación lejos (crítico ≈ 2*√k*m)
|
||||
ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca (absorción rápida)
|
||||
ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" (px)
|
||||
ROTOBALL_MAX_FORCE = 1000.0f; // Límite fuerza (seguridad)
|
||||
```
|
||||
|
||||
**Changelog de Ajustes:**
|
||||
- **v1:** `DAMPING_BASE=15.0, NEAR=50.0` → Oscilación visible (subdamped)
|
||||
- **v2:** `DAMPING_BASE=35.0, NEAR=80.0` → **Absorción rápida sin oscilación** ✅
|
||||
|
||||
### Ajustes Recomendados
|
||||
|
||||
**Si siguen oscilando (poco probable):**
|
||||
```cpp
|
||||
ROTOBALL_DAMPING_BASE = 50.0f; // Amortiguamiento super crítico
|
||||
ROTOBALL_DAMPING_NEAR = 100.0f; // Absorción instantánea
|
||||
```
|
||||
|
||||
**Si llegan muy lento:**
|
||||
```cpp
|
||||
ROTOBALL_SPRING_K = 400.0f; // Más fuerza
|
||||
ROTOBALL_DAMPING_BASE = 40.0f; // Compensar con más damping
|
||||
```
|
||||
|
||||
**Si quieres más "rebote" visual:**
|
||||
```cpp
|
||||
ROTOBALL_DAMPING_BASE = 25.0f; // Menos amortiguación
|
||||
ROTOBALL_DAMPING_NEAR = 60.0f; // Ligera oscilación
|
||||
```
|
||||
|
||||
### Ventajas del Sistema
|
||||
|
||||
✅ **Física realista**: Conservación de momento angular
|
||||
✅ **Transición orgánica**: Aceleración natural, no artificial
|
||||
✅ **Inercia preservada**: Al salir conservan velocidad
|
||||
✅ **Estabilización automática**: Damping evita oscilaciones infinitas
|
||||
✅ **Performance**: O(1) por pelota, muy eficiente
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Z-Sorting (Painter's Algorithm)
|
||||
|
||||
### Problema de Renderizado 3D
|
||||
|
||||
**Antes del Z-sorting:**
|
||||
- Pelotas renderizadas en orden fijo del vector: `Ball[0] → Ball[1] → ... → Ball[N]`
|
||||
- Orden aleatorio respecto a profundidad Z
|
||||
- **Problema:** Pelotas oscuras (fondo) pintadas sobre claras (frente)
|
||||
- Resultado: Inversión de profundidad visual incorrecta
|
||||
|
||||
**Después del Z-sorting:**
|
||||
- Pelotas ordenadas por `depth_brightness` antes de renderizar
|
||||
- Painter's Algorithm: **Fondo primero, frente último**
|
||||
- Pelotas oscuras (Z bajo) renderizadas primero
|
||||
- Pelotas claras (Z alto) renderizadas último (encima)
|
||||
- **Resultado:** Oclusión 3D correcta ✅
|
||||
|
||||
### Implementación (engine.cpp::render())
|
||||
|
||||
```cpp
|
||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||
// 1. Crear vector de índices
|
||||
std::vector<size_t> render_order;
|
||||
for (size_t i = 0; i < balls_.size(); i++) {
|
||||
render_order.push_back(i);
|
||||
}
|
||||
|
||||
// 2. Ordenar por depth_brightness (menor primero = fondo primero)
|
||||
std::sort(render_order.begin(), render_order.end(),
|
||||
[this](size_t a, size_t b) {
|
||||
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
||||
});
|
||||
|
||||
// 3. Renderizar en orden de profundidad
|
||||
for (size_t idx : render_order) {
|
||||
// Renderizar balls_[idx]...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Complejidad y Performance
|
||||
|
||||
| Operación | Complejidad | Tiempo (estimado) |
|
||||
|-----------|-------------|-------------------|
|
||||
| Crear índices | O(n) | ~0.001ms (1K pelotas) |
|
||||
| std::sort | O(n log n) | ~0.01ms (1K pelotas) |
|
||||
| Renderizar | O(n) | ~variable |
|
||||
| **Total** | **O(n log n)** | **~0.15ms (10K pelotas)** |
|
||||
|
||||
**Impacto en FPS:**
|
||||
- 100 pelotas: Imperceptible (<0.001ms)
|
||||
- 1,000 pelotas: Imperceptible (~0.01ms)
|
||||
- 10,000 pelotas: Leve (~0.15ms, ~1-2 FPS)
|
||||
- 100,000 pelotas: Moderado (~2ms, ~10-15 FPS)
|
||||
|
||||
### Optimizaciones Aplicadas
|
||||
|
||||
✅ **Solo en modo RotoBall**: Modo física no tiene overhead
|
||||
✅ **Vector de índices**: `balls_` no se modifica (física estable)
|
||||
✅ **Reserve() usado**: Evita realocaciones
|
||||
✅ **Lambda eficiente**: Acceso directo sin copias
|
||||
|
||||
### Resultado Visual
|
||||
|
||||
✅ **Profundidad correcta**: Fondo detrás, frente delante
|
||||
✅ **Oclusión apropiada**: Pelotas claras cubren oscuras
|
||||
✅ **Efecto 3D realista**: Percepción de profundidad correcta
|
||||
✅ **Sin artefactos visuales**: Ordenamiento estable cada frame
|
||||
|
||||
## Métricas del Proyecto
|
||||
|
||||
### ✅ Logros Actuales
|
||||
- **Compilación exitosa** con CMake
|
||||
- **Commit inicial** creado (dec8d43)
|
||||
- **17 archivos** versionados
|
||||
- **9,767 líneas** de código
|
||||
- **Física direccional** 100% funcional
|
||||
- **Coeficientes variables** implementados
|
||||
|
||||
### 🎯 Objetivos Cumplidos
|
||||
- ✅ Migración limpia desde vibe1_delta
|
||||
- ✅ Sistema de gravedad direccional implementado
|
||||
- ✅ Coeficientes de rebote variables (+120% diversidad)
|
||||
- ✅ **Modo RotoBall (esfera 3D rotante) implementado**
|
||||
- ✅ **Fibonacci sphere algorithm funcionando**
|
||||
- ✅ **Profundidad Z con color modulation**
|
||||
- ✅ Debug display completo y funcional
|
||||
- ✅ Controles intuitivos con teclas de cursor
|
||||
- ✅ Eliminación de sincronización entre pelotas
|
||||
|
||||
---
|
||||
|
||||
## Comandos Útiles
|
||||
|
||||
### Compilación
|
||||
```bash
|
||||
mkdir -p build && cd build && cmake .. && cmake --build .
|
||||
```
|
||||
|
||||
### Ejecución
|
||||
```bash
|
||||
./vibe3_physics.exe # Windows
|
||||
./vibe3_physics # Linux/macOS
|
||||
```
|
||||
|
||||
### Git
|
||||
```bash
|
||||
git status # Ver cambios
|
||||
git add . # Añadir archivos
|
||||
git commit -m "..." # Crear commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Archivo de seguimiento para sesiones Claude Code - ViBe3 Physics*
|
||||
*Actualizado: Implementación de gravedad direccional completada*
|
||||
@@ -25,7 +25,7 @@ if (NOT SDL3_ttf_FOUND)
|
||||
endif()
|
||||
|
||||
# Archivos fuente (excluir main_old.cpp)
|
||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/shapes/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/boids_mgr/*.cpp source/input/*.cpp source/scene/*.cpp source/shapes/*.cpp source/shapes_mgr/*.cpp source/state/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
||||
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||
|
||||
# Comprobar si se encontraron archivos fuente
|
||||
@@ -48,6 +48,9 @@ endif()
|
||||
# Incluir directorios de SDL3 y SDL3_ttf
|
||||
include_directories(${SDL3_INCLUDE_DIRS} ${SDL3_ttf_INCLUDE_DIRS})
|
||||
|
||||
# Incluir directorio source/ para poder usar includes desde la raíz del proyecto
|
||||
include_directories(${CMAKE_SOURCE_DIR}/source)
|
||||
|
||||
# Añadir el ejecutable reutilizando el nombre del proyecto
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||
|
||||
@@ -56,3 +59,8 @@ set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAK
|
||||
|
||||
# Enlazar las bibliotecas necesarias
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS})
|
||||
|
||||
# Tool: pack_resources
|
||||
add_executable(pack_resources tools/pack_resources.cpp source/resource_pack.cpp)
|
||||
target_include_directories(pack_resources PRIVATE ${CMAKE_SOURCE_DIR}/source)
|
||||
set_target_properties(pack_resources PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools")
|
||||
|
||||
193
Makefile
193
Makefile
@@ -42,64 +42,69 @@ endif
|
||||
|
||||
# Nombres para los ficheros de lanzamiento
|
||||
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
||||
MACOS_INTEL_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-intel.dmg
|
||||
MACOS_APPLE_SILICON_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-apple-silicon.dmg
|
||||
MACOS_INTEL_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-intel.dmg
|
||||
MACOS_APPLE_SILICON_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-apple-silicon.dmg
|
||||
LINUX_RELEASE := $(TARGET_FILE)-$(VERSION)-linux.tar.gz
|
||||
RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
|
||||
|
||||
# Lista completa de archivos fuente (basada en estructura de ViBe3)
|
||||
APP_SOURCES := \
|
||||
source/ball.cpp \
|
||||
source/engine.cpp \
|
||||
source/main.cpp \
|
||||
source/resource_pack.cpp \
|
||||
source/external/mouse.cpp \
|
||||
source/external/sprite.cpp \
|
||||
source/external/texture.cpp \
|
||||
source/shapes/atom_shape.cpp \
|
||||
source/shapes/cube_shape.cpp \
|
||||
source/shapes/cylinder_shape.cpp \
|
||||
source/shapes/helix_shape.cpp \
|
||||
source/shapes/icosahedron_shape.cpp \
|
||||
source/shapes/png_shape.cpp \
|
||||
source/shapes/sphere_shape.cpp \
|
||||
source/shapes/torus_shape.cpp \
|
||||
source/shapes/wave_grid_shape.cpp
|
||||
# Lista completa de archivos fuente (detección automática con wildcards, como CMakeLists.txt)
|
||||
APP_SOURCES := $(wildcard source/*.cpp) \
|
||||
$(wildcard source/external/*.cpp) \
|
||||
$(wildcard source/shapes/*.cpp) \
|
||||
$(wildcard source/themes/*.cpp) \
|
||||
$(wildcard source/state/*.cpp) \
|
||||
$(wildcard source/input/*.cpp) \
|
||||
$(wildcard source/scene/*.cpp) \
|
||||
$(wildcard source/shapes_mgr/*.cpp) \
|
||||
$(wildcard source/boids_mgr/*.cpp) \
|
||||
$(wildcard source/text/*.cpp) \
|
||||
$(wildcard source/ui/*.cpp)
|
||||
|
||||
# Excluir archivos antiguos si existen
|
||||
APP_SOURCES := $(filter-out source/main_old.cpp, $(APP_SOURCES))
|
||||
|
||||
# Includes
|
||||
INCLUDES := -Isource -Isource/external
|
||||
|
||||
# Variables según el sistema operativo
|
||||
CXXFLAGS_BASE := -std=c++20 -Wall
|
||||
CXXFLAGS := $(CXXFLAGS_BASE) -Os -ffunction-sections -fdata-sections
|
||||
CXXFLAGS_DEBUG := $(CXXFLAGS_BASE) -g -D_DEBUG
|
||||
LDFLAGS :=
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
FixPath = $(subst /,\\,$1)
|
||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows -DWINDOWS_BUILD
|
||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
||||
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
||||
RM := del /Q
|
||||
CXXFLAGS += -DWINDOWS_BUILD
|
||||
CXXFLAGS_DEBUG += -DWINDOWS_BUILD
|
||||
LDFLAGS += -Wl,--gc-sections -static-libstdc++ -static-libgcc \
|
||||
-Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows \
|
||||
-lmingw32 -lws2_32 -lSDL3 -lSDL3_ttf
|
||||
RMFILE := del /Q
|
||||
RMDIR := rmdir /S /Q
|
||||
MKDIR := mkdir
|
||||
else
|
||||
FixPath = $1
|
||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections
|
||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG
|
||||
LDFLAGS := -lSDL3
|
||||
LDFLAGS += -lSDL3 -lSDL3_ttf
|
||||
RMFILE := rm -f
|
||||
RMDIR := rm -rdf
|
||||
RMDIR := rm -rf
|
||||
MKDIR := mkdir -p
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
CXXFLAGS += -DLINUX_BUILD
|
||||
LDFLAGS += -lGL
|
||||
CXXFLAGS += -DLINUX_BUILD
|
||||
CXXFLAGS_DEBUG += -DLINUX_BUILD
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
CXXFLAGS += -Wno-deprecated -DMACOS_BUILD
|
||||
CXXFLAGS_DEBUG += -Wno-deprecated -DMACOS_BUILD
|
||||
LDFLAGS += -framework OpenGL
|
||||
# Configurar arquitectura (por defecto arm64, como en CMake)
|
||||
CXXFLAGS += -arch arm64
|
||||
CXXFLAGS_DEBUG += -arch arm64
|
||||
CXXFLAGS += -DMACOS_BUILD -arch arm64
|
||||
CXXFLAGS_DEBUG += -DMACOS_BUILD -arch arm64
|
||||
# Si quieres binarios universales:
|
||||
# CXXFLAGS += -arch x86_64
|
||||
# CXXFLAGS_DEBUG += -arch x86_64
|
||||
# Y frameworks si hacen falta:
|
||||
# LDFLAGS += -framework Cocoa -framework IOKit
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Reglas para herramienta de empaquetado y resources.pack
|
||||
$(PACK_TOOL): $(PACK_SOURCES)
|
||||
@echo "Compilando herramienta de empaquetado..."
|
||||
@@ -108,11 +113,21 @@ $(PACK_TOOL): $(PACK_SOURCES)
|
||||
|
||||
pack_tool: $(PACK_TOOL)
|
||||
|
||||
resources.pack: $(PACK_TOOL)
|
||||
# Detectar todos los archivos en data/ como dependencias (regenera si cualquiera cambia)
|
||||
DATA_FILES := $(shell find data -type f 2>/dev/null)
|
||||
|
||||
resources.pack: $(PACK_TOOL) $(DATA_FILES)
|
||||
@echo "Generando resources.pack desde directorio data/..."
|
||||
$(PACK_TOOL) data resources.pack
|
||||
@echo "✓ resources.pack generado exitosamente"
|
||||
|
||||
# Target para forzar regeneración de resources.pack (usado por releases)
|
||||
.PHONY: force_resource_pack
|
||||
force_resource_pack: $(PACK_TOOL)
|
||||
@echo "Regenerando resources.pack para release..."
|
||||
$(PACK_TOOL) data resources.pack
|
||||
@echo "✓ resources.pack regenerado exitosamente"
|
||||
|
||||
# Reglas para compilación
|
||||
windows:
|
||||
@echo off
|
||||
@@ -131,20 +146,20 @@ windows_debug:
|
||||
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_debug.exe"
|
||||
|
||||
windows_release: resources.pack
|
||||
windows_release: force_resource_pack
|
||||
@echo "Creando release para Windows - Version: $(VERSION)"
|
||||
|
||||
# Crea carpeta temporal 'RELEASE_FOLDER'
|
||||
@rm -rf "$(RELEASE_FOLDER)"
|
||||
@mkdir -p "$(RELEASE_FOLDER)"
|
||||
@if exist "$(RELEASE_FOLDER)" rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||
@mkdir "$(RELEASE_FOLDER)"
|
||||
|
||||
# Copia el archivo 'resources.pack'
|
||||
@cp -f "resources.pack" "$(RELEASE_FOLDER)/"
|
||||
@copy /Y "resources.pack" "$(RELEASE_FOLDER)\" >nul
|
||||
|
||||
# Copia los ficheros que estan en la raíz del proyecto
|
||||
@cp -f "LICENSE" "$(RELEASE_FOLDER)/" 2>/dev/null || echo "LICENSE not found (optional)"
|
||||
@cp -f "README.md" "$(RELEASE_FOLDER)/"
|
||||
@cp -f release/*.dll "$(RELEASE_FOLDER)/" 2>/dev/null || echo "No DLL files found (optional)"
|
||||
@copy /Y "LICENSE" "$(RELEASE_FOLDER)\" >nul 2>&1 || echo LICENSE not found (optional)
|
||||
@copy /Y "README.md" "$(RELEASE_FOLDER)\" >nul
|
||||
@copy /Y release\dll\*.dll "$(RELEASE_FOLDER)\" >nul 2>&1 || echo DLLs copied successfully
|
||||
|
||||
# Compila
|
||||
@windres release/vibe3.rc -O coff -o $(RESOURCE_FILE)
|
||||
@@ -152,12 +167,12 @@ windows_release: resources.pack
|
||||
@strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
|
||||
|
||||
# Crea el fichero .zip
|
||||
@rm -f "$(WINDOWS_RELEASE)"
|
||||
@if exist "$(WINDOWS_RELEASE)" del /Q "$(WINDOWS_RELEASE)"
|
||||
@powershell.exe -Command "Compress-Archive -Path '$(RELEASE_FOLDER)/*' -DestinationPath '$(WINDOWS_RELEASE)' -Force"
|
||||
@echo "Release creado: $(WINDOWS_RELEASE)"
|
||||
|
||||
# Elimina la carpeta temporal 'RELEASE_FOLDER'
|
||||
@rm -rf "$(RELEASE_FOLDER)"
|
||||
@rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||
|
||||
macos:
|
||||
@echo "Compilando para macOS: $(TARGET_NAME)"
|
||||
@@ -167,15 +182,24 @@ macos_debug:
|
||||
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
macos_release: resources.pack
|
||||
macos_release: force_resource_pack
|
||||
@echo "Creando release para macOS - Version: $(VERSION)"
|
||||
|
||||
# Verificar e instalar create-dmg si es necesario
|
||||
@which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg)
|
||||
|
||||
# Elimina datos de compilaciones anteriores
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
$(RMDIR) Frameworks
|
||||
$(RMFILE) tmp.dmg
|
||||
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
|
||||
$(RMFILE) "$(MACOS_APPLE_SILICON_RELEASE)"
|
||||
|
||||
# Limpia archivos temporales de create-dmg y desmonta volúmenes
|
||||
@echo "Limpiando archivos temporales y volúmenes montados..."
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
@hdiutil detach "/Volumes/$(APP_NAME)" 2>/dev/null || true
|
||||
@hdiutil detach "/Volumes/ViBe3 Physics" 2>/dev/null || true
|
||||
|
||||
# Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macos
|
||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS"
|
||||
@@ -185,40 +209,76 @@ macos_release: resources.pack
|
||||
# Copia carpetas y ficheros
|
||||
cp resources.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
cp -R release/frameworks/SDL3_ttf.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
cp -R release/frameworks/SDL3.xcframework Frameworks
|
||||
cp -R release/frameworks/SDL3_ttf.xcframework Frameworks
|
||||
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp release/Info.plist "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents"
|
||||
cp LICENSE "$(RELEASE_FOLDER)"
|
||||
cp README.md "$(RELEASE_FOLDER)"
|
||||
|
||||
# Crea enlaces
|
||||
ln -s /Applications "$(RELEASE_FOLDER)"/Applications
|
||||
# NOTA: create-dmg crea automáticamente el enlace a /Applications con --app-drop-link
|
||||
# No es necesario crearlo manualmente aquí
|
||||
|
||||
# Compila la versión para procesadores Intel
|
||||
ifdef ENABLE_MACOS_X86_64
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.15
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos12
|
||||
|
||||
# Firma la aplicación
|
||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||
|
||||
# Empaqueta el .dmg de la versión Intel
|
||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
|
||||
$(RMFILE) tmp.dmg
|
||||
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
|
||||
# Empaqueta el .dmg de la versión Intel con create-dmg
|
||||
@echo "Creando DMG Intel con iconos de 96x96..."
|
||||
@create-dmg \
|
||||
--volname "$(APP_NAME)" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 720 300 \
|
||||
--icon-size 96 \
|
||||
--text-size 12 \
|
||||
--icon "$(APP_NAME).app" 278 102 \
|
||||
--icon "LICENSE" 441 102 \
|
||||
--icon "README.md" 604 102 \
|
||||
--app-drop-link 115 102 \
|
||||
--hide-extension "$(APP_NAME).app" \
|
||||
"$(MACOS_INTEL_RELEASE)" \
|
||||
"$(RELEASE_FOLDER)"
|
||||
@if [ -f "$(MACOS_INTEL_RELEASE)" ]; then \
|
||||
echo "✓ Release Intel creado exitosamente: $(MACOS_INTEL_RELEASE)"; \
|
||||
else \
|
||||
echo "✗ Error: No se pudo crear el DMG Intel"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
endif
|
||||
|
||||
# Compila la versión para procesadores Apple Silicon
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos12
|
||||
|
||||
# Firma la aplicación
|
||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||
|
||||
# Empaqueta el .dmg de la versión Apple Silicon
|
||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
|
||||
$(RMFILE) tmp.dmg
|
||||
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
|
||||
# Empaqueta el .dmg de la versión Apple Silicon con create-dmg
|
||||
@echo "Creando DMG Apple Silicon con iconos de 96x96..."
|
||||
@create-dmg \
|
||||
--volname "$(APP_NAME)" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 720 300 \
|
||||
--icon-size 96 \
|
||||
--text-size 12 \
|
||||
--icon "$(APP_NAME).app" 278 102 \
|
||||
--icon "LICENSE" 441 102 \
|
||||
--icon "README.md" 604 102 \
|
||||
--app-drop-link 115 102 \
|
||||
--hide-extension "$(APP_NAME).app" \
|
||||
"$(MACOS_APPLE_SILICON_RELEASE)" \
|
||||
"$(RELEASE_FOLDER)"
|
||||
@if [ -f "$(MACOS_APPLE_SILICON_RELEASE)" ]; then \
|
||||
echo "✓ Release Apple Silicon creado exitosamente: $(MACOS_APPLE_SILICON_RELEASE)"; \
|
||||
else \
|
||||
echo "✗ Error: No se pudo crear el DMG Apple Silicon"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
|
||||
# Elimina las carpetas temporales
|
||||
$(RMDIR) Frameworks
|
||||
@@ -233,7 +293,7 @@ linux_debug:
|
||||
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
linux_release: resources.pack
|
||||
linux_release: force_resource_pack
|
||||
@echo "Creando release para Linux - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -258,7 +318,7 @@ linux_release: resources.pack
|
||||
# Elimina la carpeta temporal
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
|
||||
linux_release_desktop: resources.pack
|
||||
linux_release_desktop: force_resource_pack
|
||||
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -362,7 +422,7 @@ raspi_debug:
|
||||
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
raspi_release: resources.pack
|
||||
raspi_release: force_resource_pack
|
||||
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -387,7 +447,7 @@ raspi_release: resources.pack
|
||||
# Elimina la carpeta temporal
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
|
||||
anbernic: resources.pack
|
||||
anbernic: force_resource_pack
|
||||
@echo "Compilando para Anbernic: $(TARGET_NAME)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
|
||||
@@ -426,6 +486,7 @@ help:
|
||||
@echo " macos_release - Crear release completo para macOS (.dmg)"
|
||||
@echo " pack_tool - Compilar herramienta de empaquetado"
|
||||
@echo " resources.pack - Generar pack de recursos desde data/"
|
||||
@echo " force_resource_pack - Regenerar resources.pack (usado por releases)"
|
||||
@echo " show_version - Mostrar version actual ($(VERSION))"
|
||||
@echo " help - Mostrar esta ayuda"
|
||||
|
||||
|
||||
339
ROADMAP.md
339
ROADMAP.md
@@ -1,339 +0,0 @@
|
||||
# ROADMAP - ViBe3 Physics
|
||||
|
||||
## Estado Actual ✅
|
||||
|
||||
### Figuras 3D (8/8 Completadas)
|
||||
- ✅ Q - SPHERE (Esfera Fibonacci)
|
||||
- ✅ W - WAVE_GRID (Malla ondeante) - ⚠️ Necesita mejora de movimiento
|
||||
- ✅ E - HELIX (Espiral helicoidal)
|
||||
- ✅ R - TORUS (Toroide/donut)
|
||||
- ✅ T - CUBE (Cubo rotante)
|
||||
- ✅ Y - CYLINDER (Cilindro) - ⚠️ Necesita rotación multi-eje
|
||||
- ✅ U - ICOSAHEDRON (Icosaedro D20)
|
||||
- ✅ I - ATOM (Núcleo + órbitas)
|
||||
|
||||
### Temas Visuales (7/7 Completadas)
|
||||
- ✅ SUNSET (Atardecer)
|
||||
- ✅ OCEAN (Océano)
|
||||
- ✅ NEON (Neón vibrante)
|
||||
- ✅ FOREST (Bosque)
|
||||
- ✅ RGB (Círculo cromático matemático)
|
||||
- ✅ MONOCHROME (Monocromo - blanco puro)
|
||||
- ✅ LAVENDER (Lavanda - degradado violeta-azul, pelotas doradas)
|
||||
|
||||
### Sistemas de Presentación
|
||||
- ✅ Transiciones LERP entre temas (0.5s suaves)
|
||||
- ✅ Carga dinámica de texturas desde data/balls/
|
||||
- ✅ Hot-swap de sprites con tecla N (cicla entre todas las texturas)
|
||||
- ✅ PNG_SHAPE (O) - Logo "JAILGAMES" con rotación legible
|
||||
|
||||
---
|
||||
|
||||
## Mejoras de Presentación 🎨
|
||||
|
||||
### 1. ✅ Mejorar Animaciones de Figuras 3D
|
||||
**Descripción:** Añadir movimientos más dinámicos e interesantes a algunas figuras
|
||||
**Prioridad:** Media
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**Detalles:**
|
||||
|
||||
#### CYLINDER (Y):
|
||||
- ✅ **Rotación principal en eje Y** (spin horizontal continuo)
|
||||
- ✅ **Tumbling ocasional en eje X** cada 3-5 segundos
|
||||
- ✅ Transiciones suaves con ease-in-out (1.5s duración)
|
||||
- ✅ Efecto visual: cilindro "se da una vuelta" ocasionalmente
|
||||
|
||||
#### WAVE_GRID (W):
|
||||
- ✅ **Vista frontal paralela a pantalla** (sin rotación confusa)
|
||||
- ✅ **Pivoteo sutil en ejes X e Y**
|
||||
- ✅ Esquinas se mueven adelante/atrás según posición
|
||||
- ✅ Movimiento ondulatorio + pivoteo = efecto "océano"
|
||||
- ✅ Velocidades lentas (0.3-0.5 rad/s) para organicidad
|
||||
|
||||
### 2. ✅ Modo DEMO (Auto-play)
|
||||
**Descripción:** Modo demostración automática con acciones aleatorias
|
||||
**Prioridad:** Alta
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**Detalles:**
|
||||
- ✅ Toggle con tecla `D`
|
||||
- ✅ Timer que ejecuta acciones cada 3-8 segundos (configurable)
|
||||
- ✅ Acciones: gravedad, figuras, temas, escenarios, impulso, profundidad, escala, sprite
|
||||
- ✅ Secuencia pseudo-aleatoria con pesos configurables (defines.h)
|
||||
- ✅ Totalmente interactivo - usuario puede seguir usando controles
|
||||
- ✅ Indicador visual "DEMO MODE" centrado en pantalla (naranja)
|
||||
- ✅ Eliminado sistema auto-restart antiguo (ya no necesario)
|
||||
|
||||
### 3. ✅ Resolución Lógica Configurable
|
||||
**Descripción:** Especificar resolución lógica por parámetros de línea de comandos
|
||||
**Prioridad:** Media
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**Detalles:**
|
||||
- ✅ Parámetros `-w/--width <px>` y `-h/--height <px>`
|
||||
- ✅ Parámetro `-f/--fullscreen` para pantalla completa
|
||||
- ✅ Defaults: 1280x720 en ventana (si no se especifica)
|
||||
- ✅ Validación: mínimo 640x480
|
||||
- ✅ Help text con `--help`
|
||||
- Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f`
|
||||
|
||||
### 4. ✅ Implementar Modo Logo (Easter Egg)
|
||||
**Descripción:** Modo especial que muestra el logo JAILGAMES como "marca de agua"
|
||||
**Prioridad:** Alta (característica distintiva)
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**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)
|
||||
**Prioridad:** Alta (bug visible)
|
||||
**Estimación:** 30 minutos
|
||||
**Detalles:**
|
||||
- **Síntoma:** Pelotas en reposo (velocidad ≈ 0) se escalan incorrectamente
|
||||
- **Posible causa:**
|
||||
- Scale factor calculado desde velocidad o energía
|
||||
- División por cero o valor muy pequeño
|
||||
- Interpolación incorrecta en modo transición
|
||||
- **Investigar:** Ball::render(), scale calculations, depth brightness
|
||||
- **Solución esperada:** Tamaño constante independiente de velocidad
|
||||
|
||||
### 6. ✅ Sistema de Release
|
||||
**Descripción:** Empaquetado para distribución standalone
|
||||
**Prioridad:** Baja
|
||||
**Estimación:** 30 minutos
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**Detalles:**
|
||||
- ✅ Carpeta `release/` con recursos
|
||||
- ✅ ResourcePack sistema de empaquetado binario (VBE3 format)
|
||||
- ✅ Tool `pack_resources` para generar resources.pack
|
||||
- ✅ SDL3.dll incluido en release
|
||||
- ✅ Carga híbrida: resources.pack con fallback a data/
|
||||
- ✅ Target `make windows_release` en Makefile
|
||||
- ✅ ZIP generado: vibe3_physics-YYYY-MM-DD-win32-x64.zip
|
||||
|
||||
### 7. ⏳ Logo/Autor Sobreimpreso (Watermark)
|
||||
**Descripción:** Mostrar logo JAILGAMES en esquina con animación periódica
|
||||
**Prioridad:** Media
|
||||
**Estimación:** 2 horas
|
||||
**Detalles:**
|
||||
- **Posición:** Esquina inferior derecha (o configurable)
|
||||
- **Aparición:** Cada X segundos (ej: cada 30-60s)
|
||||
- **Animación entrada:** Fade-in + slide desde fuera de pantalla
|
||||
- **Duración visible:** 3-5 segundos
|
||||
- **Animación salida:** Fade-out + slide hacia fuera
|
||||
- **Rendering:** Textura PNG con alpha blending
|
||||
- **Configuración:**
|
||||
- Intervalo de aparición (LOGO_WATERMARK_INTERVAL)
|
||||
- Duración visible (LOGO_WATERMARK_DURATION)
|
||||
- Tamaño relativo a pantalla (ej: 10-15% ancho)
|
||||
- Opacidad máxima (ej: 70-80% alpha)
|
||||
- **Integración:** No interfiere con debug display ni modos DEMO/LOGO
|
||||
- **Asset:** Reutilizar data/jailgames_logo.png existente
|
||||
|
||||
### 8. ⏳ Mejorar Sistema de Renderizado de Texto
|
||||
**Descripción:** Actualizar tipografía y mejorar clase dbgtxt para mejor legibilidad
|
||||
**Prioridad:** Media
|
||||
**Estimación:** 3-4 horas
|
||||
**Detalles:**
|
||||
- **Problemas actuales:**
|
||||
- Fuente bitmap actual poco legible en resoluciones altas
|
||||
- Sistema dbgtxt limitado (solo fuente fija)
|
||||
- Sin suavizado (aliasing visible)
|
||||
- Tamaño no escala con resolución
|
||||
- **Soluciones propuestas:**
|
||||
- **Opción A - SDL_ttf:** Usar fuentes TrueType (.ttf)
|
||||
- Mayor calidad y escalabilidad
|
||||
- Antialiasing nativo
|
||||
- Soporte Unicode completo
|
||||
- Requiere añadir dependencia SDL3_ttf
|
||||
- **Opción B - Bitmap mejorada:** Nueva fuente bitmap de mayor calidad
|
||||
- Sin dependencias adicionales
|
||||
- Textura PNG con caracteres ASCII
|
||||
- Escalado nearest-neighbor para estética pixel-art
|
||||
- Más control sobre aspecto retro
|
||||
- **Mejoras clase dbgtxt:**
|
||||
- Soporte múltiples tamaños (pequeño/normal/grande)
|
||||
- Sombra/outline configurable para mejor contraste
|
||||
- Alineación (izquierda/centro/derecha)
|
||||
- Color y alpha por texto individual
|
||||
- Medición de ancho de texto (para centrado dinámico)
|
||||
- **Assets necesarios:**
|
||||
- Si TTF: Fuente .ttf embebida (ej: Roboto Mono, Source Code Pro)
|
||||
- Si Bitmap: Nueva textura font_atlas.png de mayor resolución
|
||||
- **Retrocompatibilidad:** Mantener API actual de dbgtxt
|
||||
|
||||
### 9. ⏳ Temas Dinámicos (Color Generativo)
|
||||
**Descripción:** Sistema de generación procedural de temas de colores
|
||||
**Prioridad:** Baja
|
||||
**Estimación:** 4-6 horas
|
||||
**Detalles:**
|
||||
- **Objetivos:**
|
||||
- Gradiente de fondo variable (color base + variaciones automáticas)
|
||||
- Color de pelotas variable (monocromático, complementario, análogo, etc.)
|
||||
- Generación algorítmica de paletas armónicas
|
||||
- **Implementación propuesta:**
|
||||
- **HSV Color Space:** Generar paletas en espacio HSV para control intuitivo
|
||||
- Hue (matiz): 0-360° para variación de color
|
||||
- Saturation (saturación): 0-100% para intensidad
|
||||
- Value (brillo): 0-100% para luminosidad
|
||||
- **Esquemas de color:**
|
||||
- Monocromático (un matiz + variaciones de saturación/brillo)
|
||||
- Complementario (matiz opuesto en rueda de color)
|
||||
- Análogo (matices adyacentes ±30°)
|
||||
- Triádico (3 matices equidistantes 120°)
|
||||
- Tetrádico (4 matices en cuadrado/rectángulo)
|
||||
- **Parámetros configurables:**
|
||||
- Base hue (0-360°): Color principal del tema
|
||||
- Saturation range (0-100%): Rango de intensidad
|
||||
- Value range (0-100%): Rango de brillo
|
||||
- Esquema de armonía: mono/complementario/análogo/etc.
|
||||
- Gradiente de fondo: Automático según base hue
|
||||
- **Controles de usuario:**
|
||||
- Tecla G: Generar nuevo tema aleatorio
|
||||
- Tecla Shift+G: Ciclar esquemas de armonía
|
||||
- Guardar temas generados favoritos (slot 8-12)
|
||||
- **Algoritmos:**
|
||||
- **Gradiente de fondo:** Base hue → Variación análoga oscura (fondo inferior)
|
||||
- **Color de pelotas:** Según esquema elegido (complementario para contraste máximo)
|
||||
- **Conversión HSV → RGB:** Algoritmo estándar para SDL rendering
|
||||
- **Ejemplos de temas generados:**
|
||||
- Tema "Cyberpunk": Base cyan (180°) + Complementario magenta (300°)
|
||||
- Tema "Autumn": Base naranja (30°) + Análogo rojo-amarillo
|
||||
- Tema "Ocean Deep": Base azul (240°) + Monocromático variaciones
|
||||
- Tema "Toxic": Base verde lima (120°) + Complementario púrpura
|
||||
- **Persistencia:**
|
||||
- Guardar temas generados en config.ini (opcional)
|
||||
- Teclas 8-9-0 para slots de temas custom
|
||||
- **Compatibilidad:**
|
||||
- No reemplaza temas existentes (1-7)
|
||||
- Modo adicional de selección de tema
|
||||
|
||||
---
|
||||
|
||||
## Futuras Mejoras (Ideas)
|
||||
|
||||
### Performance
|
||||
- [ ] Spatial partitioning para colisiones ball-to-ball
|
||||
- [ ] Level-of-detail para 100K+ pelotas
|
||||
- [ ] GPU compute shaders para física masiva
|
||||
|
||||
### Efectos Visuales
|
||||
- [ ] Trails (estelas de movimiento)
|
||||
- [ ] Heatmaps de velocidad/energía
|
||||
- [ ] Bloom/glow para sprites
|
||||
|
||||
### Física Avanzada
|
||||
- [ ] Colisiones entre partículas
|
||||
- [ ] Viento (fuerza horizontal)
|
||||
- [ ] Campos magnéticos (atracción/repulsión)
|
||||
- [ ] Turbulencia
|
||||
|
||||
### Shapes PNG
|
||||
- [ ] **Voxelización 3D para PNG_SHAPE** (Enfoque B)
|
||||
- Actualmente: Extrusión 2D simple (píxeles → capas Z)
|
||||
- Futuro: Voxelización real con detección de interior/exterior
|
||||
- Permite formas 3D más complejas desde imágenes
|
||||
- Rotación volumétrica en vez de extrusión plana
|
||||
|
||||
### Interactividad
|
||||
- [ ] Mouse: click para aplicar fuerzas
|
||||
- [ ] Mouse: drag para crear campos
|
||||
- [ ] Mouse wheel: ajustar intensidad
|
||||
|
||||
---
|
||||
|
||||
## Historial de Cambios
|
||||
|
||||
### 2025-10-04 (Sesión 5) - PNG Shape + Texturas Dinámicas + CLI
|
||||
- ✅ **PNG_SHAPE implementado** - Tecla O para activar logo "JAILGAMES"
|
||||
- ✅ Carga de PNG 1-bit con stb_image
|
||||
- ✅ Extrusión 2D (Enfoque A) - píxeles → capas Z
|
||||
- ✅ Detección de bordes vs relleno completo (configurable)
|
||||
- ✅ Tamaño 80% pantalla (como otras figuras)
|
||||
- ✅ Rotación "legible" - De frente con volteretas ocasionales (3-8s idle)
|
||||
- ✅ Fix: Z forzado a máximo cuando está de frente (texto brillante)
|
||||
- ✅ Excluido de DEMO/DEMO_LITE (logo especial)
|
||||
- ✅ **Sistema de texturas dinámicas** - Carga automática desde data/balls/
|
||||
- ✅ Tecla N cicla entre todas las texturas PNG encontradas
|
||||
- ✅ Orden alfabético con normal.png primero por defecto
|
||||
- ✅ Display dinámico del nombre de textura (uppercase)
|
||||
- ✅ **Física mejorada SHAPE** - Constantes separadas de ROTOBALL
|
||||
- ✅ Pegajosidad 2.67x mayor (SPRING_K=800 vs 300)
|
||||
- ✅ Pelotas se adhieren mejor durante rotaciones rápidas
|
||||
- ✅ **Parámetros de línea de comandos** - `-w/-h/-f/--help`
|
||||
- ✅ Resolución configurable (mínimo 640x480)
|
||||
- 📝 Preparado para voxelización 3D (Enfoque B) en futuro
|
||||
|
||||
### 2025-10-04 (Sesión 4) - Modo DEMO + Mejoras Animaciones
|
||||
- ✅ **Implementado Modo DEMO (auto-play)** - Tecla D para toggle
|
||||
- ✅ Sistema de acciones aleatorias cada 3-8 segundos (configurable)
|
||||
- ✅ 8 tipos de acciones con pesos de probabilidad ajustables
|
||||
- ✅ Totalmente interactivo - usuario puede seguir controlando
|
||||
- ✅ Display visual "DEMO MODE" centrado en naranja
|
||||
- ✅ **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)
|
||||
|
||||
### 2025-10-04 (Sesión 3)
|
||||
- ✅ Implementado tema MONOCHROME (6º tema)
|
||||
- ✅ Sistema LERP para transiciones suaves de temas (0.5s)
|
||||
- ✅ Hot-swap de sprites con tecla N (ball.png ↔ ball_small.png)
|
||||
- ✅ Tamaño dinámico de pelotas desde texture->getWidth()
|
||||
- ✅ Ajuste de posiciones inteligente al cambiar sprite
|
||||
- 📝 Añadidas mejoras propuestas para CYLINDER y WAVE_GRID
|
||||
|
||||
### 2025-10-03 (Sesión 2)
|
||||
- ✅ Implementadas 8 figuras 3D (SPHERE, WAVE_GRID, HELIX, TORUS, CUBE, CYLINDER, ICOSAHEDRON, ATOM)
|
||||
- ✅ Sistema polimórfico de shapes con herencia virtual
|
||||
|
||||
### 2025-10-02 (Sesión 1)
|
||||
- ✅ Migración desde vibe1_delta
|
||||
- ✅ Sistema de gravedad direccional (4 direcciones)
|
||||
- ✅ Coeficientes de rebote variables (0.30-0.95)
|
||||
- ✅ 5 temas de colores iniciales
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2025-10-04
|
||||
BIN
data/logo/logo.png
Normal file
BIN
data/logo/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
BIN
data/logo/logo2.png
Normal file
BIN
data/logo/logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
@@ -29,7 +29,7 @@
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<string>12.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
BIN
release/dll/SDL3.dll
Normal file
BIN
release/dll/SDL3.dll
Normal file
Binary file not shown.
BIN
release/dll/SDL3_ttf.dll
Normal file
BIN
release/dll/SDL3_ttf.dll
Normal file
Binary file not shown.
BIN
release/dll/libwinpthread-1.dll
Normal file
BIN
release/dll/libwinpthread-1.dll
Normal file
Binary file not shown.
90
release/frameworks/SDL3_ttf.xcframework/Info.plist
Normal file
90
release/frameworks/SDL3_ttf.xcframework/Info.plist
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/Versions/A/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>macos-arm64_x86_64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>macos</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
BIN
release/frameworks/SDL3_ttf.xcframework/ios-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
BIN
release/frameworks/SDL3_ttf.xcframework/ios-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
Binary file not shown.
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<data>
|
||||
7QAtKpC/pLIq6TK3F59Ax1hg3tc=
|
||||
</data>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<data>
|
||||
90S4SFzJy1lUuMotaCRWpTbzRa4=
|
||||
</data>
|
||||
<key>INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
9F72T63xvwi9u+kC0p9N011q3IM=
|
||||
</data>
|
||||
<key>LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/SDL3_ttf
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>23H420</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SDL3_ttf</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.libsdl.SDL3-ttf</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SDL3_ttf</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.2.2</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.2.2</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string></string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>14.5</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>23F73</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx14.5</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1540</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>15F31d</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>Resources/CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Resources/INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Resources/Info.plist</key>
|
||||
<data>
|
||||
Q+NCd9YwE/D/Y4ptVnrhOldXz6U=
|
||||
</data>
|
||||
<key>Resources/LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>Resources/README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Info.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
LwUSgLeBsUUT/M3w+W5AAfTziViNTWX1o7Ly+x3J2u0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
A
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
BIN
release/frameworks/SDL3_ttf.xcframework/tvos-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
BIN
release/frameworks/SDL3_ttf.xcframework/tvos-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
Binary file not shown.
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<data>
|
||||
7QAtKpC/pLIq6TK3F59Ax1hg3tc=
|
||||
</data>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<data>
|
||||
90S4SFzJy1lUuMotaCRWpTbzRa4=
|
||||
</data>
|
||||
<key>INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
+nBymgIjvQrMA5f3CqiBB03/KB0=
|
||||
</data>
|
||||
<key>LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
release/vibe3.res
Normal file
BIN
release/vibe3.res
Normal file
Binary file not shown.
BIN
resources.pack
BIN
resources.pack
Binary file not shown.
687
source/app_logo.cpp
Normal file
687
source/app_logo.cpp
Normal file
@@ -0,0 +1,687 @@
|
||||
#include "app_logo.hpp"
|
||||
|
||||
#include <SDL3/SDL_render.h> // for SDL_DestroyTexture, SDL_RenderGeometry, SDL_SetTextureAlphaMod
|
||||
#include <cmath> // for powf, sinf, cosf
|
||||
#include <cstdlib> // for free()
|
||||
#include <iostream> // for std::cout
|
||||
|
||||
#include "logo_scaler.hpp" // for LogoScaler
|
||||
#include "defines.hpp" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
|
||||
|
||||
// ============================================================================
|
||||
// Destructor - Liberar las 4 texturas SDL
|
||||
// ============================================================================
|
||||
|
||||
AppLogo::~AppLogo() {
|
||||
if (logo1_base_texture_) {
|
||||
SDL_DestroyTexture(logo1_base_texture_);
|
||||
logo1_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo1_native_texture_) {
|
||||
SDL_DestroyTexture(logo1_native_texture_);
|
||||
logo1_native_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_base_texture_) {
|
||||
SDL_DestroyTexture(logo2_base_texture_);
|
||||
logo2_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_native_texture_) {
|
||||
SDL_DestroyTexture(logo2_native_texture_);
|
||||
logo2_native_texture_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Inicialización - Pre-escalar logos a 2 resoluciones (base y nativa)
|
||||
// ============================================================================
|
||||
|
||||
bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) {
|
||||
renderer_ = renderer;
|
||||
base_screen_width_ = screen_width;
|
||||
base_screen_height_ = screen_height;
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
|
||||
// ========================================================================
|
||||
// 1. Detectar resolución nativa del monitor
|
||||
// ========================================================================
|
||||
if (!LogoScaler::detectNativeResolution(native_screen_width_, native_screen_height_)) {
|
||||
std::cout << "No se pudo detectar resolución nativa, usando solo base" << std::endl;
|
||||
// Fallback: usar resolución base como nativa
|
||||
native_screen_width_ = screen_width;
|
||||
native_screen_height_ = screen_height;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 2. Calcular alturas finales para ambas resoluciones
|
||||
// ========================================================================
|
||||
int logo_base_target_height = static_cast<int>(base_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
int logo_native_target_height = static_cast<int>(native_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
|
||||
std::cout << "Pre-escalando logos:" << std::endl;
|
||||
std::cout << " Base: " << base_screen_width_ << "x" << base_screen_height_
|
||||
<< " (altura logo: " << logo_base_target_height << "px)" << std::endl;
|
||||
std::cout << " Nativa: " << native_screen_width_ << "x" << native_screen_height_
|
||||
<< " (altura logo: " << logo_native_target_height << "px)" << std::endl;
|
||||
|
||||
// ========================================================================
|
||||
// 3. Cargar y escalar LOGO1 (data/logo/logo.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo1_path = resources_dir + "/data/logo/logo.png";
|
||||
|
||||
// 3a. Versión BASE de logo1
|
||||
unsigned char* logo1_base_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente por aspect ratio
|
||||
logo_base_target_height,
|
||||
logo1_base_width_,
|
||||
logo1_base_height_
|
||||
);
|
||||
if (logo1_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo1_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_base_data, logo1_base_width_, logo1_base_height_
|
||||
);
|
||||
free(logo1_base_data); // Liberar buffer temporal
|
||||
|
||||
if (logo1_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Habilitar alpha blending
|
||||
SDL_SetTextureBlendMode(logo1_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 3b. Versión NATIVA de logo1
|
||||
unsigned char* logo1_native_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente
|
||||
logo_native_target_height,
|
||||
logo1_native_width_,
|
||||
logo1_native_height_
|
||||
);
|
||||
if (logo1_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo1_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_native_data, logo1_native_width_, logo1_native_height_
|
||||
);
|
||||
free(logo1_native_data);
|
||||
|
||||
if (logo1_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo1_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// 4. Cargar y escalar LOGO2 (data/logo/logo2.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo2_path = resources_dir + "/data/logo/logo2.png";
|
||||
|
||||
// 4a. Versión BASE de logo2
|
||||
unsigned char* logo2_base_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_base_target_height,
|
||||
logo2_base_width_,
|
||||
logo2_base_height_
|
||||
);
|
||||
if (logo2_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo2_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_base_data, logo2_base_width_, logo2_base_height_
|
||||
);
|
||||
free(logo2_base_data);
|
||||
|
||||
if (logo2_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo2_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 4b. Versión NATIVA de logo2
|
||||
unsigned char* logo2_native_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_native_target_height,
|
||||
logo2_native_width_,
|
||||
logo2_native_height_
|
||||
);
|
||||
if (logo2_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo2_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_native_data, logo2_native_width_, logo2_native_height_
|
||||
);
|
||||
free(logo2_native_data);
|
||||
|
||||
if (logo2_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo2_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// 5. Inicialmente usar texturas BASE (la resolución de inicio)
|
||||
// ========================================================================
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "Logos pre-escalados exitosamente (4 texturas creadas)" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
// Si estamos en SANDBOX, resetear y no hacer nada (logo desactivado)
|
||||
if (current_mode == AppMode::SANDBOX) {
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Máquina de estados con fade in/out
|
||||
timer_ += delta_time;
|
||||
|
||||
switch (state_) {
|
||||
case AppLogoState::HIDDEN:
|
||||
// Esperando el intervalo de espera
|
||||
if (timer_ >= APPLOGO_DISPLAY_INTERVAL) {
|
||||
state_ = AppLogoState::FADE_IN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Elegir UNA animación aleatoria (misma para ambos logos, misma entrada y salida)
|
||||
current_animation_ = getRandomAnimation();
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_IN:
|
||||
// Fade in: alpha de 0 a 255, con Logo 2 retrasado 0.25s
|
||||
{
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
|
||||
|
||||
// Verificar si fade in completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade in completado para ambos logos
|
||||
state_ = AppLogoState::VISIBLE;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
logo2_scale_ = 1.0f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha con retraso de forma LINEAL (sin easing)
|
||||
logo1_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo1) * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo2) * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar MISMA animación (current_animation_) a ambos logos
|
||||
// con sus respectivos progresos
|
||||
// ================================================================
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
logo2_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float elastic_t1 = easeOutElastic(prog1);
|
||||
logo1_scale_ = 1.2f - (elastic_t1 * 0.2f);
|
||||
float squash_t1 = easeOutBack(prog1);
|
||||
logo1_squash_y_ = 0.6f + (squash_t1 * 0.4f);
|
||||
logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float elastic_t2 = easeOutElastic(prog2);
|
||||
logo2_scale_ = 1.2f - (elastic_t2 * 0.2f);
|
||||
float squash_t2 = easeOutBack(prog2);
|
||||
logo2_squash_y_ = 0.6f + (squash_t2 * 0.4f);
|
||||
logo2_stretch_x_ = 1.0f + (1.0f - logo2_squash_y_) * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 0.3f + (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = (1.0f - prog1) * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 0.3f + (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = (1.0f - prog2) * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float bounce_t1 = easeOutBounce(prog1);
|
||||
logo1_scale_ = 1.0f;
|
||||
float squash_amount1 = (1.0f - bounce_t1) * 0.3f;
|
||||
logo1_squash_y_ = 1.0f - squash_amount1;
|
||||
logo1_stretch_x_ = 1.0f + squash_amount1 * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float bounce_t2 = easeOutBounce(prog2);
|
||||
logo2_scale_ = 1.0f;
|
||||
float squash_amount2 = (1.0f - bounce_t2) * 0.3f;
|
||||
logo2_squash_y_ = 1.0f - squash_amount2;
|
||||
logo2_stretch_x_ = 1.0f + squash_amount2 * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::VISIBLE:
|
||||
// Logo completamente visible, esperando duración
|
||||
if (timer_ >= APPLOGO_DISPLAY_DURATION) {
|
||||
state_ = AppLogoState::FADE_OUT;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// NO elegir nueva animación - reutilizar current_animation_ (simetría entrada/salida)
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_OUT:
|
||||
// Fade out: alpha de 255 a 0, con Logo 2 retrasado 0.25s (misma animación que entrada)
|
||||
{
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
|
||||
|
||||
// Verificar si fade out completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade out completado, volver a HIDDEN
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
logo2_scale_ = 1.0f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha con retraso de forma LINEAL (255 → 0, sin easing)
|
||||
logo1_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo1)) * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo2)) * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar MISMA animación (current_animation_) de forma invertida
|
||||
// ================================================================
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
logo2_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
logo1_scale_ = 1.0f + (prog1 * prog1 * 0.2f);
|
||||
logo1_squash_y_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f - (prog1 * 0.2f);
|
||||
logo1_rotation_ = prog1 * 0.1f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
logo2_scale_ = 1.0f + (prog2 * prog2 * 0.2f);
|
||||
logo2_squash_y_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f - (prog2 * 0.2f);
|
||||
logo2_rotation_ = prog2 * 0.1f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 1.0f - (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = prog1 * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 1.0f - (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = prog2 * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
if (prog1 < 0.2f) {
|
||||
float squash_t = prog1 / 0.2f;
|
||||
logo1_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (prog1 - 0.2f) / 0.8f;
|
||||
logo1_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo1_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo1_scale_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
if (prog2 < 0.2f) {
|
||||
float squash_t = prog2 / 0.2f;
|
||||
logo2_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (prog2 - 0.2f) / 0.8f;
|
||||
logo2_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo2_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo2_scale_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogo::render() {
|
||||
// Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT)
|
||||
if (state_ != AppLogoState::HIDDEN) {
|
||||
// Renderizar LOGO1 primero (fondo), luego LOGO2 (encima)
|
||||
renderWithGeometry(1);
|
||||
renderWithGeometry(2);
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogo::updateScreenSize(int screen_width, int screen_height) {
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
// ========================================================================
|
||||
// Detectar si coincide con resolución nativa o base, cambiar texturas
|
||||
// ========================================================================
|
||||
bool is_native = (screen_width == native_screen_width_ && screen_height == native_screen_height_);
|
||||
|
||||
if (is_native) {
|
||||
// Cambiar a texturas nativas (F4 fullscreen)
|
||||
logo1_current_texture_ = logo1_native_texture_;
|
||||
logo1_current_width_ = logo1_native_width_;
|
||||
logo1_current_height_ = logo1_native_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_native_texture_;
|
||||
logo2_current_width_ = logo2_native_width_;
|
||||
logo2_current_height_ = logo2_native_height_;
|
||||
|
||||
std::cout << "AppLogo: Cambiado a texturas NATIVAS" << std::endl;
|
||||
} else {
|
||||
// Cambiar a texturas base (ventana redimensionable)
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "AppLogo: Cambiado a texturas BASE" << std::endl;
|
||||
}
|
||||
|
||||
// Nota: No es necesario recalcular escalas porque las texturas están pre-escaladas
|
||||
// al tamaño exacto de pantalla. Solo renderizamos al 100% (o con deformaciones de animación).
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Funciones de easing para animaciones
|
||||
// ============================================================================
|
||||
|
||||
float AppLogo::easeOutElastic(float t) {
|
||||
// Elastic easing out: bounce elástico al final
|
||||
const float c4 = (2.0f * 3.14159f) / 3.0f;
|
||||
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
|
||||
return powf(2.0f, -10.0f * t) * sinf((t * 10.0f - 0.75f) * c4) + 1.0f;
|
||||
}
|
||||
|
||||
float AppLogo::easeOutBack(float t) {
|
||||
// Back easing out: overshoot suave al final
|
||||
const float c1 = 1.70158f;
|
||||
const float c3 = c1 + 1.0f;
|
||||
|
||||
return 1.0f + c3 * powf(t - 1.0f, 3.0f) + c1 * powf(t - 1.0f, 2.0f);
|
||||
}
|
||||
|
||||
float AppLogo::easeOutBounce(float t) {
|
||||
// Bounce easing out: rebotes decrecientes (para BOUNCE_SQUASH)
|
||||
const float n1 = 7.5625f;
|
||||
const float d1 = 2.75f;
|
||||
|
||||
if (t < 1.0f / d1) {
|
||||
return n1 * t * t;
|
||||
} else if (t < 2.0f / d1) {
|
||||
t -= 1.5f / d1;
|
||||
return n1 * t * t + 0.75f;
|
||||
} else if (t < 2.5f / d1) {
|
||||
t -= 2.25f / d1;
|
||||
return n1 * t * t + 0.9375f;
|
||||
} else {
|
||||
t -= 2.625f / d1;
|
||||
return n1 * t * t + 0.984375f;
|
||||
}
|
||||
}
|
||||
|
||||
float AppLogo::easeInOutQuad(float t) {
|
||||
// Quadratic easing in/out: aceleración suave (para ROTATE_SPIRAL)
|
||||
if (t < 0.5f) {
|
||||
return 2.0f * t * t;
|
||||
} else {
|
||||
return 1.0f - powf(-2.0f * t + 2.0f, 2.0f) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Función auxiliar para aleatorización
|
||||
// ============================================================================
|
||||
|
||||
AppLogoAnimationType AppLogo::getRandomAnimation() {
|
||||
// Generar número aleatorio entre 0 y 3 (4 tipos de animación)
|
||||
int random_value = rand() % 4;
|
||||
|
||||
switch (random_value) {
|
||||
case 0:
|
||||
return AppLogoAnimationType::ZOOM_ONLY;
|
||||
case 1:
|
||||
return AppLogoAnimationType::ELASTIC_STICK;
|
||||
case 2:
|
||||
return AppLogoAnimationType::ROTATE_SPIRAL;
|
||||
case 3:
|
||||
default:
|
||||
return AppLogoAnimationType::BOUNCE_SQUASH;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Renderizado con geometría (para todos los logos, con deformaciones)
|
||||
// ============================================================================
|
||||
|
||||
void AppLogo::renderWithGeometry(int logo_index) {
|
||||
if (!renderer_) return;
|
||||
|
||||
// Seleccionar variables según el logo_index (1 = logo1, 2 = logo2)
|
||||
SDL_Texture* texture;
|
||||
int base_width, base_height;
|
||||
float scale, squash_y, stretch_x, rotation;
|
||||
|
||||
if (logo_index == 1) {
|
||||
if (!logo1_current_texture_) return;
|
||||
texture = logo1_current_texture_;
|
||||
base_width = logo1_current_width_;
|
||||
base_height = logo1_current_height_;
|
||||
scale = logo1_scale_;
|
||||
squash_y = logo1_squash_y_;
|
||||
stretch_x = logo1_stretch_x_;
|
||||
rotation = logo1_rotation_;
|
||||
} else if (logo_index == 2) {
|
||||
if (!logo2_current_texture_) return;
|
||||
texture = logo2_current_texture_;
|
||||
base_width = logo2_current_width_;
|
||||
base_height = logo2_current_height_;
|
||||
scale = logo2_scale_;
|
||||
squash_y = logo2_squash_y_;
|
||||
stretch_x = logo2_stretch_x_;
|
||||
rotation = logo2_rotation_;
|
||||
} else {
|
||||
return; // Índice inválido
|
||||
}
|
||||
|
||||
// Aplicar alpha específico de cada logo (con retraso para logo2)
|
||||
int alpha = (logo_index == 1) ? logo1_alpha_ : logo2_alpha_;
|
||||
float alpha_normalized = static_cast<float>(alpha) / 255.0f; // Convertir 0-255 → 0.0-1.0
|
||||
// NO usar SDL_SetTextureAlphaMod - aplicar alpha directamente a vértices
|
||||
|
||||
// Calcular padding desde bordes derecho e inferior
|
||||
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
||||
float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT;
|
||||
|
||||
// Calcular esquina BASE (sin escala) para obtener centro FIJO
|
||||
// Esto asegura que el centro no se mueva cuando cambia scale/squash/stretch
|
||||
float corner_x_base = screen_width_ - base_width - padding_x;
|
||||
float corner_y_base = screen_height_ - base_height - padding_y;
|
||||
|
||||
// Centro FIJO del logo (no cambia con scale/squash/stretch)
|
||||
float center_x = corner_x_base + (base_width / 2.0f);
|
||||
float center_y = corner_y_base + (base_height / 2.0f);
|
||||
|
||||
// Calcular tamaño ESCALADO (para vértices)
|
||||
// (base_width y base_height ya están pre-escalados al tamaño correcto de pantalla)
|
||||
float width = base_width * scale * stretch_x;
|
||||
float height = base_height * scale * squash_y;
|
||||
|
||||
// Pre-calcular seno y coseno de rotación
|
||||
float cos_rot = cosf(rotation);
|
||||
float sin_rot = sinf(rotation);
|
||||
|
||||
// Crear 4 vértices del quad (centrado en center_x, center_y)
|
||||
SDL_Vertex vertices[4];
|
||||
|
||||
// Offset desde el centro
|
||||
float half_w = width / 2.0f;
|
||||
float half_h = height / 2.0f;
|
||||
|
||||
// Vértice superior izquierdo (rotado)
|
||||
{
|
||||
float local_x = -half_w;
|
||||
float local_y = -half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[0].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[0].tex_coord = {0.0f, 0.0f};
|
||||
vertices[0].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice superior derecho (rotado)
|
||||
{
|
||||
float local_x = half_w;
|
||||
float local_y = -half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[1].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[1].tex_coord = {1.0f, 0.0f};
|
||||
vertices[1].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice inferior derecho (rotado)
|
||||
{
|
||||
float local_x = half_w;
|
||||
float local_y = half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[2].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[2].tex_coord = {1.0f, 1.0f};
|
||||
vertices[2].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice inferior izquierdo (rotado)
|
||||
{
|
||||
float local_x = -half_w;
|
||||
float local_y = half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[3].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[3].tex_coord = {0.0f, 1.0f};
|
||||
vertices[3].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Índices para 2 triángulos
|
||||
int indices[6] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
// Renderizar con la textura del logo correspondiente
|
||||
SDL_RenderGeometry(renderer_, texture, vertices, 4, indices, 6);
|
||||
}
|
||||
117
source/app_logo.hpp
Normal file
117
source/app_logo.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
||||
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
|
||||
#include "defines.hpp" // for AppMode
|
||||
|
||||
class Texture;
|
||||
class Sprite;
|
||||
|
||||
// Estados de la máquina de estados del logo
|
||||
enum class AppLogoState {
|
||||
HIDDEN, // Logo oculto, esperando APPLOGO_DISPLAY_INTERVAL
|
||||
FADE_IN, // Apareciendo (alpha 0 → 255)
|
||||
VISIBLE, // Completamente visible, esperando APPLOGO_DISPLAY_DURATION
|
||||
FADE_OUT // Desapareciendo (alpha 255 → 0)
|
||||
};
|
||||
|
||||
// Tipo de animación de entrada/salida
|
||||
enum class AppLogoAnimationType {
|
||||
ZOOM_ONLY, // A: Solo zoom simple (120% → 100% → 120%)
|
||||
ELASTIC_STICK, // B: Zoom + deformación elástica tipo "pegatina"
|
||||
ROTATE_SPIRAL, // C: Rotación en espiral (entra girando, sale girando)
|
||||
BOUNCE_SQUASH // D: Rebote con aplastamiento (cae rebotando, salta)
|
||||
};
|
||||
|
||||
class AppLogo {
|
||||
public:
|
||||
AppLogo() = default;
|
||||
~AppLogo(); // Necesario para liberar las 4 texturas SDL
|
||||
|
||||
// Inicializar textura y sprite del logo
|
||||
bool initialize(SDL_Renderer* renderer, int screen_width, int screen_height);
|
||||
|
||||
// Actualizar temporizadores y estado de visibilidad
|
||||
void update(float delta_time, AppMode current_mode);
|
||||
|
||||
// Renderizar logo si está visible
|
||||
void render();
|
||||
|
||||
// Actualizar tamaño de pantalla (reposicionar logo)
|
||||
void updateScreenSize(int screen_width, int screen_height);
|
||||
|
||||
private:
|
||||
// ====================================================================
|
||||
// Texturas pre-escaladas (4 texturas: 2 logos × 2 resoluciones)
|
||||
// ====================================================================
|
||||
SDL_Texture* logo1_base_texture_ = nullptr; // Logo1 para resolución base
|
||||
SDL_Texture* logo1_native_texture_ = nullptr; // Logo1 para resolución nativa (F4)
|
||||
SDL_Texture* logo2_base_texture_ = nullptr; // Logo2 para resolución base
|
||||
SDL_Texture* logo2_native_texture_ = nullptr; // Logo2 para resolución nativa (F4)
|
||||
|
||||
// Dimensiones pre-calculadas para cada textura
|
||||
int logo1_base_width_ = 0, logo1_base_height_ = 0;
|
||||
int logo1_native_width_ = 0, logo1_native_height_ = 0;
|
||||
int logo2_base_width_ = 0, logo2_base_height_ = 0;
|
||||
int logo2_native_width_ = 0, logo2_native_height_ = 0;
|
||||
|
||||
// Texturas actualmente en uso (apuntan a base o native según resolución)
|
||||
SDL_Texture* logo1_current_texture_ = nullptr;
|
||||
SDL_Texture* logo2_current_texture_ = nullptr;
|
||||
int logo1_current_width_ = 0, logo1_current_height_ = 0;
|
||||
int logo2_current_width_ = 0, logo2_current_height_ = 0;
|
||||
|
||||
// Resoluciones conocidas
|
||||
int base_screen_width_ = 0, base_screen_height_ = 0; // Resolución inicial
|
||||
int native_screen_width_ = 0, native_screen_height_ = 0; // Resolución nativa (F4)
|
||||
|
||||
// ====================================================================
|
||||
// Variables COMPARTIDAS (sincronización de ambos logos)
|
||||
// ====================================================================
|
||||
AppLogoState state_ = AppLogoState::HIDDEN; // Estado actual de la máquina de estados
|
||||
float timer_ = 0.0f; // Contador de tiempo para estado actual
|
||||
|
||||
// Alpha INDEPENDIENTE para cada logo (Logo 2 con retraso)
|
||||
int logo1_alpha_ = 0; // Alpha de Logo 1 (0-255)
|
||||
int logo2_alpha_ = 0; // Alpha de Logo 2 (0-255, con retraso)
|
||||
|
||||
// Animación COMPARTIDA (misma para ambos logos, misma entrada y salida)
|
||||
AppLogoAnimationType current_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
|
||||
// Variables de deformación INDEPENDIENTES para logo1
|
||||
float logo1_scale_ = 1.0f; // Escala actual de logo1 (1.0 = 100%)
|
||||
float logo1_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo1
|
||||
float logo1_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo1
|
||||
float logo1_rotation_ = 0.0f; // Rotación en radianes logo1
|
||||
|
||||
// Variables de deformación INDEPENDIENTES para logo2
|
||||
float logo2_scale_ = 1.0f; // Escala actual de logo2 (1.0 = 100%)
|
||||
float logo2_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo2
|
||||
float logo2_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo2
|
||||
float logo2_rotation_ = 0.0f; // Rotación en radianes logo2
|
||||
|
||||
int screen_width_ = 0; // Ancho de pantalla (para centrar)
|
||||
int screen_height_ = 0; // Alto de pantalla (para centrar)
|
||||
|
||||
// Tamaño base del logo (calculado una vez)
|
||||
float base_width_ = 0.0f;
|
||||
float base_height_ = 0.0f;
|
||||
|
||||
// SDL renderer (necesario para renderizado con geometría)
|
||||
SDL_Renderer* renderer_ = nullptr;
|
||||
|
||||
// Métodos privados auxiliares
|
||||
void updateLogoPosition(); // Centrar ambos logos en pantalla (superpuestos)
|
||||
void renderWithGeometry(int logo_index); // Renderizar logo con vértices deformados (1 o 2)
|
||||
|
||||
// Funciones de easing
|
||||
float easeOutElastic(float t); // Elastic bounce out
|
||||
float easeOutBack(float t); // Overshoot out
|
||||
float easeOutBounce(float t); // Bounce easing (para BOUNCE_SQUASH)
|
||||
float easeInOutQuad(float t); // Quadratic easing (para ROTATE_SPIRAL)
|
||||
|
||||
// Función auxiliar para elegir animación aleatoria
|
||||
AppLogoAnimationType getRandomAnimation();
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "ball.h"
|
||||
#include "ball.hpp"
|
||||
|
||||
#include <stdlib.h> // for rand
|
||||
|
||||
#include <cmath> // for fabs
|
||||
|
||||
#include "defines.h" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
||||
#include "defines.hpp" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
||||
class Texture;
|
||||
|
||||
// Función auxiliar para generar pérdida aleatoria en rebotes
|
||||
@@ -22,9 +22,9 @@ float generateLateralLoss() {
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir, float mass_factor)
|
||||
Ball::Ball(float x, float y, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir, float mass_factor)
|
||||
: sprite_(std::make_unique<Sprite>(texture)),
|
||||
pos_({x, 0.0f, static_cast<float>(ball_size), static_cast<float>(ball_size)}) {
|
||||
pos_({x, y, static_cast<float>(ball_size), static_cast<float>(ball_size)}) {
|
||||
// Convertir velocidades de píxeles/frame a píxeles/segundo (multiplicar por 60)
|
||||
vx_ = vx * 60.0f;
|
||||
vy_ = vy * 60.0f;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
|
||||
#include "defines.h" // for Color
|
||||
#include "external/sprite.h" // for Sprite
|
||||
#include "defines.hpp" // for Color
|
||||
#include "external/sprite.hpp" // for Sprite
|
||||
class Texture;
|
||||
|
||||
class Ball {
|
||||
@@ -31,7 +31,7 @@ class Ball {
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f);
|
||||
Ball(float x, float y, float vx, float vy, Color color, std::shared_ptr<Texture> texture, int screen_width, int screen_height, int ball_size, GravityDirection gravity_dir = GravityDirection::DOWN, float mass_factor = 1.0f);
|
||||
|
||||
// Destructor
|
||||
~Ball() = default;
|
||||
@@ -71,6 +71,13 @@ class Ball {
|
||||
GravityDirection getGravityDirection() const { return gravity_direction_; }
|
||||
bool isOnSurface() const { return on_surface_; }
|
||||
|
||||
// Getters/Setters para velocidad (usado por BoidManager)
|
||||
void getVelocity(float& vx, float& vy) const { vx = vx_; vy = vy_; }
|
||||
void setVelocity(float vx, float vy) { vx_ = vx; vy_ = vy; }
|
||||
|
||||
// Setter para posición simple (usado por BoidManager)
|
||||
void setPosition(float x, float y) { pos_.x = x; pos_.y = y; }
|
||||
|
||||
// Getters/Setters para batch rendering
|
||||
SDL_FRect getPosition() const { return pos_; }
|
||||
Color getColor() const { return color_; }
|
||||
404
source/boids_mgr/boid_manager.cpp
Normal file
404
source/boids_mgr/boid_manager.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
#include "boid_manager.hpp"
|
||||
|
||||
#include <algorithm> // for std::min, std::max
|
||||
#include <cmath> // for sqrt, atan2
|
||||
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "engine.hpp" // for Engine (si se necesita)
|
||||
#include "scene/scene_manager.hpp" // for SceneManager
|
||||
#include "state/state_manager.hpp" // for StateManager
|
||||
#include "ui/ui_manager.hpp" // for UIManager
|
||||
|
||||
BoidManager::BoidManager()
|
||||
: engine_(nullptr)
|
||||
, scene_mgr_(nullptr)
|
||||
, ui_mgr_(nullptr)
|
||||
, state_mgr_(nullptr)
|
||||
, screen_width_(0)
|
||||
, screen_height_(0)
|
||||
, boids_active_(false)
|
||||
, spatial_grid_(800, 600, BOID_GRID_CELL_SIZE) // Tamaño por defecto, se actualiza en initialize()
|
||||
, separation_radius_(BOID_SEPARATION_RADIUS)
|
||||
, alignment_radius_(BOID_ALIGNMENT_RADIUS)
|
||||
, cohesion_radius_(BOID_COHESION_RADIUS)
|
||||
, separation_weight_(BOID_SEPARATION_WEIGHT)
|
||||
, alignment_weight_(BOID_ALIGNMENT_WEIGHT)
|
||||
, cohesion_weight_(BOID_COHESION_WEIGHT)
|
||||
, max_speed_(BOID_MAX_SPEED)
|
||||
, min_speed_(BOID_MIN_SPEED)
|
||||
, max_force_(BOID_MAX_FORCE)
|
||||
, boundary_margin_(BOID_BOUNDARY_MARGIN)
|
||||
, boundary_weight_(BOID_BOUNDARY_WEIGHT) {
|
||||
}
|
||||
|
||||
BoidManager::~BoidManager() {
|
||||
}
|
||||
|
||||
void BoidManager::initialize(Engine* engine, SceneManager* scene_mgr, UIManager* ui_mgr,
|
||||
StateManager* state_mgr, int screen_width, int screen_height) {
|
||||
engine_ = engine;
|
||||
scene_mgr_ = scene_mgr;
|
||||
ui_mgr_ = ui_mgr;
|
||||
state_mgr_ = state_mgr;
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
// Actualizar dimensiones del spatial grid
|
||||
spatial_grid_.updateWorldSize(screen_width, screen_height);
|
||||
}
|
||||
|
||||
void BoidManager::updateScreenSize(int width, int height) {
|
||||
screen_width_ = width;
|
||||
screen_height_ = height;
|
||||
|
||||
// Actualizar dimensiones del spatial grid (FASE 2)
|
||||
spatial_grid_.updateWorldSize(width, height);
|
||||
}
|
||||
|
||||
void BoidManager::activateBoids() {
|
||||
boids_active_ = true;
|
||||
|
||||
// Desactivar gravedad al entrar en modo boids
|
||||
scene_mgr_->forceBallsGravityOff();
|
||||
|
||||
// Inicializar velocidades aleatorias para los boids
|
||||
auto& balls = scene_mgr_->getBallsMutable();
|
||||
for (auto& ball : balls) {
|
||||
// Dar velocidad inicial aleatoria si está quieto
|
||||
float vx, vy;
|
||||
ball->getVelocity(vx, vy);
|
||||
if (vx == 0.0f && vy == 0.0f) {
|
||||
// Velocidad aleatoria entre -60 y +60 px/s (time-based)
|
||||
vx = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||
vy = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||
ball->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar notificación (solo si NO estamos en modo demo o logo)
|
||||
if (state_mgr_ && ui_mgr_ && state_mgr_->getCurrentMode() == AppMode::SANDBOX) {
|
||||
ui_mgr_->showNotification("Modo Boids");
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::deactivateBoids(bool force_gravity_on) {
|
||||
if (!boids_active_) return;
|
||||
|
||||
boids_active_ = false;
|
||||
|
||||
// Activar gravedad al salir (si se especifica)
|
||||
if (force_gravity_on) {
|
||||
scene_mgr_->forceBallsGravityOn();
|
||||
}
|
||||
|
||||
// Mostrar notificación (solo si NO estamos en modo demo o logo)
|
||||
if (state_mgr_ && ui_mgr_ && state_mgr_->getCurrentMode() == AppMode::SANDBOX) {
|
||||
ui_mgr_->showNotification("Modo Física");
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::toggleBoidsMode(bool force_gravity_on) {
|
||||
if (boids_active_) {
|
||||
deactivateBoids(force_gravity_on);
|
||||
} else {
|
||||
activateBoids();
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::update(float delta_time) {
|
||||
if (!boids_active_) return;
|
||||
|
||||
auto& balls = scene_mgr_->getBallsMutable();
|
||||
|
||||
// FASE 2: Poblar spatial grid al inicio de cada frame (O(n))
|
||||
spatial_grid_.clear();
|
||||
for (auto& ball : balls) {
|
||||
SDL_FRect pos = ball->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
spatial_grid_.insert(ball.get(), center_x, center_y);
|
||||
}
|
||||
|
||||
// Aplicar las tres reglas de Reynolds a cada boid
|
||||
// FASE 2: Ahora usa spatial grid para búsquedas O(1) en lugar de O(n)
|
||||
for (auto& ball : balls) {
|
||||
applySeparation(ball.get(), delta_time);
|
||||
applyAlignment(ball.get(), delta_time);
|
||||
applyCohesion(ball.get(), delta_time);
|
||||
applyBoundaries(ball.get());
|
||||
limitSpeed(ball.get());
|
||||
}
|
||||
|
||||
// Actualizar posiciones con velocidades resultantes (time-based)
|
||||
for (auto& ball : balls) {
|
||||
float vx, vy;
|
||||
ball->getVelocity(vx, vy);
|
||||
|
||||
SDL_FRect pos = ball->getPosition();
|
||||
pos.x += vx * delta_time; // time-based
|
||||
pos.y += vy * delta_time;
|
||||
|
||||
ball->setPosition(pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REGLAS DE REYNOLDS (1987)
|
||||
// ============================================================================
|
||||
|
||||
void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||
// Regla 1: Separación - Evitar colisiones con vecinos cercanos
|
||||
float steer_x = 0.0f;
|
||||
float steer_y = 0.0f;
|
||||
int count = 0;
|
||||
|
||||
SDL_FRect pos = boid->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, separation_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue; // Ignorar a sí mismo
|
||||
|
||||
SDL_FRect other_pos = other->getPosition();
|
||||
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||
|
||||
float dx = center_x - other_x;
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0.0f && distance < separation_radius_) {
|
||||
// FASE 1.3: Separación más fuerte cuando más cerca (inversamente proporcional a distancia)
|
||||
// Fuerza proporcional a cercanía: 0% en radio máximo, 100% en colisión
|
||||
float separation_strength = (separation_radius_ - distance) / separation_radius_;
|
||||
steer_x += (dx / distance) * separation_strength;
|
||||
steer_y += (dy / distance) * separation_strength;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
// Promedio
|
||||
steer_x /= count;
|
||||
steer_y /= count;
|
||||
|
||||
// Aplicar fuerza de separación
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
vx += steer_x * separation_weight_ * delta_time;
|
||||
vy += steer_y * separation_weight_ * delta_time;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
// Regla 2: Alineación - Seguir dirección promedio del grupo
|
||||
float avg_vx = 0.0f;
|
||||
float avg_vy = 0.0f;
|
||||
int count = 0;
|
||||
|
||||
SDL_FRect pos = boid->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, alignment_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue;
|
||||
|
||||
SDL_FRect other_pos = other->getPosition();
|
||||
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||
|
||||
float dx = center_x - other_x;
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < alignment_radius_) {
|
||||
float other_vx, other_vy;
|
||||
other->getVelocity(other_vx, other_vy);
|
||||
avg_vx += other_vx;
|
||||
avg_vy += other_vy;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
// Velocidad promedio del grupo
|
||||
avg_vx /= count;
|
||||
avg_vy /= count;
|
||||
|
||||
// Steering hacia la velocidad promedio
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
float steer_x = (avg_vx - vx) * alignment_weight_ * delta_time;
|
||||
float steer_y = (avg_vy - vy) * alignment_weight_ * delta_time;
|
||||
|
||||
// Limitar fuerza máxima de steering
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
vx += steer_x;
|
||||
vy += steer_y;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
// Regla 3: Cohesión - Moverse hacia el centro de masa del grupo
|
||||
float center_of_mass_x = 0.0f;
|
||||
float center_of_mass_y = 0.0f;
|
||||
int count = 0;
|
||||
|
||||
SDL_FRect pos = boid->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, cohesion_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue;
|
||||
|
||||
SDL_FRect other_pos = other->getPosition();
|
||||
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||
|
||||
float dx = center_x - other_x;
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < cohesion_radius_) {
|
||||
center_of_mass_x += other_x;
|
||||
center_of_mass_y += other_y;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
// Centro de masa del grupo
|
||||
center_of_mass_x /= count;
|
||||
center_of_mass_y /= count;
|
||||
|
||||
// FASE 1.4: Normalizar dirección hacia el centro (CRÍTICO - antes no estaba normalizado!)
|
||||
float dx_to_center = center_of_mass_x - center_x;
|
||||
float dy_to_center = center_of_mass_y - center_y;
|
||||
float distance_to_center = std::sqrt(dx_to_center * dx_to_center + dy_to_center * dy_to_center);
|
||||
|
||||
// Solo aplicar si hay distancia al centro (evitar división por cero)
|
||||
if (distance_to_center > 0.1f) {
|
||||
// Normalizar vector dirección (fuerza independiente de distancia)
|
||||
float steer_x = (dx_to_center / distance_to_center) * cohesion_weight_ * delta_time;
|
||||
float steer_y = (dy_to_center / distance_to_center) * cohesion_weight_ * delta_time;
|
||||
|
||||
// Limitar fuerza máxima de steering
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
vx += steer_x;
|
||||
vy += steer_y;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::applyBoundaries(Ball* boid) {
|
||||
// NUEVA IMPLEMENTACIÓN: Bordes como obstáculos (repulsión en lugar de wrapping)
|
||||
// Cuando un boid se acerca a un borde, se aplica una fuerza alejándolo
|
||||
SDL_FRect pos = boid->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
float steer_x = 0.0f;
|
||||
float steer_y = 0.0f;
|
||||
|
||||
// Borde izquierdo (x < boundary_margin_)
|
||||
if (center_x < boundary_margin_) {
|
||||
float distance = center_x; // Distancia al borde (0 = colisión)
|
||||
if (distance < boundary_margin_) {
|
||||
// Fuerza proporcional a cercanía: 0% en margen, 100% en colisión
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x += repulsion_strength; // Empujar hacia la derecha
|
||||
}
|
||||
}
|
||||
|
||||
// Borde derecho (x > screen_width_ - boundary_margin_)
|
||||
if (center_x > screen_width_ - boundary_margin_) {
|
||||
float distance = screen_width_ - center_x;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x -= repulsion_strength; // Empujar hacia la izquierda
|
||||
}
|
||||
}
|
||||
|
||||
// Borde superior (y < boundary_margin_)
|
||||
if (center_y < boundary_margin_) {
|
||||
float distance = center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y += repulsion_strength; // Empujar hacia abajo
|
||||
}
|
||||
}
|
||||
|
||||
// Borde inferior (y > screen_height_ - boundary_margin_)
|
||||
if (center_y > screen_height_ - boundary_margin_) {
|
||||
float distance = screen_height_ - center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y -= repulsion_strength; // Empujar hacia arriba
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar fuerza de repulsión si hay alguna
|
||||
if (steer_x != 0.0f || steer_y != 0.0f) {
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
|
||||
// Normalizar fuerza de repulsión (para que todas las direcciones tengan la misma intensidad)
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > 0.0f) {
|
||||
steer_x /= steer_mag;
|
||||
steer_y /= steer_mag;
|
||||
}
|
||||
|
||||
// Aplicar aceleración de repulsión (time-based)
|
||||
// boundary_weight_ es más fuerte que separation para garantizar que no escapen
|
||||
vx += steer_x * boundary_weight_ * (1.0f / 60.0f); // Simular delta_time fijo para independencia
|
||||
vy += steer_y * boundary_weight_ * (1.0f / 60.0f);
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
void BoidManager::limitSpeed(Ball* boid) {
|
||||
// Limitar velocidad máxima del boid
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
|
||||
float speed = std::sqrt(vx * vx + vy * vy);
|
||||
|
||||
// Limitar velocidad máxima
|
||||
if (speed > max_speed_) {
|
||||
vx = (vx / speed) * max_speed_;
|
||||
vy = (vy / speed) * max_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
|
||||
// FASE 1.2: Aplicar velocidad mínima (evitar boids estáticos)
|
||||
if (speed > 0.0f && speed < min_speed_) {
|
||||
vx = (vx / speed) * min_speed_;
|
||||
vy = (vy / speed) * min_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
126
source/boids_mgr/boid_manager.hpp
Normal file
126
source/boids_mgr/boid_manager.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
|
||||
#include "defines.hpp" // for SimulationMode, AppMode
|
||||
#include "spatial_grid.hpp" // for SpatialGrid
|
||||
|
||||
// Forward declarations
|
||||
class Engine;
|
||||
class SceneManager;
|
||||
class UIManager;
|
||||
class StateManager;
|
||||
class Ball;
|
||||
|
||||
/**
|
||||
* @class BoidManager
|
||||
* @brief Gestiona el comportamiento de enjambre (boids)
|
||||
*
|
||||
* Responsabilidad única: Implementación de algoritmo de boids (Reynolds 1987)
|
||||
*
|
||||
* Características:
|
||||
* - Separación: Evitar colisiones con vecinos cercanos
|
||||
* - Alineación: Seguir dirección promedio del grupo
|
||||
* - Cohesión: Moverse hacia el centro de masa del grupo
|
||||
* - Comportamiento emergente sin control centralizado
|
||||
* - Física de steering behavior (velocidad limitada)
|
||||
*/
|
||||
class BoidManager {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
BoidManager();
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~BoidManager();
|
||||
|
||||
/**
|
||||
* @brief Inicializa el BoidManager con referencias a componentes del Engine
|
||||
* @param engine Puntero al Engine (para acceso a recursos)
|
||||
* @param scene_mgr Puntero a SceneManager (acceso a bolas)
|
||||
* @param ui_mgr Puntero a UIManager (notificaciones)
|
||||
* @param state_mgr Puntero a StateManager (estados de aplicación)
|
||||
* @param screen_width Ancho de pantalla actual
|
||||
* @param screen_height Alto de pantalla actual
|
||||
*/
|
||||
void initialize(Engine* engine, SceneManager* scene_mgr, UIManager* ui_mgr,
|
||||
StateManager* state_mgr, int screen_width, int screen_height);
|
||||
|
||||
/**
|
||||
* @brief Actualiza el tamaño de pantalla (llamado en resize/fullscreen)
|
||||
* @param width Nuevo ancho de pantalla
|
||||
* @param height Nuevo alto de pantalla
|
||||
*/
|
||||
void updateScreenSize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Activa el modo boids
|
||||
*/
|
||||
void activateBoids();
|
||||
|
||||
/**
|
||||
* @brief Desactiva el modo boids (vuelve a física normal)
|
||||
* @param force_gravity_on Si debe forzar gravedad ON al salir
|
||||
*/
|
||||
void deactivateBoids(bool force_gravity_on = true);
|
||||
|
||||
/**
|
||||
* @brief Toggle entre modo boids y modo física
|
||||
* @param force_gravity_on Si debe forzar gravedad ON al salir de boids
|
||||
*/
|
||||
void toggleBoidsMode(bool force_gravity_on = true);
|
||||
|
||||
/**
|
||||
* @brief Actualiza el comportamiento de todas las bolas como boids
|
||||
* @param delta_time Delta time para física
|
||||
*/
|
||||
void update(float delta_time);
|
||||
|
||||
/**
|
||||
* @brief Verifica si el modo boids está activo
|
||||
* @return true si modo boids está activo
|
||||
*/
|
||||
bool isBoidsActive() const { return boids_active_; }
|
||||
|
||||
private:
|
||||
// Referencias a componentes del Engine
|
||||
Engine* engine_;
|
||||
SceneManager* scene_mgr_;
|
||||
UIManager* ui_mgr_;
|
||||
StateManager* state_mgr_;
|
||||
|
||||
// Tamaño de pantalla
|
||||
int screen_width_;
|
||||
int screen_height_;
|
||||
|
||||
// Estado del modo boids
|
||||
bool boids_active_;
|
||||
|
||||
// Spatial Hash Grid para optimización O(n²) → O(n)
|
||||
// FASE 2: Grid reutilizable para búsquedas de vecinos
|
||||
SpatialGrid spatial_grid_;
|
||||
|
||||
// === Parámetros ajustables en runtime (inicializados con valores de defines.h) ===
|
||||
// Permite modificar comportamiento sin recompilar (para tweaking/debug visual)
|
||||
float separation_radius_; // Radio de separación (evitar colisiones)
|
||||
float alignment_radius_; // Radio de alineación (matching de velocidad)
|
||||
float cohesion_radius_; // Radio de cohesión (centro de masa)
|
||||
float separation_weight_; // Peso fuerza de separación (aceleración px/s²)
|
||||
float alignment_weight_; // Peso fuerza de alineación (steering proporcional)
|
||||
float cohesion_weight_; // Peso fuerza de cohesión (aceleración px/s²)
|
||||
float max_speed_; // Velocidad máxima (px/s)
|
||||
float min_speed_; // Velocidad mínima (px/s)
|
||||
float max_force_; // Fuerza máxima de steering (px/s)
|
||||
float boundary_margin_; // Margen para repulsión de bordes (px)
|
||||
float boundary_weight_; // Peso fuerza de repulsión de bordes (aceleración px/s²)
|
||||
|
||||
// Métodos privados para las reglas de Reynolds
|
||||
void applySeparation(Ball* boid, float delta_time);
|
||||
void applyAlignment(Ball* boid, float delta_time);
|
||||
void applyCohesion(Ball* boid, float delta_time);
|
||||
void applyBoundaries(Ball* boid); // Repulsión de bordes (ya no wrapping)
|
||||
void limitSpeed(Ball* boid); // Limitar velocidad máxima
|
||||
};
|
||||
@@ -8,9 +8,9 @@
|
||||
constexpr char WINDOW_CAPTION[] = "ViBe3 Physics (JailDesigner 2025)";
|
||||
|
||||
// Resolución por defecto (usada si no se especifica en CLI)
|
||||
constexpr int DEFAULT_SCREEN_WIDTH = 320; // Ancho lógico por defecto (si no hay -w)
|
||||
constexpr int DEFAULT_SCREEN_HEIGHT = 240; // Alto lógico por defecto (si no hay -h)
|
||||
constexpr int DEFAULT_WINDOW_ZOOM = 3; // Zoom inicial de ventana (1x = sin zoom)
|
||||
constexpr int DEFAULT_SCREEN_WIDTH = 1280; // Ancho lógico por defecto (si no hay -w)
|
||||
constexpr int DEFAULT_SCREEN_HEIGHT = 720; // Alto lógico por defecto (si no hay -h)
|
||||
constexpr int DEFAULT_WINDOW_ZOOM = 1; // Zoom inicial de ventana (1x = sin zoom)
|
||||
|
||||
// Configuración de zoom dinámico de ventana
|
||||
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
||||
@@ -22,17 +22,17 @@ constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones
|
||||
constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²)
|
||||
|
||||
// Configuración de interfaz
|
||||
constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms) - OBSOLETO, usar NOTIFICATION_DURATION
|
||||
constexpr float THEME_TRANSITION_DURATION = 0.5f; // Duración de transiciones LERP entre temas (segundos)
|
||||
|
||||
// Configuración de notificaciones (sistema Notifier)
|
||||
constexpr int TEXT_ABSOLUTE_SIZE = 12; // Tamaño fuente base en píxeles físicos (múltiplo de 12px, tamaño nativo de la fuente)
|
||||
constexpr Uint64 NOTIFICATION_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (ms)
|
||||
constexpr Uint64 NOTIFICATION_FADE_TIME = 200; // Duración animación salida (ms)
|
||||
constexpr float NOTIFICATION_BG_ALPHA = 0.7f; // Opacidad fondo semitransparente (0.0-1.0)
|
||||
constexpr int NOTIFICATION_PADDING = 10; // Padding interno del fondo (píxeles físicos)
|
||||
constexpr int NOTIFICATION_TOP_MARGIN = 20; // Margen superior desde borde pantalla (píxeles físicos)
|
||||
constexpr int TEXT_ABSOLUTE_SIZE = 12; // Tamaño fuente base en píxeles físicos (múltiplo de 12px, tamaño nativo de la fuente)
|
||||
constexpr Uint64 NOTIFICATION_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (ms)
|
||||
constexpr Uint64 NOTIFICATION_FADE_TIME = 200; // Duración animación salida (ms)
|
||||
constexpr float NOTIFICATION_BG_ALPHA = 0.7f; // Opacidad fondo semitransparente (0.0-1.0)
|
||||
constexpr int NOTIFICATION_PADDING = 10; // Padding interno del fondo (píxeles físicos)
|
||||
constexpr int NOTIFICATION_TOP_MARGIN = 20; // Margen superior desde borde pantalla (píxeles físicos)
|
||||
constexpr char KIOSK_NOTIFICATION_TEXT[] = "MODO KIOSKO";
|
||||
|
||||
// Configuración de pérdida aleatoria en rebotes
|
||||
constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas
|
||||
@@ -53,6 +53,14 @@ constexpr float BALL_SPAWN_MARGIN = 0.15f; // Margen lateral para spawn (0.25 =
|
||||
// Escenarios de número de pelotas (teclas 1-8)
|
||||
constexpr int BALL_COUNT_SCENARIOS[8] = {10, 50, 100, 500, 1000, 5000, 10000, 50000};
|
||||
|
||||
// Límites de escenario para modos automáticos (índices en BALL_COUNT_SCENARIOS)
|
||||
// BALL_COUNT_SCENARIOS = {10, 50, 100, 500, 1000, 5000, 10000, 50000}
|
||||
// 0 1 2 3 4 5 6 7
|
||||
constexpr int DEMO_AUTO_MIN_SCENARIO = 2; // mínimo 100 bolas
|
||||
constexpr int DEMO_AUTO_MAX_SCENARIO = 7; // máximo sin restricción hardware (ajustado por benchmark)
|
||||
constexpr int LOGO_MIN_SCENARIO_IDX = 4; // mínimo 1000 bolas (sustituye LOGO_MODE_MIN_BALLS)
|
||||
constexpr int CUSTOM_SCENARIO_IDX = 8; // Escenario custom opcional (tecla 9, --custom-balls)
|
||||
|
||||
// Estructura para representar colores RGB
|
||||
struct Color {
|
||||
int r, g, b; // Componentes rojo, verde, azul (0-255)
|
||||
@@ -60,12 +68,12 @@ struct Color {
|
||||
|
||||
// Estructura de tema de colores estático
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
// Estructura para keyframe de tema dinámico
|
||||
@@ -99,21 +107,21 @@ enum class GravityDirection {
|
||||
// Enum para temas de colores (seleccionables con teclado numérico y Shift+Numpad)
|
||||
// Todos los temas usan ahora sistema dinámico de keyframes
|
||||
enum class ColorTheme {
|
||||
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
||||
OCEAN = 1, // Azules, turquesas, blancos (estático: 1 keyframe)
|
||||
NEON = 2, // Cian, magenta, verde lima, amarillo vibrante (estático: 1 keyframe)
|
||||
FOREST = 3, // Verdes, marrones, amarillos otoño (estático: 1 keyframe)
|
||||
RGB = 4, // RGB puros y subdivisiones matemáticas - fondo blanco (estático: 1 keyframe)
|
||||
MONOCHROME = 5, // Fondo negro degradado, sprites blancos monocromáticos (estático: 1 keyframe)
|
||||
LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe)
|
||||
CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (estático: 1 keyframe)
|
||||
EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe)
|
||||
SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo)
|
||||
OCEAN_WAVES = 10, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo)
|
||||
NEON_PULSE = 11, // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong)
|
||||
FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo)
|
||||
AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s ciclo)
|
||||
VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo)
|
||||
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
||||
OCEAN = 1, // Azules, turquesas, blancos (estático: 1 keyframe)
|
||||
NEON = 2, // Cian, magenta, verde lima, amarillo vibrante (estático: 1 keyframe)
|
||||
FOREST = 3, // Verdes, marrones, amarillos otoño (estático: 1 keyframe)
|
||||
RGB = 4, // RGB puros y subdivisiones matemáticas - fondo blanco (estático: 1 keyframe)
|
||||
MONOCHROME = 5, // Fondo negro degradado, sprites blancos monocromáticos (estático: 1 keyframe)
|
||||
LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe)
|
||||
CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (estático: 1 keyframe)
|
||||
EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe)
|
||||
SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo)
|
||||
OCEAN_WAVES = 10, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo)
|
||||
NEON_PULSE = 11, // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong)
|
||||
FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo)
|
||||
AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s ciclo)
|
||||
VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo)
|
||||
};
|
||||
|
||||
// Enum para tipo de figura 3D
|
||||
@@ -133,7 +141,16 @@ enum class ShapeType {
|
||||
// Enum para modo de simulación
|
||||
enum class SimulationMode {
|
||||
PHYSICS, // Modo física normal con gravedad
|
||||
SHAPE // Modo figura 3D (Shape polimórfico)
|
||||
SHAPE, // Modo figura 3D (Shape polimórfico)
|
||||
BOIDS // Modo enjambre (Boids - comportamiento emergente)
|
||||
};
|
||||
|
||||
// Enum para modo de aplicación (mutuamente excluyentes)
|
||||
enum class AppMode {
|
||||
SANDBOX, // Control manual del usuario (modo sandbox)
|
||||
DEMO, // Modo demo completo (auto-play)
|
||||
DEMO_LITE, // Modo demo lite (solo física/figuras)
|
||||
LOGO // Modo logo (easter egg)
|
||||
};
|
||||
|
||||
// Enum para modo de escalado en fullscreen (F5)
|
||||
@@ -273,11 +290,42 @@ constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO = 5; // 5% probabilidad en D
|
||||
constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE = 3; // 3% probabilidad en DEMO LITE (aún más raro)
|
||||
|
||||
// Sistema de espera de flips en LOGO MODE (camino alternativo de decisión)
|
||||
constexpr int LOGO_FLIP_WAIT_MIN = 1; // Mínimo de flips a esperar antes de cambiar a PHYSICS
|
||||
constexpr int LOGO_FLIP_WAIT_MAX = 3; // Máximo de flips a esperar
|
||||
constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progreso de flip para trigger
|
||||
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
||||
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
||||
constexpr int LOGO_FLIP_WAIT_MIN = 1; // Mínimo de flips a esperar antes de cambiar a PHYSICS
|
||||
constexpr int LOGO_FLIP_WAIT_MAX = 3; // Máximo de flips a esperar
|
||||
constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progreso de flip para trigger
|
||||
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
||||
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
||||
|
||||
// Configuración de AppLogo (logo periódico en pantalla)
|
||||
constexpr float APPLOGO_DISPLAY_INTERVAL = 90.0f; // Intervalo entre apariciones del logo (segundos)
|
||||
constexpr float APPLOGO_DISPLAY_DURATION = 30.0f; // Duración de visibilidad del logo (segundos)
|
||||
constexpr float APPLOGO_ANIMATION_DURATION = 0.5f; // Duración de animación entrada/salida (segundos)
|
||||
constexpr float APPLOGO_HEIGHT_PERCENT = 0.4f; // Altura del logo = 40% de la altura de pantalla
|
||||
constexpr float APPLOGO_PADDING_PERCENT = 0.05f; // Padding desde esquina inferior-derecha = 10%
|
||||
constexpr float APPLOGO_LOGO2_DELAY = 0.25f; // Retraso de Logo 2 respecto a Logo 1 (segundos)
|
||||
|
||||
// Configuración de Modo BOIDS (comportamiento de enjambre)
|
||||
// TIME-BASED CONVERSION (frame-based → time-based):
|
||||
// - Radios: sin cambios (píxeles)
|
||||
// - Velocidades (MAX_SPEED, MIN_SPEED): ×60 (px/frame → px/s)
|
||||
// - Aceleraciones puras (SEPARATION, COHESION): ×60² = ×3600 (px/frame² → px/s²)
|
||||
// - Steering proporcional (ALIGNMENT): ×60 (proporcional a velocidad)
|
||||
// - Límite velocidad (MAX_FORCE): ×60 (px/frame → px/s)
|
||||
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
||||
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
||||
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
||||
constexpr float BOID_SEPARATION_WEIGHT = 5400.0f; // Aceleración de separación (px/s²) [era 1.5 × 3600]
|
||||
constexpr float BOID_ALIGNMENT_WEIGHT = 60.0f; // Steering de alineación (proporcional) [era 1.0 × 60]
|
||||
constexpr float BOID_COHESION_WEIGHT = 3.6f; // Aceleración de cohesión (px/s²) [era 0.001 × 3600]
|
||||
constexpr float BOID_MAX_SPEED = 150.0f; // Velocidad máxima (px/s) [era 2.5 × 60]
|
||||
constexpr float BOID_MAX_FORCE = 3.0f; // Fuerza máxima de steering (px/s) [era 0.05 × 60]
|
||||
constexpr float BOID_MIN_SPEED = 18.0f; // Velocidad mínima (px/s) [era 0.3 × 60]
|
||||
constexpr float BOID_BOUNDARY_MARGIN = 50.0f; // Distancia a borde para activar repulsión (píxeles)
|
||||
constexpr float BOID_BOUNDARY_WEIGHT = 7200.0f; // Aceleración de repulsión de bordes (px/s²) [más fuerte que separation]
|
||||
|
||||
// FASE 2: Spatial Hash Grid para optimización O(n²) → O(n)
|
||||
constexpr float BOID_GRID_CELL_SIZE = 100.0f; // Tamaño de celda del grid (píxeles)
|
||||
// Debe ser ≥ BOID_COHESION_RADIUS para funcionar correctamente
|
||||
|
||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||
|
||||
1856
source/engine.cpp
1856
source/engine.cpp
File diff suppressed because it is too large
Load Diff
196
source/engine.h
196
source/engine.h
@@ -1,196 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h> // for SDL_Event
|
||||
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
||||
#include <SDL3/SDL_stdinc.h> // for Uint64
|
||||
#include <SDL3/SDL_video.h> // for SDL_Window
|
||||
|
||||
#include <array> // for array
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ball.h" // for Ball
|
||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||
#include "external/texture.h" // for Texture
|
||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||
#include "text/textrenderer.h" // for TextRenderer
|
||||
#include "theme_manager.h" // for ThemeManager
|
||||
#include "ui/notifier.h" // for Notifier
|
||||
|
||||
// Modos de aplicación mutuamente excluyentes
|
||||
enum class AppMode {
|
||||
SANDBOX, // Control manual del usuario (modo sandbox)
|
||||
DEMO, // Modo demo completo (auto-play)
|
||||
DEMO_LITE, // Modo demo lite (solo física/figuras)
|
||||
LOGO // Modo logo (easter egg)
|
||||
};
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
// Interfaz pública
|
||||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false);
|
||||
void run();
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
// Recursos SDL
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_Renderer* renderer_ = nullptr;
|
||||
std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
|
||||
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
|
||||
std::vector<std::string> texture_names_; // Nombres de texturas (sin extensión)
|
||||
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)
|
||||
|
||||
// Estado del simulador
|
||||
std::vector<std::unique_ptr<Ball>> balls_;
|
||||
GravityDirection current_gravity_ = GravityDirection::DOWN;
|
||||
int scenario_ = 0;
|
||||
bool should_exit_ = false;
|
||||
|
||||
// Sistema de timing
|
||||
Uint64 last_frame_time_ = 0;
|
||||
float delta_time_ = 0.0f;
|
||||
|
||||
// UI y debug
|
||||
bool show_debug_ = false;
|
||||
bool show_text_ = true; // OBSOLETO: usar notifier_ en su lugar
|
||||
TextRenderer text_renderer_; // Sistema de renderizado de texto para display (centrado)
|
||||
TextRenderer text_renderer_debug_; // Sistema de renderizado de texto para debug (HUD)
|
||||
TextRenderer text_renderer_notifier_; // Sistema de renderizado de texto para notificaciones (tamaño fijo)
|
||||
Notifier notifier_; // Sistema de notificaciones estilo iOS/Android
|
||||
|
||||
// Sistema de zoom dinámico
|
||||
int current_window_zoom_ = DEFAULT_WINDOW_ZOOM;
|
||||
std::string text_;
|
||||
int text_pos_ = 0;
|
||||
Uint64 text_init_time_ = 0;
|
||||
|
||||
// FPS y V-Sync
|
||||
Uint64 fps_last_time_ = 0;
|
||||
int fps_frame_count_ = 0;
|
||||
int fps_current_ = 0;
|
||||
std::string fps_text_ = "FPS: 0";
|
||||
bool vsync_enabled_ = true;
|
||||
std::string vsync_text_ = "VSYNC ON";
|
||||
bool fullscreen_enabled_ = false;
|
||||
bool real_fullscreen_enabled_ = false;
|
||||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
||||
|
||||
// Resolución base (configurada por CLI o default)
|
||||
int base_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int base_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Resolución dinámica actual (cambia en fullscreen real)
|
||||
int current_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int current_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Resolución física real de ventana/pantalla (para texto absoluto)
|
||||
int physical_window_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int physical_window_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Sistema de temas (delegado a ThemeManager)
|
||||
std::unique_ptr<ThemeManager> theme_manager_;
|
||||
int theme_page_ = 0; // Página actual de temas (0 o 1) para acceso por Numpad
|
||||
|
||||
// Sistema de Figuras 3D (polimórfico)
|
||||
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
||||
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)
|
||||
AppMode current_app_mode_ = AppMode::SANDBOX; // Modo actual (mutuamente excluyente)
|
||||
AppMode previous_app_mode_ = AppMode::SANDBOX; // Modo previo antes de entrar a LOGO
|
||||
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)
|
||||
|
||||
// Sistema de convergencia para LOGO MODE (escala con resolución)
|
||||
float shape_convergence_ = 0.0f; // % de pelotas cerca del objetivo (0.0-1.0)
|
||||
float logo_convergence_threshold_ = 0.90f; // Threshold aleatorio (75-100%)
|
||||
float logo_min_time_ = 3.0f; // Tiempo mínimo escalado con resolución
|
||||
float logo_max_time_ = 5.0f; // Tiempo máximo escalado (backup)
|
||||
|
||||
// Sistema de espera de flips en LOGO MODE (camino alternativo)
|
||||
bool logo_waiting_for_flip_ = false; // true si eligió el camino "esperar flip"
|
||||
int logo_target_flip_number_ = 0; // En qué flip actuar (1, 2 o 3)
|
||||
float logo_target_flip_percentage_ = 0.0f; // % de flip a esperar (0.2-0.8)
|
||||
int logo_current_flip_count_ = 0; // Flips observados hasta ahora
|
||||
|
||||
// Control de entrada manual vs automática a LOGO MODE
|
||||
bool logo_entered_manually_ = false; // true si se activó con tecla K, false si automático desde DEMO
|
||||
|
||||
// Estado previo antes de entrar a Logo Mode (para restaurar al salir)
|
||||
int logo_previous_theme_ = 0; // Índice de tema (0-9)
|
||||
size_t logo_previous_texture_index_ = 0;
|
||||
float logo_previous_shape_scale_ = 1.0f;
|
||||
|
||||
// Batch rendering
|
||||
std::vector<SDL_Vertex> batch_vertices_;
|
||||
std::vector<int> batch_indices_;
|
||||
|
||||
// Configuración del sistema de texto (constantes configurables)
|
||||
static constexpr const char* TEXT_FONT_PATH = "data/fonts/determination.ttf";
|
||||
static constexpr int TEXT_BASE_SIZE = 24; // Tamaño base para 240p
|
||||
static constexpr bool TEXT_ANTIALIASING = true; // true = suavizado, false = píxeles nítidos
|
||||
|
||||
// Métodos principales del loop
|
||||
void calculateDeltaTime();
|
||||
void update();
|
||||
void handleEvents();
|
||||
void render();
|
||||
|
||||
// Métodos auxiliares
|
||||
void initBalls(int value);
|
||||
void setText(); // DEPRECATED - usar showNotificationForAction() en su lugar
|
||||
void showNotificationForAction(const std::string& text); // Mostrar notificación solo en modo MANUAL
|
||||
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;
|
||||
|
||||
// Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO)
|
||||
void setState(AppMode new_mode); // Cambiar modo de aplicación (mutuamente excluyente)
|
||||
|
||||
// Sistema de Modo DEMO
|
||||
void updateDemoMode();
|
||||
void performDemoAction(bool is_lite);
|
||||
void randomizeOnDemoStart(bool is_lite);
|
||||
void toggleGravityOnOff();
|
||||
|
||||
// Sistema de Modo Logo (easter egg)
|
||||
void toggleLogoMode(); // Activar/desactivar modo logo manual (tecla K)
|
||||
void enterLogoMode(bool from_demo = false); // Entrar al modo logo (manual o automático)
|
||||
void exitLogoMode(bool return_to_demo = false); // Salir del modo logo
|
||||
|
||||
// Sistema de cambio de sprites dinámico
|
||||
void switchTexture(bool show_notification = true); // Cambia a siguiente textura disponible
|
||||
void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño
|
||||
|
||||
// Sistema de zoom dinámico
|
||||
int calculateMaxWindowZoom() const;
|
||||
void setWindowZoom(int new_zoom);
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void updatePhysicalWindowSize(); // Actualizar tamaño físico real de ventana
|
||||
|
||||
// Rendering
|
||||
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
|
||||
};
|
||||
266
source/engine.hpp
Normal file
266
source/engine.hpp
Normal file
@@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h> // for SDL_Event
|
||||
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
||||
#include <SDL3/SDL_stdinc.h> // for Uint64
|
||||
#include <SDL3/SDL_video.h> // for SDL_Window
|
||||
|
||||
#include <array> // for array
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "app_logo.hpp" // for AppLogo
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "boids_mgr/boid_manager.hpp" // for BoidManager
|
||||
#include "defines.hpp" // for GravityDirection, ColorTheme, ShapeType
|
||||
#include "external/texture.hpp" // for Texture
|
||||
#include "input/input_handler.hpp" // for InputHandler
|
||||
#include "scene/scene_manager.hpp" // for SceneManager
|
||||
#include "shapes/shape.hpp" // for Shape (interfaz polimórfica)
|
||||
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
|
||||
#include "state/state_manager.hpp" // for StateManager
|
||||
#include "theme_manager.hpp" // for ThemeManager
|
||||
#include "ui/ui_manager.hpp" // for UIManager
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
// Interfaz pública principal
|
||||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
|
||||
void run();
|
||||
void shutdown();
|
||||
|
||||
// === Métodos públicos para InputHandler ===
|
||||
|
||||
// Gravedad y física
|
||||
void pushBallsAwayFromGravity();
|
||||
void handleGravityToggle();
|
||||
void handleGravityDirectionChange(GravityDirection direction, const char* notification_text);
|
||||
|
||||
// Display y depuración
|
||||
void toggleVSync();
|
||||
void toggleDebug();
|
||||
void toggleHelp();
|
||||
|
||||
// Figuras 3D
|
||||
void toggleShapeMode();
|
||||
void activateShape(ShapeType type, const char* notification_text);
|
||||
void handleShapeScaleChange(bool increase);
|
||||
void resetShapeScale();
|
||||
void toggleDepthZoom();
|
||||
|
||||
// Boids (comportamiento de enjambre)
|
||||
void toggleBoidsMode(bool force_gravity_on = true);
|
||||
|
||||
// Temas de colores
|
||||
void cycleTheme(bool forward);
|
||||
void switchThemeByNumpad(int numpad_key);
|
||||
void toggleThemePage();
|
||||
void pauseDynamicTheme();
|
||||
|
||||
// Sprites/Texturas
|
||||
void switchTexture();
|
||||
|
||||
// Escenarios (número de pelotas)
|
||||
void changeScenario(int scenario_id, const char* notification_text);
|
||||
|
||||
// Zoom y fullscreen
|
||||
void handleZoomIn();
|
||||
void handleZoomOut();
|
||||
void toggleFullscreen();
|
||||
void toggleRealFullscreen();
|
||||
void toggleIntegerScaling();
|
||||
|
||||
// Modo kiosko
|
||||
void setKioskMode(bool enabled) { kiosk_mode_ = enabled; }
|
||||
bool isKioskMode() const { return kiosk_mode_; }
|
||||
|
||||
// Escenario custom (tecla 9, --custom-balls)
|
||||
void setCustomScenario(int balls);
|
||||
bool isCustomScenarioEnabled() const { return custom_scenario_enabled_; }
|
||||
bool isCustomAutoAvailable() const { return custom_auto_available_; }
|
||||
int getCustomScenarioBalls() const { return custom_scenario_balls_; }
|
||||
|
||||
// Control manual del benchmark (--skip-benchmark, --max-balls)
|
||||
void setSkipBenchmark();
|
||||
void setMaxBallsOverride(int n);
|
||||
|
||||
// Notificaciones (público para InputHandler)
|
||||
void showNotificationForAction(const std::string& text);
|
||||
|
||||
// Modos de aplicación (DEMO/LOGO)
|
||||
void toggleDemoMode();
|
||||
void toggleDemoLiteMode();
|
||||
void toggleLogoMode();
|
||||
|
||||
// === Métodos públicos para StateManager (callbacks) ===
|
||||
// NOTA: StateManager coordina estados, Engine proporciona implementación
|
||||
// Estos callbacks permiten que StateManager ejecute acciones complejas que
|
||||
// requieren acceso a múltiples componentes (SceneManager, ThemeManager, ShapeManager, etc.)
|
||||
// Este enfoque es pragmático y mantiene la separación de responsabilidades limpia
|
||||
void performLogoAction(bool logo_waiting_for_flip);
|
||||
void executeDemoAction(bool is_lite);
|
||||
void executeRandomizeOnDemoStart(bool is_lite);
|
||||
void executeToggleGravityOnOff();
|
||||
void executeEnterLogoMode(size_t ball_count);
|
||||
void executeExitLogoMode();
|
||||
|
||||
// === Getters públicos para UIManager (Debug HUD) ===
|
||||
bool getVSyncEnabled() const { return vsync_enabled_; }
|
||||
bool getFullscreenEnabled() const { return fullscreen_enabled_; }
|
||||
bool getRealFullscreenEnabled() const { return real_fullscreen_enabled_; }
|
||||
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
|
||||
int getCurrentScreenWidth() const { return current_screen_width_; }
|
||||
int getCurrentScreenHeight() const { return current_screen_height_; }
|
||||
std::string getCurrentTextureName() const {
|
||||
if (texture_names_.empty()) return "";
|
||||
return texture_names_[current_texture_index_];
|
||||
}
|
||||
int getBaseScreenWidth() const { return base_screen_width_; }
|
||||
int getBaseScreenHeight() const { return base_screen_height_; }
|
||||
int getMaxAutoScenario() const { return max_auto_scenario_; }
|
||||
|
||||
private:
|
||||
// === Componentes del sistema (Composición) ===
|
||||
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
||||
std::unique_ptr<SceneManager> scene_manager_; // Gestión de bolas y física
|
||||
std::unique_ptr<ShapeManager> shape_manager_; // Gestión de figuras 3D
|
||||
std::unique_ptr<BoidManager> boid_manager_; // Gestión de comportamiento boids
|
||||
std::unique_ptr<StateManager> state_manager_; // Gestión de estados (DEMO/LOGO)
|
||||
std::unique_ptr<UIManager> ui_manager_; // Gestión de UI (HUD, FPS, notificaciones)
|
||||
std::unique_ptr<AppLogo> app_logo_; // Gestión de logo periódico en pantalla
|
||||
|
||||
// Recursos SDL
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_Renderer* renderer_ = nullptr;
|
||||
std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
|
||||
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
|
||||
std::vector<std::string> texture_names_; // Nombres de texturas (sin extensión)
|
||||
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)
|
||||
|
||||
// Estado del simulador
|
||||
bool should_exit_ = false;
|
||||
|
||||
// Sistema de timing
|
||||
Uint64 last_frame_time_ = 0;
|
||||
float delta_time_ = 0.0f;
|
||||
|
||||
// Sistema de zoom dinámico
|
||||
int current_window_zoom_ = DEFAULT_WINDOW_ZOOM;
|
||||
|
||||
// V-Sync
|
||||
bool vsync_enabled_ = true;
|
||||
bool fullscreen_enabled_ = false;
|
||||
bool real_fullscreen_enabled_ = false;
|
||||
bool kiosk_mode_ = false;
|
||||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
||||
|
||||
// Resolución base (configurada por CLI o default)
|
||||
int base_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int base_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Resolución dinámica actual (cambia en fullscreen real)
|
||||
int current_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int current_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Resolución física real de ventana/pantalla (para texto absoluto)
|
||||
int physical_window_width_ = DEFAULT_SCREEN_WIDTH;
|
||||
int physical_window_height_ = DEFAULT_SCREEN_HEIGHT;
|
||||
|
||||
// Sistema de temas (delegado a ThemeManager)
|
||||
std::unique_ptr<ThemeManager> theme_manager_;
|
||||
int theme_page_ = 0; // Página actual de temas (0 o 1) para acceso por Numpad
|
||||
|
||||
// Sistema de Figuras 3D (polimórfico)
|
||||
// NOTA: Engine mantiene implementación de figuras usada por callbacks DEMO/LOGO
|
||||
// ShapeManager tiene implementación paralela para controles manuales del usuario
|
||||
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
||||
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) y LOGO
|
||||
// NOTA: Engine mantiene estado de implementación para callbacks performLogoAction()
|
||||
// StateManager coordina los triggers y timers, Engine ejecuta las acciones
|
||||
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)
|
||||
int max_auto_scenario_ = 5; // Índice máximo en modos auto (default conservador: 5000 bolas)
|
||||
|
||||
// Escenario custom (--custom-balls)
|
||||
int custom_scenario_balls_ = 0;
|
||||
bool custom_scenario_enabled_ = false;
|
||||
bool custom_auto_available_ = false;
|
||||
bool skip_benchmark_ = false;
|
||||
|
||||
// Sistema de convergencia para LOGO MODE (escala con resolución)
|
||||
// Usado por performLogoAction() para detectar cuando las bolas forman el logo
|
||||
float shape_convergence_ = 0.0f; // % de pelotas cerca del objetivo (0.0-1.0)
|
||||
float logo_convergence_threshold_ = 0.90f; // Threshold aleatorio (75-100%)
|
||||
float logo_min_time_ = 3.0f; // Tiempo mínimo escalado con resolución
|
||||
float logo_max_time_ = 5.0f; // Tiempo máximo escalado (backup)
|
||||
|
||||
// Sistema de espera de flips en LOGO MODE (camino alternativo)
|
||||
// Permite que LOGO espere a que ocurran rotaciones antes de cambiar estado
|
||||
bool logo_waiting_for_flip_ = false; // true si eligió el camino "esperar flip"
|
||||
int logo_target_flip_number_ = 0; // En qué flip actuar (1, 2 o 3)
|
||||
float logo_target_flip_percentage_ = 0.0f; // % de flip a esperar (0.2-0.8)
|
||||
int logo_current_flip_count_ = 0; // Flips observados hasta ahora
|
||||
|
||||
// NOTA: logo_entered_manually_ fue eliminado de Engine (duplicado)
|
||||
// Ahora se obtiene de StateManager con state_manager_->getLogoEnteredManually()
|
||||
// Esto evita desincronización entre Engine y StateManager
|
||||
|
||||
// Estado previo antes de entrar a Logo Mode (para restaurar al salir)
|
||||
// Guardado por executeEnterLogoMode(), restaurado por executeExitLogoMode()
|
||||
int logo_previous_theme_ = 0; // Índice de tema (0-9)
|
||||
size_t logo_previous_texture_index_ = 0;
|
||||
float logo_previous_shape_scale_ = 1.0f;
|
||||
|
||||
// Batch rendering
|
||||
std::vector<SDL_Vertex> batch_vertices_;
|
||||
std::vector<int> batch_indices_;
|
||||
|
||||
// Bucket sort per z-ordering (SHAPE mode)
|
||||
static constexpr int DEPTH_SORT_BUCKETS = 256;
|
||||
std::array<std::vector<size_t>, DEPTH_SORT_BUCKETS> depth_buckets_;
|
||||
|
||||
// Configuración del sistema de texto (constantes configurables)
|
||||
static constexpr const char* TEXT_FONT_PATH = "data/fonts/determination.ttf";
|
||||
static constexpr int TEXT_BASE_SIZE = 24; // Tamaño base para 240p
|
||||
static constexpr bool TEXT_ANTIALIASING = true; // true = suavizado, false = píxeles nítidos
|
||||
|
||||
// Métodos principales del loop
|
||||
void calculateDeltaTime();
|
||||
void update();
|
||||
void render();
|
||||
|
||||
// Benchmark de rendimiento (determina max_auto_scenario_ al inicio)
|
||||
void runPerformanceBenchmark();
|
||||
|
||||
// Métodos auxiliares privados (llamados por la interfaz pública)
|
||||
|
||||
// Sistema de cambio de sprites dinámico - Métodos privados
|
||||
void switchTextureInternal(bool show_notification); // Implementación interna del cambio de textura
|
||||
|
||||
// Sistema de zoom dinámico - Métodos privados
|
||||
int calculateMaxWindowZoom() const;
|
||||
void setWindowZoom(int new_zoom);
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void updatePhysicalWindowSize(); // Actualizar tamaño físico real de ventana
|
||||
|
||||
// Rendering
|
||||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f);
|
||||
|
||||
// Sistema de Figuras 3D - Métodos privados
|
||||
// NOTA FASE 7: Métodos DUPLICADOS con ShapeManager (Engine mantiene implementación para DEMO/LOGO)
|
||||
// TODO FASE 8: Convertir en wrappers puros cuando migremos DEMO/LOGO
|
||||
void toggleShapeModeInternal(bool force_gravity_on_exit = true); // Implementación interna del toggle
|
||||
void activateShapeInternal(ShapeType type); // Implementación interna de activación
|
||||
void updateShape(); // Actualizar figura activa
|
||||
void generateShape(); // Generar puntos de figura activa
|
||||
void clampShapeScale(); // Limitar escala para evitar clipping
|
||||
};
|
||||
78
source/external/dbgtxt.h
vendored
78
source/external/dbgtxt.h
vendored
@@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace {
|
||||
SDL_Texture* dbg_tex = nullptr;
|
||||
SDL_Renderer* dbg_ren = nullptr;
|
||||
} // namespace
|
||||
|
||||
inline void dbg_init(SDL_Renderer* renderer) {
|
||||
dbg_ren = renderer;
|
||||
Uint8 font[448] = {0x42, 0x4D, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x18, 0xF3, 0x83, 0x83, 0xCF, 0x83, 0x87, 0x00, 0x00, 0xF3, 0x39, 0x39, 0xCF, 0x79, 0xF3, 0x00, 0x00, 0x01, 0xF9, 0x39, 0xCF, 0x61, 0xF9, 0x00, 0x00, 0x33, 0xF9, 0x03, 0xE7, 0x87, 0x81, 0x00, 0x00, 0x93, 0x03, 0x3F, 0xF3, 0x1B, 0x39, 0x00, 0x00, 0xC3, 0x3F, 0x9F, 0x39, 0x3B, 0x39, 0x00, 0x41, 0xE3, 0x03, 0xC3, 0x01, 0x87, 0x83, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0x01, 0xC7, 0x81, 0x01, 0x83, 0x00, 0x00, 0xE7, 0x1F, 0x9B, 0xE7, 0x1F, 0x39, 0x00, 0x00, 0xE7, 0x8F, 0x39, 0xE7, 0x87, 0xF9, 0x00, 0x00, 0xC3, 0xC7, 0x39, 0xE7, 0xC3, 0xC3, 0x00, 0x00, 0x99, 0xE3, 0x39, 0xE7, 0xF1, 0xE7, 0x00, 0x00, 0x99, 0xF1, 0xB3, 0xC7, 0x39, 0xF3, 0x00, 0x00, 0x99, 0x01, 0xC7, 0xE7, 0x83, 0x81, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x83, 0xE7, 0x83, 0xEF, 0x39, 0x39, 0x00, 0x00, 0x39, 0xE7, 0x39, 0xC7, 0x11, 0x11, 0x00, 0x00, 0xF9, 0xE7, 0x39, 0x83, 0x01, 0x83, 0x00, 0x00, 0x83, 0xE7, 0x39, 0x11, 0x01, 0xC7, 0x00, 0x00, 0x3F, 0xE7, 0x39, 0x39, 0x29, 0x83, 0x00, 0x00, 0x33, 0xE7, 0x39, 0x39, 0x39, 0x11, 0x00, 0x00, 0x87, 0x81, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x39, 0x39, 0x83, 0x3F, 0x85, 0x31, 0x00, 0x00, 0x39, 0x31, 0x39, 0x3F, 0x33, 0x23, 0x00, 0x00, 0x29, 0x21, 0x39, 0x03, 0x21, 0x07, 0x00, 0x00, 0x01, 0x01, 0x39, 0x39, 0x39, 0x31, 0x00, 0x00, 0x01, 0x09, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x11, 0x19, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x39, 0x39, 0x83, 0x03, 0x83, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xC1, 0x39, 0x81, 0x83, 0x31, 0x01, 0x00, 0x00, 0x99, 0x39, 0xE7, 0x39, 0x23, 0x3F, 0x00, 0x00, 0x39, 0x39, 0xE7, 0xF9, 0x07, 0x3F, 0x00, 0x00, 0x31, 0x01, 0xE7, 0xF9, 0x0F, 0x3F, 0x00, 0x00, 0x3F, 0x39, 0xE7, 0xF9, 0x27, 0x3F, 0x00, 0x00, 0x9F, 0x39, 0xE7, 0xF9, 0x33, 0x3F, 0x00, 0x00, 0xC1, 0x39, 0x81, 0xF9, 0x39, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x39, 0x03, 0xC3, 0x07, 0x01, 0x3F, 0x00, 0x00, 0x39, 0x39, 0x99, 0x33, 0x3F, 0x3F, 0x00, 0x00, 0x01, 0x39, 0x3F, 0x39, 0x3F, 0x3F, 0x00, 0x00, 0x39, 0x03, 0x3F, 0x39, 0x03, 0x03, 0x00, 0x00, 0x39, 0x39, 0x3F, 0x39, 0x3F, 0x3F, 0x00, 0x00, 0x93, 0x39, 0x99, 0x33, 0x3F, 0x3F, 0x00, 0x00, 0xC7, 0x03, 0xC3, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
// Cargar surface del bitmap font
|
||||
SDL_Surface* font_surface = SDL_LoadBMP_IO(SDL_IOFromMem(font, 448), 1);
|
||||
if (font_surface != nullptr) {
|
||||
// Crear una nueva surface de 32 bits con canal alpha
|
||||
SDL_Surface* rgba_surface = SDL_CreateSurface(font_surface->w, font_surface->h, SDL_PIXELFORMAT_RGBA8888);
|
||||
if (rgba_surface != nullptr) {
|
||||
// Obtener píxeles de ambas surfaces
|
||||
Uint8* src_pixels = (Uint8*)font_surface->pixels;
|
||||
Uint32* dst_pixels = (Uint32*)rgba_surface->pixels;
|
||||
|
||||
int width = font_surface->w;
|
||||
int height = font_surface->h;
|
||||
|
||||
// Procesar cada píxel
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int byte_index = y * font_surface->pitch + (x / 8);
|
||||
int bit_index = 7 - (x % 8);
|
||||
|
||||
// Extraer bit del bitmap monocromo
|
||||
bool is_white = (src_pixels[byte_index] >> bit_index) & 1;
|
||||
|
||||
if (is_white) // Fondo blanco original -> transparente
|
||||
{
|
||||
dst_pixels[y * width + x] = 0x00000000; // Transparente
|
||||
} else // Texto negro original -> blanco opaco
|
||||
{
|
||||
dst_pixels[y * width + x] = 0xFFFFFFFF; // Blanco opaco
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbg_tex = SDL_CreateTextureFromSurface(dbg_ren, rgba_surface);
|
||||
SDL_DestroySurface(rgba_surface);
|
||||
}
|
||||
SDL_DestroySurface(font_surface);
|
||||
}
|
||||
|
||||
// Configurar filtro nearest neighbor para píxel perfect del texto
|
||||
if (dbg_tex != nullptr) {
|
||||
SDL_SetTextureScaleMode(dbg_tex, SDL_SCALEMODE_NEAREST);
|
||||
// Configurar blend mode para transparencia normal
|
||||
SDL_SetTextureBlendMode(dbg_tex, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
inline void dbg_print(int x, int y, const char* text, Uint8 r, Uint8 g, Uint8 b) {
|
||||
int cc = 0;
|
||||
SDL_SetTextureColorMod(dbg_tex, r, g, b);
|
||||
SDL_FRect src = {0, 0, 8, 8};
|
||||
SDL_FRect dst = {static_cast<float>(x), static_cast<float>(y), 8, 8};
|
||||
while (text[cc] != 0) {
|
||||
if (text[cc] != 32) {
|
||||
if (text[cc] >= 65) {
|
||||
src.x = ((text[cc] - 65) % 6) * 8;
|
||||
src.y = ((text[cc] - 65) / 6) * 8;
|
||||
} else {
|
||||
src.x = ((text[cc] - 22) % 6) * 8;
|
||||
src.y = ((text[cc] - 22) / 6) * 8;
|
||||
}
|
||||
|
||||
SDL_RenderTexture(dbg_ren, dbg_tex, &src, &dst);
|
||||
}
|
||||
cc++;
|
||||
dst.x += 8;
|
||||
}
|
||||
}
|
||||
2
source/external/mouse.cpp
vendored
2
source/external/mouse.cpp
vendored
@@ -1,4 +1,4 @@
|
||||
#include "mouse.h"
|
||||
#include "mouse.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_ShowCursor
|
||||
|
||||
|
||||
4
source/external/sprite.cpp
vendored
4
source/external/sprite.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
#include "sprite.h"
|
||||
#include "sprite.hpp"
|
||||
|
||||
#include "texture.h" // for Texture
|
||||
#include "texture.hpp" // for Texture
|
||||
|
||||
// Constructor
|
||||
Sprite::Sprite(std::shared_ptr<Texture> texture)
|
||||
|
||||
10630
source/external/stb_image_resize2.h
vendored
Normal file
10630
source/external/stb_image_resize2.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
85
source/external/texture.cpp
vendored
85
source/external/texture.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "texture.h"
|
||||
#include "texture.hpp"
|
||||
|
||||
#include <SDL3/SDL_error.h> // Para SDL_GetError
|
||||
#include <SDL3/SDL_log.h> // Para SDL_Log
|
||||
@@ -12,38 +12,7 @@
|
||||
#include <string> // Para operator<<, string
|
||||
|
||||
#include "stb_image.h" // Para stbi_failure_reason, stbi_image_free
|
||||
#include "../resource_pack.h" // Sistema de empaquetado de recursos
|
||||
|
||||
// Instancia global de ResourcePack (se inicializa al primer uso)
|
||||
static ResourcePack* g_resourcePack = nullptr;
|
||||
|
||||
// Inicializar el sistema de recursos (llamar desde main antes de cargar texturas)
|
||||
void Texture::initResourceSystem(const std::string& packFilePath) {
|
||||
if (g_resourcePack == nullptr) {
|
||||
g_resourcePack = new ResourcePack();
|
||||
if (!g_resourcePack->loadPack(packFilePath)) {
|
||||
// Si falla, borrar instancia (usará fallback a disco)
|
||||
delete g_resourcePack;
|
||||
g_resourcePack = nullptr;
|
||||
std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl;
|
||||
} else {
|
||||
std::cout << "resources.pack cargado (" << g_resourcePack->getResourceCount() << " recursos)" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener lista de recursos disponibles en el pack
|
||||
std::vector<std::string> Texture::getPackResourceList() {
|
||||
if (g_resourcePack != nullptr) {
|
||||
return g_resourcePack->getResourceList();
|
||||
}
|
||||
return std::vector<std::string>(); // Vacío si no hay pack
|
||||
}
|
||||
|
||||
// Verificar si el pack está cargado
|
||||
bool Texture::isPackLoaded() {
|
||||
return g_resourcePack != nullptr;
|
||||
}
|
||||
#include "resource_manager.hpp" // Sistema de empaquetado de recursos centralizado
|
||||
|
||||
Texture::Texture(SDL_Renderer *renderer)
|
||||
: renderer_(renderer),
|
||||
@@ -70,30 +39,29 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
||||
int width, height, orig_format;
|
||||
unsigned char *data = nullptr;
|
||||
|
||||
// 1. Intentar cargar desde pack (si está inicializado)
|
||||
if (g_resourcePack != nullptr) {
|
||||
ResourcePack::ResourceData packData = g_resourcePack->loadResource(file_path);
|
||||
if (packData.data != nullptr) {
|
||||
// Descodificar imagen desde memoria usando stb_image
|
||||
data = stbi_load_from_memory(packData.data, static_cast<int>(packData.size),
|
||||
&width, &height, &orig_format, req_format);
|
||||
delete[] packData.data; // Liberar buffer temporal del pack
|
||||
// 1. Intentar cargar desde ResourceManager (pack o disco)
|
||||
unsigned char* resourceData = nullptr;
|
||||
size_t resourceSize = 0;
|
||||
|
||||
if (data != nullptr) {
|
||||
if (ResourceManager::loadResource(file_path, resourceData, resourceSize)) {
|
||||
// Descodificar imagen desde memoria usando stb_image
|
||||
data = stbi_load_from_memory(resourceData, static_cast<int>(resourceSize),
|
||||
&width, &height, &orig_format, req_format);
|
||||
delete[] resourceData; // Liberar buffer temporal
|
||||
|
||||
if (data != nullptr) {
|
||||
if (ResourceManager::isPackLoaded()) {
|
||||
std::cout << "Imagen cargada desde pack: " << filename.c_str() << std::endl;
|
||||
} else {
|
||||
std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: cargar desde disco (modo desarrollo)
|
||||
// 2. Si todo falla, error
|
||||
if (data == nullptr) {
|
||||
data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
||||
if (data == nullptr) {
|
||||
SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
|
||||
exit(1);
|
||||
} else {
|
||||
std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
|
||||
}
|
||||
SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int pitch;
|
||||
@@ -128,6 +96,9 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
||||
|
||||
// Configurar filtro nearest neighbor para píxel perfect
|
||||
SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
// Habilitar alpha blending para transparencias
|
||||
SDL_SetTextureBlendMode(new_texture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
// Destruye la superficie cargada
|
||||
@@ -169,3 +140,17 @@ int Texture::getHeight() {
|
||||
void Texture::setColor(int r, int g, int b) {
|
||||
SDL_SetTextureColorMod(texture_, r, g, b);
|
||||
}
|
||||
|
||||
// Modula el alpha de la textura
|
||||
void Texture::setAlpha(int alpha) {
|
||||
if (texture_ != nullptr) {
|
||||
SDL_SetTextureAlphaMod(texture_, static_cast<Uint8>(alpha));
|
||||
}
|
||||
}
|
||||
|
||||
// Configurar modo de escalado
|
||||
void Texture::setScaleMode(SDL_ScaleMode mode) {
|
||||
if (texture_ != nullptr) {
|
||||
SDL_SetTextureScaleMode(texture_, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,6 @@ class Texture {
|
||||
int height_;
|
||||
|
||||
public:
|
||||
// Sistema de recursos empaquetados (inicializar desde main)
|
||||
static void initResourceSystem(const std::string& packFilePath);
|
||||
static std::vector<std::string> getPackResourceList();
|
||||
static bool isPackLoaded();
|
||||
|
||||
// Inicializa las variables
|
||||
explicit Texture(SDL_Renderer *renderer);
|
||||
Texture(SDL_Renderer *renderer, const std::string &file_path);
|
||||
@@ -44,6 +39,12 @@ class Texture {
|
||||
// Modula el color de la textura
|
||||
void setColor(int r, int g, int b);
|
||||
|
||||
// Modula el alpha (transparencia) de la textura
|
||||
void setAlpha(int alpha);
|
||||
|
||||
// Configurar modo de escalado (NEAREST para pixel art, LINEAR para suavizado)
|
||||
void setScaleMode(SDL_ScaleMode mode);
|
||||
|
||||
// Getter para batch rendering
|
||||
SDL_Texture *getSDLTexture() const { return texture_; }
|
||||
};
|
||||
292
source/input/input_handler.cpp
Normal file
292
source/input/input_handler.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
#include "input_handler.hpp"
|
||||
|
||||
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
||||
#include <string> // for std::string, std::to_string
|
||||
|
||||
#include "defines.hpp" // for KIOSK_NOTIFICATION_TEXT
|
||||
#include "engine.hpp" // for Engine
|
||||
#include "external/mouse.hpp" // for Mouse namespace
|
||||
|
||||
bool InputHandler::processEvents(Engine& engine) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
// Procesar eventos de ratón (auto-ocultar cursor)
|
||||
Mouse::handleEvent(event);
|
||||
|
||||
// Salir del bucle si se detecta una petición de cierre
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
return true; // Solicitar salida
|
||||
}
|
||||
|
||||
// Procesar eventos de teclado
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) {
|
||||
switch (event.key.key) {
|
||||
case SDLK_ESCAPE:
|
||||
if (engine.isKioskMode()) {
|
||||
engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||
break;
|
||||
}
|
||||
return true; // Solicitar salida
|
||||
|
||||
case SDLK_SPACE:
|
||||
engine.pushBallsAwayFromGravity();
|
||||
break;
|
||||
|
||||
case SDLK_G:
|
||||
engine.handleGravityToggle();
|
||||
break;
|
||||
|
||||
// Controles de dirección de gravedad con teclas de cursor
|
||||
case SDLK_UP:
|
||||
engine.handleGravityDirectionChange(GravityDirection::UP, "Gravedad Arriba");
|
||||
break;
|
||||
|
||||
case SDLK_DOWN:
|
||||
engine.handleGravityDirectionChange(GravityDirection::DOWN, "Gravedad Abajo");
|
||||
break;
|
||||
|
||||
case SDLK_LEFT:
|
||||
engine.handleGravityDirectionChange(GravityDirection::LEFT, "Gravedad Izquierda");
|
||||
break;
|
||||
|
||||
case SDLK_RIGHT:
|
||||
engine.handleGravityDirectionChange(GravityDirection::RIGHT, "Gravedad Derecha");
|
||||
break;
|
||||
|
||||
case SDLK_V:
|
||||
engine.toggleVSync();
|
||||
break;
|
||||
|
||||
case SDLK_H:
|
||||
engine.toggleHelp(); // Toggle ayuda de teclas
|
||||
break;
|
||||
|
||||
// Toggle Física ↔ Última Figura (antes era C)
|
||||
case SDLK_F:
|
||||
engine.toggleShapeMode();
|
||||
break;
|
||||
|
||||
// Selección directa de figuras 3D
|
||||
case SDLK_Q:
|
||||
engine.activateShape(ShapeType::SPHERE, "Esfera");
|
||||
break;
|
||||
|
||||
case SDLK_W:
|
||||
engine.activateShape(ShapeType::LISSAJOUS, "Lissajous");
|
||||
break;
|
||||
|
||||
case SDLK_E:
|
||||
engine.activateShape(ShapeType::HELIX, "Hélice");
|
||||
break;
|
||||
|
||||
case SDLK_R:
|
||||
engine.activateShape(ShapeType::TORUS, "Toroide");
|
||||
break;
|
||||
|
||||
case SDLK_T:
|
||||
engine.activateShape(ShapeType::CUBE, "Cubo");
|
||||
break;
|
||||
|
||||
case SDLK_Y:
|
||||
engine.activateShape(ShapeType::CYLINDER, "Cilindro");
|
||||
break;
|
||||
|
||||
case SDLK_U:
|
||||
engine.activateShape(ShapeType::ICOSAHEDRON, "Icosaedro");
|
||||
break;
|
||||
|
||||
case SDLK_I:
|
||||
engine.activateShape(ShapeType::ATOM, "Átomo");
|
||||
break;
|
||||
|
||||
case SDLK_O:
|
||||
engine.activateShape(ShapeType::PNG_SHAPE, "Forma PNG");
|
||||
break;
|
||||
|
||||
// Toggle Modo Boids (comportamiento de enjambre)
|
||||
case SDLK_B:
|
||||
engine.toggleBoidsMode();
|
||||
break;
|
||||
|
||||
// Ciclar temas de color (movido de B a C)
|
||||
case SDLK_C:
|
||||
{
|
||||
// Detectar si Shift está presionado
|
||||
SDL_Keymod modstate = SDL_GetModState();
|
||||
if (modstate & SDL_KMOD_SHIFT) {
|
||||
// Shift+C: Ciclar hacia atrás (tema anterior)
|
||||
engine.cycleTheme(false);
|
||||
} else {
|
||||
// C solo: Ciclar hacia adelante (tema siguiente)
|
||||
engine.cycleTheme(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Temas de colores con teclado numérico (con transición suave)
|
||||
case SDLK_KP_1:
|
||||
engine.switchThemeByNumpad(1);
|
||||
break;
|
||||
|
||||
case SDLK_KP_2:
|
||||
engine.switchThemeByNumpad(2);
|
||||
break;
|
||||
|
||||
case SDLK_KP_3:
|
||||
engine.switchThemeByNumpad(3);
|
||||
break;
|
||||
|
||||
case SDLK_KP_4:
|
||||
engine.switchThemeByNumpad(4);
|
||||
break;
|
||||
|
||||
case SDLK_KP_5:
|
||||
engine.switchThemeByNumpad(5);
|
||||
break;
|
||||
|
||||
case SDLK_KP_6:
|
||||
engine.switchThemeByNumpad(6);
|
||||
break;
|
||||
|
||||
case SDLK_KP_7:
|
||||
engine.switchThemeByNumpad(7);
|
||||
break;
|
||||
|
||||
case SDLK_KP_8:
|
||||
engine.switchThemeByNumpad(8);
|
||||
break;
|
||||
|
||||
case SDLK_KP_9:
|
||||
engine.switchThemeByNumpad(9);
|
||||
break;
|
||||
|
||||
case SDLK_KP_0:
|
||||
engine.switchThemeByNumpad(0);
|
||||
break;
|
||||
|
||||
// Toggle de página de temas (Numpad Enter)
|
||||
case SDLK_KP_ENTER:
|
||||
engine.toggleThemePage();
|
||||
break;
|
||||
|
||||
// Cambio de sprite/textura dinámico
|
||||
case SDLK_N:
|
||||
engine.switchTexture();
|
||||
break;
|
||||
|
||||
// Control de escala de figura (solo en modo SHAPE)
|
||||
case SDLK_KP_PLUS:
|
||||
engine.handleShapeScaleChange(true); // Aumentar
|
||||
break;
|
||||
|
||||
case SDLK_KP_MINUS:
|
||||
engine.handleShapeScaleChange(false); // Disminuir
|
||||
break;
|
||||
|
||||
case SDLK_KP_MULTIPLY:
|
||||
engine.resetShapeScale();
|
||||
break;
|
||||
|
||||
case SDLK_KP_DIVIDE:
|
||||
engine.toggleDepthZoom();
|
||||
break;
|
||||
|
||||
// Cambio de número de pelotas (escenarios 1-8)
|
||||
case SDLK_1:
|
||||
engine.changeScenario(0, "10 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_2:
|
||||
engine.changeScenario(1, "50 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_3:
|
||||
engine.changeScenario(2, "100 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_4:
|
||||
engine.changeScenario(3, "500 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_5:
|
||||
engine.changeScenario(4, "1,000 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_6:
|
||||
engine.changeScenario(5, "5,000 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_7:
|
||||
engine.changeScenario(6, "10,000 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_8:
|
||||
engine.changeScenario(7, "50,000 Pelotas");
|
||||
break;
|
||||
|
||||
case SDLK_9:
|
||||
if (engine.isCustomScenarioEnabled()) {
|
||||
std::string custom_notif = std::to_string(engine.getCustomScenarioBalls()) + " Pelotas";
|
||||
engine.changeScenario(CUSTOM_SCENARIO_IDX, custom_notif.c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
// Controles de zoom dinámico (solo si no estamos en fullscreen)
|
||||
case SDLK_F1:
|
||||
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||
else engine.handleZoomOut();
|
||||
break;
|
||||
|
||||
case SDLK_F2:
|
||||
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||
else engine.handleZoomIn();
|
||||
break;
|
||||
|
||||
// Control de pantalla completa
|
||||
case SDLK_F3:
|
||||
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||
else engine.toggleFullscreen();
|
||||
break;
|
||||
|
||||
// Modo real fullscreen (cambia resolución interna)
|
||||
case SDLK_F4:
|
||||
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||
else engine.toggleRealFullscreen();
|
||||
break;
|
||||
|
||||
// Toggle escalado entero/estirado (solo en fullscreen F3)
|
||||
case SDLK_F5:
|
||||
engine.toggleIntegerScaling();
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO COMPLETO (auto-play) o Pausar tema dinámico (Shift+D)
|
||||
case SDLK_D:
|
||||
// Shift+D = Pausar tema dinámico
|
||||
if (event.key.mod & SDL_KMOD_SHIFT) {
|
||||
engine.pauseDynamicTheme();
|
||||
} else {
|
||||
// D sin Shift = Toggle DEMO ↔ SANDBOX
|
||||
engine.toggleDemoMode();
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO LITE (solo física/figuras)
|
||||
case SDLK_L:
|
||||
engine.toggleDemoLiteMode();
|
||||
break;
|
||||
|
||||
// Toggle Modo LOGO (easter egg - marca de agua)
|
||||
case SDLK_K:
|
||||
engine.toggleLogoMode();
|
||||
break;
|
||||
|
||||
// Toggle Debug Display (movido de H a F12)
|
||||
case SDLK_F12:
|
||||
engine.toggleDebug();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No se solicitó salida
|
||||
}
|
||||
32
source/input/input_handler.hpp
Normal file
32
source/input/input_handler.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h> // for SDL_Event
|
||||
|
||||
// Forward declaration para evitar dependencia circular
|
||||
class Engine;
|
||||
|
||||
/**
|
||||
* @class InputHandler
|
||||
* @brief Procesa eventos de entrada (teclado, ratón, ventana) y los traduce a acciones del Engine
|
||||
*
|
||||
* Responsabilidad única: Manejo de input SDL y traducción a comandos de alto nivel
|
||||
*
|
||||
* Características:
|
||||
* - Procesa todos los eventos SDL (teclado, ratón, quit)
|
||||
* - Traduce inputs a llamadas de métodos del Engine
|
||||
* - Mantiene el Engine desacoplado de la lógica de input SDL
|
||||
* - Soporta todos los controles del proyecto (gravedad, figuras, temas, zoom, fullscreen)
|
||||
*/
|
||||
class InputHandler {
|
||||
public:
|
||||
/**
|
||||
* @brief Procesa todos los eventos SDL pendientes
|
||||
* @param engine Referencia al engine para ejecutar acciones
|
||||
* @return true si se debe salir de la aplicación (ESC o cerrar ventana), false en caso contrario
|
||||
*/
|
||||
bool processEvents(Engine& engine);
|
||||
|
||||
private:
|
||||
// Sin estado interno por ahora - el InputHandler es stateless
|
||||
// Todos los estados se delegan al Engine
|
||||
};
|
||||
157
source/logo_scaler.cpp
Normal file
157
source/logo_scaler.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include "logo_scaler.hpp"
|
||||
|
||||
#include <SDL3/SDL_error.h> // Para SDL_GetError
|
||||
#include <SDL3/SDL_log.h> // Para SDL_Log
|
||||
#include <SDL3/SDL_pixels.h> // Para SDL_PixelFormat
|
||||
#include <SDL3/SDL_render.h> // Para SDL_CreateTexture
|
||||
#include <SDL3/SDL_surface.h> // Para SDL_CreateSurfaceFrom
|
||||
#include <SDL3/SDL_video.h> // Para SDL_GetDisplays
|
||||
|
||||
#include <cstdlib> // Para free()
|
||||
#include <iostream> // Para std::cout
|
||||
|
||||
#include "external/stb_image.h" // Para stbi_load, stbi_image_free
|
||||
#include "external/stb_image_resize2.h" // Para stbir_resize_uint8_srgb
|
||||
#include "resource_manager.hpp" // Para cargar desde pack
|
||||
|
||||
// ============================================================================
|
||||
// Detectar resolución nativa del monitor principal
|
||||
// ============================================================================
|
||||
|
||||
bool LogoScaler::detectNativeResolution(int& native_width, int& native_height) {
|
||||
int num_displays = 0;
|
||||
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
||||
|
||||
if (displays == nullptr || num_displays == 0) {
|
||||
SDL_Log("Error al obtener displays: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtener resolución del display principal (displays[0])
|
||||
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
||||
if (dm == nullptr) {
|
||||
SDL_Log("Error al obtener modo del display: %s", SDL_GetError());
|
||||
SDL_free(displays);
|
||||
return false;
|
||||
}
|
||||
|
||||
native_width = dm->w;
|
||||
native_height = dm->h;
|
||||
|
||||
SDL_free(displays);
|
||||
|
||||
std::cout << "Resolución nativa detectada: " << native_width << "x" << native_height << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Cargar PNG y escalar al tamaño especificado
|
||||
// ============================================================================
|
||||
|
||||
unsigned char* LogoScaler::loadAndScale(const std::string& path,
|
||||
int target_width, int target_height,
|
||||
int& out_width, int& out_height) {
|
||||
// 1. Intentar cargar imagen desde ResourceManager (pack o disco)
|
||||
int orig_width, orig_height, orig_channels;
|
||||
unsigned char* orig_data = nullptr;
|
||||
|
||||
// 1a. Cargar desde ResourceManager
|
||||
unsigned char* resourceData = nullptr;
|
||||
size_t resourceSize = 0;
|
||||
|
||||
if (ResourceManager::loadResource(path, resourceData, resourceSize)) {
|
||||
// Descodificar imagen desde memoria usando stb_image
|
||||
orig_data = stbi_load_from_memory(resourceData, static_cast<int>(resourceSize),
|
||||
&orig_width, &orig_height, &orig_channels, STBI_rgb_alpha);
|
||||
delete[] resourceData; // Liberar buffer temporal
|
||||
}
|
||||
|
||||
// 1b. Si falla todo, error
|
||||
if (orig_data == nullptr) {
|
||||
SDL_Log("Error al cargar imagen %s: %s", path.c_str(), stbi_failure_reason());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << "Imagen cargada: " << path << " (" << orig_width << "x" << orig_height << ")" << std::endl;
|
||||
|
||||
// 2. Calcular tamaño final manteniendo aspect ratio
|
||||
// El alto está fijado por target_height (APPLOGO_HEIGHT_PERCENT)
|
||||
// Calcular ancho proporcional al aspect ratio original
|
||||
float aspect_ratio = static_cast<float>(orig_width) / static_cast<float>(orig_height);
|
||||
out_width = static_cast<int>(target_height * aspect_ratio);
|
||||
out_height = target_height;
|
||||
|
||||
std::cout << " Escalando a: " << out_width << "x" << out_height << std::endl;
|
||||
|
||||
// 3. Alocar buffer para imagen escalada (RGBA = 4 bytes por píxel)
|
||||
unsigned char* scaled_data = static_cast<unsigned char*>(malloc(out_width * out_height * 4));
|
||||
if (scaled_data == nullptr) {
|
||||
SDL_Log("Error al alocar memoria para imagen escalada");
|
||||
stbi_image_free(orig_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4. Escalar con stb_image_resize2 (algoritmo Mitchell, espacio sRGB)
|
||||
// La función devuelve el puntero de salida, o nullptr si falla
|
||||
unsigned char* result = stbir_resize_uint8_srgb(
|
||||
orig_data, orig_width, orig_height, 0, // Input
|
||||
scaled_data, out_width, out_height, 0, // Output
|
||||
STBIR_RGBA // Formato píxel
|
||||
);
|
||||
|
||||
// Liberar imagen original (ya no la necesitamos)
|
||||
stbi_image_free(orig_data);
|
||||
|
||||
if (result == nullptr) {
|
||||
SDL_Log("Error al escalar imagen");
|
||||
free(scaled_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << " Escalado completado correctamente" << std::endl;
|
||||
return scaled_data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Crear textura SDL desde buffer RGBA
|
||||
// ============================================================================
|
||||
|
||||
SDL_Texture* LogoScaler::createTextureFromBuffer(SDL_Renderer* renderer,
|
||||
unsigned char* data,
|
||||
int width, int height) {
|
||||
if (renderer == nullptr || data == nullptr || width <= 0 || height <= 0) {
|
||||
SDL_Log("Parámetros inválidos para createTextureFromBuffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 1. Crear surface SDL desde buffer RGBA
|
||||
int pitch = width * 4; // 4 bytes por píxel (RGBA)
|
||||
SDL_PixelFormat pixel_format = SDL_PIXELFORMAT_RGBA32;
|
||||
|
||||
SDL_Surface* surface = SDL_CreateSurfaceFrom(
|
||||
width, height,
|
||||
pixel_format,
|
||||
data,
|
||||
pitch
|
||||
);
|
||||
|
||||
if (surface == nullptr) {
|
||||
SDL_Log("Error al crear surface: %s", SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 2. Crear textura desde surface
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
|
||||
if (texture == nullptr) {
|
||||
SDL_Log("Error al crear textura: %s", SDL_GetError());
|
||||
SDL_DestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 3. Liberar surface (la textura ya tiene los datos)
|
||||
SDL_DestroySurface(surface);
|
||||
|
||||
return texture;
|
||||
}
|
||||
61
source/logo_scaler.hpp
Normal file
61
source/logo_scaler.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_render.h> // Para SDL_Renderer, SDL_Texture
|
||||
#include <SDL3/SDL_video.h> // Para SDL_DisplayID, SDL_GetDisplays
|
||||
|
||||
#include <string> // Para std::string
|
||||
|
||||
/**
|
||||
* @brief Helper class para pre-escalar logos usando stb_image_resize2
|
||||
*
|
||||
* Proporciona funciones para:
|
||||
* - Detectar resolución nativa del monitor
|
||||
* - Cargar PNG y escalar a tamaño específico con algoritmos de alta calidad
|
||||
* - Crear texturas SDL desde buffers escalados
|
||||
*
|
||||
* Usado por AppLogo para pre-generar versiones de logos al tamaño exacto
|
||||
* de pantalla, eliminando el escalado dinámico de SDL y mejorando calidad visual.
|
||||
*/
|
||||
class LogoScaler {
|
||||
public:
|
||||
/**
|
||||
* @brief Detecta la resolución nativa del monitor principal
|
||||
*
|
||||
* @param native_width [out] Ancho nativo del display en píxeles
|
||||
* @param native_height [out] Alto nativo del display en píxeles
|
||||
* @return true si se pudo detectar, false si hubo error
|
||||
*/
|
||||
static bool detectNativeResolution(int& native_width, int& native_height);
|
||||
|
||||
/**
|
||||
* @brief Carga un PNG y lo escala al tamaño especificado
|
||||
*
|
||||
* Usa stb_image para cargar y stb_image_resize2 para escalar con
|
||||
* algoritmo Mitchell (balance calidad/velocidad) en espacio sRGB.
|
||||
*
|
||||
* @param path Ruta al archivo PNG (ej: "data/logo/logo.png")
|
||||
* @param target_width Ancho destino en píxeles
|
||||
* @param target_height Alto destino en píxeles
|
||||
* @param out_width [out] Ancho real de la imagen escalada
|
||||
* @param out_height [out] Alto real de la imagen escalada
|
||||
* @return Buffer RGBA (4 bytes por píxel) o nullptr si falla
|
||||
* IMPORTANTE: El caller debe liberar con free() cuando termine
|
||||
*/
|
||||
static unsigned char* loadAndScale(const std::string& path,
|
||||
int target_width, int target_height,
|
||||
int& out_width, int& out_height);
|
||||
|
||||
/**
|
||||
* @brief Crea una textura SDL desde un buffer RGBA
|
||||
*
|
||||
* @param renderer Renderizador SDL activo
|
||||
* @param data Buffer RGBA (4 bytes por píxel)
|
||||
* @param width Ancho del buffer en píxeles
|
||||
* @param height Alto del buffer en píxeles
|
||||
* @return Textura SDL creada o nullptr si falla
|
||||
* IMPORTANTE: El caller debe destruir con SDL_DestroyTexture()
|
||||
*/
|
||||
static SDL_Texture* createTextureFromBuffer(SDL_Renderer* renderer,
|
||||
unsigned char* data,
|
||||
int width, int height);
|
||||
};
|
||||
103
source/main.cpp
103
source/main.cpp
@@ -1,6 +1,9 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include "engine.h"
|
||||
#include <string>
|
||||
#include "engine.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "resource_manager.hpp"
|
||||
|
||||
// getExecutableDirectory() ya está definido en defines.h como inline
|
||||
|
||||
@@ -11,13 +14,24 @@ void printHelp() {
|
||||
std::cout << " -w, --width <px> Ancho de resolución (default: 320)\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 (F3 - letterbox)\n";
|
||||
std::cout << " -F, --real-fullscreen Modo pantalla completa real (F4 - nativo)\n";
|
||||
std::cout << " -k, --kiosk Modo kiosko (F4 fijo, sin ESC, sin zoom)\n";
|
||||
std::cout << " -m, --mode <mode> Modo inicial: sandbox, demo, demo-lite, logo (default: sandbox)\n";
|
||||
std::cout << " --custom-balls <n> Activa escenario custom (tecla 9) con N pelotas\n";
|
||||
std::cout << " --skip-benchmark Salta el benchmark y usa el máximo de bolas (50000)\n";
|
||||
std::cout << " --max-balls <n> Limita el máximo de bolas en modos DEMO/DEMO_LITE\n";
|
||||
std::cout << " --help Mostrar esta ayuda\n\n";
|
||||
std::cout << "Ejemplos:\n";
|
||||
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
|
||||
std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 (auto)\n";
|
||||
std::cout << " vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n";
|
||||
std::cout << " vibe3_physics -w 1920 -h 1080 -f # 1920x1080 fullscreen\n\n";
|
||||
std::cout << " vibe3_physics -f # Fullscreen letterbox (F3)\n";
|
||||
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n";
|
||||
std::cout << " vibe3_physics -k # Modo kiosko (pantalla completa real, bloqueado)\n";
|
||||
std::cout << " vibe3_physics --mode demo # Arrancar en modo DEMO (auto-play)\n";
|
||||
std::cout << " vibe3_physics -m demo-lite # Arrancar en modo DEMO_LITE (solo física)\n";
|
||||
std::cout << " vibe3_physics -F --mode logo # Fullscreen + modo LOGO (easter egg)\n\n";
|
||||
std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n";
|
||||
}
|
||||
|
||||
@@ -25,7 +39,13 @@ int main(int argc, char* argv[]) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int zoom = 0;
|
||||
int custom_balls = 0;
|
||||
bool fullscreen = false;
|
||||
bool real_fullscreen = false;
|
||||
bool kiosk_mode = false;
|
||||
bool skip_benchmark = false;
|
||||
int max_balls_override = 0;
|
||||
AppMode initial_mode = AppMode::SANDBOX; // Modo inicial (default: SANDBOX)
|
||||
|
||||
// Parsear argumentos
|
||||
for (int i = 1; i < argc; i++) {
|
||||
@@ -35,8 +55,8 @@ int main(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--width") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
width = atoi(argv[++i]);
|
||||
if (width < 640) {
|
||||
std::cerr << "Error: Ancho mínimo es 640px\n";
|
||||
if (width < 320) {
|
||||
std::cerr << "Error: Ancho mínimo es 320\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@@ -46,8 +66,8 @@ int main(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--height") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
height = atoi(argv[++i]);
|
||||
if (height < 480) {
|
||||
std::cerr << "Error: Alto mínimo es 480px\n";
|
||||
if (height < 240) {
|
||||
std::cerr << "Error: Alto mínimo es 240\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@@ -67,6 +87,55 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) {
|
||||
fullscreen = true;
|
||||
} else if (strcmp(argv[i], "-F") == 0 || strcmp(argv[i], "--real-fullscreen") == 0) {
|
||||
real_fullscreen = true;
|
||||
} else if (strcmp(argv[i], "-k") == 0 || strcmp(argv[i], "--kiosk") == 0) {
|
||||
kiosk_mode = true;
|
||||
} else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--mode") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
std::string mode_str = argv[++i];
|
||||
if (mode_str == "sandbox") {
|
||||
initial_mode = AppMode::SANDBOX;
|
||||
} else if (mode_str == "demo") {
|
||||
initial_mode = AppMode::DEMO;
|
||||
} else if (mode_str == "demo-lite") {
|
||||
initial_mode = AppMode::DEMO_LITE;
|
||||
} else if (mode_str == "logo") {
|
||||
initial_mode = AppMode::LOGO;
|
||||
} else {
|
||||
std::cerr << "Error: Modo '" << mode_str << "' no válido. Usa: sandbox, demo, demo-lite, logo\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: -m/--mode requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "--custom-balls") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
int n = atoi(argv[++i]);
|
||||
if (n < 1) {
|
||||
std::cerr << "Error: --custom-balls requiere un valor >= 1\n";
|
||||
return -1;
|
||||
}
|
||||
custom_balls = n;
|
||||
} else {
|
||||
std::cerr << "Error: --custom-balls requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "--skip-benchmark") == 0) {
|
||||
skip_benchmark = true;
|
||||
} else if (strcmp(argv[i], "--max-balls") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
int n = atoi(argv[++i]);
|
||||
if (n < 1) {
|
||||
std::cerr << "Error: --max-balls requiere un valor >= 1\n";
|
||||
return -1;
|
||||
}
|
||||
max_balls_override = n;
|
||||
} else {
|
||||
std::cerr << "Error: --max-balls requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
|
||||
printHelp();
|
||||
@@ -77,15 +146,31 @@ int main(int argc, char* argv[]) {
|
||||
// Inicializar sistema de recursos empaquetados (intentar cargar resources.pack)
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
std::string pack_path = resources_dir + "/resources.pack";
|
||||
Texture::initResourceSystem(pack_path);
|
||||
ResourceManager::init(pack_path);
|
||||
|
||||
Engine engine;
|
||||
|
||||
if (!engine.initialize(width, height, zoom, fullscreen)) {
|
||||
if (custom_balls > 0)
|
||||
engine.setCustomScenario(custom_balls); // pre-init: asigna campos antes del benchmark
|
||||
|
||||
if (max_balls_override > 0)
|
||||
engine.setMaxBallsOverride(max_balls_override);
|
||||
else if (skip_benchmark)
|
||||
engine.setSkipBenchmark();
|
||||
|
||||
if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) {
|
||||
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Si se especificó real fullscreen (F4) o modo kiosko, activar después de inicializar
|
||||
if (real_fullscreen || kiosk_mode) {
|
||||
engine.toggleRealFullscreen();
|
||||
}
|
||||
if (kiosk_mode) {
|
||||
engine.setKioskMode(true);
|
||||
}
|
||||
|
||||
engine.run();
|
||||
engine.shutdown();
|
||||
|
||||
|
||||
99
source/resource_manager.cpp
Normal file
99
source/resource_manager.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "resource_manager.hpp"
|
||||
#include "resource_pack.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
// Inicializar estáticos
|
||||
ResourcePack* ResourceManager::resourcePack_ = nullptr;
|
||||
std::map<std::string, std::vector<unsigned char>> ResourceManager::cache_;
|
||||
|
||||
bool ResourceManager::init(const std::string& packFilePath) {
|
||||
// Si ya estaba inicializado, liberar primero
|
||||
if (resourcePack_ != nullptr) {
|
||||
delete resourcePack_;
|
||||
resourcePack_ = nullptr;
|
||||
}
|
||||
|
||||
// Intentar cargar el pack
|
||||
resourcePack_ = new ResourcePack();
|
||||
if (!resourcePack_->loadPack(packFilePath)) {
|
||||
// Si falla, borrar instancia (usará fallback a disco)
|
||||
delete resourcePack_;
|
||||
resourcePack_ = nullptr;
|
||||
std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "resources.pack cargado (" << resourcePack_->getResourceCount() << " recursos)" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceManager::shutdown() {
|
||||
cache_.clear();
|
||||
if (resourcePack_ != nullptr) {
|
||||
delete resourcePack_;
|
||||
resourcePack_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::loadResource(const std::string& resourcePath, unsigned char*& data, size_t& size) {
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
|
||||
// 1. Consultar caché en RAM (sin I/O)
|
||||
auto it = cache_.find(resourcePath);
|
||||
if (it != cache_.end()) {
|
||||
size = it->second.size();
|
||||
data = new unsigned char[size];
|
||||
std::memcpy(data, it->second.data(), size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Intentar cargar desde pack (si está disponible)
|
||||
if (resourcePack_ != nullptr) {
|
||||
ResourcePack::ResourceData packData = resourcePack_->loadResource(resourcePath);
|
||||
if (packData.data != nullptr) {
|
||||
cache_[resourcePath] = std::vector<unsigned char>(packData.data, packData.data + packData.size);
|
||||
data = packData.data;
|
||||
size = packData.size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fallback: cargar desde disco
|
||||
std::ifstream file(resourcePath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::string dataPath = "data/" + resourcePath;
|
||||
file.open(dataPath, std::ios::binary | std::ios::ate);
|
||||
if (!file) { return false; }
|
||||
}
|
||||
size = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
data = new unsigned char[size];
|
||||
file.read(reinterpret_cast<char*>(data), size);
|
||||
file.close();
|
||||
|
||||
// Guardar en caché
|
||||
cache_[resourcePath] = std::vector<unsigned char>(data, data + size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceManager::isPackLoaded() {
|
||||
return resourcePack_ != nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> ResourceManager::getResourceList() {
|
||||
if (resourcePack_ != nullptr) {
|
||||
return resourcePack_->getResourceList();
|
||||
}
|
||||
return std::vector<std::string>(); // Vacío si no hay pack
|
||||
}
|
||||
|
||||
size_t ResourceManager::getResourceCount() {
|
||||
if (resourcePack_ != nullptr) {
|
||||
return resourcePack_->getResourceCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
86
source/resource_manager.hpp
Normal file
86
source/resource_manager.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ResourcePack;
|
||||
|
||||
/**
|
||||
* ResourceManager - Gestor centralizado de recursos empaquetados
|
||||
*
|
||||
* Singleton que administra el sistema de recursos empaquetados (resources.pack)
|
||||
* y proporciona fallback automático a disco cuando el pack no está disponible.
|
||||
*
|
||||
* Uso:
|
||||
* // En main.cpp, antes de inicializar cualquier sistema:
|
||||
* ResourceManager::init("resources.pack");
|
||||
*
|
||||
* // Desde cualquier clase que necesite recursos:
|
||||
* unsigned char* data = nullptr;
|
||||
* size_t size = 0;
|
||||
* if (ResourceManager::loadResource("textures/ball.png", data, size)) {
|
||||
* // Usar datos...
|
||||
* delete[] data; // Liberar cuando termine
|
||||
* }
|
||||
*/
|
||||
class ResourceManager {
|
||||
public:
|
||||
/**
|
||||
* Inicializa el sistema de recursos empaquetados
|
||||
* Debe llamarse una única vez al inicio del programa
|
||||
*
|
||||
* @param packFilePath Ruta al archivo .pack (ej: "resources.pack")
|
||||
* @return true si el pack se cargó correctamente, false si no existe (fallback a disco)
|
||||
*/
|
||||
static bool init(const std::string& packFilePath);
|
||||
|
||||
/**
|
||||
* Libera el sistema de recursos
|
||||
* Opcional - se llama automáticamente al cerrar el programa
|
||||
*/
|
||||
static void shutdown();
|
||||
|
||||
/**
|
||||
* Carga un recurso desde el pack (o disco si no existe pack)
|
||||
*
|
||||
* @param resourcePath Ruta relativa del recurso (ej: "textures/ball.png")
|
||||
* @param data [out] Puntero donde se almacenará el buffer (debe liberar con delete[])
|
||||
* @param size [out] Tamaño del buffer en bytes
|
||||
* @return true si se cargó correctamente, false si falla
|
||||
*/
|
||||
static bool loadResource(const std::string& resourcePath, unsigned char*& data, size_t& size);
|
||||
|
||||
/**
|
||||
* Verifica si el pack está cargado
|
||||
* @return true si hay un pack cargado, false si se usa disco
|
||||
*/
|
||||
static bool isPackLoaded();
|
||||
|
||||
/**
|
||||
* Obtiene la lista de recursos disponibles en el pack
|
||||
* @return Vector con las rutas de todos los recursos, vacío si no hay pack
|
||||
*/
|
||||
static std::vector<std::string> getResourceList();
|
||||
|
||||
/**
|
||||
* Obtiene el número de recursos en el pack
|
||||
* @return Número de recursos, 0 si no hay pack
|
||||
*/
|
||||
static size_t getResourceCount();
|
||||
|
||||
private:
|
||||
// Constructor privado (singleton)
|
||||
ResourceManager() = default;
|
||||
~ResourceManager() = default;
|
||||
|
||||
// Deshabilitar copia y asignación
|
||||
ResourceManager(const ResourceManager&) = delete;
|
||||
ResourceManager& operator=(const ResourceManager&) = delete;
|
||||
|
||||
// Instancia del pack (nullptr si no está cargado)
|
||||
static ResourcePack* resourcePack_;
|
||||
|
||||
// Caché en RAM para evitar I/O repetido en el bucle principal
|
||||
static std::map<std::string, std::vector<unsigned char>> cache_;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "resource_pack.h"
|
||||
#include "resource_pack.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef RESOURCE_PACK_H
|
||||
#define RESOURCE_PACK_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
@@ -64,5 +63,3 @@ private:
|
||||
uint32_t calculateChecksum(const unsigned char* data, size_t size);
|
||||
std::string normalizePath(const std::string& path);
|
||||
};
|
||||
|
||||
#endif // RESOURCE_PACK_H
|
||||
241
source/scene/scene_manager.cpp
Normal file
241
source/scene/scene_manager.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "scene_manager.hpp"
|
||||
|
||||
#include <cstdlib> // for rand
|
||||
|
||||
#include "defines.hpp" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
|
||||
#include "external/texture.hpp" // for Texture
|
||||
#include "theme_manager.hpp" // for ThemeManager
|
||||
|
||||
SceneManager::SceneManager(int screen_width, int screen_height)
|
||||
: current_gravity_(GravityDirection::DOWN)
|
||||
, scenario_(0)
|
||||
, screen_width_(screen_width)
|
||||
, screen_height_(screen_height)
|
||||
, current_ball_size_(10)
|
||||
, texture_(nullptr)
|
||||
, theme_manager_(nullptr) {
|
||||
}
|
||||
|
||||
void SceneManager::initialize(int scenario, std::shared_ptr<Texture> texture, ThemeManager* theme_manager) {
|
||||
scenario_ = scenario;
|
||||
texture_ = texture;
|
||||
theme_manager_ = theme_manager;
|
||||
current_ball_size_ = texture_->getWidth();
|
||||
|
||||
// Crear bolas iniciales (siempre en modo PHYSICS al inicio)
|
||||
changeScenario(scenario_, SimulationMode::PHYSICS);
|
||||
}
|
||||
|
||||
void SceneManager::update(float delta_time) {
|
||||
// Actualizar física de todas las bolas
|
||||
for (auto& ball : balls_) {
|
||||
ball->update(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::changeScenario(int scenario_id, SimulationMode mode) {
|
||||
// Guardar escenario
|
||||
scenario_ = scenario_id;
|
||||
|
||||
// Limpiar las bolas actuales
|
||||
balls_.clear();
|
||||
|
||||
// Resetear gravedad al estado por defecto (DOWN) al cambiar escenario
|
||||
changeGravityDirection(GravityDirection::DOWN);
|
||||
|
||||
// Crear las bolas según el escenario
|
||||
int ball_count = (scenario_id == CUSTOM_SCENARIO_IDX)
|
||||
? custom_ball_count_
|
||||
: BALL_COUNT_SCENARIOS[scenario_id];
|
||||
for (int i = 0; i < ball_count; ++i) {
|
||||
float X, Y, VX, VY;
|
||||
|
||||
// Inicialización según SimulationMode (RULES.md líneas 23-26)
|
||||
switch (mode) {
|
||||
case SimulationMode::PHYSICS: {
|
||||
// PHYSICS: Parte superior, 75% distribución central en X
|
||||
const int SIGN = ((rand() % 2) * 2) - 1;
|
||||
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
|
||||
const int spawn_zone_width = screen_width_ - (2 * margin);
|
||||
X = (rand() % spawn_zone_width) + margin;
|
||||
Y = 0.0f; // Parte superior
|
||||
VX = (((rand() % 20) + 10) * 0.1f) * SIGN;
|
||||
VY = ((rand() % 60) - 30) * 0.1f;
|
||||
break;
|
||||
}
|
||||
|
||||
case SimulationMode::SHAPE: {
|
||||
// SHAPE: Centro de pantalla, sin velocidad inicial
|
||||
X = screen_width_ / 2.0f;
|
||||
Y = screen_height_ / 2.0f; // Centro vertical
|
||||
VX = 0.0f;
|
||||
VY = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
case SimulationMode::BOIDS: {
|
||||
// BOIDS: Posiciones aleatorias, velocidades aleatorias
|
||||
const int SIGN_X = ((rand() % 2) * 2) - 1;
|
||||
const int SIGN_Y = ((rand() % 2) * 2) - 1;
|
||||
X = static_cast<float>(rand() % screen_width_);
|
||||
Y = static_cast<float>(rand() % screen_height_); // Posición Y aleatoria
|
||||
VX = (((rand() % 40) + 10) * 0.1f) * SIGN_X; // 1.0 - 5.0 px/frame
|
||||
VY = (((rand() % 40) + 10) * 0.1f) * SIGN_Y;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Fallback a PHYSICS por seguridad
|
||||
const int SIGN = ((rand() % 2) * 2) - 1;
|
||||
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
|
||||
const int spawn_zone_width = screen_width_ - (2 * margin);
|
||||
X = (rand() % spawn_zone_width) + margin;
|
||||
Y = 0.0f; // Parte superior
|
||||
VX = (((rand() % 20) + 10) * 0.1f) * SIGN;
|
||||
VY = ((rand() % 60) - 30) * 0.1f;
|
||||
break;
|
||||
}
|
||||
|
||||
// Seleccionar color de la paleta del tema actual (delegado a ThemeManager)
|
||||
int random_index = rand();
|
||||
Color COLOR = theme_manager_->getInitialBallColor(random_index);
|
||||
|
||||
// Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada)
|
||||
float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN);
|
||||
|
||||
balls_.emplace_back(std::make_unique<Ball>(
|
||||
X, Y, VX, VY, COLOR, texture_,
|
||||
screen_width_, screen_height_, current_ball_size_,
|
||||
current_gravity_, mass_factor
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::updateBallTexture(std::shared_ptr<Texture> new_texture, int new_ball_size) {
|
||||
if (balls_.empty()) return;
|
||||
|
||||
// Guardar tamaño antiguo
|
||||
int old_size = current_ball_size_;
|
||||
|
||||
// Actualizar textura y tamaño
|
||||
texture_ = new_texture;
|
||||
current_ball_size_ = new_ball_size;
|
||||
|
||||
// Actualizar texturas de todas las pelotas
|
||||
for (auto& ball : balls_) {
|
||||
ball->setTexture(texture_);
|
||||
}
|
||||
|
||||
// Ajustar posiciones según el cambio de tamaño
|
||||
updateBallSizes(old_size, new_ball_size);
|
||||
}
|
||||
|
||||
void SceneManager::pushBallsAwayFromGravity() {
|
||||
for (auto& ball : balls_) {
|
||||
const int SIGNO = ((rand() % 2) * 2) - 1;
|
||||
const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO;
|
||||
const float MAIN = ((rand() % 40) * 0.1f) + 5;
|
||||
|
||||
float vx = 0, vy = 0;
|
||||
switch (current_gravity_) {
|
||||
case GravityDirection::DOWN: // Impulsar ARRIBA
|
||||
vx = LATERAL;
|
||||
vy = -MAIN;
|
||||
break;
|
||||
case GravityDirection::UP: // Impulsar ABAJO
|
||||
vx = LATERAL;
|
||||
vy = MAIN;
|
||||
break;
|
||||
case GravityDirection::LEFT: // Impulsar DERECHA
|
||||
vx = MAIN;
|
||||
vy = LATERAL;
|
||||
break;
|
||||
case GravityDirection::RIGHT: // Impulsar IZQUIERDA
|
||||
vx = -MAIN;
|
||||
vy = LATERAL;
|
||||
break;
|
||||
}
|
||||
ball->modVel(vx, vy); // Modifica la velocidad según dirección de gravedad
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::switchBallsGravity() {
|
||||
for (auto& ball : balls_) {
|
||||
ball->switchGravity();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::enableBallsGravityIfDisabled() {
|
||||
for (auto& ball : balls_) {
|
||||
ball->enableGravityIfDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::forceBallsGravityOn() {
|
||||
for (auto& ball : balls_) {
|
||||
ball->forceGravityOn();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::forceBallsGravityOff() {
|
||||
// Contar cuántas pelotas están en superficie (suelo/techo/pared)
|
||||
int balls_on_surface = 0;
|
||||
for (const auto& ball : balls_) {
|
||||
if (ball->isOnSurface()) {
|
||||
balls_on_surface++;
|
||||
}
|
||||
}
|
||||
|
||||
// Si la mayoría (>50%) están en superficie, aplicar impulso para que se vea el efecto
|
||||
float surface_ratio = static_cast<float>(balls_on_surface) / static_cast<float>(balls_.size());
|
||||
if (surface_ratio > 0.5f) {
|
||||
pushBallsAwayFromGravity(); // Dar impulso contrario a gravedad
|
||||
}
|
||||
|
||||
// Desactivar gravedad
|
||||
for (auto& ball : balls_) {
|
||||
ball->forceGravityOff();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::changeGravityDirection(GravityDirection direction) {
|
||||
current_gravity_ = direction;
|
||||
for (auto& ball : balls_) {
|
||||
ball->setGravityDirection(direction);
|
||||
ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::updateScreenSize(int width, int height) {
|
||||
screen_width_ = width;
|
||||
screen_height_ = height;
|
||||
|
||||
// NOTA: No actualizamos las bolas existentes, solo afecta a futuras creaciones
|
||||
// Si se requiere reposicionar bolas existentes, implementar aquí
|
||||
}
|
||||
|
||||
// === Métodos privados ===
|
||||
|
||||
void SceneManager::updateBallSizes(int old_size, int new_size) {
|
||||
for (auto& ball : balls_) {
|
||||
SDL_FRect pos = ball->getPosition();
|
||||
|
||||
// Ajustar posición para compensar cambio de tamaño
|
||||
// Si aumenta tamaño, mover hacia centro; si disminuye, alejar del centro
|
||||
float center_x = screen_width_ / 2.0f;
|
||||
float center_y = screen_height_ / 2.0f;
|
||||
|
||||
float dx = pos.x - center_x;
|
||||
float dy = pos.y - center_y;
|
||||
|
||||
// Ajustar proporcionalmente (evitar divisiones por cero)
|
||||
if (old_size > 0) {
|
||||
float scale_factor = static_cast<float>(new_size) / static_cast<float>(old_size);
|
||||
pos.x = center_x + dx * scale_factor;
|
||||
pos.y = center_y + dy * scale_factor;
|
||||
}
|
||||
|
||||
// Actualizar tamaño del hitbox
|
||||
ball->updateSize(new_size);
|
||||
}
|
||||
}
|
||||
174
source/scene/scene_manager.hpp
Normal file
174
source/scene/scene_manager.hpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "defines.hpp" // for GravityDirection
|
||||
|
||||
// Forward declarations
|
||||
class Texture;
|
||||
class ThemeManager;
|
||||
|
||||
/**
|
||||
* @class SceneManager
|
||||
* @brief Gestiona toda la lógica de creación, física y actualización de bolas
|
||||
*
|
||||
* Responsabilidad única: Manejo de la escena (bolas, gravedad, física)
|
||||
*
|
||||
* Características:
|
||||
* - Crea y destruye bolas según escenario seleccionado
|
||||
* - Controla la dirección y estado de la gravedad
|
||||
* - Actualiza física de todas las bolas cada frame
|
||||
* - Proporciona acceso controlado a las bolas para rendering
|
||||
* - Mantiene el Engine desacoplado de la lógica de física
|
||||
*/
|
||||
class SceneManager {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param screen_width Ancho lógico de la pantalla
|
||||
* @param screen_height Alto lógico de la pantalla
|
||||
*/
|
||||
SceneManager(int screen_width, int screen_height);
|
||||
|
||||
/**
|
||||
* @brief Inicializa el manager con configuración inicial
|
||||
* @param scenario Escenario inicial (índice de BALL_COUNT_SCENARIOS)
|
||||
* @param texture Textura compartida para sprites de bolas
|
||||
* @param theme_manager Puntero al gestor de temas (para colores)
|
||||
*/
|
||||
void initialize(int scenario, std::shared_ptr<Texture> texture, ThemeManager* theme_manager);
|
||||
|
||||
/**
|
||||
* @brief Actualiza física de todas las bolas
|
||||
* @param delta_time Tiempo transcurrido desde último frame (segundos)
|
||||
*/
|
||||
void update(float delta_time);
|
||||
|
||||
// === Gestión de bolas ===
|
||||
|
||||
/**
|
||||
* @brief Cambia el número de bolas según escenario
|
||||
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas; 8 = custom)
|
||||
* @param mode Modo de simulación actual (afecta inicialización)
|
||||
*/
|
||||
void changeScenario(int scenario_id, SimulationMode mode);
|
||||
|
||||
/**
|
||||
* @brief Configura el número de bolas para el escenario custom (índice 8)
|
||||
* @param n Número de bolas del escenario custom
|
||||
*/
|
||||
void setCustomBallCount(int n) { custom_ball_count_ = n; }
|
||||
|
||||
/**
|
||||
* @brief Actualiza textura y tamaño de todas las bolas
|
||||
* @param new_texture Nueva textura compartida
|
||||
* @param new_ball_size Nuevo tamaño de bolas (píxeles)
|
||||
*/
|
||||
void updateBallTexture(std::shared_ptr<Texture> new_texture, int new_ball_size);
|
||||
|
||||
// === Control de gravedad ===
|
||||
|
||||
/**
|
||||
* @brief Aplica impulso a todas las bolas alejándolas de la superficie de gravedad
|
||||
*/
|
||||
void pushBallsAwayFromGravity();
|
||||
|
||||
/**
|
||||
* @brief Alterna el estado de gravedad (ON/OFF) en todas las bolas
|
||||
*/
|
||||
void switchBallsGravity();
|
||||
|
||||
/**
|
||||
* @brief Reactiva gravedad solo si estaba desactivada
|
||||
*/
|
||||
void enableBallsGravityIfDisabled();
|
||||
|
||||
/**
|
||||
* @brief Fuerza gravedad ON en todas las bolas
|
||||
*/
|
||||
void forceBallsGravityOn();
|
||||
|
||||
/**
|
||||
* @brief Fuerza gravedad OFF en todas las bolas (con impulso si >50% en superficie)
|
||||
*/
|
||||
void forceBallsGravityOff();
|
||||
|
||||
/**
|
||||
* @brief Cambia la dirección de la gravedad
|
||||
* @param direction Nueva dirección (UP/DOWN/LEFT/RIGHT)
|
||||
*/
|
||||
void changeGravityDirection(GravityDirection direction);
|
||||
|
||||
// === Acceso a datos (read-only) ===
|
||||
|
||||
/**
|
||||
* @brief Obtiene referencia constante al vector de bolas (para rendering)
|
||||
*/
|
||||
const std::vector<std::unique_ptr<Ball>>& getBalls() const { return balls_; }
|
||||
|
||||
/**
|
||||
* @brief Obtiene referencia mutable al vector de bolas (para ShapeManager)
|
||||
* NOTA: Usar con cuidado, solo para sistemas que necesitan modificar estado de bolas
|
||||
*/
|
||||
std::vector<std::unique_ptr<Ball>>& getBallsMutable() { return balls_; }
|
||||
|
||||
/**
|
||||
* @brief Obtiene número total de bolas
|
||||
*/
|
||||
size_t getBallCount() const { return balls_.size(); }
|
||||
|
||||
/**
|
||||
* @brief Verifica si hay al menos una bola
|
||||
*/
|
||||
bool hasBalls() const { return !balls_.empty(); }
|
||||
|
||||
/**
|
||||
* @brief Obtiene puntero a la primera bola (para debug info)
|
||||
* @return Puntero constante o nullptr si no hay bolas
|
||||
*/
|
||||
const Ball* getFirstBall() const { return balls_.empty() ? nullptr : balls_[0].get(); }
|
||||
|
||||
/**
|
||||
* @brief Obtiene dirección actual de gravedad
|
||||
*/
|
||||
GravityDirection getCurrentGravity() const { return current_gravity_; }
|
||||
|
||||
/**
|
||||
* @brief Obtiene escenario actual
|
||||
*/
|
||||
int getCurrentScenario() const { return scenario_; }
|
||||
|
||||
/**
|
||||
* @brief Actualiza resolución de pantalla (para resize/fullscreen)
|
||||
* @param width Nuevo ancho lógico
|
||||
* @param height Nuevo alto lógico
|
||||
*/
|
||||
void updateScreenSize(int width, int height);
|
||||
|
||||
private:
|
||||
// === Datos de escena ===
|
||||
std::vector<std::unique_ptr<Ball>> balls_;
|
||||
GravityDirection current_gravity_;
|
||||
int scenario_;
|
||||
int custom_ball_count_ = 0; // Número de bolas para escenario custom (índice 8)
|
||||
|
||||
// === Configuración de pantalla ===
|
||||
int screen_width_;
|
||||
int screen_height_;
|
||||
int current_ball_size_;
|
||||
|
||||
// === Referencias a otros sistemas (no owned) ===
|
||||
std::shared_ptr<Texture> texture_;
|
||||
ThemeManager* theme_manager_;
|
||||
|
||||
// === Métodos privados auxiliares ===
|
||||
|
||||
/**
|
||||
* @brief Ajusta posiciones de bolas al cambiar tamaño de sprite
|
||||
* @param old_size Tamaño anterior
|
||||
* @param new_size Tamaño nuevo
|
||||
*/
|
||||
void updateBallSizes(int old_size, int new_size);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "atom_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "atom_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Átomo con núcleo central y órbitas electrónicas
|
||||
// Comportamiento: Núcleo estático + electrones orbitando en planos inclinados
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "cube_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "cube_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
#include <vector>
|
||||
|
||||
// Figura: Cubo 3D rotante
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "cylinder_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "cylinder_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
#include <cstdlib> // Para rand()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Cilindro 3D rotante
|
||||
// Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "helix_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "helix_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void HelixShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Espiral helicoidal 3D con distribución uniforme
|
||||
// Comportamiento: Rotación en eje Y + animación de fase vertical
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "icosahedron_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "icosahedron_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Icosaedro 3D (D20, poliedro regular de 20 caras)
|
||||
// Comportamiento: 12 vértices distribuidos uniformemente con rotación triple
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "lissajous_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "lissajous_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void LissajousShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Curva de Lissajous 3D
|
||||
// Comportamiento: Curva paramétrica 3D con rotación global y animación de fase
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "png_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "../external/stb_image.h"
|
||||
#include "png_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "external/stb_image.h"
|
||||
#include "resource_manager.hpp"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -9,6 +10,7 @@
|
||||
PNGShape::PNGShape(const char* png_path) {
|
||||
// Cargar PNG desde path
|
||||
if (!loadPNG(png_path)) {
|
||||
std::cerr << "[PNGShape] Usando fallback 10x10" << std::endl;
|
||||
// Fallback: generar un cuadrado simple si falla la carga
|
||||
image_width_ = 10;
|
||||
image_height_ = 10;
|
||||
@@ -19,24 +21,29 @@ PNGShape::PNGShape(const char* png_path) {
|
||||
next_idle_time_ = PNG_IDLE_TIME_MIN + (rand() % 1000) / 1000.0f * (PNG_IDLE_TIME_MAX - PNG_IDLE_TIME_MIN);
|
||||
}
|
||||
|
||||
bool PNGShape::loadPNG(const char* path) {
|
||||
int width, height, channels;
|
||||
unsigned char* data = stbi_load(path, &width, &height, &channels, 1); // Forzar 1 canal (grayscale)
|
||||
|
||||
if (!data) {
|
||||
bool PNGShape::loadPNG(const char* resource_key) {
|
||||
std::cout << "[PNGShape] Cargando recurso: " << resource_key << std::endl;
|
||||
unsigned char* file_data = nullptr;
|
||||
size_t file_size = 0;
|
||||
if (!ResourceManager::loadResource(resource_key, file_data, file_size)) {
|
||||
std::cerr << "[PNGShape] ERROR: recurso no encontrado: " << resource_key << std::endl;
|
||||
return false;
|
||||
}
|
||||
int width, height, channels;
|
||||
unsigned char* pixels = stbi_load_from_memory(file_data, static_cast<int>(file_size),
|
||||
&width, &height, &channels, 1);
|
||||
delete[] file_data;
|
||||
if (!pixels) {
|
||||
std::cerr << "[PNGShape] ERROR al decodificar PNG: " << stbi_failure_reason() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
image_width_ = width;
|
||||
image_height_ = height;
|
||||
pixel_data_.resize(width * height);
|
||||
|
||||
// Convertir a mapa booleano (true = píxel blanco/visible, false = negro/transparente)
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
pixel_data_[i] = (data[i] > 128); // Umbral: >128 = blanco
|
||||
pixel_data_[i] = (pixels[i] > 128);
|
||||
}
|
||||
|
||||
stbi_image_free(data);
|
||||
stbi_image_free(pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "../defines.h" // Para PNG_IDLE_TIME_MIN/MAX constantes
|
||||
#include "shape.hpp"
|
||||
#include "defines.hpp" // Para PNG_IDLE_TIME_MIN/MAX constantes
|
||||
#include <vector>
|
||||
#include <cstdlib> // Para rand()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user