Compare commits
62 Commits
9cabbd867f
...
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 |
21
.gitignore
vendored
21
.gitignore
vendored
@@ -12,7 +12,6 @@ vibe3_physics.exe
|
|||||||
*.lib
|
*.lib
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
|
||||||
|
|
||||||
# Archivos de compilación y enlazado
|
# Archivos de compilación y enlazado
|
||||||
*.d
|
*.d
|
||||||
@@ -26,6 +25,7 @@ Build/
|
|||||||
BUILD/
|
BUILD/
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
.cmake/
|
.cmake/
|
||||||
|
.cache/
|
||||||
|
|
||||||
# Archivos generados por CMake
|
# Archivos generados por CMake
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
@@ -93,4 +93,21 @@ Thumbs.db
|
|||||||
*.temp
|
*.temp
|
||||||
|
|
||||||
# Claude Code
|
# 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
|
||||||
709
BOIDS_ROADMAP.md
709
BOIDS_ROADMAP.md
@@ -1,709 +0,0 @@
|
|||||||
# BOIDS ROADMAP - Plan de Mejora Completo
|
|
||||||
|
|
||||||
**Proyecto:** ViBe3 Physics - Sistema de Boids (Flocking Behavior)
|
|
||||||
**Fecha de creación:** 2025-01-XX
|
|
||||||
**Estado actual:** Implementación básica funcional pero con problemas críticos
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Diagnóstico de Problemas Actuales
|
|
||||||
|
|
||||||
### 🔴 CRÍTICO: Bug de Clustering (Colapso a Punto Único)
|
|
||||||
|
|
||||||
**Problema observado:**
|
|
||||||
- Los boids se agrupan correctamente en grupos separados
|
|
||||||
- **PERO** dentro de cada grupo, todos colapsan al mismo punto exacto
|
|
||||||
- Las pelotas se superponen completamente, formando una "masa" sin espacio entre ellas
|
|
||||||
|
|
||||||
**Causa raíz identificada:**
|
|
||||||
1. **Desbalance de fuerzas**: Cohesión (80px radio) domina sobre Separación (30px radio)
|
|
||||||
2. **Aplicación de fuerzas**: Se aplican fuerzas cada frame sin velocidad mínima
|
|
||||||
3. **Fuerza máxima muy baja**: `BOID_MAX_FORCE = 0.1` es insuficiente para separación efectiva
|
|
||||||
4. **Sin velocidad mínima**: Los boids pueden quedarse completamente estáticos (vx=0, vy=0)
|
|
||||||
|
|
||||||
**Impacto:** Sistema de boids inutilizable visualmente
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔴 CRÍTICO: Rendimiento O(n²) Inaceptable
|
|
||||||
|
|
||||||
**Problema observado:**
|
|
||||||
- 100 boids: ~60 FPS ✅
|
|
||||||
- 1,000 boids: ~15-20 FPS ❌ (caída del 70%)
|
|
||||||
- 5,000+ boids: < 5 FPS ❌ (completamente inutilizable)
|
|
||||||
|
|
||||||
**Causa raíz identificada:**
|
|
||||||
```cpp
|
|
||||||
// Cada boid revisa TODOS los demás boids (3 veces: separation, alignment, cohesion)
|
|
||||||
for (auto& boid : balls) {
|
|
||||||
applySeparation(boid); // O(n) - itera todos los balls
|
|
||||||
applyAlignment(boid); // O(n) - itera todos los balls
|
|
||||||
applyCohesion(boid); // O(n) - itera todos los balls
|
|
||||||
}
|
|
||||||
// Complejidad total: O(n²) × 3 = O(3n²)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cálculos de complejidad:**
|
|
||||||
- 100 boids: 100 × 100 × 3 = **30,000 checks/frame**
|
|
||||||
- 1,000 boids: 1,000 × 1,000 × 3 = **3,000,000 checks/frame** (100x más lento)
|
|
||||||
- 10,000 boids: 10,000 × 10,000 × 3 = **300,000,000 checks/frame** (imposible)
|
|
||||||
|
|
||||||
**Impacto:** No escalable más allá de ~500 boids
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🟡 MEDIO: Comportamiento Visual Pobre
|
|
||||||
|
|
||||||
**Problemas identificados:**
|
|
||||||
1. **Sin variedad visual**: Todos los boids idénticos (mismo tamaño, color)
|
|
||||||
2. **Movimiento robótico**: Steering demasiado directo, sin suavizado
|
|
||||||
3. **Wrapping abrupto**: Teletransporte visible rompe inmersión
|
|
||||||
4. **Sin personalidad**: Todos los boids se comportan idénticamente
|
|
||||||
|
|
||||||
**Impacto:** Resultado visual poco interesante y repetitivo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Plan de Fases de Mejora
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 1: Fix Clustering Bug (CRÍTICO)** ⚠️
|
|
||||||
|
|
||||||
**Objetivo:** Eliminar el colapso a punto único, mantener grupos dispersos
|
|
||||||
|
|
||||||
**Prioridad:** CRÍTICA
|
|
||||||
**Tiempo estimado:** 2-3 horas
|
|
||||||
**Complejidad:** Baja (ajustes de parámetros + lógica mínima)
|
|
||||||
|
|
||||||
### Cambios a Implementar
|
|
||||||
|
|
||||||
#### 1.1 Rebalanceo de Radios y Pesos
|
|
||||||
|
|
||||||
**Problema actual:**
|
|
||||||
```cpp
|
|
||||||
// defines.h - VALORES ACTUALES (INCORRECTOS)
|
|
||||||
BOID_SEPARATION_RADIUS = 30.0f; // Radio muy pequeño
|
|
||||||
BOID_ALIGNMENT_RADIUS = 50.0f;
|
|
||||||
BOID_COHESION_RADIUS = 80.0f; // Radio muy grande (domina)
|
|
||||||
BOID_SEPARATION_WEIGHT = 1.5f; // Peso insuficiente
|
|
||||||
BOID_ALIGNMENT_WEIGHT = 1.0f;
|
|
||||||
BOID_COHESION_WEIGHT = 0.8f;
|
|
||||||
BOID_MAX_FORCE = 0.1f; // Fuerza máxima muy débil
|
|
||||||
BOID_MAX_SPEED = 3.0f;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solución propuesta:**
|
|
||||||
```cpp
|
|
||||||
// defines.h - VALORES CORREGIDOS
|
|
||||||
BOID_SEPARATION_RADIUS = 25.0f; // Radio pequeño pero suficiente
|
|
||||||
BOID_ALIGNMENT_RADIUS = 40.0f;
|
|
||||||
BOID_COHESION_RADIUS = 60.0f; // Reducido (menos dominante)
|
|
||||||
BOID_SEPARATION_WEIGHT = 3.0f; // TRIPLICADO (alta prioridad)
|
|
||||||
BOID_ALIGNMENT_WEIGHT = 1.0f; // Sin cambios
|
|
||||||
BOID_COHESION_WEIGHT = 0.5f; // REDUCIDO a la mitad
|
|
||||||
BOID_MAX_FORCE = 0.5f; // QUINTUPLICADO (permite reacción rápida)
|
|
||||||
BOID_MAX_SPEED = 3.0f; // Sin cambios
|
|
||||||
BOID_MIN_SPEED = 0.5f; // NUEVO: velocidad mínima
|
|
||||||
```
|
|
||||||
|
|
||||||
**Justificación:**
|
|
||||||
- **Separation dominante**: Evita colapso con peso 3x mayor
|
|
||||||
- **Cohesion reducida**: Radio 60px (antes 80px) + peso 0.5 (antes 0.8)
|
|
||||||
- **Max force aumentada**: Permite correcciones rápidas
|
|
||||||
- **Min speed añadida**: Evita boids estáticos
|
|
||||||
|
|
||||||
#### 1.2 Implementar Velocidad Mínima
|
|
||||||
|
|
||||||
**Archivo:** `source/boids_mgr/boid_manager.cpp`
|
|
||||||
|
|
||||||
**Añadir al final de `limitSpeed()`:**
|
|
||||||
```cpp
|
|
||||||
void BoidManager::limitSpeed(Ball* boid) {
|
|
||||||
float vx, vy;
|
|
||||||
boid->getVelocity(vx, vy);
|
|
||||||
|
|
||||||
float speed = std::sqrt(vx * vx + vy * vy);
|
|
||||||
|
|
||||||
// Limitar velocidad máxima
|
|
||||||
if (speed > BOID_MAX_SPEED) {
|
|
||||||
vx = (vx / speed) * BOID_MAX_SPEED;
|
|
||||||
vy = (vy / speed) * BOID_MAX_SPEED;
|
|
||||||
boid->setVelocity(vx, vy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NUEVO: Aplicar velocidad mínima (evitar boids estáticos)
|
|
||||||
if (speed > 0.0f && speed < BOID_MIN_SPEED) {
|
|
||||||
vx = (vx / speed) * BOID_MIN_SPEED;
|
|
||||||
vy = (vy / speed) * BOID_MIN_SPEED;
|
|
||||||
boid->setVelocity(vx, vy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 1.3 Mejorar Aplicación de Fuerza de Separación
|
|
||||||
|
|
||||||
**Problema actual:** Separación se divide por distancia² (muy débil cuando cerca)
|
|
||||||
|
|
||||||
**Archivo:** `source/boids_mgr/boid_manager.cpp::applySeparation()`
|
|
||||||
|
|
||||||
**Cambio:**
|
|
||||||
```cpp
|
|
||||||
// ANTES (línea 145):
|
|
||||||
steer_x += (dx / distance) / distance; // Dividir por distance² hace fuerza muy débil
|
|
||||||
steer_y += (dy / distance) / distance;
|
|
||||||
|
|
||||||
// DESPUÉS:
|
|
||||||
// Separación más fuerte cuando más cerca (inversa de distancia, no cuadrado)
|
|
||||||
float separation_strength = (BOID_SEPARATION_RADIUS - distance) / BOID_SEPARATION_RADIUS;
|
|
||||||
steer_x += (dx / distance) * separation_strength;
|
|
||||||
steer_y += (dy / distance) * separation_strength;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Justificación:** Fuerza de separación ahora es proporcional a cercanía (0% en radio máximo, 100% en colisión)
|
|
||||||
|
|
||||||
### Testing de Fase 1
|
|
||||||
|
|
||||||
**Checklist de validación:**
|
|
||||||
- [ ] Con 100 boids: Grupos visibles con espacio entre boids individuales
|
|
||||||
- [ ] Con 1000 boids: Sin colapso a puntos únicos
|
|
||||||
- [ ] Ningún boid completamente estático (velocidad > 0.5)
|
|
||||||
- [ ] Distancia mínima entre boids vecinos: ~10-15px
|
|
||||||
- [ ] FPS con 1000 boids: ~15-20 FPS (sin mejorar, pero funcional)
|
|
||||||
|
|
||||||
**Criterio de éxito:**
|
|
||||||
✅ Los boids mantienen distancia personal dentro de grupos sin colapsar
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 2: Spatial Hash Grid (ALTO IMPACTO)** 🚀 ✅ **COMPLETADA**
|
|
||||||
|
|
||||||
**Objetivo:** O(n²) → O(n) mediante optimización espacial
|
|
||||||
|
|
||||||
**Prioridad:** ALTA
|
|
||||||
**Tiempo estimado:** 4-6 horas → **Real: 2 horas**
|
|
||||||
**Complejidad:** Media (nueva estructura de datos)
|
|
||||||
|
|
||||||
### Concepto: Spatial Hash Grid
|
|
||||||
|
|
||||||
**Problema actual:**
|
|
||||||
```
|
|
||||||
Cada boid revisa TODOS los demás boids
|
|
||||||
→ 1000 boids × 1000 checks = 1,000,000 comparaciones
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solución:**
|
|
||||||
```
|
|
||||||
Dividir espacio en grid de celdas
|
|
||||||
Cada boid solo revisa boids en celdas vecinas (3×3 = 9 celdas)
|
|
||||||
→ 1000 boids × ~10 vecinos = 10,000 comparaciones (100x más rápido)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Implementación
|
|
||||||
|
|
||||||
#### 2.1 Crear Estructura de Spatial Grid
|
|
||||||
|
|
||||||
**Nuevo archivo:** `source/boids_mgr/spatial_grid.h`
|
|
||||||
```cpp
|
|
||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class Ball;
|
|
||||||
|
|
||||||
// Clase para optimización espacial de búsqueda de vecinos
|
|
||||||
class SpatialGrid {
|
|
||||||
public:
|
|
||||||
SpatialGrid(int screen_width, int screen_height, float cell_size);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void insert(Ball* boid);
|
|
||||||
std::vector<Ball*> getNearby(Ball* boid, float radius);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int screen_width_;
|
|
||||||
int screen_height_;
|
|
||||||
float cell_size_;
|
|
||||||
int grid_width_;
|
|
||||||
int grid_height_;
|
|
||||||
|
|
||||||
// Hash map: cell_id → vector de boids en esa celda
|
|
||||||
std::unordered_map<int, std::vector<Ball*>> grid_;
|
|
||||||
|
|
||||||
int getCellId(float x, float y) const;
|
|
||||||
void getCellCoords(int cell_id, int& cx, int& cy) const;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Nuevo archivo:** `source/boids_mgr/spatial_grid.cpp`
|
|
||||||
```cpp
|
|
||||||
#include "spatial_grid.h"
|
|
||||||
#include "../ball.h"
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
SpatialGrid::SpatialGrid(int screen_width, int screen_height, float cell_size)
|
|
||||||
: screen_width_(screen_width)
|
|
||||||
, screen_height_(screen_height)
|
|
||||||
, cell_size_(cell_size)
|
|
||||||
, grid_width_(static_cast<int>(std::ceil(screen_width / cell_size)))
|
|
||||||
, grid_height_(static_cast<int>(std::ceil(screen_height / cell_size))) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpatialGrid::clear() {
|
|
||||||
grid_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpatialGrid::insert(Ball* boid) {
|
|
||||||
SDL_FRect pos = boid->getPosition();
|
|
||||||
float center_x = pos.x + pos.w / 2.0f;
|
|
||||||
float center_y = pos.y + pos.h / 2.0f;
|
|
||||||
|
|
||||||
int cell_id = getCellId(center_x, center_y);
|
|
||||||
grid_[cell_id].push_back(boid);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Ball*> SpatialGrid::getNearby(Ball* boid, float radius) {
|
|
||||||
std::vector<Ball*> nearby;
|
|
||||||
|
|
||||||
SDL_FRect pos = boid->getPosition();
|
|
||||||
float center_x = pos.x + pos.w / 2.0f;
|
|
||||||
float center_y = pos.y + pos.h / 2.0f;
|
|
||||||
|
|
||||||
// Calcular rango de celdas a revisar (3x3 en el peor caso)
|
|
||||||
int min_cx = static_cast<int>((center_x - radius) / cell_size_);
|
|
||||||
int max_cx = static_cast<int>((center_x + radius) / cell_size_);
|
|
||||||
int min_cy = static_cast<int>((center_y - radius) / cell_size_);
|
|
||||||
int max_cy = static_cast<int>((center_y + radius) / cell_size_);
|
|
||||||
|
|
||||||
// Clamp a límites de grid
|
|
||||||
min_cx = std::max(0, min_cx);
|
|
||||||
max_cx = std::min(grid_width_ - 1, max_cx);
|
|
||||||
min_cy = std::max(0, min_cy);
|
|
||||||
max_cy = std::min(grid_height_ - 1, max_cy);
|
|
||||||
|
|
||||||
// Recopilar boids de celdas vecinas
|
|
||||||
for (int cy = min_cy; cy <= max_cy; ++cy) {
|
|
||||||
for (int cx = min_cx; cx <= max_cx; ++cx) {
|
|
||||||
int cell_id = cy * grid_width_ + cx;
|
|
||||||
auto it = grid_.find(cell_id);
|
|
||||||
if (it != grid_.end()) {
|
|
||||||
for (Ball* other : it->second) {
|
|
||||||
if (other != boid) {
|
|
||||||
nearby.push_back(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nearby;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SpatialGrid::getCellId(float x, float y) const {
|
|
||||||
int cx = static_cast<int>(x / cell_size_);
|
|
||||||
int cy = static_cast<int>(y / cell_size_);
|
|
||||||
cx = std::max(0, std::min(grid_width_ - 1, cx));
|
|
||||||
cy = std::max(0, std::min(grid_height_ - 1, cy));
|
|
||||||
return cy * grid_width_ + cx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpatialGrid::getCellCoords(int cell_id, int& cx, int& cy) const {
|
|
||||||
cx = cell_id % grid_width_;
|
|
||||||
cy = cell_id / grid_width_;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 Integrar SpatialGrid en BoidManager
|
|
||||||
|
|
||||||
**Archivo:** `source/boids_mgr/boid_manager.h`
|
|
||||||
```cpp
|
|
||||||
#include "spatial_grid.h"
|
|
||||||
|
|
||||||
class BoidManager {
|
|
||||||
private:
|
|
||||||
// ... miembros existentes ...
|
|
||||||
std::unique_ptr<SpatialGrid> spatial_grid_; // NUEVO
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Archivo:** `source/boids_mgr/boid_manager.cpp`
|
|
||||||
|
|
||||||
**Modificar `initialize()`:**
|
|
||||||
```cpp
|
|
||||||
void BoidManager::initialize(...) {
|
|
||||||
// ... código existente ...
|
|
||||||
|
|
||||||
// Crear spatial grid con tamaño de celda = radio máximo de búsqueda
|
|
||||||
float max_radius = std::max({BOID_SEPARATION_RADIUS, BOID_ALIGNMENT_RADIUS, BOID_COHESION_RADIUS});
|
|
||||||
spatial_grid_ = std::make_unique<SpatialGrid>(screen_width, screen_height, max_radius);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Modificar `update()`:**
|
|
||||||
```cpp
|
|
||||||
void BoidManager::update(float delta_time) {
|
|
||||||
if (!boids_active_) return;
|
|
||||||
|
|
||||||
auto& balls = scene_mgr_->getBallsMutable();
|
|
||||||
|
|
||||||
// NUEVO: Reconstruir spatial grid cada frame
|
|
||||||
spatial_grid_->clear();
|
|
||||||
for (auto& ball : balls) {
|
|
||||||
spatial_grid_->insert(ball.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aplicar reglas (ahora con grid optimizado)
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... resto del código ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Modificar `applySeparation()`, `applyAlignment()`, `applyCohesion()`:**
|
|
||||||
|
|
||||||
**ANTES:**
|
|
||||||
```cpp
|
|
||||||
const auto& balls = scene_mgr_->getBalls();
|
|
||||||
for (const auto& other : balls) { // O(n) - itera TODOS
|
|
||||||
```
|
|
||||||
|
|
||||||
**DESPUÉS:**
|
|
||||||
```cpp
|
|
||||||
// O(1) amortizado - solo vecinos cercanos
|
|
||||||
auto nearby = spatial_grid_->getNearby(boid, BOID_SEPARATION_RADIUS);
|
|
||||||
for (Ball* other : nearby) { // Solo ~10-50 boids
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing de Fase 2
|
|
||||||
|
|
||||||
**Métricas de rendimiento esperadas:**
|
|
||||||
|
|
||||||
| Cantidad Boids | FPS Antes | FPS Después | Mejora |
|
|
||||||
|----------------|-----------|-------------|--------|
|
|
||||||
| 100 | 60 | 60 | 1x (sin cambio) |
|
|
||||||
| 1,000 | 15-20 | 60+ | **3-4x** ✅ |
|
|
||||||
| 5,000 | <5 | 40-50 | **10x+** ✅ |
|
|
||||||
| 10,000 | <1 | 20-30 | **30x+** ✅ |
|
|
||||||
| 50,000 | imposible | 5-10 | **funcional** ✅ |
|
|
||||||
|
|
||||||
**Checklist de validación:**
|
|
||||||
- [x] FPS con 1000 boids: >50 FPS → **Pendiente de medición**
|
|
||||||
- [x] FPS con 5000 boids: >30 FPS → **Pendiente de medición**
|
|
||||||
- [x] FPS con 10000 boids: >15 FPS → **Pendiente de medición**
|
|
||||||
- [x] Comportamiento visual idéntico a Fase 1 → **Garantizado (misma lógica)**
|
|
||||||
- [x] Sin boids "perdidos" (todos actualizados correctamente) → **Verificado en código**
|
|
||||||
|
|
||||||
**Criterio de éxito:**
|
|
||||||
✅ Mejora de rendimiento **10x+** para 5000+ boids → **ESPERADO**
|
|
||||||
|
|
||||||
### Resultados de Implementación (Fase 2)
|
|
||||||
|
|
||||||
**Implementación completada:**
|
|
||||||
- ✅ SpatialGrid genérico creado (spatial_grid.h/.cpp)
|
|
||||||
- ✅ Integración completa en BoidManager
|
|
||||||
- ✅ Grid poblado cada frame (O(n))
|
|
||||||
- ✅ 3 reglas de Reynolds usando queryRadius() (O(1) amortizado)
|
|
||||||
- ✅ Compilación exitosa sin errores
|
|
||||||
- ✅ Sistema reutilizable para futuras colisiones físicas
|
|
||||||
|
|
||||||
**Código añadido:**
|
|
||||||
- 206 líneas nuevas (+5 archivos modificados)
|
|
||||||
- spatial_grid.cpp: 89 líneas de implementación
|
|
||||||
- spatial_grid.h: 74 líneas con documentación exhaustiva
|
|
||||||
- defines.h: BOID_GRID_CELL_SIZE = 100.0f
|
|
||||||
|
|
||||||
**Arquitectura:**
|
|
||||||
- Tamaño de celda: 100px (≥ BOID_COHESION_RADIUS de 80px)
|
|
||||||
- Hash map: unordered_map<int, vector<Ball*>>
|
|
||||||
- Búsqueda: Solo celdas adyacentes (máx 9 celdas)
|
|
||||||
- Clear + repoblación cada frame: ~0.01ms para 10K boids
|
|
||||||
|
|
||||||
**Próximo paso:** Medir rendimiento real y comparar con estimaciones
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 3: Mejoras Visuales y de Comportamiento** 🎨
|
|
||||||
|
|
||||||
**Objetivo:** Hacer el comportamiento más interesante y natural
|
|
||||||
|
|
||||||
**Prioridad:** MEDIA
|
|
||||||
**Tiempo estimado:** 3-4 horas
|
|
||||||
**Complejidad:** Baja-Media
|
|
||||||
|
|
||||||
### 3.1 Variedad Visual por Boid
|
|
||||||
|
|
||||||
**Añadir propiedades individuales:**
|
|
||||||
```cpp
|
|
||||||
// En ball.h (si no existen ya)
|
|
||||||
struct BoidProperties {
|
|
||||||
float size_scale; // 0.8-1.2 (variación de tamaño)
|
|
||||||
float speed_factor; // 0.9-1.1 (algunos más rápidos)
|
|
||||||
Color original_color; // Color base individual
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Aplicar al activar boids:**
|
|
||||||
- Tamaños variados (80%-120% del tamaño base)
|
|
||||||
- Velocidades máximas ligeramente diferentes
|
|
||||||
- Colores con variación de tinte
|
|
||||||
|
|
||||||
### 3.2 Steering Suavizado
|
|
||||||
|
|
||||||
**Problema:** Fuerzas aplicadas directamente causan movimiento robótico
|
|
||||||
|
|
||||||
**Solución:** Interpolación exponencial (smoothing)
|
|
||||||
```cpp
|
|
||||||
// Aplicar smooth steering
|
|
||||||
float smooth_factor = 0.3f; // 0-1 (menor = más suave)
|
|
||||||
vx += steer_x * smooth_factor;
|
|
||||||
vy += steer_y * smooth_factor;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 Boundaries Suaves (Soft Wrapping)
|
|
||||||
|
|
||||||
**Problema actual:** Teletransporte abrupto visible
|
|
||||||
|
|
||||||
**Solución:** "Avoid edges" behavior
|
|
||||||
```cpp
|
|
||||||
void BoidManager::applyEdgeAvoidance(Ball* boid, float delta_time) {
|
|
||||||
SDL_FRect pos = boid->getPosition();
|
|
||||||
float center_x = pos.x + pos.w / 2.0f;
|
|
||||||
float center_y = pos.y + pos.h / 2.0f;
|
|
||||||
|
|
||||||
float margin = 50.0f; // Margen de detección de borde
|
|
||||||
float turn_force = 0.5f;
|
|
||||||
|
|
||||||
float steer_x = 0.0f, steer_y = 0.0f;
|
|
||||||
|
|
||||||
if (center_x < margin) steer_x += turn_force;
|
|
||||||
if (center_x > screen_width_ - margin) steer_x -= turn_force;
|
|
||||||
if (center_y < margin) steer_y += turn_force;
|
|
||||||
if (center_y > screen_height_ - margin) steer_y -= turn_force;
|
|
||||||
|
|
||||||
if (steer_x != 0.0f || steer_y != 0.0f) {
|
|
||||||
float vx, vy;
|
|
||||||
boid->getVelocity(vx, vy);
|
|
||||||
vx += steer_x * delta_time;
|
|
||||||
vy += steer_y * delta_time;
|
|
||||||
boid->setVelocity(vx, vy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing de Fase 3
|
|
||||||
|
|
||||||
**Checklist de validación:**
|
|
||||||
- [ ] Boids con tamaños variados visibles
|
|
||||||
- [ ] Movimiento más orgánico y fluido
|
|
||||||
- [ ] Giros en bordes de pantalla suaves (no teletransporte)
|
|
||||||
- [ ] Variación de colores perceptible
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 4: Comportamientos Avanzados** 🎮
|
|
||||||
|
|
||||||
**Objetivo:** Añadir interactividad y dinámicas interesantes
|
|
||||||
|
|
||||||
**Prioridad:** BAJA (opcional)
|
|
||||||
**Tiempo estimado:** 4-6 horas
|
|
||||||
**Complejidad:** Media-Alta
|
|
||||||
|
|
||||||
### 4.1 Obstacle Avoidance (Ratón)
|
|
||||||
|
|
||||||
**Funcionalidad:**
|
|
||||||
- Mouse position actúa como "predador"
|
|
||||||
- Boids huyen del cursor en un radio de 100px
|
|
||||||
|
|
||||||
**Implementación:**
|
|
||||||
```cpp
|
|
||||||
void BoidManager::applyMouseAvoidance(Ball* boid, int mouse_x, int mouse_y) {
|
|
||||||
SDL_FRect pos = boid->getPosition();
|
|
||||||
float center_x = pos.x + pos.w / 2.0f;
|
|
||||||
float center_y = pos.y + pos.h / 2.0f;
|
|
||||||
|
|
||||||
float dx = center_x - mouse_x;
|
|
||||||
float dy = center_y - mouse_y;
|
|
||||||
float distance = std::sqrt(dx * dx + dy * dy);
|
|
||||||
|
|
||||||
const float AVOID_RADIUS = 100.0f;
|
|
||||||
const float AVOID_STRENGTH = 2.0f;
|
|
||||||
|
|
||||||
if (distance < AVOID_RADIUS && distance > 0.0f) {
|
|
||||||
float flee_x = (dx / distance) * AVOID_STRENGTH;
|
|
||||||
float flee_y = (dy / distance) * AVOID_STRENGTH;
|
|
||||||
|
|
||||||
float vx, vy;
|
|
||||||
boid->getVelocity(vx, vy);
|
|
||||||
vx += flee_x;
|
|
||||||
vy += flee_y;
|
|
||||||
boid->setVelocity(vx, vy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 Predator/Prey Dynamics
|
|
||||||
|
|
||||||
**Concepto:**
|
|
||||||
- 10% de boids son "predadores" (color rojo)
|
|
||||||
- 90% son "presas" (color normal)
|
|
||||||
- Predadores persiguen presas
|
|
||||||
- Presas huyen de predadores
|
|
||||||
|
|
||||||
### 4.3 Leader Following
|
|
||||||
|
|
||||||
**Concepto:**
|
|
||||||
- Un boid aleatorio es designado "líder"
|
|
||||||
- Otros boids tienen peso adicional hacia el líder
|
|
||||||
- El líder se mueve con input del usuario (teclas WASD)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 5: Optimizaciones Avanzadas** ⚡
|
|
||||||
|
|
||||||
**Objetivo:** Rendimiento extremo para 50K+ boids
|
|
||||||
|
|
||||||
**Prioridad:** MUY BAJA (solo si necesario)
|
|
||||||
**Tiempo estimado:** 8-12 horas
|
|
||||||
**Complejidad:** Alta
|
|
||||||
|
|
||||||
### 5.1 Multi-threading (Parallel Processing)
|
|
||||||
|
|
||||||
**Concepto:** Dividir trabajo entre múltiples hilos CPU
|
|
||||||
|
|
||||||
**Complejidad:** Alta (requiere thread-safety, atomic ops, etc.)
|
|
||||||
|
|
||||||
### 5.2 SIMD Vectorization
|
|
||||||
|
|
||||||
**Concepto:** Procesar 4-8 boids simultáneamente con instrucciones SSE/AVX
|
|
||||||
|
|
||||||
**Complejidad:** Muy Alta (requiere conocimiento de intrinsics)
|
|
||||||
|
|
||||||
### 5.3 GPU Compute Shaders
|
|
||||||
|
|
||||||
**Concepto:** Mover toda la física de boids a GPU
|
|
||||||
|
|
||||||
**Complejidad:** Extrema (requiere OpenGL compute o Vulkan)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **FASE 6: Integración y Pulido** ✨
|
|
||||||
|
|
||||||
**Objetivo:** Integrar boids con sistemas existentes
|
|
||||||
|
|
||||||
**Prioridad:** MEDIA
|
|
||||||
**Tiempo estimado:** 2-3 horas
|
|
||||||
**Complejidad:** Baja
|
|
||||||
|
|
||||||
### 6.1 Integración con Modo DEMO
|
|
||||||
|
|
||||||
**Añadir boids al repertorio de acciones aleatorias:**
|
|
||||||
```cpp
|
|
||||||
// En defines.h
|
|
||||||
constexpr int DEMO_WEIGHT_BOIDS = 8; // 8% probabilidad de activar boids
|
|
||||||
|
|
||||||
// En state_manager.cpp
|
|
||||||
case Action::ACTIVATE_BOIDS:
|
|
||||||
engine_->toggleBoidsMode();
|
|
||||||
break;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.2 Debug Visualization
|
|
||||||
|
|
||||||
**Funcionalidad:** Tecla "H" muestra overlay de debug:
|
|
||||||
- Radios de separación/alignment/cohesion (círculos)
|
|
||||||
- Vectores de velocidad (flechas)
|
|
||||||
- Spatial grid (líneas de celdas)
|
|
||||||
- ID de boid y vecinos
|
|
||||||
|
|
||||||
### 6.3 Configuración Runtime
|
|
||||||
|
|
||||||
**Sistema de "presets" de comportamiento:**
|
|
||||||
- Preset 1: "Tight Flocks" (cohesión alta)
|
|
||||||
- Preset 2: "Loose Swarms" (separación alta)
|
|
||||||
- Preset 3: "Chaotic" (todos los pesos bajos)
|
|
||||||
- Preset 4: "Fast" (velocidad alta)
|
|
||||||
|
|
||||||
**Controles:**
|
|
||||||
- Numpad 1-4 (en modo boids) para cambiar preset
|
|
||||||
- Shift+Numpad +/- para ajustar parámetros en vivo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📈 Métricas de Éxito del Roadmap Completo
|
|
||||||
|
|
||||||
### Funcionalidad
|
|
||||||
- ✅ Sin clustering (grupos dispersos correctamente)
|
|
||||||
- ✅ Comportamiento natural y orgánico
|
|
||||||
- ✅ Transiciones suaves (no teletransporte visible)
|
|
||||||
|
|
||||||
### Rendimiento
|
|
||||||
- ✅ 1,000 boids: >50 FPS
|
|
||||||
- ✅ 5,000 boids: >30 FPS
|
|
||||||
- ✅ 10,000 boids: >15 FPS
|
|
||||||
|
|
||||||
### Visual
|
|
||||||
- ✅ Variedad perceptible entre boids
|
|
||||||
- ✅ Movimiento fluido y dinámico
|
|
||||||
- ✅ Efectos visuales opcionales funcionales
|
|
||||||
|
|
||||||
### Integración
|
|
||||||
- ✅ Compatible con modo DEMO
|
|
||||||
- ✅ Debug overlay útil y claro
|
|
||||||
- ✅ Configuración runtime funcional
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 Orden de Implementación Recomendado
|
|
||||||
|
|
||||||
### Mínimo Viable (MVP)
|
|
||||||
1. **FASE 1** (CRÍTICO) - Fix clustering
|
|
||||||
2. **FASE 2** (ALTO) - Spatial grid
|
|
||||||
|
|
||||||
**Resultado:** Boids funcionales y performantes para 1K-5K boids
|
|
||||||
|
|
||||||
### Producto Completo
|
|
||||||
3. **FASE 3** (MEDIO) - Mejoras visuales
|
|
||||||
4. **FASE 6** (MEDIO) - Integración y debug
|
|
||||||
|
|
||||||
**Resultado:** Experiencia pulida y profesional
|
|
||||||
|
|
||||||
### Opcional (Si hay tiempo)
|
|
||||||
5. **FASE 4** (BAJO) - Comportamientos avanzados
|
|
||||||
6. **FASE 5** (MUY BAJO) - Optimizaciones extremas
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 Notas de Implementación
|
|
||||||
|
|
||||||
### Archivos a Modificar (Fase 1-2)
|
|
||||||
- `source/defines.h` - Constantes de boids
|
|
||||||
- `source/boids_mgr/boid_manager.h` - Header del manager
|
|
||||||
- `source/boids_mgr/boid_manager.cpp` - Implementación
|
|
||||||
- `source/boids_mgr/spatial_grid.h` - NUEVO archivo
|
|
||||||
- `source/boids_mgr/spatial_grid.cpp` - NUEVO archivo
|
|
||||||
- `CMakeLists.txt` - Sin cambios (glob ya incluye boids_mgr/*.cpp)
|
|
||||||
|
|
||||||
### Estrategia de Testing
|
|
||||||
1. **Compilar después de cada cambio**
|
|
||||||
2. **Probar con 100 boids primero** (debug rápido)
|
|
||||||
3. **Escalar a 1000, 5000, 10000** (validar rendimiento)
|
|
||||||
4. **Usar modo debug (tecla H)** para visualizar parámetros
|
|
||||||
|
|
||||||
### Compatibilidad con Sistema Actual
|
|
||||||
- ✅ No interfiere con modo PHYSICS
|
|
||||||
- ✅ No interfiere con modo SHAPE
|
|
||||||
- ✅ Compatible con todos los temas
|
|
||||||
- ✅ Compatible con cambio de resolución
|
|
||||||
- ✅ Compatible con modo DEMO/LOGO
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**FIN DEL ROADMAP**
|
|
||||||
|
|
||||||
*Documento vivo - Se actualizará según avance la implementación*
|
|
||||||
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*
|
|
||||||
@@ -48,6 +48,9 @@ endif()
|
|||||||
# Incluir directorios de SDL3 y SDL3_ttf
|
# Incluir directorios de SDL3 y SDL3_ttf
|
||||||
include_directories(${SDL3_INCLUDE_DIRS} ${SDL3_ttf_INCLUDE_DIRS})
|
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
|
# Añadir el ejecutable reutilizando el nombre del proyecto
|
||||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||||
|
|
||||||
@@ -56,3 +59,8 @@ set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAK
|
|||||||
|
|
||||||
# Enlazar las bibliotecas necesarias
|
# Enlazar las bibliotecas necesarias
|
||||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS})
|
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
|
# Nombres para los ficheros de lanzamiento
|
||||||
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
||||||
MACOS_INTEL_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-intel.dmg
|
MACOS_INTEL_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-intel.dmg
|
||||||
MACOS_APPLE_SILICON_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-apple-silicon.dmg
|
MACOS_APPLE_SILICON_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-apple-silicon.dmg
|
||||||
LINUX_RELEASE := $(TARGET_FILE)-$(VERSION)-linux.tar.gz
|
LINUX_RELEASE := $(TARGET_FILE)-$(VERSION)-linux.tar.gz
|
||||||
RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
|
RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
|
||||||
|
|
||||||
# Lista completa de archivos fuente (basada en estructura de ViBe3)
|
# Lista completa de archivos fuente (detección automática con wildcards, como CMakeLists.txt)
|
||||||
APP_SOURCES := \
|
APP_SOURCES := $(wildcard source/*.cpp) \
|
||||||
source/ball.cpp \
|
$(wildcard source/external/*.cpp) \
|
||||||
source/engine.cpp \
|
$(wildcard source/shapes/*.cpp) \
|
||||||
source/main.cpp \
|
$(wildcard source/themes/*.cpp) \
|
||||||
source/resource_pack.cpp \
|
$(wildcard source/state/*.cpp) \
|
||||||
source/external/mouse.cpp \
|
$(wildcard source/input/*.cpp) \
|
||||||
source/external/sprite.cpp \
|
$(wildcard source/scene/*.cpp) \
|
||||||
source/external/texture.cpp \
|
$(wildcard source/shapes_mgr/*.cpp) \
|
||||||
source/shapes/atom_shape.cpp \
|
$(wildcard source/boids_mgr/*.cpp) \
|
||||||
source/shapes/cube_shape.cpp \
|
$(wildcard source/text/*.cpp) \
|
||||||
source/shapes/cylinder_shape.cpp \
|
$(wildcard source/ui/*.cpp)
|
||||||
source/shapes/helix_shape.cpp \
|
|
||||||
source/shapes/icosahedron_shape.cpp \
|
# Excluir archivos antiguos si existen
|
||||||
source/shapes/png_shape.cpp \
|
APP_SOURCES := $(filter-out source/main_old.cpp, $(APP_SOURCES))
|
||||||
source/shapes/sphere_shape.cpp \
|
|
||||||
source/shapes/torus_shape.cpp \
|
|
||||||
source/shapes/wave_grid_shape.cpp
|
|
||||||
|
|
||||||
# Includes
|
# Includes
|
||||||
INCLUDES := -Isource -Isource/external
|
INCLUDES := -Isource -Isource/external
|
||||||
|
|
||||||
# Variables según el sistema operativo
|
# 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)
|
ifeq ($(OS),Windows_NT)
|
||||||
FixPath = $(subst /,\\,$1)
|
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 += -DWINDOWS_BUILD
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
CXXFLAGS_DEBUG += -DWINDOWS_BUILD
|
||||||
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
LDFLAGS += -Wl,--gc-sections -static-libstdc++ -static-libgcc \
|
||||||
RM := del /Q
|
-Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows \
|
||||||
|
-lmingw32 -lws2_32 -lSDL3 -lSDL3_ttf
|
||||||
|
RMFILE := del /Q
|
||||||
|
RMDIR := rmdir /S /Q
|
||||||
MKDIR := mkdir
|
MKDIR := mkdir
|
||||||
else
|
else
|
||||||
FixPath = $1
|
FixPath = $1
|
||||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections
|
LDFLAGS += -lSDL3 -lSDL3_ttf
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG
|
|
||||||
LDFLAGS := -lSDL3
|
|
||||||
RMFILE := rm -f
|
RMFILE := rm -f
|
||||||
RMDIR := rm -rdf
|
RMDIR := rm -rf
|
||||||
MKDIR := mkdir -p
|
MKDIR := mkdir -p
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
CXXFLAGS += -DLINUX_BUILD
|
CXXFLAGS += -DLINUX_BUILD
|
||||||
LDFLAGS += -lGL
|
CXXFLAGS_DEBUG += -DLINUX_BUILD
|
||||||
endif
|
endif
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
CXXFLAGS += -Wno-deprecated -DMACOS_BUILD
|
CXXFLAGS += -DMACOS_BUILD -arch arm64
|
||||||
CXXFLAGS_DEBUG += -Wno-deprecated -DMACOS_BUILD
|
CXXFLAGS_DEBUG += -DMACOS_BUILD -arch arm64
|
||||||
LDFLAGS += -framework OpenGL
|
# Si quieres binarios universales:
|
||||||
# Configurar arquitectura (por defecto arm64, como en CMake)
|
# CXXFLAGS += -arch x86_64
|
||||||
CXXFLAGS += -arch arm64
|
# CXXFLAGS_DEBUG += -arch x86_64
|
||||||
CXXFLAGS_DEBUG += -arch arm64
|
# Y frameworks si hacen falta:
|
||||||
|
# LDFLAGS += -framework Cocoa -framework IOKit
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
# Reglas para herramienta de empaquetado y resources.pack
|
# Reglas para herramienta de empaquetado y resources.pack
|
||||||
$(PACK_TOOL): $(PACK_SOURCES)
|
$(PACK_TOOL): $(PACK_SOURCES)
|
||||||
@echo "Compilando herramienta de empaquetado..."
|
@echo "Compilando herramienta de empaquetado..."
|
||||||
@@ -108,11 +113,21 @@ $(PACK_TOOL): $(PACK_SOURCES)
|
|||||||
|
|
||||||
pack_tool: $(PACK_TOOL)
|
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/..."
|
@echo "Generando resources.pack desde directorio data/..."
|
||||||
$(PACK_TOOL) data resources.pack
|
$(PACK_TOOL) data resources.pack
|
||||||
@echo "✓ resources.pack generado exitosamente"
|
@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
|
# Reglas para compilación
|
||||||
windows:
|
windows:
|
||||||
@echo off
|
@echo off
|
||||||
@@ -131,20 +146,20 @@ windows_debug:
|
|||||||
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe"
|
@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"
|
$(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)"
|
@echo "Creando release para Windows - Version: $(VERSION)"
|
||||||
|
|
||||||
# Crea carpeta temporal 'RELEASE_FOLDER'
|
# Crea carpeta temporal 'RELEASE_FOLDER'
|
||||||
@rm -rf "$(RELEASE_FOLDER)"
|
@if exist "$(RELEASE_FOLDER)" rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||||
@mkdir -p "$(RELEASE_FOLDER)"
|
@mkdir "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia el archivo 'resources.pack'
|
# 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
|
# Copia los ficheros que estan en la raíz del proyecto
|
||||||
@cp -f "LICENSE" "$(RELEASE_FOLDER)/" 2>/dev/null || echo "LICENSE not found (optional)"
|
@copy /Y "LICENSE" "$(RELEASE_FOLDER)\" >nul 2>&1 || echo LICENSE not found (optional)
|
||||||
@cp -f "README.md" "$(RELEASE_FOLDER)/"
|
@copy /Y "README.md" "$(RELEASE_FOLDER)\" >nul
|
||||||
@cp -f release/*.dll "$(RELEASE_FOLDER)/" 2>/dev/null || echo "No DLL files found (optional)"
|
@copy /Y release\dll\*.dll "$(RELEASE_FOLDER)\" >nul 2>&1 || echo DLLs copied successfully
|
||||||
|
|
||||||
# Compila
|
# Compila
|
||||||
@windres release/vibe3.rc -O coff -o $(RESOURCE_FILE)
|
@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
|
@strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
|
||||||
|
|
||||||
# Crea el fichero .zip
|
# 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"
|
@powershell.exe -Command "Compress-Archive -Path '$(RELEASE_FOLDER)/*' -DestinationPath '$(WINDOWS_RELEASE)' -Force"
|
||||||
@echo "Release creado: $(WINDOWS_RELEASE)"
|
@echo "Release creado: $(WINDOWS_RELEASE)"
|
||||||
|
|
||||||
# Elimina la carpeta temporal 'RELEASE_FOLDER'
|
# Elimina la carpeta temporal 'RELEASE_FOLDER'
|
||||||
@rm -rf "$(RELEASE_FOLDER)"
|
@rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
@echo "Compilando para macOS: $(TARGET_NAME)"
|
@echo "Compilando para macOS: $(TARGET_NAME)"
|
||||||
@@ -167,15 +182,24 @@ macos_debug:
|
|||||||
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
|
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)"
|
@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
|
# Elimina datos de compilaciones anteriores
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
$(RMDIR) Frameworks
|
$(RMDIR) Frameworks
|
||||||
$(RMFILE) tmp.dmg
|
|
||||||
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
|
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
|
||||||
$(RMFILE) "$(MACOS_APPLE_SILICON_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
|
# 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/Frameworks"
|
||||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS"
|
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS"
|
||||||
@@ -185,40 +209,76 @@ macos_release: resources.pack
|
|||||||
# Copia carpetas y ficheros
|
# Copia carpetas y ficheros
|
||||||
cp resources.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
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.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.xcframework Frameworks
|
||||||
|
cp -R release/frameworks/SDL3_ttf.xcframework Frameworks
|
||||||
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
cp release/Info.plist "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents"
|
cp release/Info.plist "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Crea enlaces
|
# NOTA: create-dmg crea automáticamente el enlace a /Applications con --app-drop-link
|
||||||
ln -s /Applications "$(RELEASE_FOLDER)"/Applications
|
# No es necesario crearlo manualmente aquí
|
||||||
|
|
||||||
# Compila la versión para procesadores Intel
|
# Compila la versión para procesadores Intel
|
||||||
ifdef ENABLE_MACOS_X86_64
|
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
|
# Firma la aplicación
|
||||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||||
|
|
||||||
# Empaqueta el .dmg de la versión Intel
|
# Empaqueta el .dmg de la versión Intel con create-dmg
|
||||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
@echo "Creando DMG Intel con iconos de 96x96..."
|
||||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
|
@create-dmg \
|
||||||
$(RMFILE) tmp.dmg
|
--volname "$(APP_NAME)" \
|
||||||
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
|
--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
|
endif
|
||||||
|
|
||||||
# Compila la versión para procesadores Apple Silicon
|
# 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
|
# Firma la aplicación
|
||||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||||
|
|
||||||
# Empaqueta el .dmg de la versión Apple Silicon
|
# Empaqueta el .dmg de la versión Apple Silicon con create-dmg
|
||||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
@echo "Creando DMG Apple Silicon con iconos de 96x96..."
|
||||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
|
@create-dmg \
|
||||||
$(RMFILE) tmp.dmg
|
--volname "$(APP_NAME)" \
|
||||||
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
|
--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
|
# Elimina las carpetas temporales
|
||||||
$(RMDIR) Frameworks
|
$(RMDIR) Frameworks
|
||||||
@@ -233,7 +293,7 @@ linux_debug:
|
|||||||
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
|
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)"
|
@echo "Creando release para Linux - Version: $(VERSION)"
|
||||||
# Elimina carpetas previas
|
# Elimina carpetas previas
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
@@ -258,7 +318,7 @@ linux_release: resources.pack
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
linux_release_desktop: resources.pack
|
linux_release_desktop: force_resource_pack
|
||||||
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
||||||
# Elimina carpetas previas
|
# Elimina carpetas previas
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
@@ -362,7 +422,7 @@ raspi_debug:
|
|||||||
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
|
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)"
|
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
|
||||||
# Elimina carpetas previas
|
# Elimina carpetas previas
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
@@ -387,7 +447,7 @@ raspi_release: resources.pack
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
anbernic: resources.pack
|
anbernic: force_resource_pack
|
||||||
@echo "Compilando para Anbernic: $(TARGET_NAME)"
|
@echo "Compilando para Anbernic: $(TARGET_NAME)"
|
||||||
# Elimina carpetas previas
|
# Elimina carpetas previas
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
|
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
|
||||||
@@ -426,6 +486,7 @@ help:
|
|||||||
@echo " macos_release - Crear release completo para macOS (.dmg)"
|
@echo " macos_release - Crear release completo para macOS (.dmg)"
|
||||||
@echo " pack_tool - Compilar herramienta de empaquetado"
|
@echo " pack_tool - Compilar herramienta de empaquetado"
|
||||||
@echo " resources.pack - Generar pack de recursos desde data/"
|
@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 " show_version - Mostrar version actual ($(VERSION))"
|
||||||
@echo " help - Mostrar esta ayuda"
|
@echo " help - Mostrar esta ayuda"
|
||||||
|
|
||||||
|
|||||||
218
REFACTOR_PLAN.md
218
REFACTOR_PLAN.md
@@ -1,218 +0,0 @@
|
|||||||
# Plan de Refactorización - ViBe3 Physics Engine
|
|
||||||
|
|
||||||
## Objetivo
|
|
||||||
Aplicar el **Principio de Responsabilidad Única (SRP)** al motor Engine para:
|
|
||||||
- Mejorar mantenibilidad del código
|
|
||||||
- Facilitar extensión de funcionalidades
|
|
||||||
- Reducir acoplamiento entre sistemas
|
|
||||||
- Hacer el código más testeable
|
|
||||||
|
|
||||||
## Métricas Iniciales (Pre-refactorización)
|
|
||||||
- **engine.cpp**: 2341 líneas
|
|
||||||
- **engine.h**: 196 líneas con 40+ miembros privados
|
|
||||||
- **Responsabilidades mezcladas**: 7 subsistemas en una sola clase
|
|
||||||
|
|
||||||
## Progreso de Refactorización
|
|
||||||
|
|
||||||
### ✅ FASE 1: InputHandler (COMPLETADA)
|
|
||||||
**Fecha**: 10/01/2025
|
|
||||||
**Commit**: (pendiente)
|
|
||||||
|
|
||||||
**Impacto**: ~430 líneas extraídas del `handleEvents()`
|
|
||||||
|
|
||||||
**Archivos creados**:
|
|
||||||
- `source/input/input_handler.h`
|
|
||||||
- `source/input/input_handler.cpp`
|
|
||||||
|
|
||||||
**Métodos públicos agregados a Engine (24 total)**:
|
|
||||||
```cpp
|
|
||||||
// Gravedad y física
|
|
||||||
void pushBallsAwayFromGravity();
|
|
||||||
void handleGravityToggle();
|
|
||||||
void handleGravityDirectionChange(GravityDirection, const char*);
|
|
||||||
|
|
||||||
// Display y depuración
|
|
||||||
void toggleVSync();
|
|
||||||
void toggleDebug();
|
|
||||||
|
|
||||||
// Figuras 3D
|
|
||||||
void toggleShapeMode();
|
|
||||||
void activateShape(ShapeType, const char*);
|
|
||||||
void handleShapeScaleChange(bool);
|
|
||||||
void resetShapeScale();
|
|
||||||
void toggleDepthZoom();
|
|
||||||
|
|
||||||
// Temas de colores
|
|
||||||
void cycleTheme(bool);
|
|
||||||
void switchThemeByNumpad(int);
|
|
||||||
void toggleThemePage();
|
|
||||||
void pauseDynamicTheme();
|
|
||||||
|
|
||||||
// Sprites/Texturas
|
|
||||||
void switchTexture();
|
|
||||||
|
|
||||||
// Escenarios
|
|
||||||
void changeScenario(int, const char*);
|
|
||||||
|
|
||||||
// Zoom y fullscreen
|
|
||||||
void handleZoomIn();
|
|
||||||
void handleZoomOut();
|
|
||||||
void toggleFullscreen();
|
|
||||||
void toggleRealFullscreen();
|
|
||||||
void toggleIntegerScaling();
|
|
||||||
|
|
||||||
// Modos de aplicación
|
|
||||||
void toggleDemoMode();
|
|
||||||
void toggleDemoLiteMode();
|
|
||||||
void toggleLogoMode();
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cambios internos**:
|
|
||||||
- Métodos internos renombrados con sufijo `Internal`:
|
|
||||||
- `toggleShapeMode()` → `toggleShapeModeInternal()`
|
|
||||||
- `activateShape()` → `activateShapeInternal()`
|
|
||||||
- `switchTexture()` → `switchTextureInternal()`
|
|
||||||
- Eliminado método `handleEvents()` (420 líneas)
|
|
||||||
- Bucle `run()` simplificado a 12 líneas
|
|
||||||
|
|
||||||
**Beneficios**:
|
|
||||||
- ✅ Engine desacoplado de eventos SDL
|
|
||||||
- ✅ InputHandler stateless (fácilmente testeable)
|
|
||||||
- ✅ Clara separación entre detección de input y ejecución de lógica
|
|
||||||
- ✅ Compilación exitosa sin errores
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 FASE 2: SceneManager (PENDIENTE)
|
|
||||||
**Impacto estimado**: ~500 líneas + `std::vector<Ball>` movido
|
|
||||||
|
|
||||||
**Responsabilidad**: Crear, actualizar y gestionar todas las `Ball`
|
|
||||||
|
|
||||||
**Miembros a mover**:
|
|
||||||
- `std::vector<std::unique_ptr<Ball>> balls_`
|
|
||||||
- `GravityDirection current_gravity_`
|
|
||||||
- `int scenario_`
|
|
||||||
|
|
||||||
**Métodos a mover**:
|
|
||||||
- `initBalls()`
|
|
||||||
- `pushBallsAwayFromGravity()`
|
|
||||||
- `switchBallsGravity()`
|
|
||||||
- `enableBallsGravityIfDisabled()`
|
|
||||||
- `forceBallsGravityOn() / Off()`
|
|
||||||
- `changeGravityDirection()`
|
|
||||||
- `updateBallSizes()`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 FASE 3: UIManager (PENDIENTE)
|
|
||||||
**Impacto estimado**: ~300 líneas + rendering de texto movido
|
|
||||||
|
|
||||||
**Responsabilidad**: Renderizar y actualizar interfaz de usuario
|
|
||||||
|
|
||||||
**Miembros a mover**:
|
|
||||||
- `Notifier notifier_`
|
|
||||||
- `TextRenderer text_renderer_debug_`
|
|
||||||
- `bool show_debug_`
|
|
||||||
- Variables FPS (`fps_frame_count_`, `fps_current_`, `fps_text_`, `vsync_text_`)
|
|
||||||
|
|
||||||
**Métodos a mover**:
|
|
||||||
- `showNotificationForAction()`
|
|
||||||
- Renderizado de FPS, debug info, gravedad, tema, modo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 FASE 4: StateManager (PENDIENTE)
|
|
||||||
**Impacto estimado**: ~600 líneas de lógica compleja
|
|
||||||
|
|
||||||
**Responsabilidad**: Gestionar máquina de estados (DEMO/LOGO/SANDBOX)
|
|
||||||
|
|
||||||
**Miembros a mover**:
|
|
||||||
- `AppMode current_app_mode_, previous_app_mode_`
|
|
||||||
- Variables DEMO (`demo_timer_`, `demo_next_action_time_`)
|
|
||||||
- Variables LOGO (todas las relacionadas con logo mode)
|
|
||||||
|
|
||||||
**Métodos a mover**:
|
|
||||||
- `setState()`
|
|
||||||
- `updateDemoMode()`
|
|
||||||
- `performDemoAction()`
|
|
||||||
- `randomizeOnDemoStart()`
|
|
||||||
- `enterLogoMode() / exitLogoMode()`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 FASE 5: ShapeManager (PENDIENTE)
|
|
||||||
**Impacto estimado**: ~400 líneas + lógica de shapes
|
|
||||||
|
|
||||||
**Responsabilidad**: Crear, actualizar y renderizar figuras 3D polimórficas
|
|
||||||
|
|
||||||
**Miembros a mover**:
|
|
||||||
- `SimulationMode current_mode_`
|
|
||||||
- `ShapeType current_shape_type_, last_shape_type_`
|
|
||||||
- `std::unique_ptr<Shape> active_shape_`
|
|
||||||
- `float shape_scale_factor_`
|
|
||||||
- `bool depth_zoom_enabled_`
|
|
||||||
|
|
||||||
**Métodos a mover**:
|
|
||||||
- `toggleShapeModeInternal()`
|
|
||||||
- `activateShapeInternal()`
|
|
||||||
- `updateShape()`
|
|
||||||
- `generateShape()`
|
|
||||||
- `clampShapeScale()`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 FASE 6: Limpieza y Consolidación Final (PENDIENTE)
|
|
||||||
**Impacto esperado**: Engine reducido a ~400 líneas (coordinador)
|
|
||||||
|
|
||||||
**Tareas**:
|
|
||||||
1. Limpiar `engine.h` / `engine.cpp` de código legacy
|
|
||||||
2. Verificar que todos los sistemas están correctamente integrados
|
|
||||||
3. Documentar interfaz pública de Engine
|
|
||||||
4. Actualizar `CLAUDE.md` con nueva arquitectura
|
|
||||||
5. Verificar compilación y funcionamiento completo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Arquitectura Final Esperada
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
class Engine {
|
|
||||||
private:
|
|
||||||
// SDL Core
|
|
||||||
SDL_Window* window_;
|
|
||||||
SDL_Renderer* renderer_;
|
|
||||||
|
|
||||||
// Componentes (composición)
|
|
||||||
std::unique_ptr<InputHandler> input_handler_;
|
|
||||||
std::unique_ptr<SceneManager> scene_manager_;
|
|
||||||
std::unique_ptr<UIManager> ui_manager_;
|
|
||||||
std::unique_ptr<StateManager> state_manager_;
|
|
||||||
std::unique_ptr<ShapeManager> shape_manager_;
|
|
||||||
std::unique_ptr<ThemeManager> theme_manager_;
|
|
||||||
|
|
||||||
// Estado mínimo
|
|
||||||
bool should_exit_;
|
|
||||||
float delta_time_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void run() {
|
|
||||||
while (!should_exit_) {
|
|
||||||
calculateDeltaTime();
|
|
||||||
input_handler_->process(*this);
|
|
||||||
update();
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notas
|
|
||||||
- Cada fase incluye su propio **commit atómico**
|
|
||||||
- Las fases son **secuenciales** (cada una depende de la anterior)
|
|
||||||
- Se preserva **100% de funcionalidad** en cada fase
|
|
||||||
- Compilación verificada después de cada commit
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Documento de seguimiento para refactorización ViBe3 Physics*
|
|
||||||
*Última actualización: 2025-01-10 - Fase 1 completada*
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
# Engine Refactoring Summary
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Successful refactoring of `engine.cpp` (2341 → 1759 lines, -25%) following Single Responsibility Principle using facade/delegation pattern.
|
|
||||||
|
|
||||||
## Completed Phases
|
|
||||||
|
|
||||||
### Phase 1: InputHandler ✅
|
|
||||||
- **Lines extracted:** ~420 lines
|
|
||||||
- **Files created:**
|
|
||||||
- `source/input/input_handler.h`
|
|
||||||
- `source/input/input_handler.cpp`
|
|
||||||
- **Responsibility:** SDL event handling, keyboard/mouse input processing
|
|
||||||
- **Commit:** 7629c14
|
|
||||||
|
|
||||||
### Phase 2: SceneManager ✅
|
|
||||||
- **Lines extracted:** ~500 lines
|
|
||||||
- **Files created:**
|
|
||||||
- `source/scene/scene_manager.h`
|
|
||||||
- `source/scene/scene_manager.cpp`
|
|
||||||
- **Responsibility:** Ball physics, collision detection, gravity management, scenarios
|
|
||||||
- **Commit:** 71aea6e
|
|
||||||
|
|
||||||
### Phase 3: UIManager ✅
|
|
||||||
- **Lines extracted:** ~300 lines
|
|
||||||
- **Files created:**
|
|
||||||
- `source/ui/ui_manager.h`
|
|
||||||
- `source/ui/ui_manager.cpp`
|
|
||||||
- **Responsibility:** HUD rendering, FPS display, debug info, notifications
|
|
||||||
- **Commit:** e655c64
|
|
||||||
- **Note:** Moved AppMode enum to defines.h for global access
|
|
||||||
|
|
||||||
### Phase 4: StateManager ✅
|
|
||||||
- **Approach:** Facade/delegation pattern
|
|
||||||
- **Files created:**
|
|
||||||
- `source/state/state_manager.h`
|
|
||||||
- `source/state/state_manager.cpp`
|
|
||||||
- **Responsibility:** Application state machine (SANDBOX/DEMO/DEMO_LITE/LOGO)
|
|
||||||
- **Commits:** e2a60e4, e4636c8
|
|
||||||
- **Note:** StateManager maintains state, Engine keeps complex logic temporarily
|
|
||||||
|
|
||||||
### Phase 5: ShapeManager ✅
|
|
||||||
- **Approach:** Facade pattern (structure only)
|
|
||||||
- **Files created:**
|
|
||||||
- `source/shapes_mgr/shape_manager.h`
|
|
||||||
- `source/shapes_mgr/shape_manager.cpp`
|
|
||||||
- **Responsibility:** 3D shape management (sphere, cube, PNG shapes, etc.)
|
|
||||||
- **Commit:** 8be4c55
|
|
||||||
- **Note:** Stub implementation, full migration deferred
|
|
||||||
|
|
||||||
### Phase 6: Consolidation ✅
|
|
||||||
- **Result:** Engine acts as coordinator between components
|
|
||||||
- **Final metrics:**
|
|
||||||
- engine.cpp: 2341 → 1759 lines (-582 lines, -25%)
|
|
||||||
- engine.h: 237 → 205 lines (-32 lines, -13%)
|
|
||||||
|
|
||||||
## Architecture Pattern
|
|
||||||
|
|
||||||
**Facade/Delegation Hybrid:**
|
|
||||||
- Components maintain state and provide interfaces
|
|
||||||
- Engine delegates calls to components
|
|
||||||
- Complex logic remains in Engine temporarily (pragmatic approach)
|
|
||||||
- Allows future incremental migration without breaking functionality
|
|
||||||
|
|
||||||
## Component Composition
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
class Engine {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<InputHandler> input_handler_; // Input management
|
|
||||||
std::unique_ptr<SceneManager> scene_manager_; // Ball physics
|
|
||||||
std::unique_ptr<ShapeManager> shape_manager_; // 3D shapes
|
|
||||||
std::unique_ptr<StateManager> state_manager_; // App modes
|
|
||||||
std::unique_ptr<UIManager> ui_manager_; // UI/HUD
|
|
||||||
std::unique_ptr<ThemeManager> theme_manager_; // Color themes (pre-existing)
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Decisions
|
|
||||||
|
|
||||||
1. **Token Budget Constraint:** After Phase 3, pivoted from "full migration" to "facade pattern" to stay within 200k token budget
|
|
||||||
|
|
||||||
2. **Incremental Refactoring:** Each phase:
|
|
||||||
- Has atomic commit
|
|
||||||
- Compiles successfully
|
|
||||||
- Preserves 100% functionality
|
|
||||||
- Can be reviewed independently
|
|
||||||
|
|
||||||
3. **Pragmatic Approach:** Prioritized:
|
|
||||||
- Structural improvements over perfection
|
|
||||||
- Compilation success over complete migration
|
|
||||||
- Interface clarity over implementation relocation
|
|
||||||
|
|
||||||
## Benefits Achieved
|
|
||||||
|
|
||||||
✅ **Separation of Concerns:** Clear component boundaries
|
|
||||||
✅ **Testability:** Components can be unit tested independently
|
|
||||||
✅ **Maintainability:** Smaller, focused files easier to navigate
|
|
||||||
✅ **Extensibility:** New features can target specific components
|
|
||||||
✅ **Readability:** Engine.cpp 25% smaller, easier to understand
|
|
||||||
✅ **Compilation Speed:** Smaller translation units compile faster
|
|
||||||
|
|
||||||
## Future Work
|
|
||||||
|
|
||||||
### Deferred Migrations (Optional)
|
|
||||||
1. Complete StateManager logic migration (~600 lines)
|
|
||||||
2. Complete ShapeManager logic migration (~400 lines)
|
|
||||||
3. Remove duplicate state members from Engine
|
|
||||||
4. Extract ThemeManager to separate component (currently inline)
|
|
||||||
|
|
||||||
### Architectural Improvements
|
|
||||||
1. Consider event bus for component communication
|
|
||||||
2. Add observer pattern for state change notifications
|
|
||||||
3. Implement proper dependency injection
|
|
||||||
4. Add component lifecycle management
|
|
||||||
|
|
||||||
## Metrics
|
|
||||||
|
|
||||||
| Metric | Before | After | Change |
|
|
||||||
|--------|--------|-------|--------|
|
|
||||||
| engine.cpp | 2341 lines | 1759 lines | -582 (-25%) |
|
|
||||||
| engine.h | 237 lines | 205 lines | -32 (-13%) |
|
|
||||||
| Components | 1 (Engine) | 6 (Engine + 5 managers) | +5 |
|
|
||||||
| Files | 2 | 12 | +10 |
|
|
||||||
| Separation of concerns | ❌ Monolithic | ✅ Modular | ✅ |
|
|
||||||
|
|
||||||
## Post-Refactor Bug Fix
|
|
||||||
|
|
||||||
### Critical Crash: Nullptr Dereference (Commit 0fe2efc)
|
|
||||||
|
|
||||||
**Problem Discovered:**
|
|
||||||
- Refactor compiled successfully but crashed immediately at runtime
|
|
||||||
- Stack trace: `UIManager::updatePhysicalWindowSize()` → `Engine::updatePhysicalWindowSize()` → `Engine::initialize()`
|
|
||||||
- Root cause: `Engine::initialize()` line 228 called `updatePhysicalWindowSize()` BEFORE creating `ui_manager_` at line 232
|
|
||||||
|
|
||||||
**Solution Implemented:**
|
|
||||||
```cpp
|
|
||||||
// BEFORE (crashed):
|
|
||||||
updatePhysicalWindowSize(); // Calls ui_manager_->updatePhysicalWindowSize() → nullptr dereference
|
|
||||||
ui_manager_ = std::make_unique<UIManager>();
|
|
||||||
|
|
||||||
// AFTER (fixed):
|
|
||||||
int window_w = 0, window_h = 0;
|
|
||||||
SDL_GetWindowSizeInPixels(window_, &window_w, &window_h);
|
|
||||||
physical_window_width_ = window_w;
|
|
||||||
physical_window_height_ = window_h;
|
|
||||||
ui_manager_ = std::make_unique<UIManager>();
|
|
||||||
ui_manager_->initialize(renderer_, theme_manager_.get(), physical_window_width_, physical_window_height_);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Additional Documentation:**
|
|
||||||
- Added comments to `engine.h` explaining pragmatic state duplication (Engine ↔ StateManager)
|
|
||||||
- Documented facade pattern stubs in `shape_manager.cpp` with rationale for each method
|
|
||||||
- Clarified future migration paths
|
|
||||||
|
|
||||||
**Verification:**
|
|
||||||
- ✅ Compilation successful
|
|
||||||
- ✅ Application runs without crashes
|
|
||||||
- ✅ All resources load correctly
|
|
||||||
- ✅ Initialization order corrected
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
All phases verified with:
|
|
||||||
- ✅ Successful compilation (CMake + MinGW)
|
|
||||||
- ✅ No linker errors
|
|
||||||
- ✅ All components initialized correctly
|
|
||||||
- ✅ Engine runs as coordinator
|
|
||||||
- ✅ No runtime crashes (post-fix verification)
|
|
||||||
- ✅ Application executes successfully with all features functional
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Refactoring completed successfully within constraints:
|
|
||||||
- ✅ All 6 phases done
|
|
||||||
- ✅ 25% code reduction in engine.cpp
|
|
||||||
- ✅ Clean component architecture
|
|
||||||
- ✅ 100% functional preservation
|
|
||||||
- ✅ Critical crash bug fixed (commit 0fe2efc)
|
|
||||||
- ✅ Comprehensive documentation added
|
|
||||||
- ✅ Token budget respected (~65k / 200k used)
|
|
||||||
|
|
||||||
**Status:** COMPLETED AND VERIFIED ✅
|
|
||||||
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>
|
<key>CSResourcesFileMapped</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.15</string>
|
<string>12.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<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 <stdlib.h> // for rand
|
||||||
|
|
||||||
#include <cmath> // for fabs
|
#include <cmath> // for fabs
|
||||||
|
|
||||||
#include "defines.h" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
#include "defines.hpp" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Función auxiliar para generar pérdida aleatoria en rebotes
|
// Función auxiliar para generar pérdida aleatoria en rebotes
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
#include <memory> // for shared_ptr, unique_ptr
|
#include <memory> // for shared_ptr, unique_ptr
|
||||||
|
|
||||||
#include "defines.h" // for Color
|
#include "defines.hpp" // for Color
|
||||||
#include "external/sprite.h" // for Sprite
|
#include "external/sprite.hpp" // for Sprite
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
class Ball {
|
class Ball {
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "boid_manager.h"
|
#include "boid_manager.hpp"
|
||||||
|
|
||||||
#include <algorithm> // for std::min, std::max
|
#include <algorithm> // for std::min, std::max
|
||||||
#include <cmath> // for sqrt, atan2
|
#include <cmath> // for sqrt, atan2
|
||||||
|
|
||||||
#include "../ball.h" // for Ball
|
#include "ball.hpp" // for Ball
|
||||||
#include "../engine.h" // for Engine (si se necesita)
|
#include "engine.hpp" // for Engine (si se necesita)
|
||||||
#include "../scene/scene_manager.h" // for SceneManager
|
#include "scene/scene_manager.hpp" // for SceneManager
|
||||||
#include "../state/state_manager.h" // for StateManager
|
#include "state/state_manager.hpp" // for StateManager
|
||||||
#include "../ui/ui_manager.h" // for UIManager
|
#include "ui/ui_manager.hpp" // for UIManager
|
||||||
|
|
||||||
BoidManager::BoidManager()
|
BoidManager::BoidManager()
|
||||||
: engine_(nullptr)
|
: engine_(nullptr)
|
||||||
@@ -17,7 +17,18 @@ BoidManager::BoidManager()
|
|||||||
, screen_width_(0)
|
, screen_width_(0)
|
||||||
, screen_height_(0)
|
, screen_height_(0)
|
||||||
, boids_active_(false)
|
, boids_active_(false)
|
||||||
, spatial_grid_(800, 600, BOID_GRID_CELL_SIZE) { // Tamaño por defecto, se actualiza en initialize()
|
, 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() {
|
BoidManager::~BoidManager() {
|
||||||
@@ -57,9 +68,9 @@ void BoidManager::activateBoids() {
|
|||||||
float vx, vy;
|
float vx, vy;
|
||||||
ball->getVelocity(vx, vy);
|
ball->getVelocity(vx, vy);
|
||||||
if (vx == 0.0f && vy == 0.0f) {
|
if (vx == 0.0f && vy == 0.0f) {
|
||||||
// Velocidad aleatoria entre -1 y 1
|
// Velocidad aleatoria entre -60 y +60 px/s (time-based)
|
||||||
vx = (rand() % 200 - 100) / 100.0f;
|
vx = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||||
vy = (rand() % 200 - 100) / 100.0f;
|
vy = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||||
ball->setVelocity(vx, vy);
|
ball->setVelocity(vx, vy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,14 +129,14 @@ void BoidManager::update(float delta_time) {
|
|||||||
limitSpeed(ball.get());
|
limitSpeed(ball.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar posiciones con velocidades resultantes
|
// Actualizar posiciones con velocidades resultantes (time-based)
|
||||||
for (auto& ball : balls) {
|
for (auto& ball : balls) {
|
||||||
float vx, vy;
|
float vx, vy;
|
||||||
ball->getVelocity(vx, vy);
|
ball->getVelocity(vx, vy);
|
||||||
|
|
||||||
SDL_FRect pos = ball->getPosition();
|
SDL_FRect pos = ball->getPosition();
|
||||||
pos.x += vx;
|
pos.x += vx * delta_time; // time-based
|
||||||
pos.y += vy;
|
pos.y += vy * delta_time;
|
||||||
|
|
||||||
ball->setPosition(pos.x, pos.y);
|
ball->setPosition(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
@@ -146,7 +157,7 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
|||||||
float center_y = pos.y + pos.h / 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))
|
// 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, BOID_SEPARATION_RADIUS);
|
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, separation_radius_);
|
||||||
|
|
||||||
for (Ball* other : neighbors) {
|
for (Ball* other : neighbors) {
|
||||||
if (other == boid) continue; // Ignorar a sí mismo
|
if (other == boid) continue; // Ignorar a sí mismo
|
||||||
@@ -159,10 +170,10 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
|||||||
float dy = center_y - other_y;
|
float dy = center_y - other_y;
|
||||||
float distance = std::sqrt(dx * dx + dy * dy);
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
if (distance > 0.0f && distance < BOID_SEPARATION_RADIUS) {
|
if (distance > 0.0f && distance < separation_radius_) {
|
||||||
// FASE 1.3: Separación más fuerte cuando más cerca (inversamente proporcional a distancia)
|
// 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
|
// Fuerza proporcional a cercanía: 0% en radio máximo, 100% en colisión
|
||||||
float separation_strength = (BOID_SEPARATION_RADIUS - distance) / BOID_SEPARATION_RADIUS;
|
float separation_strength = (separation_radius_ - distance) / separation_radius_;
|
||||||
steer_x += (dx / distance) * separation_strength;
|
steer_x += (dx / distance) * separation_strength;
|
||||||
steer_y += (dy / distance) * separation_strength;
|
steer_y += (dy / distance) * separation_strength;
|
||||||
count++;
|
count++;
|
||||||
@@ -177,8 +188,8 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
|||||||
// Aplicar fuerza de separación
|
// Aplicar fuerza de separación
|
||||||
float vx, vy;
|
float vx, vy;
|
||||||
boid->getVelocity(vx, vy);
|
boid->getVelocity(vx, vy);
|
||||||
vx += steer_x * BOID_SEPARATION_WEIGHT * delta_time;
|
vx += steer_x * separation_weight_ * delta_time;
|
||||||
vy += steer_y * BOID_SEPARATION_WEIGHT * delta_time;
|
vy += steer_y * separation_weight_ * delta_time;
|
||||||
boid->setVelocity(vx, vy);
|
boid->setVelocity(vx, vy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +205,7 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
|||||||
float center_y = pos.y + pos.h / 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))
|
// 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, BOID_ALIGNMENT_RADIUS);
|
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, alignment_radius_);
|
||||||
|
|
||||||
for (Ball* other : neighbors) {
|
for (Ball* other : neighbors) {
|
||||||
if (other == boid) continue;
|
if (other == boid) continue;
|
||||||
@@ -207,7 +218,7 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
|||||||
float dy = center_y - other_y;
|
float dy = center_y - other_y;
|
||||||
float distance = std::sqrt(dx * dx + dy * dy);
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
if (distance < BOID_ALIGNMENT_RADIUS) {
|
if (distance < alignment_radius_) {
|
||||||
float other_vx, other_vy;
|
float other_vx, other_vy;
|
||||||
other->getVelocity(other_vx, other_vy);
|
other->getVelocity(other_vx, other_vy);
|
||||||
avg_vx += other_vx;
|
avg_vx += other_vx;
|
||||||
@@ -224,14 +235,14 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
|||||||
// Steering hacia la velocidad promedio
|
// Steering hacia la velocidad promedio
|
||||||
float vx, vy;
|
float vx, vy;
|
||||||
boid->getVelocity(vx, vy);
|
boid->getVelocity(vx, vy);
|
||||||
float steer_x = (avg_vx - vx) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
float steer_x = (avg_vx - vx) * alignment_weight_ * delta_time;
|
||||||
float steer_y = (avg_vy - vy) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
float steer_y = (avg_vy - vy) * alignment_weight_ * delta_time;
|
||||||
|
|
||||||
// Limitar fuerza máxima de steering
|
// Limitar fuerza máxima de steering
|
||||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||||
if (steer_mag > BOID_MAX_FORCE) {
|
if (steer_mag > max_force_) {
|
||||||
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
steer_x = (steer_x / steer_mag) * max_force_;
|
||||||
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
steer_y = (steer_y / steer_mag) * max_force_;
|
||||||
}
|
}
|
||||||
|
|
||||||
vx += steer_x;
|
vx += steer_x;
|
||||||
@@ -251,7 +262,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
|||||||
float center_y = pos.y + pos.h / 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))
|
// 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, BOID_COHESION_RADIUS);
|
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, cohesion_radius_);
|
||||||
|
|
||||||
for (Ball* other : neighbors) {
|
for (Ball* other : neighbors) {
|
||||||
if (other == boid) continue;
|
if (other == boid) continue;
|
||||||
@@ -264,7 +275,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
|||||||
float dy = center_y - other_y;
|
float dy = center_y - other_y;
|
||||||
float distance = std::sqrt(dx * dx + dy * dy);
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
if (distance < BOID_COHESION_RADIUS) {
|
if (distance < cohesion_radius_) {
|
||||||
center_of_mass_x += other_x;
|
center_of_mass_x += other_x;
|
||||||
center_of_mass_y += other_y;
|
center_of_mass_y += other_y;
|
||||||
count++;
|
count++;
|
||||||
@@ -284,14 +295,14 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
|||||||
// Solo aplicar si hay distancia al centro (evitar división por cero)
|
// Solo aplicar si hay distancia al centro (evitar división por cero)
|
||||||
if (distance_to_center > 0.1f) {
|
if (distance_to_center > 0.1f) {
|
||||||
// Normalizar vector dirección (fuerza independiente de distancia)
|
// Normalizar vector dirección (fuerza independiente de distancia)
|
||||||
float steer_x = (dx_to_center / distance_to_center) * BOID_COHESION_WEIGHT * delta_time;
|
float steer_x = (dx_to_center / distance_to_center) * cohesion_weight_ * delta_time;
|
||||||
float steer_y = (dy_to_center / distance_to_center) * BOID_COHESION_WEIGHT * delta_time;
|
float steer_y = (dy_to_center / distance_to_center) * cohesion_weight_ * delta_time;
|
||||||
|
|
||||||
// Limitar fuerza máxima de steering
|
// Limitar fuerza máxima de steering
|
||||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||||
if (steer_mag > BOID_MAX_FORCE) {
|
if (steer_mag > max_force_) {
|
||||||
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
steer_x = (steer_x / steer_mag) * max_force_;
|
||||||
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
steer_y = (steer_y / steer_mag) * max_force_;
|
||||||
}
|
}
|
||||||
|
|
||||||
float vx, vy;
|
float vx, vy;
|
||||||
@@ -304,32 +315,69 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BoidManager::applyBoundaries(Ball* boid) {
|
void BoidManager::applyBoundaries(Ball* boid) {
|
||||||
// Mantener boids dentro de los límites de la pantalla
|
// NUEVA IMPLEMENTACIÓN: Bordes como obstáculos (repulsión en lugar de wrapping)
|
||||||
// Comportamiento "wrapping" (teletransporte al otro lado)
|
// Cuando un boid se acerca a un borde, se aplica una fuerza alejándolo
|
||||||
SDL_FRect pos = boid->getPosition();
|
SDL_FRect pos = boid->getPosition();
|
||||||
float center_x = pos.x + pos.w / 2.0f;
|
float center_x = pos.x + pos.w / 2.0f;
|
||||||
float center_y = pos.y + pos.h / 2.0f;
|
float center_y = pos.y + pos.h / 2.0f;
|
||||||
|
|
||||||
bool wrapped = false;
|
float steer_x = 0.0f;
|
||||||
|
float steer_y = 0.0f;
|
||||||
|
|
||||||
if (center_x < 0) {
|
// Borde izquierdo (x < boundary_margin_)
|
||||||
pos.x = screen_width_ - pos.w / 2.0f;
|
if (center_x < boundary_margin_) {
|
||||||
wrapped = true;
|
float distance = center_x; // Distancia al borde (0 = colisión)
|
||||||
} else if (center_x > screen_width_) {
|
if (distance < boundary_margin_) {
|
||||||
pos.x = -pos.w / 2.0f;
|
// Fuerza proporcional a cercanía: 0% en margen, 100% en colisión
|
||||||
wrapped = true;
|
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||||
|
steer_x += repulsion_strength; // Empujar hacia la derecha
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (center_y < 0) {
|
// Borde derecho (x > screen_width_ - boundary_margin_)
|
||||||
pos.y = screen_height_ - pos.h / 2.0f;
|
if (center_x > screen_width_ - boundary_margin_) {
|
||||||
wrapped = true;
|
float distance = screen_width_ - center_x;
|
||||||
} else if (center_y > screen_height_) {
|
if (distance < boundary_margin_) {
|
||||||
pos.y = -pos.h / 2.0f;
|
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||||
wrapped = true;
|
steer_x -= repulsion_strength; // Empujar hacia la izquierda
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapped) {
|
// Borde superior (y < boundary_margin_)
|
||||||
boid->setPosition(pos.x, pos.y);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,16 +389,16 @@ void BoidManager::limitSpeed(Ball* boid) {
|
|||||||
float speed = std::sqrt(vx * vx + vy * vy);
|
float speed = std::sqrt(vx * vx + vy * vy);
|
||||||
|
|
||||||
// Limitar velocidad máxima
|
// Limitar velocidad máxima
|
||||||
if (speed > BOID_MAX_SPEED) {
|
if (speed > max_speed_) {
|
||||||
vx = (vx / speed) * BOID_MAX_SPEED;
|
vx = (vx / speed) * max_speed_;
|
||||||
vy = (vy / speed) * BOID_MAX_SPEED;
|
vy = (vy / speed) * max_speed_;
|
||||||
boid->setVelocity(vx, vy);
|
boid->setVelocity(vx, vy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FASE 1.2: Aplicar velocidad mínima (evitar boids estáticos)
|
// FASE 1.2: Aplicar velocidad mínima (evitar boids estáticos)
|
||||||
if (speed > 0.0f && speed < BOID_MIN_SPEED) {
|
if (speed > 0.0f && speed < min_speed_) {
|
||||||
vx = (vx / speed) * BOID_MIN_SPEED;
|
vx = (vx / speed) * min_speed_;
|
||||||
vy = (vy / speed) * BOID_MIN_SPEED;
|
vy = (vy / speed) * min_speed_;
|
||||||
boid->setVelocity(vx, vy);
|
boid->setVelocity(vx, vy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include <cstddef> // for size_t
|
#include <cstddef> // for size_t
|
||||||
|
|
||||||
#include "../defines.h" // for SimulationMode, AppMode
|
#include "defines.hpp" // for SimulationMode, AppMode
|
||||||
#include "../spatial_grid.h" // for SpatialGrid
|
#include "spatial_grid.hpp" // for SpatialGrid
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Engine;
|
class Engine;
|
||||||
@@ -103,10 +103,24 @@ class BoidManager {
|
|||||||
// FASE 2: Grid reutilizable para búsquedas de vecinos
|
// FASE 2: Grid reutilizable para búsquedas de vecinos
|
||||||
SpatialGrid spatial_grid_;
|
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
|
// Métodos privados para las reglas de Reynolds
|
||||||
void applySeparation(Ball* boid, float delta_time);
|
void applySeparation(Ball* boid, float delta_time);
|
||||||
void applyAlignment(Ball* boid, float delta_time);
|
void applyAlignment(Ball* boid, float delta_time);
|
||||||
void applyCohesion(Ball* boid, float delta_time);
|
void applyCohesion(Ball* boid, float delta_time);
|
||||||
void applyBoundaries(Ball* boid); // Mantener boids dentro de pantalla
|
void applyBoundaries(Ball* boid); // Repulsión de bordes (ya no wrapping)
|
||||||
void limitSpeed(Ball* boid); // Limitar velocidad máxima
|
void limitSpeed(Ball* boid); // Limitar velocidad máxima
|
||||||
};
|
};
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
constexpr char WINDOW_CAPTION[] = "ViBe3 Physics (JailDesigner 2025)";
|
constexpr char WINDOW_CAPTION[] = "ViBe3 Physics (JailDesigner 2025)";
|
||||||
|
|
||||||
// Resolución por defecto (usada si no se especifica en CLI)
|
// 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_WIDTH = 1280; // 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_SCREEN_HEIGHT = 720; // 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_WINDOW_ZOOM = 1; // Zoom inicial de ventana (1x = sin zoom)
|
||||||
|
|
||||||
// Configuración de zoom dinámico de ventana
|
// Configuración de zoom dinámico de ventana
|
||||||
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
||||||
@@ -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²)
|
constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²)
|
||||||
|
|
||||||
// Configuración de interfaz
|
// 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)
|
constexpr float THEME_TRANSITION_DURATION = 0.5f; // Duración de transiciones LERP entre temas (segundos)
|
||||||
|
|
||||||
// Configuración de notificaciones (sistema Notifier)
|
// 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 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_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||||
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (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 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 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_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 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
|
// Configuración de pérdida aleatoria en rebotes
|
||||||
constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas
|
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)
|
// Escenarios de número de pelotas (teclas 1-8)
|
||||||
constexpr int BALL_COUNT_SCENARIOS[8] = {10, 50, 100, 500, 1000, 5000, 10000, 50000};
|
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
|
// Estructura para representar colores RGB
|
||||||
struct Color {
|
struct Color {
|
||||||
int r, g, b; // Componentes rojo, verde, azul (0-255)
|
int r, g, b; // Componentes rojo, verde, azul (0-255)
|
||||||
@@ -60,12 +68,12 @@ struct Color {
|
|||||||
|
|
||||||
// Estructura de tema de colores estático
|
// Estructura de tema de colores estático
|
||||||
struct ThemeColors {
|
struct ThemeColors {
|
||||||
const char* name_en; // Nombre en inglés (para debug)
|
const char* name_en; // Nombre en inglés (para debug)
|
||||||
const char* name_es; // Nombre en español (para display)
|
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
|
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_top_r, bg_top_g, bg_top_b;
|
||||||
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
||||||
std::vector<Color> ball_colors;
|
std::vector<Color> ball_colors;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Estructura para keyframe de tema dinámico
|
// 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)
|
// Enum para temas de colores (seleccionables con teclado numérico y Shift+Numpad)
|
||||||
// Todos los temas usan ahora sistema dinámico de keyframes
|
// Todos los temas usan ahora sistema dinámico de keyframes
|
||||||
enum class ColorTheme {
|
enum class ColorTheme {
|
||||||
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
||||||
OCEAN = 1, // Azules, turquesas, blancos (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)
|
NEON = 2, // Cian, magenta, verde lima, amarillo vibrante (estático: 1 keyframe)
|
||||||
FOREST = 3, // Verdes, marrones, amarillos otoño (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)
|
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)
|
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)
|
LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe)
|
||||||
CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (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)
|
EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe)
|
||||||
SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo)
|
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)
|
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)
|
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)
|
FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo)
|
||||||
AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s 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)
|
VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enum para tipo de figura 3D
|
// Enum para tipo de figura 3D
|
||||||
@@ -282,27 +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)
|
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)
|
// 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_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 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_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 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_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)
|
// Configuración de Modo BOIDS (comportamiento de enjambre)
|
||||||
// FASE 1.1 REVISADA: Parámetros ajustados tras detectar cohesión mal normalizada
|
// TIME-BASED CONVERSION (frame-based → time-based):
|
||||||
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
// - Radios: sin cambios (píxeles)
|
||||||
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
// - Velocidades (MAX_SPEED, MIN_SPEED): ×60 (px/frame → px/s)
|
||||||
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
// - Aceleraciones puras (SEPARATION, COHESION): ×60² = ×3600 (px/frame² → px/s²)
|
||||||
constexpr float BOID_SEPARATION_WEIGHT = 1.5f; // Peso de separación
|
// - Steering proporcional (ALIGNMENT): ×60 (proporcional a velocidad)
|
||||||
constexpr float BOID_ALIGNMENT_WEIGHT = 1.0f; // Peso de alineación
|
// - Límite velocidad (MAX_FORCE): ×60 (px/frame → px/s)
|
||||||
constexpr float BOID_COHESION_WEIGHT = 0.001f; // Peso de cohesión (MICRO - 1000x menor por falta de normalización)
|
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
||||||
constexpr float BOID_MAX_SPEED = 2.5f; // Velocidad máxima (píxeles/frame - REDUCIDA)
|
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
||||||
constexpr float BOID_MAX_FORCE = 0.05f; // Fuerza máxima de steering (REDUCIDA para evitar aceleración excesiva)
|
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
||||||
constexpr float BOID_MIN_SPEED = 0.3f; // Velocidad mínima (evita boids estáticos)
|
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)
|
// 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)
|
constexpr float BOID_GRID_CELL_SIZE = 100.0f; // Tamaño de celda del grid (píxeles)
|
||||||
// Debe ser ≥ BOID_COHESION_RADIUS para funcionar correctamente
|
// Debe ser ≥ BOID_COHESION_RADIUS para funcionar correctamente
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "engine.h"
|
#include "engine.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL_error.h> // for SDL_GetError
|
#include <SDL3/SDL_error.h> // for SDL_GetError
|
||||||
#include <SDL3/SDL_events.h> // for SDL_Event, SDL_PollEvent
|
#include <SDL3/SDL_events.h> // for SDL_Event, SDL_PollEvent
|
||||||
@@ -17,27 +17,29 @@
|
|||||||
#include <iostream> // for cout
|
#include <iostream> // for cout
|
||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
|
|
||||||
|
#include "resource_manager.hpp" // for ResourceManager
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h> // for GetModuleFileName
|
#include <windows.h> // for GetModuleFileName
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "ball.hpp" // for Ball
|
||||||
#include "external/mouse.h" // for Mouse namespace
|
#include "external/mouse.hpp" // for Mouse namespace
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.hpp" // for Texture
|
||||||
#include "shapes/atom_shape.h" // for AtomShape
|
#include "shapes/atom_shape.hpp" // for AtomShape
|
||||||
#include "shapes/cube_shape.h" // for CubeShape
|
#include "shapes/cube_shape.hpp" // for CubeShape
|
||||||
#include "shapes/cylinder_shape.h" // for CylinderShape
|
#include "shapes/cylinder_shape.hpp" // for CylinderShape
|
||||||
#include "shapes/helix_shape.h" // for HelixShape
|
#include "shapes/helix_shape.hpp" // for HelixShape
|
||||||
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
#include "shapes/icosahedron_shape.hpp" // for IcosahedronShape
|
||||||
#include "shapes/lissajous_shape.h" // for LissajousShape
|
#include "shapes/lissajous_shape.hpp" // for LissajousShape
|
||||||
#include "shapes/png_shape.h" // for PNGShape
|
#include "shapes/png_shape.hpp" // for PNGShape
|
||||||
#include "shapes/sphere_shape.h" // for SphereShape
|
#include "shapes/sphere_shape.hpp" // for SphereShape
|
||||||
#include "shapes/torus_shape.h" // for TorusShape
|
#include "shapes/torus_shape.hpp" // for TorusShape
|
||||||
|
|
||||||
// getExecutableDirectory() ya está definido en defines.h como inline
|
// getExecutableDirectory() ya está definido en defines.h como inline
|
||||||
|
|
||||||
// Implementación de métodos públicos
|
// Implementación de métodos públicos
|
||||||
bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMode initial_mode) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
// Obtener resolución de pantalla para validación
|
// Obtener resolución de pantalla para validación
|
||||||
@@ -72,7 +74,16 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
|
|
||||||
// VALIDACIÓN 2: Calcular max_zoom y ajustar si es necesario
|
// VALIDACIÓN 2: Calcular max_zoom y ajustar si es necesario
|
||||||
int max_zoom = std::min(screen_w / logical_width, screen_h / logical_height);
|
int max_zoom = std::min(screen_w / logical_width, screen_h / logical_height);
|
||||||
if (window_zoom > max_zoom) {
|
if (max_zoom < 1) {
|
||||||
|
// Resolució lògica no cap en pantalla ni a zoom=1: escalar-la per fer-la càpida
|
||||||
|
float scale = std::min(static_cast<float>(screen_w) / logical_width,
|
||||||
|
static_cast<float>(screen_h) / logical_height);
|
||||||
|
logical_width = std::max(320, static_cast<int>(logical_width * scale));
|
||||||
|
logical_height = std::max(240, static_cast<int>(logical_height * scale));
|
||||||
|
window_zoom = 1;
|
||||||
|
std::cout << "Advertencia: Resolución no cabe en pantalla. Ajustando a "
|
||||||
|
<< logical_width << "x" << logical_height << "\n";
|
||||||
|
} else if (window_zoom > max_zoom) {
|
||||||
std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom
|
std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom
|
||||||
<< " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n";
|
<< " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n";
|
||||||
window_zoom = max_zoom;
|
window_zoom = max_zoom;
|
||||||
@@ -164,9 +175,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback: cargar texturas desde pack usando la lista del ResourcePack
|
// Fallback: cargar texturas desde pack usando la lista del ResourceManager
|
||||||
if (Texture::isPackLoaded()) {
|
if (ResourceManager::isPackLoaded()) {
|
||||||
auto pack_resources = Texture::getPackResourceList();
|
auto pack_resources = ResourceManager::getResourceList();
|
||||||
|
|
||||||
// Filtrar solo los recursos en balls/ con extensión .png
|
// Filtrar solo los recursos en balls/ con extensión .png
|
||||||
for (const auto& resource : pack_resources) {
|
for (const auto& resource : pack_resources) {
|
||||||
@@ -224,6 +235,10 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_);
|
scene_manager_ = std::make_unique<SceneManager>(current_screen_width_, current_screen_height_);
|
||||||
scene_manager_->initialize(0, texture_, theme_manager_.get()); // Escenario 0 (10 bolas) por defecto
|
scene_manager_->initialize(0, texture_, theme_manager_.get()); // Escenario 0 (10 bolas) por defecto
|
||||||
|
|
||||||
|
// Propagar configuración custom si fue establecida antes de initialize()
|
||||||
|
if (custom_scenario_enabled_)
|
||||||
|
scene_manager_->setCustomBallCount(custom_scenario_balls_);
|
||||||
|
|
||||||
// Calcular tamaño físico de ventana ANTES de inicializar UIManager
|
// Calcular tamaño físico de ventana ANTES de inicializar UIManager
|
||||||
// NOTA: No llamar a updatePhysicalWindowSize() aquí porque ui_manager_ aún no existe
|
// NOTA: No llamar a updatePhysicalWindowSize() aquí porque ui_manager_ aún no existe
|
||||||
// Calcular manualmente para poder pasar valores al constructor de UIManager
|
// Calcular manualmente para poder pasar valores al constructor de UIManager
|
||||||
@@ -235,7 +250,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
// Inicializar UIManager (HUD, FPS, notificaciones)
|
// Inicializar UIManager (HUD, FPS, notificaciones)
|
||||||
// NOTA: Debe llamarse DESPUÉS de calcular physical_window_* y ThemeManager
|
// NOTA: Debe llamarse DESPUÉS de calcular physical_window_* y ThemeManager
|
||||||
ui_manager_ = std::make_unique<UIManager>();
|
ui_manager_ = std::make_unique<UIManager>();
|
||||||
ui_manager_->initialize(renderer_, theme_manager_.get(), physical_window_width_, physical_window_height_);
|
ui_manager_->initialize(renderer_, theme_manager_.get(),
|
||||||
|
physical_window_width_, physical_window_height_,
|
||||||
|
current_screen_width_, current_screen_height_);
|
||||||
|
|
||||||
// Inicializar ShapeManager (gestión de figuras 3D)
|
// Inicializar ShapeManager (gestión de figuras 3D)
|
||||||
shape_manager_ = std::make_unique<ShapeManager>();
|
shape_manager_ = std::make_unique<ShapeManager>();
|
||||||
@@ -246,6 +263,22 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
state_manager_ = std::make_unique<StateManager>();
|
state_manager_ = std::make_unique<StateManager>();
|
||||||
state_manager_->initialize(this); // Callback al Engine
|
state_manager_->initialize(this); // Callback al Engine
|
||||||
|
|
||||||
|
// Establecer modo inicial si no es SANDBOX (default)
|
||||||
|
// Usar métodos de alto nivel que ejecutan las acciones de configuración
|
||||||
|
if (initial_mode == AppMode::DEMO) {
|
||||||
|
state_manager_->toggleDemoMode(current_screen_width_, current_screen_height_);
|
||||||
|
// Como estamos en SANDBOX (default), toggleDemoMode() cambiará a DEMO + randomizará
|
||||||
|
}
|
||||||
|
else if (initial_mode == AppMode::DEMO_LITE) {
|
||||||
|
state_manager_->toggleDemoLiteMode(current_screen_width_, current_screen_height_);
|
||||||
|
// Como estamos en SANDBOX (default), toggleDemoLiteMode() cambiará a DEMO_LITE + randomizará
|
||||||
|
}
|
||||||
|
else if (initial_mode == AppMode::LOGO) {
|
||||||
|
size_t initial_ball_count = scene_manager_->getBallCount();
|
||||||
|
state_manager_->enterLogoMode(false, current_screen_width_, current_screen_height_, initial_ball_count);
|
||||||
|
// enterLogoMode() hace: setState(LOGO) + executeEnterLogoMode() (tema, PNG_SHAPE, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
// Actualizar ShapeManager con StateManager (dependencia circular - StateManager debe existir primero)
|
// Actualizar ShapeManager con StateManager (dependencia circular - StateManager debe existir primero)
|
||||||
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||||
current_screen_width_, current_screen_height_);
|
current_screen_width_, current_screen_height_);
|
||||||
@@ -254,6 +287,27 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
boid_manager_ = std::make_unique<BoidManager>();
|
boid_manager_ = std::make_unique<BoidManager>();
|
||||||
boid_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
boid_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||||
current_screen_width_, current_screen_height_);
|
current_screen_width_, current_screen_height_);
|
||||||
|
|
||||||
|
// Inicializar AppLogo (logo periódico en pantalla)
|
||||||
|
app_logo_ = std::make_unique<AppLogo>();
|
||||||
|
if (!app_logo_->initialize(renderer_, current_screen_width_, current_screen_height_)) {
|
||||||
|
std::cerr << "Advertencia: No se pudo inicializar AppLogo (logo periódico)" << std::endl;
|
||||||
|
// No es crítico, continuar sin logo
|
||||||
|
app_logo_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark de rendimiento (determina max_auto_scenario_ para modos automáticos)
|
||||||
|
if (!skip_benchmark_)
|
||||||
|
runPerformanceBenchmark();
|
||||||
|
else if (custom_scenario_enabled_)
|
||||||
|
custom_auto_available_ = true; // benchmark omitido: confiar en que el hardware lo soporta
|
||||||
|
|
||||||
|
// Precalentar caché: shapes PNG (evitar I/O en primera activación de PNG_SHAPE)
|
||||||
|
{
|
||||||
|
unsigned char* tmp = nullptr; size_t tmp_size = 0;
|
||||||
|
ResourceManager::loadResource("shapes/jailgames.png", tmp, tmp_size);
|
||||||
|
delete[] tmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@@ -334,6 +388,11 @@ void Engine::update() {
|
|||||||
|
|
||||||
// Actualizar transiciones de temas (delegado a ThemeManager)
|
// Actualizar transiciones de temas (delegado a ThemeManager)
|
||||||
theme_manager_->update(delta_time_);
|
theme_manager_->update(delta_time_);
|
||||||
|
|
||||||
|
// Actualizar AppLogo (logo periódico)
|
||||||
|
if (app_logo_) {
|
||||||
|
app_logo_->update(delta_time_, state_manager_->getCurrentMode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === IMPLEMENTACIÓN DE MÉTODOS PÚBLICOS PARA INPUT HANDLER ===
|
// === IMPLEMENTACIÓN DE MÉTODOS PÚBLICOS PARA INPUT HANDLER ===
|
||||||
@@ -343,7 +402,7 @@ void Engine::handleGravityToggle() {
|
|||||||
// Si estamos en modo boids, salir a modo física CON GRAVEDAD OFF
|
// Si estamos en modo boids, salir a modo física CON GRAVEDAD OFF
|
||||||
// Según RULES.md: "BOIDS a PHYSICS: Pulsando la tecla G: Gravedad OFF"
|
// Según RULES.md: "BOIDS a PHYSICS: Pulsando la tecla G: Gravedad OFF"
|
||||||
if (current_mode_ == SimulationMode::BOIDS) {
|
if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
toggleBoidsMode(); // Cambiar a PHYSICS (preserva inercia, gravedad ya está OFF desde activateBoids)
|
toggleBoidsMode(false); // Cambiar a PHYSICS sin activar gravedad (preserva inercia)
|
||||||
// NO llamar a forceBallsGravityOff() porque aplica impulsos que destruyen la inercia de BOIDS
|
// NO llamar a forceBallsGravityOff() porque aplica impulsos que destruyen la inercia de BOIDS
|
||||||
// La gravedad ya está desactivada por BoidManager::activateBoids() y se mantiene al salir
|
// La gravedad ya está desactivada por BoidManager::activateBoids() y se mantiene al salir
|
||||||
showNotificationForAction("Modo Física - Gravedad Off");
|
showNotificationForAction("Modo Física - Gravedad Off");
|
||||||
@@ -364,18 +423,19 @@ void Engine::handleGravityToggle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::handleGravityDirectionChange(GravityDirection direction, const char* notification_text) {
|
void Engine::handleGravityDirectionChange(GravityDirection direction, const char* notification_text) {
|
||||||
// Si estamos en modo boids, salir a modo física primero
|
// Si estamos en modo boids, salir a modo física primero PRESERVANDO VELOCIDAD
|
||||||
if (current_mode_ == SimulationMode::BOIDS) {
|
if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
toggleBoidsMode(); // Esto cambia a PHYSICS y activa gravedad
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
// Continuar para aplicar la dirección de gravedad
|
boid_manager_->deactivateBoids(false); // NO activar gravedad aún (preservar momentum)
|
||||||
|
scene_manager_->forceBallsGravityOn(); // Activar gravedad SIN impulsos (preserva velocidad)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si estamos en modo figura, salir a modo física CON gravedad
|
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
else if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
scene_manager_->enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
scene_manager_->enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
|
|
||||||
scene_manager_->changeGravityDirection(direction);
|
scene_manager_->changeGravityDirection(direction);
|
||||||
showNotificationForAction(notification_text);
|
showNotificationForAction(notification_text);
|
||||||
}
|
}
|
||||||
@@ -435,11 +495,11 @@ void Engine::toggleDepthZoom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Boids (comportamiento de enjambre)
|
// Boids (comportamiento de enjambre)
|
||||||
void Engine::toggleBoidsMode() {
|
void Engine::toggleBoidsMode(bool force_gravity_on) {
|
||||||
if (current_mode_ == SimulationMode::BOIDS) {
|
if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
// Salir del modo boids
|
// Salir del modo boids (velocidades ya son time-based, no requiere conversión)
|
||||||
current_mode_ = SimulationMode::PHYSICS;
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
boid_manager_->deactivateBoids();
|
boid_manager_->deactivateBoids(force_gravity_on); // Pasar parámetro para control preciso
|
||||||
} else {
|
} else {
|
||||||
// Entrar al modo boids (desde PHYSICS o SHAPE)
|
// Entrar al modo boids (desde PHYSICS o SHAPE)
|
||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
@@ -506,6 +566,30 @@ void Engine::switchTexture() {
|
|||||||
switchTextureInternal(true); // Mostrar notificación en modo manual
|
switchTextureInternal(true); // Mostrar notificación en modo manual
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Control manual del benchmark (--skip-benchmark, --max-balls)
|
||||||
|
void Engine::setSkipBenchmark() {
|
||||||
|
skip_benchmark_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::setMaxBallsOverride(int n) {
|
||||||
|
skip_benchmark_ = true;
|
||||||
|
int best = DEMO_AUTO_MIN_SCENARIO;
|
||||||
|
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= DEMO_AUTO_MAX_SCENARIO; ++i) {
|
||||||
|
if (BALL_COUNT_SCENARIOS[i] <= n) best = i;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
max_auto_scenario_ = best;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escenario custom (--custom-balls)
|
||||||
|
void Engine::setCustomScenario(int balls) {
|
||||||
|
custom_scenario_balls_ = balls;
|
||||||
|
custom_scenario_enabled_ = true;
|
||||||
|
// scene_manager_ puede no existir aún (llamada pre-init); propagación en initialize()
|
||||||
|
if (scene_manager_)
|
||||||
|
scene_manager_->setCustomBallCount(balls);
|
||||||
|
}
|
||||||
|
|
||||||
// Escenarios (número de pelotas)
|
// Escenarios (número de pelotas)
|
||||||
void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
||||||
// Pasar el modo actual al SceneManager para inicialización correcta
|
// Pasar el modo actual al SceneManager para inicialización correcta
|
||||||
@@ -522,6 +606,11 @@ void Engine::changeScenario(int scenario_id, const char* notification_text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Si estamos en modo BOIDS, desactivar gravedad (modo BOIDS = gravedad OFF siempre)
|
||||||
|
if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
|
scene_manager_->forceBallsGravityOff();
|
||||||
|
}
|
||||||
|
|
||||||
showNotificationForAction(notification_text);
|
showNotificationForAction(notification_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,34 +718,31 @@ void Engine::render() {
|
|||||||
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
||||||
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
||||||
|
|
||||||
// Crear vector de índices para ordenamiento
|
// Bucket sort per profunditat Z (O(N) vs O(N log N))
|
||||||
std::vector<size_t> render_order;
|
|
||||||
render_order.reserve(balls.size());
|
|
||||||
for (size_t i = 0; i < balls.size(); i++) {
|
for (size_t i = 0; i < balls.size(); i++) {
|
||||||
render_order.push_back(i);
|
int b = static_cast<int>(balls[i]->getDepthBrightness() * (DEPTH_SORT_BUCKETS - 1));
|
||||||
|
depth_buckets_[std::clamp(b, 0, DEPTH_SORT_BUCKETS - 1)].push_back(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
// Renderizar en orden de profundidad (bucket 0 = fons, bucket 255 = davant)
|
||||||
std::sort(render_order.begin(), render_order.end(), [&balls](size_t a, size_t b) {
|
for (int b = 0; b < DEPTH_SORT_BUCKETS; b++) {
|
||||||
return balls[a]->getDepthBrightness() < balls[b]->getDepthBrightness();
|
for (size_t idx : depth_buckets_[b]) {
|
||||||
});
|
SDL_FRect pos = balls[idx]->getPosition();
|
||||||
|
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||||
|
float brightness = balls[idx]->getDepthBrightness();
|
||||||
|
float depth_scale = balls[idx]->getDepthScale();
|
||||||
|
|
||||||
// Renderizar en orden de profundidad (fondo → frente)
|
// Mapear brightness de 0-1 a rango MIN-MAX
|
||||||
for (size_t idx : render_order) {
|
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
||||||
SDL_FRect pos = balls[idx]->getPosition();
|
|
||||||
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
|
||||||
float brightness = balls[idx]->getDepthBrightness();
|
|
||||||
float depth_scale = balls[idx]->getDepthScale();
|
|
||||||
|
|
||||||
// Mapear brightness de 0-1 a rango MIN-MAX
|
// Aplicar factor de brillo al color
|
||||||
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
int r_mod = static_cast<int>(color.r * brightness_factor);
|
||||||
|
int g_mod = static_cast<int>(color.g * brightness_factor);
|
||||||
|
int b_mod = static_cast<int>(color.b * brightness_factor);
|
||||||
|
|
||||||
// Aplicar factor de brillo al color
|
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod, depth_scale);
|
||||||
int r_mod = static_cast<int>(color.r * brightness_factor);
|
}
|
||||||
int g_mod = static_cast<int>(color.g * brightness_factor);
|
depth_buckets_[b].clear(); // netejar per al proper frame
|
||||||
int b_mod = static_cast<int>(color.b * brightness_factor);
|
|
||||||
|
|
||||||
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod, depth_scale);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
||||||
@@ -710,6 +796,11 @@ void Engine::render() {
|
|||||||
active_shape_.get(), shape_convergence_,
|
active_shape_.get(), shape_convergence_,
|
||||||
physical_window_width_, physical_window_height_, current_screen_width_);
|
physical_window_width_, physical_window_height_, current_screen_width_);
|
||||||
|
|
||||||
|
// Renderizar AppLogo (logo periódico) - después de UI, antes de present
|
||||||
|
if (app_logo_) {
|
||||||
|
app_logo_->render();
|
||||||
|
}
|
||||||
|
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,6 +835,12 @@ void Engine::toggleFullscreen() {
|
|||||||
fullscreen_enabled_ = !fullscreen_enabled_;
|
fullscreen_enabled_ = !fullscreen_enabled_;
|
||||||
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
|
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
|
||||||
|
|
||||||
|
// Si acabamos de salir de fullscreen, restaurar tamaño de ventana
|
||||||
|
if (!fullscreen_enabled_) {
|
||||||
|
SDL_SetWindowSize(window_, base_screen_width_ * current_window_zoom_, base_screen_height_ * current_window_zoom_);
|
||||||
|
SDL_SetWindowPosition(window_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||||
|
}
|
||||||
|
|
||||||
// Actualizar dimensiones físicas después del cambio
|
// Actualizar dimensiones físicas después del cambio
|
||||||
updatePhysicalWindowSize();
|
updatePhysicalWindowSize();
|
||||||
}
|
}
|
||||||
@@ -784,6 +881,22 @@ void Engine::toggleRealFullscreen() {
|
|||||||
|
|
||||||
// Actualizar tamaño de pantalla para boids (wrapping boundaries)
|
// Actualizar tamaño de pantalla para boids (wrapping boundaries)
|
||||||
boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
|
|
||||||
|
// Actualizar AppLogo con nueva resolución
|
||||||
|
if (app_logo_) {
|
||||||
|
app_logo_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
generateShape(); // Regenerar figura con nuevas dimensiones de pantalla
|
||||||
|
|
||||||
|
// Activar atracción física en las bolas nuevas (crítico tras changeScenario)
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SDL_free(displays);
|
SDL_free(displays);
|
||||||
}
|
}
|
||||||
@@ -806,6 +919,22 @@ void Engine::toggleRealFullscreen() {
|
|||||||
// Reinicar la escena con resolución original
|
// Reinicar la escena con resolución original
|
||||||
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_);
|
scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_);
|
||||||
|
|
||||||
|
// Actualizar AppLogo con resolución restaurada
|
||||||
|
if (app_logo_) {
|
||||||
|
app_logo_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si estamos en modo SHAPE, regenerar la figura con nuevas dimensiones
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
generateShape(); // Regenerar figura con nuevas dimensiones de pantalla
|
||||||
|
|
||||||
|
// Activar atracción física en las bolas nuevas (crítico tras changeScenario)
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1140,20 +1269,26 @@ void Engine::performLogoAction(bool logo_waiting_for_flip) {
|
|||||||
demo_next_action_time_ = logo_min_time_ + (rand() % 1000) / 1000.0f * interval_range;
|
demo_next_action_time_ = logo_min_time_ + (rand() % 1000) / 1000.0f * interval_range;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Logo animado (PHYSICS) → 3 opciones posibles
|
// Logo animado (PHYSICS) → 4 opciones posibles
|
||||||
if (action < 60) {
|
if (action < 50) {
|
||||||
// 60%: PHYSICS → SHAPE (reconstruir logo y ver rotaciones)
|
// 50%: PHYSICS → SHAPE (reconstruir logo y ver rotaciones)
|
||||||
toggleShapeModeInternal(false);
|
toggleShapeModeInternal(false);
|
||||||
|
|
||||||
// Resetear variables de espera de flips al volver a SHAPE
|
// Resetear variables de espera de flips al volver a SHAPE
|
||||||
logo_waiting_for_flip_ = false;
|
logo_waiting_for_flip_ = false;
|
||||||
logo_current_flip_count_ = 0;
|
logo_current_flip_count_ = 0;
|
||||||
} else if (action < 80) {
|
} else if (action < 68) {
|
||||||
// 20%: Forzar gravedad ON (empezar a caer mientras da vueltas)
|
// 18%: Forzar gravedad ON (empezar a caer mientras da vueltas)
|
||||||
scene_manager_->forceBallsGravityOn();
|
scene_manager_->forceBallsGravityOn();
|
||||||
} else {
|
} else if (action < 84) {
|
||||||
// 20%: Forzar gravedad OFF (flotar mientras da vueltas)
|
// 16%: Forzar gravedad OFF (flotar mientras da vueltas)
|
||||||
scene_manager_->forceBallsGravityOff();
|
scene_manager_->forceBallsGravityOff();
|
||||||
|
} else {
|
||||||
|
// 16%: Cambiar dirección de gravedad (nueva variación)
|
||||||
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
|
// Si la gravedad está OFF, activarla para que el cambio sea visible
|
||||||
|
scene_manager_->forceBallsGravityOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resetear timer con intervalos escalados
|
// Resetear timer con intervalos escalados
|
||||||
@@ -1194,7 +1329,7 @@ void Engine::executeDemoAction(bool is_lite) {
|
|||||||
|
|
||||||
if (is_lite) {
|
if (is_lite) {
|
||||||
// DEMO LITE: Verificar condiciones para salto a Logo Mode
|
// DEMO LITE: Verificar condiciones para salto a Logo Mode
|
||||||
if (static_cast<int>(scene_manager_->getBallCount()) >= LOGO_MODE_MIN_BALLS &&
|
if (static_cast<int>(scene_manager_->getBallCount()) >= BALL_COUNT_SCENARIOS[LOGO_MIN_SCENARIO_IDX] &&
|
||||||
theme_manager_->getCurrentThemeIndex() == 5) { // MONOCHROME
|
theme_manager_->getCurrentThemeIndex() == 5) { // MONOCHROME
|
||||||
// 10% probabilidad de saltar a Logo Mode
|
// 10% probabilidad de saltar a Logo Mode
|
||||||
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE) {
|
||||||
@@ -1204,7 +1339,7 @@ void Engine::executeDemoAction(bool is_lite) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// DEMO COMPLETO: Verificar condiciones para salto a Logo Mode
|
// DEMO COMPLETO: Verificar condiciones para salto a Logo Mode
|
||||||
if (static_cast<int>(scene_manager_->getBallCount()) >= LOGO_MODE_MIN_BALLS) {
|
if (static_cast<int>(scene_manager_->getBallCount()) >= BALL_COUNT_SCENARIOS[LOGO_MIN_SCENARIO_IDX]) {
|
||||||
// 15% probabilidad de saltar a Logo Mode
|
// 15% probabilidad de saltar a Logo Mode
|
||||||
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
|
if (rand() % 100 < LOGO_JUMP_PROBABILITY_FROM_DEMO) {
|
||||||
state_manager_->enterLogoMode(true, current_screen_width_, current_screen_height_, scene_manager_->getBallCount());
|
state_manager_->enterLogoMode(true, current_screen_width_, current_screen_height_, scene_manager_->getBallCount());
|
||||||
@@ -1318,13 +1453,29 @@ void Engine::executeDemoAction(bool is_lite) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cambiar escenario (10%) - EXCLUIR índices 0, 6, 7 (1, 50K, 100K pelotas)
|
// Cambiar escenario (10%) - rango dinámico según benchmark de rendimiento
|
||||||
accumulated_weight += DEMO_WEIGHT_SCENARIO;
|
accumulated_weight += DEMO_WEIGHT_SCENARIO;
|
||||||
if (random_value < accumulated_weight) {
|
if (random_value < accumulated_weight) {
|
||||||
// Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas)
|
int auto_max = std::min(max_auto_scenario_, DEMO_AUTO_MAX_SCENARIO);
|
||||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
std::vector<int> candidates;
|
||||||
int new_scenario = valid_scenarios[rand() % 5];
|
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i)
|
||||||
|
candidates.push_back(i);
|
||||||
|
if (custom_scenario_enabled_ && custom_auto_available_)
|
||||||
|
candidates.push_back(CUSTOM_SCENARIO_IDX);
|
||||||
|
int new_scenario = candidates[rand() % candidates.size()];
|
||||||
scene_manager_->changeScenario(new_scenario, current_mode_);
|
scene_manager_->changeScenario(new_scenario, current_mode_);
|
||||||
|
|
||||||
|
// Si estamos en modo SHAPE, regenerar la figura con nuevo número de pelotas
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
generateShape();
|
||||||
|
|
||||||
|
// Activar atracción física en las bolas nuevas (crítico tras changeScenario)
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1406,21 +1557,7 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
|
|||||||
} else {
|
} else {
|
||||||
// DEMO COMPLETO: Randomizar TODO
|
// DEMO COMPLETO: Randomizar TODO
|
||||||
|
|
||||||
// 1. Escenario (excluir índices 0, 6, 7)
|
// 1. Física o Figura (decidir PRIMERO antes de cambiar escenario)
|
||||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
|
||||||
int new_scenario = valid_scenarios[rand() % 5];
|
|
||||||
scene_manager_->changeScenario(new_scenario, current_mode_);
|
|
||||||
|
|
||||||
// 2. Tema (elegir entre TODOS los 15 temas)
|
|
||||||
int random_theme_index = rand() % 15;
|
|
||||||
theme_manager_->switchToTheme(random_theme_index);
|
|
||||||
|
|
||||||
// 3. Sprite
|
|
||||||
if (rand() % 2 == 0) {
|
|
||||||
switchTextureInternal(false); // Suprimir notificación al activar modo DEMO
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Física o Figura
|
|
||||||
if (rand() % 2 == 0) {
|
if (rand() % 2 == 0) {
|
||||||
// Modo física
|
// Modo física
|
||||||
if (current_mode_ == SimulationMode::SHAPE) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
@@ -1429,20 +1566,88 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
|
|||||||
} else {
|
} else {
|
||||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::LISSAJOUS, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||||
activateShapeInternal(shapes[rand() % 8]);
|
ShapeType selected_shape = shapes[rand() % 8];
|
||||||
|
|
||||||
// 5. Profundidad (solo si estamos en figura)
|
// Configurar figura SIN generar puntos (changeScenario lo hará después)
|
||||||
|
last_shape_type_ = selected_shape;
|
||||||
|
current_shape_type_ = selected_shape;
|
||||||
|
current_mode_ = SimulationMode::SHAPE;
|
||||||
|
|
||||||
|
// Crear instancia de la figura sin generar puntos todavía
|
||||||
|
switch (selected_shape) {
|
||||||
|
case ShapeType::SPHERE:
|
||||||
|
active_shape_ = std::make_unique<SphereShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::CUBE:
|
||||||
|
active_shape_ = std::make_unique<CubeShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::HELIX:
|
||||||
|
active_shape_ = std::make_unique<HelixShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::TORUS:
|
||||||
|
active_shape_ = std::make_unique<TorusShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::LISSAJOUS:
|
||||||
|
active_shape_ = std::make_unique<LissajousShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::CYLINDER:
|
||||||
|
active_shape_ = std::make_unique<CylinderShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::ICOSAHEDRON:
|
||||||
|
active_shape_ = std::make_unique<IcosahedronShape>();
|
||||||
|
break;
|
||||||
|
case ShapeType::ATOM:
|
||||||
|
active_shape_ = std::make_unique<AtomShape>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
active_shape_ = std::make_unique<SphereShape>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profundidad (solo si estamos en figura)
|
||||||
if (rand() % 2 == 0) {
|
if (rand() % 2 == 0) {
|
||||||
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Escala de figura (aleatoria entre 0.5x y 2.0x)
|
// Escala de figura (aleatoria entre 0.5x y 2.0x)
|
||||||
shape_scale_factor_ = 0.5f + (rand() % 1500) / 1000.0f;
|
shape_scale_factor_ = 0.5f + (rand() % 1500) / 1000.0f;
|
||||||
clampShapeScale();
|
clampShapeScale();
|
||||||
generateShape();
|
|
||||||
|
// NOTA: NO llamar a generateShape() ni activar atracción aquí
|
||||||
|
// changeScenario() creará las pelotas y luego llamará a generateShape()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Gravedad: dirección + ON/OFF
|
// 2. Escenario - rango dinámico según benchmark de rendimiento
|
||||||
|
int auto_max = std::min(max_auto_scenario_, DEMO_AUTO_MAX_SCENARIO);
|
||||||
|
std::vector<int> candidates;
|
||||||
|
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= auto_max; ++i)
|
||||||
|
candidates.push_back(i);
|
||||||
|
if (custom_scenario_enabled_ && custom_auto_available_)
|
||||||
|
candidates.push_back(CUSTOM_SCENARIO_IDX);
|
||||||
|
int new_scenario = candidates[rand() % candidates.size()];
|
||||||
|
scene_manager_->changeScenario(new_scenario, current_mode_);
|
||||||
|
|
||||||
|
// Si estamos en modo SHAPE, generar la figura y activar atracción
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
generateShape();
|
||||||
|
|
||||||
|
// Activar atracción física en las bolas nuevas
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Tema (elegir entre TODOS los 15 temas)
|
||||||
|
int random_theme_index = rand() % 15;
|
||||||
|
theme_manager_->switchToTheme(random_theme_index);
|
||||||
|
|
||||||
|
// 4. Sprite
|
||||||
|
if (rand() % 2 == 0) {
|
||||||
|
switchTextureInternal(false); // Suprimir notificación al activar modo DEMO
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Gravedad: dirección + ON/OFF
|
||||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||||
scene_manager_->changeGravityDirection(new_direction);
|
scene_manager_->changeGravityDirection(new_direction);
|
||||||
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
||||||
@@ -1465,16 +1670,110 @@ void Engine::executeToggleGravityOnOff() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// BENCHMARK DE RENDIMIENTO
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void Engine::runPerformanceBenchmark() {
|
||||||
|
int num_displays = 0;
|
||||||
|
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
||||||
|
float monitor_hz = 60.0f;
|
||||||
|
if (displays && num_displays > 0) {
|
||||||
|
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
||||||
|
if (dm && dm->refresh_rate > 0) monitor_hz = dm->refresh_rate;
|
||||||
|
SDL_free(displays);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ocultar ventana y desactivar V-sync para medición limpia
|
||||||
|
SDL_HideWindow(window_);
|
||||||
|
SDL_SetRenderVSync(renderer_, 0);
|
||||||
|
|
||||||
|
const int BENCH_DURATION_MS = 600;
|
||||||
|
const int WARMUP_FRAMES = 5;
|
||||||
|
|
||||||
|
SimulationMode original_mode = current_mode_;
|
||||||
|
|
||||||
|
auto restore = [&]() {
|
||||||
|
SDL_SetRenderVSync(renderer_, vsync_enabled_ ? 1 : 0);
|
||||||
|
SDL_ShowWindow(window_);
|
||||||
|
current_mode_ = original_mode;
|
||||||
|
active_shape_.reset();
|
||||||
|
scene_manager_->changeScenario(0, original_mode);
|
||||||
|
last_frame_time_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test escenario custom (independiente de max_auto_scenario_)
|
||||||
|
custom_auto_available_ = false;
|
||||||
|
if (custom_scenario_enabled_) {
|
||||||
|
scene_manager_->changeScenario(CUSTOM_SCENARIO_IDX, SimulationMode::SHAPE);
|
||||||
|
activateShapeInternal(ShapeType::SPHERE);
|
||||||
|
last_frame_time_ = 0;
|
||||||
|
for (int w = 0; w < WARMUP_FRAMES; ++w) {
|
||||||
|
calculateDeltaTime();
|
||||||
|
SDL_Event e; while (SDL_PollEvent(&e)) {}
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
int frame_count = 0;
|
||||||
|
Uint64 start = SDL_GetTicks();
|
||||||
|
while (SDL_GetTicks() - start < static_cast<Uint64>(BENCH_DURATION_MS)) {
|
||||||
|
calculateDeltaTime();
|
||||||
|
SDL_Event e; while (SDL_PollEvent(&e)) {}
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
++frame_count;
|
||||||
|
}
|
||||||
|
float fps = static_cast<float>(frame_count) / (BENCH_DURATION_MS / 1000.0f);
|
||||||
|
custom_auto_available_ = (fps >= monitor_hz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probar de más pesado a más ligero
|
||||||
|
for (int idx = DEMO_AUTO_MAX_SCENARIO; idx >= DEMO_AUTO_MIN_SCENARIO; --idx) {
|
||||||
|
scene_manager_->changeScenario(idx, SimulationMode::SHAPE);
|
||||||
|
activateShapeInternal(ShapeType::SPHERE);
|
||||||
|
|
||||||
|
// Warmup: estabilizar física y pipeline GPU
|
||||||
|
last_frame_time_ = 0;
|
||||||
|
for (int w = 0; w < WARMUP_FRAMES; ++w) {
|
||||||
|
calculateDeltaTime();
|
||||||
|
SDL_Event e; while (SDL_PollEvent(&e)) {}
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Medición
|
||||||
|
int frame_count = 0;
|
||||||
|
Uint64 start = SDL_GetTicks();
|
||||||
|
while (SDL_GetTicks() - start < static_cast<Uint64>(BENCH_DURATION_MS)) {
|
||||||
|
calculateDeltaTime();
|
||||||
|
SDL_Event e;
|
||||||
|
while (SDL_PollEvent(&e)) { /* descartar */ }
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
++frame_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
float measured_fps = static_cast<float>(frame_count) / (BENCH_DURATION_MS / 1000.0f);
|
||||||
|
if (measured_fps >= monitor_hz) {
|
||||||
|
max_auto_scenario_ = idx;
|
||||||
|
restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: escenario mínimo
|
||||||
|
max_auto_scenario_ = DEMO_AUTO_MIN_SCENARIO;
|
||||||
|
restore();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// CALLBACKS PARA STATEMANAGER - LOGO MODE
|
// CALLBACKS PARA STATEMANAGER - LOGO MODE
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// Callback para StateManager - Configuración visual al entrar a LOGO MODE
|
// Callback para StateManager - Configuración visual al entrar a LOGO MODE
|
||||||
void Engine::executeEnterLogoMode(size_t ball_count) {
|
void Engine::executeEnterLogoMode(size_t ball_count) {
|
||||||
// Verificar mínimo de pelotas
|
// Verificar mínimo de pelotas (LOGO_MIN_SCENARIO_IDX = índice 4 → 1000 bolas)
|
||||||
if (static_cast<int>(ball_count) < LOGO_MODE_MIN_BALLS) {
|
if (scene_manager_->getCurrentScenario() < LOGO_MIN_SCENARIO_IDX) {
|
||||||
// Ajustar a 5000 pelotas automáticamente
|
scene_manager_->changeScenario(LOGO_MIN_SCENARIO_IDX, current_mode_);
|
||||||
scene_manager_->changeScenario(5, current_mode_); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guardar estado previo (para restaurar al salir)
|
// Guardar estado previo (para restaurar al salir)
|
||||||
@@ -1539,6 +1838,15 @@ void Engine::executeExitLogoMode() {
|
|||||||
clampShapeScale();
|
clampShapeScale();
|
||||||
generateShape();
|
generateShape();
|
||||||
|
|
||||||
|
// Activar atracción física si estamos en modo SHAPE
|
||||||
|
// (crítico para que las bolas se muevan hacia la figura restaurada)
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Desactivar modo LOGO en PNG_SHAPE (volver a flip intervals normales)
|
// Desactivar modo LOGO en PNG_SHAPE (volver a flip intervals normales)
|
||||||
if (active_shape_) {
|
if (active_shape_) {
|
||||||
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
||||||
@@ -1671,7 +1979,7 @@ void Engine::activateShapeInternal(ShapeType type) {
|
|||||||
active_shape_ = std::make_unique<AtomShape>();
|
active_shape_ = std::make_unique<AtomShape>();
|
||||||
break;
|
break;
|
||||||
case ShapeType::PNG_SHAPE:
|
case ShapeType::PNG_SHAPE:
|
||||||
active_shape_ = std::make_unique<PNGShape>("data/shapes/jailgames.png");
|
active_shape_ = std::make_unique<PNGShape>("shapes/jailgames.png");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
active_shape_ = std::make_unique<SphereShape>(); // Fallback
|
active_shape_ = std::make_unique<SphereShape>(); // Fallback
|
||||||
|
|||||||
@@ -10,22 +10,23 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "app_logo.hpp" // for AppLogo
|
||||||
#include "boids_mgr/boid_manager.h" // for BoidManager
|
#include "ball.hpp" // for Ball
|
||||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
#include "boids_mgr/boid_manager.hpp" // for BoidManager
|
||||||
#include "external/texture.h" // for Texture
|
#include "defines.hpp" // for GravityDirection, ColorTheme, ShapeType
|
||||||
#include "input/input_handler.h" // for InputHandler
|
#include "external/texture.hpp" // for Texture
|
||||||
#include "scene/scene_manager.h" // for SceneManager
|
#include "input/input_handler.hpp" // for InputHandler
|
||||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
#include "scene/scene_manager.hpp" // for SceneManager
|
||||||
#include "shapes_mgr/shape_manager.h" // for ShapeManager
|
#include "shapes/shape.hpp" // for Shape (interfaz polimórfica)
|
||||||
#include "state/state_manager.h" // for StateManager
|
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
|
||||||
#include "theme_manager.h" // for ThemeManager
|
#include "state/state_manager.hpp" // for StateManager
|
||||||
#include "ui/ui_manager.h" // for UIManager
|
#include "theme_manager.hpp" // for ThemeManager
|
||||||
|
#include "ui/ui_manager.hpp" // for UIManager
|
||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
public:
|
public:
|
||||||
// Interfaz pública principal
|
// Interfaz pública principal
|
||||||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false);
|
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
|
||||||
void run();
|
void run();
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ class Engine {
|
|||||||
void toggleDepthZoom();
|
void toggleDepthZoom();
|
||||||
|
|
||||||
// Boids (comportamiento de enjambre)
|
// Boids (comportamiento de enjambre)
|
||||||
void toggleBoidsMode();
|
void toggleBoidsMode(bool force_gravity_on = true);
|
||||||
|
|
||||||
// Temas de colores
|
// Temas de colores
|
||||||
void cycleTheme(bool forward);
|
void cycleTheme(bool forward);
|
||||||
@@ -70,6 +71,23 @@ class Engine {
|
|||||||
void toggleRealFullscreen();
|
void toggleRealFullscreen();
|
||||||
void toggleIntegerScaling();
|
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)
|
// Modos de aplicación (DEMO/LOGO)
|
||||||
void toggleDemoMode();
|
void toggleDemoMode();
|
||||||
void toggleDemoLiteMode();
|
void toggleDemoLiteMode();
|
||||||
@@ -94,8 +112,13 @@ class Engine {
|
|||||||
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
|
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
|
||||||
int getCurrentScreenWidth() const { return current_screen_width_; }
|
int getCurrentScreenWidth() const { return current_screen_width_; }
|
||||||
int getCurrentScreenHeight() const { return current_screen_height_; }
|
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 getBaseScreenWidth() const { return base_screen_width_; }
|
||||||
int getBaseScreenHeight() const { return base_screen_height_; }
|
int getBaseScreenHeight() const { return base_screen_height_; }
|
||||||
|
int getMaxAutoScenario() const { return max_auto_scenario_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// === Componentes del sistema (Composición) ===
|
// === Componentes del sistema (Composición) ===
|
||||||
@@ -105,6 +128,7 @@ class Engine {
|
|||||||
std::unique_ptr<BoidManager> boid_manager_; // Gestión de comportamiento boids
|
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<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<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
|
// Recursos SDL
|
||||||
SDL_Window* window_ = nullptr;
|
SDL_Window* window_ = nullptr;
|
||||||
@@ -129,6 +153,7 @@ class Engine {
|
|||||||
bool vsync_enabled_ = true;
|
bool vsync_enabled_ = true;
|
||||||
bool fullscreen_enabled_ = false;
|
bool fullscreen_enabled_ = false;
|
||||||
bool real_fullscreen_enabled_ = false;
|
bool real_fullscreen_enabled_ = false;
|
||||||
|
bool kiosk_mode_ = false;
|
||||||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
||||||
|
|
||||||
// Resolución base (configurada por CLI o default)
|
// Resolución base (configurada por CLI o default)
|
||||||
@@ -160,9 +185,15 @@ class Engine {
|
|||||||
// Sistema de Modo DEMO (auto-play) y LOGO
|
// Sistema de Modo DEMO (auto-play) y LOGO
|
||||||
// NOTA: Engine mantiene estado de implementación para callbacks performLogoAction()
|
// NOTA: Engine mantiene estado de implementación para callbacks performLogoAction()
|
||||||
// StateManager coordina los triggers y timers, Engine ejecuta las acciones
|
// StateManager coordina los triggers y timers, Engine ejecuta las acciones
|
||||||
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_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)
|
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)
|
// Sistema de convergencia para LOGO MODE (escala con resolución)
|
||||||
// Usado por performLogoAction() para detectar cuando las bolas forman el logo
|
// Usado por performLogoAction() para detectar cuando las bolas forman el logo
|
||||||
@@ -192,6 +223,10 @@ class Engine {
|
|||||||
std::vector<SDL_Vertex> batch_vertices_;
|
std::vector<SDL_Vertex> batch_vertices_;
|
||||||
std::vector<int> batch_indices_;
|
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)
|
// Configuración del sistema de texto (constantes configurables)
|
||||||
static constexpr const char* TEXT_FONT_PATH = "data/fonts/determination.ttf";
|
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 int TEXT_BASE_SIZE = 24; // Tamaño base para 240p
|
||||||
@@ -202,8 +237,10 @@ class Engine {
|
|||||||
void update();
|
void update();
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
|
// Benchmark de rendimiento (determina max_auto_scenario_ al inicio)
|
||||||
|
void runPerformanceBenchmark();
|
||||||
|
|
||||||
// Métodos auxiliares privados (llamados por la interfaz pública)
|
// Métodos auxiliares privados (llamados por la interfaz pública)
|
||||||
void showNotificationForAction(const std::string& text); // Mostrar notificación solo en modo MANUAL
|
|
||||||
|
|
||||||
// Sistema de cambio de sprites dinámico - Métodos privados
|
// Sistema de cambio de sprites dinámico - Métodos privados
|
||||||
void switchTextureInternal(bool show_notification); // Implementación interna del cambio de textura
|
void switchTextureInternal(bool show_notification); // Implementación interna del cambio de textura
|
||||||
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
|
#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
|
// Constructor
|
||||||
Sprite::Sprite(std::shared_ptr<Texture> texture)
|
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
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "texture.h"
|
#include "texture.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL_error.h> // Para SDL_GetError
|
#include <SDL3/SDL_error.h> // Para SDL_GetError
|
||||||
#include <SDL3/SDL_log.h> // Para SDL_Log
|
#include <SDL3/SDL_log.h> // Para SDL_Log
|
||||||
@@ -12,38 +12,7 @@
|
|||||||
#include <string> // Para operator<<, string
|
#include <string> // Para operator<<, string
|
||||||
|
|
||||||
#include "stb_image.h" // Para stbi_failure_reason, stbi_image_free
|
#include "stb_image.h" // Para stbi_failure_reason, stbi_image_free
|
||||||
#include "../resource_pack.h" // Sistema de empaquetado de recursos
|
#include "resource_manager.hpp" // Sistema de empaquetado de recursos centralizado
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture::Texture(SDL_Renderer *renderer)
|
Texture::Texture(SDL_Renderer *renderer)
|
||||||
: renderer_(renderer),
|
: renderer_(renderer),
|
||||||
@@ -70,30 +39,29 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
|||||||
int width, height, orig_format;
|
int width, height, orig_format;
|
||||||
unsigned char *data = nullptr;
|
unsigned char *data = nullptr;
|
||||||
|
|
||||||
// 1. Intentar cargar desde pack (si está inicializado)
|
// 1. Intentar cargar desde ResourceManager (pack o disco)
|
||||||
if (g_resourcePack != nullptr) {
|
unsigned char* resourceData = nullptr;
|
||||||
ResourcePack::ResourceData packData = g_resourcePack->loadResource(file_path);
|
size_t resourceSize = 0;
|
||||||
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
|
|
||||||
|
|
||||||
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;
|
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) {
|
if (data == nullptr) {
|
||||||
data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
|
||||||
if (data == nullptr) {
|
exit(1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pitch;
|
int pitch;
|
||||||
@@ -128,6 +96,9 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
|||||||
|
|
||||||
// Configurar filtro nearest neighbor para píxel perfect
|
// Configurar filtro nearest neighbor para píxel perfect
|
||||||
SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
|
// Habilitar alpha blending para transparencias
|
||||||
|
SDL_SetTextureBlendMode(new_texture, SDL_BLENDMODE_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destruye la superficie cargada
|
// Destruye la superficie cargada
|
||||||
@@ -169,3 +140,17 @@ int Texture::getHeight() {
|
|||||||
void Texture::setColor(int r, int g, int b) {
|
void Texture::setColor(int r, int g, int b) {
|
||||||
SDL_SetTextureColorMod(texture_, r, g, 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_;
|
int height_;
|
||||||
|
|
||||||
public:
|
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
|
// Inicializa las variables
|
||||||
explicit Texture(SDL_Renderer *renderer);
|
explicit Texture(SDL_Renderer *renderer);
|
||||||
Texture(SDL_Renderer *renderer, const std::string &file_path);
|
Texture(SDL_Renderer *renderer, const std::string &file_path);
|
||||||
@@ -44,6 +39,12 @@ class Texture {
|
|||||||
// Modula el color de la textura
|
// Modula el color de la textura
|
||||||
void setColor(int r, int g, int b);
|
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
|
// Getter para batch rendering
|
||||||
SDL_Texture *getSDLTexture() const { return texture_; }
|
SDL_Texture *getSDLTexture() const { return texture_; }
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#include "input_handler.h"
|
#include "input_handler.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
||||||
#include <string> // for std::string, std::to_string
|
#include <string> // for std::string, std::to_string
|
||||||
|
|
||||||
#include "../engine.h" // for Engine
|
#include "defines.hpp" // for KIOSK_NOTIFICATION_TEXT
|
||||||
#include "../external/mouse.h" // for Mouse namespace
|
#include "engine.hpp" // for Engine
|
||||||
|
#include "external/mouse.hpp" // for Mouse namespace
|
||||||
|
|
||||||
bool InputHandler::processEvents(Engine& engine) {
|
bool InputHandler::processEvents(Engine& engine) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@@ -21,6 +22,10 @@ bool InputHandler::processEvents(Engine& engine) {
|
|||||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) {
|
||||||
switch (event.key.key) {
|
switch (event.key.key) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
|
if (engine.isKioskMode()) {
|
||||||
|
engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
return true; // Solicitar salida
|
return true; // Solicitar salida
|
||||||
|
|
||||||
case SDLK_SPACE:
|
case SDLK_SPACE:
|
||||||
@@ -219,23 +224,34 @@ bool InputHandler::processEvents(Engine& engine) {
|
|||||||
engine.changeScenario(7, "50,000 Pelotas");
|
engine.changeScenario(7, "50,000 Pelotas");
|
||||||
break;
|
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)
|
// Controles de zoom dinámico (solo si no estamos en fullscreen)
|
||||||
case SDLK_F1:
|
case SDLK_F1:
|
||||||
engine.handleZoomOut();
|
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||||
|
else engine.handleZoomOut();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F2:
|
case SDLK_F2:
|
||||||
engine.handleZoomIn();
|
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||||
|
else engine.handleZoomIn();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Control de pantalla completa
|
// Control de pantalla completa
|
||||||
case SDLK_F3:
|
case SDLK_F3:
|
||||||
engine.toggleFullscreen();
|
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||||
|
else engine.toggleFullscreen();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Modo real fullscreen (cambia resolución interna)
|
// Modo real fullscreen (cambia resolución interna)
|
||||||
case SDLK_F4:
|
case SDLK_F4:
|
||||||
engine.toggleRealFullscreen();
|
if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT);
|
||||||
|
else engine.toggleRealFullscreen();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Toggle escalado entero/estirado (solo en fullscreen F3)
|
// Toggle escalado entero/estirado (solo en fullscreen F3)
|
||||||
|
|||||||
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);
|
||||||
|
};
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#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
|
// getExecutableDirectory() ya está definido en defines.h como inline
|
||||||
|
|
||||||
@@ -13,13 +16,22 @@ void printHelp() {
|
|||||||
std::cout << " -z, --zoom <n> Zoom de ventana (default: 3)\n";
|
std::cout << " -z, --zoom <n> Zoom de ventana (default: 3)\n";
|
||||||
std::cout << " -f, --fullscreen Modo pantalla completa (F3 - letterbox)\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 << " -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 << " --help Mostrar esta ayuda\n\n";
|
||||||
std::cout << "Ejemplos:\n";
|
std::cout << "Ejemplos:\n";
|
||||||
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\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 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 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n";
|
||||||
std::cout << " vibe3_physics -f # Fullscreen letterbox (F3)\n";
|
std::cout << " vibe3_physics -f # Fullscreen letterbox (F3)\n";
|
||||||
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n\n";
|
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n";
|
||||||
|
std::cout << " vibe3_physics -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";
|
std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +39,13 @@ int main(int argc, char* argv[]) {
|
|||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
int zoom = 0;
|
int zoom = 0;
|
||||||
|
int custom_balls = 0;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
bool real_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
|
// Parsear argumentos
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
@@ -38,8 +55,8 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--width") == 0) {
|
} else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--width") == 0) {
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
width = atoi(argv[++i]);
|
width = atoi(argv[++i]);
|
||||||
if (width < 640) {
|
if (width < 320) {
|
||||||
std::cerr << "Error: Ancho mínimo es 640px\n";
|
std::cerr << "Error: Ancho mínimo es 320\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -49,8 +66,8 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--height") == 0) {
|
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--height") == 0) {
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
height = atoi(argv[++i]);
|
height = atoi(argv[++i]);
|
||||||
if (height < 480) {
|
if (height < 240) {
|
||||||
std::cerr << "Error: Alto mínimo es 480px\n";
|
std::cerr << "Error: Alto mínimo es 240\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -72,6 +89,53 @@ int main(int argc, char* argv[]) {
|
|||||||
fullscreen = true;
|
fullscreen = true;
|
||||||
} else if (strcmp(argv[i], "-F") == 0 || strcmp(argv[i], "--real-fullscreen") == 0) {
|
} else if (strcmp(argv[i], "-F") == 0 || strcmp(argv[i], "--real-fullscreen") == 0) {
|
||||||
real_fullscreen = true;
|
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 {
|
} else {
|
||||||
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
|
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
|
||||||
printHelp();
|
printHelp();
|
||||||
@@ -82,19 +146,30 @@ int main(int argc, char* argv[]) {
|
|||||||
// Inicializar sistema de recursos empaquetados (intentar cargar resources.pack)
|
// Inicializar sistema de recursos empaquetados (intentar cargar resources.pack)
|
||||||
std::string resources_dir = getResourcesDirectory();
|
std::string resources_dir = getResourcesDirectory();
|
||||||
std::string pack_path = resources_dir + "/resources.pack";
|
std::string pack_path = resources_dir + "/resources.pack";
|
||||||
Texture::initResourceSystem(pack_path);
|
ResourceManager::init(pack_path);
|
||||||
|
|
||||||
Engine engine;
|
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;
|
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si se especificó real fullscreen (F4), activar después de inicializar
|
// Si se especificó real fullscreen (F4) o modo kiosko, activar después de inicializar
|
||||||
if (real_fullscreen) {
|
if (real_fullscreen || kiosk_mode) {
|
||||||
engine.toggleRealFullscreen();
|
engine.toggleRealFullscreen();
|
||||||
}
|
}
|
||||||
|
if (kiosk_mode) {
|
||||||
|
engine.setKioskMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
engine.run();
|
engine.run();
|
||||||
engine.shutdown();
|
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 <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifndef RESOURCE_PACK_H
|
#pragma once
|
||||||
#define RESOURCE_PACK_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -64,5 +63,3 @@ private:
|
|||||||
uint32_t calculateChecksum(const unsigned char* data, size_t size);
|
uint32_t calculateChecksum(const unsigned char* data, size_t size);
|
||||||
std::string normalizePath(const std::string& path);
|
std::string normalizePath(const std::string& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RESOURCE_PACK_H
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#include "scene_manager.h"
|
#include "scene_manager.hpp"
|
||||||
|
|
||||||
#include <cstdlib> // for rand
|
#include <cstdlib> // for rand
|
||||||
|
|
||||||
#include "../defines.h" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
|
#include "defines.hpp" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
|
||||||
#include "../external/texture.h" // for Texture
|
#include "external/texture.hpp" // for Texture
|
||||||
#include "../theme_manager.h" // for ThemeManager
|
#include "theme_manager.hpp" // for ThemeManager
|
||||||
|
|
||||||
SceneManager::SceneManager(int screen_width, int screen_height)
|
SceneManager::SceneManager(int screen_width, int screen_height)
|
||||||
: current_gravity_(GravityDirection::DOWN)
|
: current_gravity_(GravityDirection::DOWN)
|
||||||
@@ -44,7 +44,10 @@ void SceneManager::changeScenario(int scenario_id, SimulationMode mode) {
|
|||||||
changeGravityDirection(GravityDirection::DOWN);
|
changeGravityDirection(GravityDirection::DOWN);
|
||||||
|
|
||||||
// Crear las bolas según el escenario
|
// Crear las bolas según el escenario
|
||||||
for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) {
|
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;
|
float X, Y, VX, VY;
|
||||||
|
|
||||||
// Inicialización según SimulationMode (RULES.md líneas 23-26)
|
// Inicialización según SimulationMode (RULES.md líneas 23-26)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
#include <memory> // for unique_ptr, shared_ptr
|
#include <memory> // for unique_ptr, shared_ptr
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "../ball.h" // for Ball
|
#include "ball.hpp" // for Ball
|
||||||
#include "../defines.h" // for GravityDirection
|
#include "defines.hpp" // for GravityDirection
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Texture;
|
class Texture;
|
||||||
@@ -50,11 +50,17 @@ class SceneManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Cambia el número de bolas según escenario
|
* @brief Cambia el número de bolas según escenario
|
||||||
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas)
|
* @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)
|
* @param mode Modo de simulación actual (afecta inicialización)
|
||||||
*/
|
*/
|
||||||
void changeScenario(int scenario_id, SimulationMode mode);
|
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
|
* @brief Actualiza textura y tamaño de todas las bolas
|
||||||
* @param new_texture Nueva textura compartida
|
* @param new_texture Nueva textura compartida
|
||||||
@@ -146,6 +152,7 @@ class SceneManager {
|
|||||||
std::vector<std::unique_ptr<Ball>> balls_;
|
std::vector<std::unique_ptr<Ball>> balls_;
|
||||||
GravityDirection current_gravity_;
|
GravityDirection current_gravity_;
|
||||||
int scenario_;
|
int scenario_;
|
||||||
|
int custom_ball_count_ = 0; // Número de bolas para escenario custom (índice 8)
|
||||||
|
|
||||||
// === Configuración de pantalla ===
|
// === Configuración de pantalla ===
|
||||||
int screen_width_;
|
int screen_width_;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "atom_shape.h"
|
#include "atom_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
|
|
||||||
// Figura: Átomo con núcleo central y órbitas electrónicas
|
// Figura: Átomo con núcleo central y órbitas electrónicas
|
||||||
// Comportamiento: Núcleo estático + electrones orbitando en planos inclinados
|
// Comportamiento: Núcleo estático + electrones orbitando en planos inclinados
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "cube_shape.h"
|
#include "cube_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Figura: Cubo 3D rotante
|
// Figura: Cubo 3D rotante
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "cylinder_shape.h"
|
#include "cylinder_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib> // Para rand()
|
#include <cstdlib> // Para rand()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
|
|
||||||
// Figura: Cilindro 3D rotante
|
// Figura: Cilindro 3D rotante
|
||||||
// Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
|
// Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "helix_shape.h"
|
#include "helix_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
void HelixShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
void HelixShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
|
|
||||||
// Figura: Espiral helicoidal 3D con distribución uniforme
|
// Figura: Espiral helicoidal 3D con distribución uniforme
|
||||||
// Comportamiento: Rotación en eje Y + animación de fase vertical
|
// Comportamiento: Rotación en eje Y + animación de fase vertical
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "icosahedron_shape.h"
|
#include "icosahedron_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
|
|
||||||
// Figura: Icosaedro 3D (D20, poliedro regular de 20 caras)
|
// Figura: Icosaedro 3D (D20, poliedro regular de 20 caras)
|
||||||
// Comportamiento: 12 vértices distribuidos uniformemente con rotación triple
|
// Comportamiento: 12 vértices distribuidos uniformemente con rotación triple
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "lissajous_shape.h"
|
#include "lissajous_shape.hpp"
|
||||||
#include "../defines.h"
|
#include "defines.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
void LissajousShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
void LissajousShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.hpp"
|
||||||
|
|
||||||
// Figura: Curva de Lissajous 3D
|
// Figura: Curva de Lissajous 3D
|
||||||
// Comportamiento: Curva paramétrica 3D con rotación global y animación de fase
|
// Comportamiento: Curva paramétrica 3D con rotación global y animación de fase
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user