Compare commits
44 Commits
250b1a640d
...
2025-10-25
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -93,4 +93,21 @@ Thumbs.db
|
||||
*.temp
|
||||
|
||||
# Claude Code
|
||||
.claude/
|
||||
.claude/
|
||||
|
||||
# Archivos de recursos empaquetados
|
||||
resources.pack
|
||||
|
||||
# Archivos de distribución (resultados de release)
|
||||
*.zip
|
||||
*.dmg
|
||||
*.tar.gz
|
||||
*.AppImage
|
||||
|
||||
# Carpetas temporales de empaquetado
|
||||
vibe3_release/
|
||||
Frameworks/
|
||||
|
||||
# Binarios de herramientas
|
||||
tools/pack_resources
|
||||
tools/*.exe
|
||||
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
|
||||
include_directories(${SDL3_INCLUDE_DIRS} ${SDL3_ttf_INCLUDE_DIRS})
|
||||
|
||||
# Incluir directorio source/ para poder usar includes desde la raíz del proyecto
|
||||
include_directories(${CMAKE_SOURCE_DIR}/source)
|
||||
|
||||
# Añadir el ejecutable reutilizando el nombre del proyecto
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||
|
||||
|
||||
193
Makefile
193
Makefile
@@ -42,64 +42,69 @@ endif
|
||||
|
||||
# Nombres para los ficheros de lanzamiento
|
||||
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
||||
MACOS_INTEL_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-intel.dmg
|
||||
MACOS_APPLE_SILICON_RELEASE := $(TARGET_FILE)-$(VERSION)-macos-apple-silicon.dmg
|
||||
MACOS_INTEL_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-intel.dmg
|
||||
MACOS_APPLE_SILICON_RELEASE := $(TARGET_NAME)-$(VERSION)-macos-apple-silicon.dmg
|
||||
LINUX_RELEASE := $(TARGET_FILE)-$(VERSION)-linux.tar.gz
|
||||
RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
|
||||
|
||||
# Lista completa de archivos fuente (basada en estructura de ViBe3)
|
||||
APP_SOURCES := \
|
||||
source/ball.cpp \
|
||||
source/engine.cpp \
|
||||
source/main.cpp \
|
||||
source/resource_pack.cpp \
|
||||
source/external/mouse.cpp \
|
||||
source/external/sprite.cpp \
|
||||
source/external/texture.cpp \
|
||||
source/shapes/atom_shape.cpp \
|
||||
source/shapes/cube_shape.cpp \
|
||||
source/shapes/cylinder_shape.cpp \
|
||||
source/shapes/helix_shape.cpp \
|
||||
source/shapes/icosahedron_shape.cpp \
|
||||
source/shapes/png_shape.cpp \
|
||||
source/shapes/sphere_shape.cpp \
|
||||
source/shapes/torus_shape.cpp \
|
||||
source/shapes/wave_grid_shape.cpp
|
||||
# Lista completa de archivos fuente (detección automática con wildcards, como CMakeLists.txt)
|
||||
APP_SOURCES := $(wildcard source/*.cpp) \
|
||||
$(wildcard source/external/*.cpp) \
|
||||
$(wildcard source/shapes/*.cpp) \
|
||||
$(wildcard source/themes/*.cpp) \
|
||||
$(wildcard source/state/*.cpp) \
|
||||
$(wildcard source/input/*.cpp) \
|
||||
$(wildcard source/scene/*.cpp) \
|
||||
$(wildcard source/shapes_mgr/*.cpp) \
|
||||
$(wildcard source/boids_mgr/*.cpp) \
|
||||
$(wildcard source/text/*.cpp) \
|
||||
$(wildcard source/ui/*.cpp)
|
||||
|
||||
# Excluir archivos antiguos si existen
|
||||
APP_SOURCES := $(filter-out source/main_old.cpp, $(APP_SOURCES))
|
||||
|
||||
# Includes
|
||||
INCLUDES := -Isource -Isource/external
|
||||
|
||||
# Variables según el sistema operativo
|
||||
CXXFLAGS_BASE := -std=c++20 -Wall
|
||||
CXXFLAGS := $(CXXFLAGS_BASE) -Os -ffunction-sections -fdata-sections
|
||||
CXXFLAGS_DEBUG := $(CXXFLAGS_BASE) -g -D_DEBUG
|
||||
LDFLAGS :=
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
FixPath = $(subst /,\\,$1)
|
||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows -DWINDOWS_BUILD
|
||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
||||
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
||||
RM := del /Q
|
||||
CXXFLAGS += -DWINDOWS_BUILD
|
||||
CXXFLAGS_DEBUG += -DWINDOWS_BUILD
|
||||
LDFLAGS += -Wl,--gc-sections -static-libstdc++ -static-libgcc \
|
||||
-Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows \
|
||||
-lmingw32 -lws2_32 -lSDL3 -lSDL3_ttf
|
||||
RMFILE := del /Q
|
||||
RMDIR := rmdir /S /Q
|
||||
MKDIR := mkdir
|
||||
else
|
||||
FixPath = $1
|
||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections
|
||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG
|
||||
LDFLAGS := -lSDL3
|
||||
LDFLAGS += -lSDL3 -lSDL3_ttf
|
||||
RMFILE := rm -f
|
||||
RMDIR := rm -rdf
|
||||
RMDIR := rm -rf
|
||||
MKDIR := mkdir -p
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
CXXFLAGS += -DLINUX_BUILD
|
||||
LDFLAGS += -lGL
|
||||
CXXFLAGS += -DLINUX_BUILD
|
||||
CXXFLAGS_DEBUG += -DLINUX_BUILD
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
CXXFLAGS += -Wno-deprecated -DMACOS_BUILD
|
||||
CXXFLAGS_DEBUG += -Wno-deprecated -DMACOS_BUILD
|
||||
LDFLAGS += -framework OpenGL
|
||||
# Configurar arquitectura (por defecto arm64, como en CMake)
|
||||
CXXFLAGS += -arch arm64
|
||||
CXXFLAGS_DEBUG += -arch arm64
|
||||
CXXFLAGS += -DMACOS_BUILD -arch arm64
|
||||
CXXFLAGS_DEBUG += -DMACOS_BUILD -arch arm64
|
||||
# Si quieres binarios universales:
|
||||
# CXXFLAGS += -arch x86_64
|
||||
# CXXFLAGS_DEBUG += -arch x86_64
|
||||
# Y frameworks si hacen falta:
|
||||
# LDFLAGS += -framework Cocoa -framework IOKit
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# Reglas para herramienta de empaquetado y resources.pack
|
||||
$(PACK_TOOL): $(PACK_SOURCES)
|
||||
@echo "Compilando herramienta de empaquetado..."
|
||||
@@ -108,11 +113,21 @@ $(PACK_TOOL): $(PACK_SOURCES)
|
||||
|
||||
pack_tool: $(PACK_TOOL)
|
||||
|
||||
resources.pack: $(PACK_TOOL)
|
||||
# Detectar todos los archivos en data/ como dependencias (regenera si cualquiera cambia)
|
||||
DATA_FILES := $(shell find data -type f 2>/dev/null)
|
||||
|
||||
resources.pack: $(PACK_TOOL) $(DATA_FILES)
|
||||
@echo "Generando resources.pack desde directorio data/..."
|
||||
$(PACK_TOOL) data resources.pack
|
||||
@echo "✓ resources.pack generado exitosamente"
|
||||
|
||||
# Target para forzar regeneración de resources.pack (usado por releases)
|
||||
.PHONY: force_resource_pack
|
||||
force_resource_pack: $(PACK_TOOL)
|
||||
@echo "Regenerando resources.pack para release..."
|
||||
$(PACK_TOOL) data resources.pack
|
||||
@echo "✓ resources.pack regenerado exitosamente"
|
||||
|
||||
# Reglas para compilación
|
||||
windows:
|
||||
@echo off
|
||||
@@ -131,20 +146,20 @@ windows_debug:
|
||||
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_debug.exe"
|
||||
|
||||
windows_release: resources.pack
|
||||
windows_release: force_resource_pack
|
||||
@echo "Creando release para Windows - Version: $(VERSION)"
|
||||
|
||||
# Crea carpeta temporal 'RELEASE_FOLDER'
|
||||
@rm -rf "$(RELEASE_FOLDER)"
|
||||
@mkdir -p "$(RELEASE_FOLDER)"
|
||||
@if exist "$(RELEASE_FOLDER)" rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||
@mkdir "$(RELEASE_FOLDER)"
|
||||
|
||||
# Copia el archivo 'resources.pack'
|
||||
@cp -f "resources.pack" "$(RELEASE_FOLDER)/"
|
||||
@copy /Y "resources.pack" "$(RELEASE_FOLDER)\" >nul
|
||||
|
||||
# Copia los ficheros que estan en la raíz del proyecto
|
||||
@cp -f "LICENSE" "$(RELEASE_FOLDER)/" 2>/dev/null || echo "LICENSE not found (optional)"
|
||||
@cp -f "README.md" "$(RELEASE_FOLDER)/"
|
||||
@cp -f release/*.dll "$(RELEASE_FOLDER)/" 2>/dev/null || echo "No DLL files found (optional)"
|
||||
@copy /Y "LICENSE" "$(RELEASE_FOLDER)\" >nul 2>&1 || echo LICENSE not found (optional)
|
||||
@copy /Y "README.md" "$(RELEASE_FOLDER)\" >nul
|
||||
@copy /Y release\*.dll "$(RELEASE_FOLDER)\" >nul 2>&1 || echo DLLs copied successfully
|
||||
|
||||
# Compila
|
||||
@windres release/vibe3.rc -O coff -o $(RESOURCE_FILE)
|
||||
@@ -152,12 +167,12 @@ windows_release: resources.pack
|
||||
@strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
|
||||
|
||||
# Crea el fichero .zip
|
||||
@rm -f "$(WINDOWS_RELEASE)"
|
||||
@if exist "$(WINDOWS_RELEASE)" del /Q "$(WINDOWS_RELEASE)"
|
||||
@powershell.exe -Command "Compress-Archive -Path '$(RELEASE_FOLDER)/*' -DestinationPath '$(WINDOWS_RELEASE)' -Force"
|
||||
@echo "Release creado: $(WINDOWS_RELEASE)"
|
||||
|
||||
# Elimina la carpeta temporal 'RELEASE_FOLDER'
|
||||
@rm -rf "$(RELEASE_FOLDER)"
|
||||
@rmdir /S /Q "$(RELEASE_FOLDER)"
|
||||
|
||||
macos:
|
||||
@echo "Compilando para macOS: $(TARGET_NAME)"
|
||||
@@ -167,15 +182,24 @@ macos_debug:
|
||||
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
macos_release: resources.pack
|
||||
macos_release: force_resource_pack
|
||||
@echo "Creando release para macOS - Version: $(VERSION)"
|
||||
|
||||
# Verificar e instalar create-dmg si es necesario
|
||||
@which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg)
|
||||
|
||||
# Elimina datos de compilaciones anteriores
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
$(RMDIR) Frameworks
|
||||
$(RMFILE) tmp.dmg
|
||||
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
|
||||
$(RMFILE) "$(MACOS_APPLE_SILICON_RELEASE)"
|
||||
|
||||
# Limpia archivos temporales de create-dmg y desmonta volúmenes
|
||||
@echo "Limpiando archivos temporales y volúmenes montados..."
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
@hdiutil detach "/Volumes/$(APP_NAME)" 2>/dev/null || true
|
||||
@hdiutil detach "/Volumes/ViBe3 Physics" 2>/dev/null || true
|
||||
|
||||
# Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macos
|
||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS"
|
||||
@@ -185,40 +209,76 @@ macos_release: resources.pack
|
||||
# Copia carpetas y ficheros
|
||||
cp resources.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
cp -R release/frameworks/SDL3_ttf.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
cp -R release/frameworks/SDL3.xcframework Frameworks
|
||||
cp -R release/frameworks/SDL3_ttf.xcframework Frameworks
|
||||
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp release/Info.plist "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents"
|
||||
cp LICENSE "$(RELEASE_FOLDER)"
|
||||
cp README.md "$(RELEASE_FOLDER)"
|
||||
|
||||
# Crea enlaces
|
||||
ln -s /Applications "$(RELEASE_FOLDER)"/Applications
|
||||
# NOTA: create-dmg crea automáticamente el enlace a /Applications con --app-drop-link
|
||||
# No es necesario crearlo manualmente aquí
|
||||
|
||||
# Compila la versión para procesadores Intel
|
||||
ifdef ENABLE_MACOS_X86_64
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.15
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos12
|
||||
|
||||
# Firma la aplicación
|
||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||
|
||||
# Empaqueta el .dmg de la versión Intel
|
||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
|
||||
$(RMFILE) tmp.dmg
|
||||
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
|
||||
# Empaqueta el .dmg de la versión Intel con create-dmg
|
||||
@echo "Creando DMG Intel con iconos de 96x96..."
|
||||
@create-dmg \
|
||||
--volname "$(APP_NAME)" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 720 300 \
|
||||
--icon-size 96 \
|
||||
--text-size 12 \
|
||||
--icon "$(APP_NAME).app" 278 102 \
|
||||
--icon "LICENSE" 441 102 \
|
||||
--icon "README.md" 604 102 \
|
||||
--app-drop-link 115 102 \
|
||||
--hide-extension "$(APP_NAME).app" \
|
||||
"$(MACOS_INTEL_RELEASE)" \
|
||||
"$(RELEASE_FOLDER)"
|
||||
@if [ -f "$(MACOS_INTEL_RELEASE)" ]; then \
|
||||
echo "✓ Release Intel creado exitosamente: $(MACOS_INTEL_RELEASE)"; \
|
||||
else \
|
||||
echo "✗ Error: No se pudo crear el DMG Intel"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
endif
|
||||
|
||||
# Compila la versión para procesadores Apple Silicon
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos12
|
||||
|
||||
# Firma la aplicación
|
||||
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
|
||||
|
||||
# Empaqueta el .dmg de la versión Apple Silicon
|
||||
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
|
||||
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
|
||||
$(RMFILE) tmp.dmg
|
||||
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
|
||||
# Empaqueta el .dmg de la versión Apple Silicon con create-dmg
|
||||
@echo "Creando DMG Apple Silicon con iconos de 96x96..."
|
||||
@create-dmg \
|
||||
--volname "$(APP_NAME)" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 720 300 \
|
||||
--icon-size 96 \
|
||||
--text-size 12 \
|
||||
--icon "$(APP_NAME).app" 278 102 \
|
||||
--icon "LICENSE" 441 102 \
|
||||
--icon "README.md" 604 102 \
|
||||
--app-drop-link 115 102 \
|
||||
--hide-extension "$(APP_NAME).app" \
|
||||
"$(MACOS_APPLE_SILICON_RELEASE)" \
|
||||
"$(RELEASE_FOLDER)"
|
||||
@if [ -f "$(MACOS_APPLE_SILICON_RELEASE)" ]; then \
|
||||
echo "✓ Release Apple Silicon creado exitosamente: $(MACOS_APPLE_SILICON_RELEASE)"; \
|
||||
else \
|
||||
echo "✗ Error: No se pudo crear el DMG Apple Silicon"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@rm -f rw.*.dmg 2>/dev/null || true
|
||||
|
||||
# Elimina las carpetas temporales
|
||||
$(RMDIR) Frameworks
|
||||
@@ -233,7 +293,7 @@ linux_debug:
|
||||
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
linux_release: resources.pack
|
||||
linux_release: force_resource_pack
|
||||
@echo "Creando release para Linux - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -258,7 +318,7 @@ linux_release: resources.pack
|
||||
# Elimina la carpeta temporal
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
|
||||
linux_release_desktop: resources.pack
|
||||
linux_release_desktop: force_resource_pack
|
||||
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -362,7 +422,7 @@ raspi_debug:
|
||||
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
|
||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
||||
|
||||
raspi_release: resources.pack
|
||||
raspi_release: force_resource_pack
|
||||
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
@@ -387,7 +447,7 @@ raspi_release: resources.pack
|
||||
# Elimina la carpeta temporal
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
|
||||
anbernic: resources.pack
|
||||
anbernic: force_resource_pack
|
||||
@echo "Compilando para Anbernic: $(TARGET_NAME)"
|
||||
# Elimina carpetas previas
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
|
||||
@@ -426,6 +486,7 @@ help:
|
||||
@echo " macos_release - Crear release completo para macOS (.dmg)"
|
||||
@echo " pack_tool - Compilar herramienta de empaquetado"
|
||||
@echo " resources.pack - Generar pack de recursos desde data/"
|
||||
@echo " force_resource_pack - Regenerar resources.pack (usado por releases)"
|
||||
@echo " show_version - Mostrar version actual ($(VERSION))"
|
||||
@echo " help - Mostrar esta ayuda"
|
||||
|
||||
|
||||
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>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<string>12.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
BIN
release/SDL3.dll
Normal file
BIN
release/SDL3.dll
Normal file
Binary file not shown.
BIN
release/SDL3_ttf.dll
Normal file
BIN
release/SDL3_ttf.dll
Normal file
Binary file not shown.
90
release/frameworks/SDL3_ttf.xcframework/Info.plist
Normal file
90
release/frameworks/SDL3_ttf.xcframework/Info.plist
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3_ttf.framework/Versions/A/SDL3_ttf</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>macos-arm64_x86_64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3_ttf.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>macos</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
BIN
release/frameworks/SDL3_ttf.xcframework/ios-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
BIN
release/frameworks/SDL3_ttf.xcframework/ios-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
Binary file not shown.
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<data>
|
||||
7QAtKpC/pLIq6TK3F59Ax1hg3tc=
|
||||
</data>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<data>
|
||||
90S4SFzJy1lUuMotaCRWpTbzRa4=
|
||||
</data>
|
||||
<key>INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
9F72T63xvwi9u+kC0p9N011q3IM=
|
||||
</data>
|
||||
<key>LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
@@ -0,0 +1 @@
|
||||
Versions/Current/SDL3_ttf
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>23H420</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SDL3_ttf</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.libsdl.SDL3-ttf</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SDL3_ttf</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.2.2</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.2.2</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string></string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>14.5</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>23F73</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx14.5</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1540</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>15F31d</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>Resources/CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Resources/INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Resources/Info.plist</key>
|
||||
<data>
|
||||
Q+NCd9YwE/D/Y4ptVnrhOldXz6U=
|
||||
</data>
|
||||
<key>Resources/LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>Resources/README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/Info.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
LwUSgLeBsUUT/M3w+W5AAfTziViNTWX1o7Ly+x3J2u0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
A
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
BIN
release/frameworks/SDL3_ttf.xcframework/tvos-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
BIN
release/frameworks/SDL3_ttf.xcframework/tvos-arm64/SDL3_ttf.framework/SDL3_ttf
Executable file
Binary file not shown.
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
|
||||
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \file SDL_textengine.h
|
||||
*
|
||||
* Definitions for implementations of the TTF_TextEngine interface.
|
||||
*/
|
||||
#ifndef SDL_TTF_TEXTENGINE_H_
|
||||
#define SDL_TTF_TEXTENGINE_H_
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
#include <SDL3/SDL_begin_code.h>
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A font atlas draw command.
|
||||
*
|
||||
* \since This enum is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef enum TTF_DrawCommand
|
||||
{
|
||||
TTF_DRAW_COMMAND_NOOP,
|
||||
TTF_DRAW_COMMAND_FILL,
|
||||
TTF_DRAW_COMMAND_COPY
|
||||
} TTF_DrawCommand;
|
||||
|
||||
/**
|
||||
* A filled rectangle draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_FillOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
|
||||
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
} TTF_FillOperation;
|
||||
|
||||
/**
|
||||
* A texture copy draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa TTF_DrawOperation
|
||||
*/
|
||||
typedef struct TTF_CopyOperation
|
||||
{
|
||||
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
|
||||
int text_offset; /**< The offset in the text corresponding to this glyph.
|
||||
There may be multiple glyphs with the same text offset
|
||||
and the next text offset might be several Unicode codepoints
|
||||
later. In this case the glyphs and codepoints are grouped
|
||||
together and the group bounding box is the union of the dst
|
||||
rectangles for the corresponding glyphs. */
|
||||
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
|
||||
SDL_Rect src; /**< The area within the glyph to be drawn */
|
||||
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
|
||||
void *reserved;
|
||||
} TTF_CopyOperation;
|
||||
|
||||
/**
|
||||
* A text engine draw operation.
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*/
|
||||
typedef union TTF_DrawOperation
|
||||
{
|
||||
TTF_DrawCommand cmd;
|
||||
TTF_FillOperation fill;
|
||||
TTF_CopyOperation copy;
|
||||
} TTF_DrawOperation;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, to assist in text measurement and layout */
|
||||
typedef struct TTF_TextLayout TTF_TextLayout;
|
||||
|
||||
|
||||
/* Private data in TTF_Text, available to implementations */
|
||||
struct TTF_TextData
|
||||
{
|
||||
TTF_Font *font; /**< The font used by this text, read-only. */
|
||||
SDL_FColor color; /**< The color of the text, read-only. */
|
||||
|
||||
bool needs_layout_update; /**< True if the layout needs to be updated */
|
||||
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
|
||||
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
|
||||
int w; /**< The width of this text, in pixels, read-only. */
|
||||
int h; /**< The height of this text, in pixels, read-only. */
|
||||
int num_ops; /**< The number of drawing operations to render this text, read-only. */
|
||||
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
|
||||
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
|
||||
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
|
||||
|
||||
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
|
||||
|
||||
bool needs_engine_update; /**< True if the engine text needs to be updated */
|
||||
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
|
||||
void *engine_text; /**< The implementation-specific representation of this text */
|
||||
};
|
||||
|
||||
/**
|
||||
* A text engine interface.
|
||||
*
|
||||
* This structure should be initialized using SDL_INIT_INTERFACE()
|
||||
*
|
||||
* \since This struct is available since SDL_ttf 3.0.0.
|
||||
*
|
||||
* \sa SDL_INIT_INTERFACE
|
||||
*/
|
||||
struct TTF_TextEngine
|
||||
{
|
||||
Uint32 version; /**< The version of this interface */
|
||||
|
||||
void *userdata; /**< User data pointer passed to callbacks */
|
||||
|
||||
/* Create a text representation from draw instructions.
|
||||
*
|
||||
* All fields of `text` except `internal->engine_text` will already be filled out.
|
||||
*
|
||||
* This function should set the `internal->engine_text` field to a non-NULL value.
|
||||
*
|
||||
* \param userdata the userdata pointer in this interface.
|
||||
* \param text the text object being created.
|
||||
*/
|
||||
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
|
||||
|
||||
/**
|
||||
* Destroy a text representation.
|
||||
*/
|
||||
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
|
||||
|
||||
};
|
||||
|
||||
/* Check the size of TTF_TextEngine
|
||||
*
|
||||
* If this assert fails, either the compiler is padding to an unexpected size,
|
||||
* or the interface has been updated and this should be updated to match and
|
||||
* the code using this interface should be updated to handle the old version.
|
||||
*/
|
||||
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
|
||||
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
|
||||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_TTF_TEXTENGINE_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
|
||||
# Using this package
|
||||
|
||||
This package contains SDL_ttf built for Xcode.
|
||||
|
||||
To use this package in Xcode, drag `SDL3_ttf.framework` into your project.
|
||||
|
||||
# Documentation
|
||||
|
||||
An API reference and additional documentation is available at:
|
||||
|
||||
https://wiki.libsdl.org/SDL3_ttf
|
||||
|
||||
# Discussions
|
||||
|
||||
## Discord
|
||||
|
||||
You can join the official Discord server at:
|
||||
|
||||
https://discord.com/invite/BwpFGBWsv8
|
||||
|
||||
## Forums/mailing lists
|
||||
|
||||
You can join SDL development discussions at:
|
||||
|
||||
https://discourse.libsdl.org/
|
||||
|
||||
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
|
||||
|
||||
## Announcement list
|
||||
|
||||
You can sign up for the low traffic announcement list at:
|
||||
|
||||
https://www.libsdl.org/mailing-list.php
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
SDL_ttf 3.0
|
||||
|
||||
This library is a wrapper around the FreeType and Harfbuzz libraries, allowing you to use TrueType fonts to render text in SDL applications.
|
||||
|
||||
The latest version of this library is available from GitHub:
|
||||
https://github.com/libsdl-org/SDL_ttf/releases
|
||||
|
||||
Installation instructions and a quick introduction is available in
|
||||
[INSTALL.md](INSTALL.md)
|
||||
|
||||
This library is distributed under the terms of the zlib license,
|
||||
available in [LICENSE.txt](LICENSE.txt).
|
||||
|
||||
This library also uses the following libraries:
|
||||
- FreeType, licensed under the [FTL](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT)
|
||||
- HarfBuzz, licensed under the [MIT license](https://github.com/harfbuzz/harfbuzz/blob/main/COPYING)
|
||||
- PlutoSVG, licensed under the [MIT license](https://github.com/sammycage/plutosvg/blob/master/LICENSE)
|
||||
- PlutoVG, licensed under the [MIT license](https://github.com/sammycage/plutovg/blob/master/LICENSE)
|
||||
|
||||
Enjoy!
|
||||
|
||||
Sam Lantinga (slouken@libsdl.org)
|
||||
Binary file not shown.
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<data>
|
||||
V6UpWQTvr/puOrlm1sgAs6fktNA=
|
||||
</data>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<data>
|
||||
WW2xmNHZyYr9y3/8uAylJuutcPw=
|
||||
</data>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<data>
|
||||
7QAtKpC/pLIq6TK3F59Ax1hg3tc=
|
||||
</data>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<data>
|
||||
90S4SFzJy1lUuMotaCRWpTbzRa4=
|
||||
</data>
|
||||
<key>INSTALL.md</key>
|
||||
<data>
|
||||
3kA+9HE5dF7+nyypVt5YOfU+Uho=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
+nBymgIjvQrMA5f3CqiBB03/KB0=
|
||||
</data>
|
||||
<key>LICENSE.txt</key>
|
||||
<data>
|
||||
dp6e8JHkl0CrYD+oe2IXZfWB/iw=
|
||||
</data>
|
||||
<key>README.md</key>
|
||||
<data>
|
||||
lm034L4zWKPElKb9O2dmehurfFQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>CMake/SDL3_ttfConfig.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
VpwUT/D8TjpLXBguVImWqsMkqni9HXiIzx91C92Krqc=
|
||||
</data>
|
||||
</dict>
|
||||
<key>CMake/SDL3_ttfConfigVersion.cmake</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
tb1RnDTj72GQOzcXp6FPtiqW8tSD886UyUY09c1Ms/U=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_textengine.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Uk27FTzsWoYySpKM1gkwCB/svSxscGViuMzca93gLP8=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/SDL_ttf.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6bsCCUp3Uc3tCp+0Xxw7Tt01+UV8bra5YN1dFjpRBL0=
|
||||
</data>
|
||||
</dict>
|
||||
<key>INSTALL.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Jq9GEmdnFRmUTNnYYZZ+5mFqqrMelD86Gthhyi2kGJQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>LICENSE.txt</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
eCbsoKD35ZHzjdhE4geiAKrIGlmyDYoww6+MYoKvE+Y=
|
||||
</data>
|
||||
</dict>
|
||||
<key>README.md</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
6aipppbEU7MEd3x9OHnKqAGyFXVYiSAL8X8lm271U00=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
release/vibe3.res
Normal file
BIN
release/vibe3.res
Normal file
Binary file not shown.
BIN
resources.pack
BIN
resources.pack
Binary file not shown.
687
source/app_logo.cpp
Normal file
687
source/app_logo.cpp
Normal file
@@ -0,0 +1,687 @@
|
||||
#include "app_logo.hpp"
|
||||
|
||||
#include <SDL3/SDL_render.h> // for SDL_DestroyTexture, SDL_RenderGeometry, SDL_SetTextureAlphaMod
|
||||
#include <cmath> // for powf, sinf, cosf
|
||||
#include <cstdlib> // for free()
|
||||
#include <iostream> // for std::cout
|
||||
|
||||
#include "logo_scaler.hpp" // for LogoScaler
|
||||
#include "defines.hpp" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
|
||||
|
||||
// ============================================================================
|
||||
// Destructor - Liberar las 4 texturas SDL
|
||||
// ============================================================================
|
||||
|
||||
AppLogo::~AppLogo() {
|
||||
if (logo1_base_texture_) {
|
||||
SDL_DestroyTexture(logo1_base_texture_);
|
||||
logo1_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo1_native_texture_) {
|
||||
SDL_DestroyTexture(logo1_native_texture_);
|
||||
logo1_native_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_base_texture_) {
|
||||
SDL_DestroyTexture(logo2_base_texture_);
|
||||
logo2_base_texture_ = nullptr;
|
||||
}
|
||||
if (logo2_native_texture_) {
|
||||
SDL_DestroyTexture(logo2_native_texture_);
|
||||
logo2_native_texture_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Inicialización - Pre-escalar logos a 2 resoluciones (base y nativa)
|
||||
// ============================================================================
|
||||
|
||||
bool AppLogo::initialize(SDL_Renderer* renderer, int screen_width, int screen_height) {
|
||||
renderer_ = renderer;
|
||||
base_screen_width_ = screen_width;
|
||||
base_screen_height_ = screen_height;
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
|
||||
// ========================================================================
|
||||
// 1. Detectar resolución nativa del monitor
|
||||
// ========================================================================
|
||||
if (!LogoScaler::detectNativeResolution(native_screen_width_, native_screen_height_)) {
|
||||
std::cout << "No se pudo detectar resolución nativa, usando solo base" << std::endl;
|
||||
// Fallback: usar resolución base como nativa
|
||||
native_screen_width_ = screen_width;
|
||||
native_screen_height_ = screen_height;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 2. Calcular alturas finales para ambas resoluciones
|
||||
// ========================================================================
|
||||
int logo_base_target_height = static_cast<int>(base_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
int logo_native_target_height = static_cast<int>(native_screen_height_ * APPLOGO_HEIGHT_PERCENT);
|
||||
|
||||
std::cout << "Pre-escalando logos:" << std::endl;
|
||||
std::cout << " Base: " << base_screen_width_ << "x" << base_screen_height_
|
||||
<< " (altura logo: " << logo_base_target_height << "px)" << std::endl;
|
||||
std::cout << " Nativa: " << native_screen_width_ << "x" << native_screen_height_
|
||||
<< " (altura logo: " << logo_native_target_height << "px)" << std::endl;
|
||||
|
||||
// ========================================================================
|
||||
// 3. Cargar y escalar LOGO1 (data/logo/logo.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo1_path = resources_dir + "/data/logo/logo.png";
|
||||
|
||||
// 3a. Versión BASE de logo1
|
||||
unsigned char* logo1_base_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente por aspect ratio
|
||||
logo_base_target_height,
|
||||
logo1_base_width_,
|
||||
logo1_base_height_
|
||||
);
|
||||
if (logo1_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo1_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_base_data, logo1_base_width_, logo1_base_height_
|
||||
);
|
||||
free(logo1_base_data); // Liberar buffer temporal
|
||||
|
||||
if (logo1_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Habilitar alpha blending
|
||||
SDL_SetTextureBlendMode(logo1_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 3b. Versión NATIVA de logo1
|
||||
unsigned char* logo1_native_data = LogoScaler::loadAndScale(
|
||||
logo1_path,
|
||||
0, // width calculado automáticamente
|
||||
logo_native_target_height,
|
||||
logo1_native_width_,
|
||||
logo1_native_height_
|
||||
);
|
||||
if (logo1_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo1_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo1_native_data, logo1_native_width_, logo1_native_height_
|
||||
);
|
||||
free(logo1_native_data);
|
||||
|
||||
if (logo1_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo1 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo1_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// 4. Cargar y escalar LOGO2 (data/logo/logo2.png) a 2 versiones
|
||||
// ========================================================================
|
||||
std::string logo2_path = resources_dir + "/data/logo/logo2.png";
|
||||
|
||||
// 4a. Versión BASE de logo2
|
||||
unsigned char* logo2_base_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_base_target_height,
|
||||
logo2_base_width_,
|
||||
logo2_base_height_
|
||||
);
|
||||
if (logo2_base_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo2_base_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_base_data, logo2_base_width_, logo2_base_height_
|
||||
);
|
||||
free(logo2_base_data);
|
||||
|
||||
if (logo2_base_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (base)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo2_base_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// 4b. Versión NATIVA de logo2
|
||||
unsigned char* logo2_native_data = LogoScaler::loadAndScale(
|
||||
logo2_path,
|
||||
0,
|
||||
logo_native_target_height,
|
||||
logo2_native_width_,
|
||||
logo2_native_height_
|
||||
);
|
||||
if (logo2_native_data == nullptr) {
|
||||
std::cout << "Error: No se pudo escalar logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
logo2_native_texture_ = LogoScaler::createTextureFromBuffer(
|
||||
renderer, logo2_native_data, logo2_native_width_, logo2_native_height_
|
||||
);
|
||||
free(logo2_native_data);
|
||||
|
||||
if (logo2_native_texture_ == nullptr) {
|
||||
std::cout << "Error: No se pudo crear textura logo2 (nativa)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetTextureBlendMode(logo2_native_texture_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// ========================================================================
|
||||
// 5. Inicialmente usar texturas BASE (la resolución de inicio)
|
||||
// ========================================================================
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "Logos pre-escalados exitosamente (4 texturas creadas)" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppLogo::update(float delta_time, AppMode current_mode) {
|
||||
// Si estamos en SANDBOX, resetear y no hacer nada (logo desactivado)
|
||||
if (current_mode == AppMode::SANDBOX) {
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Máquina de estados con fade in/out
|
||||
timer_ += delta_time;
|
||||
|
||||
switch (state_) {
|
||||
case AppLogoState::HIDDEN:
|
||||
// Esperando el intervalo de espera
|
||||
if (timer_ >= APPLOGO_DISPLAY_INTERVAL) {
|
||||
state_ = AppLogoState::FADE_IN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Elegir UNA animación aleatoria (misma para ambos logos, misma entrada y salida)
|
||||
current_animation_ = getRandomAnimation();
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_IN:
|
||||
// Fade in: alpha de 0 a 255, con Logo 2 retrasado 0.25s
|
||||
{
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
|
||||
|
||||
// Verificar si fade in completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade in completado para ambos logos
|
||||
state_ = AppLogoState::VISIBLE;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
logo2_scale_ = 1.0f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha con retraso de forma LINEAL (sin easing)
|
||||
logo1_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo1) * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>(std::min(1.0f, fade_progress_logo2) * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar MISMA animación (current_animation_) a ambos logos
|
||||
// con sus respectivos progresos
|
||||
// ================================================================
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
logo2_scale_ = 1.2f - (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float elastic_t1 = easeOutElastic(prog1);
|
||||
logo1_scale_ = 1.2f - (elastic_t1 * 0.2f);
|
||||
float squash_t1 = easeOutBack(prog1);
|
||||
logo1_squash_y_ = 0.6f + (squash_t1 * 0.4f);
|
||||
logo1_stretch_x_ = 1.0f + (1.0f - logo1_squash_y_) * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float elastic_t2 = easeOutElastic(prog2);
|
||||
logo2_scale_ = 1.2f - (elastic_t2 * 0.2f);
|
||||
float squash_t2 = easeOutBack(prog2);
|
||||
logo2_squash_y_ = 0.6f + (squash_t2 * 0.4f);
|
||||
logo2_stretch_x_ = 1.0f + (1.0f - logo2_squash_y_) * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 0.3f + (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = (1.0f - prog1) * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 0.3f + (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = (1.0f - prog2) * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float bounce_t1 = easeOutBounce(prog1);
|
||||
logo1_scale_ = 1.0f;
|
||||
float squash_amount1 = (1.0f - bounce_t1) * 0.3f;
|
||||
logo1_squash_y_ = 1.0f - squash_amount1;
|
||||
logo1_stretch_x_ = 1.0f + squash_amount1 * 0.5f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float bounce_t2 = easeOutBounce(prog2);
|
||||
logo2_scale_ = 1.0f;
|
||||
float squash_amount2 = (1.0f - bounce_t2) * 0.3f;
|
||||
logo2_squash_y_ = 1.0f - squash_amount2;
|
||||
logo2_stretch_x_ = 1.0f + squash_amount2 * 0.5f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::VISIBLE:
|
||||
// Logo completamente visible, esperando duración
|
||||
if (timer_ >= APPLOGO_DISPLAY_DURATION) {
|
||||
state_ = AppLogoState::FADE_OUT;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 255;
|
||||
logo2_alpha_ = 255;
|
||||
// NO elegir nueva animación - reutilizar current_animation_ (simetría entrada/salida)
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoState::FADE_OUT:
|
||||
// Fade out: alpha de 255 a 0, con Logo 2 retrasado 0.25s (misma animación que entrada)
|
||||
{
|
||||
// Calcular progreso de cada logo (Logo 2 con retraso)
|
||||
float fade_progress_logo1 = timer_ / APPLOGO_ANIMATION_DURATION;
|
||||
float fade_progress_logo2 = std::max(0.0f, (timer_ - APPLOGO_LOGO2_DELAY) / APPLOGO_ANIMATION_DURATION);
|
||||
|
||||
// Verificar si fade out completado (cuando logo2 también termina)
|
||||
if (fade_progress_logo2 >= 1.0f) {
|
||||
// Fade out completado, volver a HIDDEN
|
||||
state_ = AppLogoState::HIDDEN;
|
||||
timer_ = 0.0f;
|
||||
logo1_alpha_ = 0;
|
||||
logo2_alpha_ = 0;
|
||||
// Resetear variables de ambos logos
|
||||
logo1_scale_ = 1.0f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
logo2_scale_ = 1.0f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
} else {
|
||||
// Interpolar alpha con retraso de forma LINEAL (255 → 0, sin easing)
|
||||
logo1_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo1)) * 255.0f);
|
||||
logo2_alpha_ = static_cast<int>((1.0f - std::min(1.0f, fade_progress_logo2)) * 255.0f);
|
||||
|
||||
// ================================================================
|
||||
// Aplicar MISMA animación (current_animation_) de forma invertida
|
||||
// ================================================================
|
||||
switch (current_animation_) {
|
||||
case AppLogoAnimationType::ZOOM_ONLY:
|
||||
logo1_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo1) * 0.2f);
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
logo2_scale_ = 1.0f + (std::min(1.0f, fade_progress_logo2) * 0.2f);
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
logo2_rotation_ = 0.0f;
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ELASTIC_STICK:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
logo1_scale_ = 1.0f + (prog1 * prog1 * 0.2f);
|
||||
logo1_squash_y_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f - (prog1 * 0.2f);
|
||||
logo1_rotation_ = prog1 * 0.1f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
logo2_scale_ = 1.0f + (prog2 * prog2 * 0.2f);
|
||||
logo2_squash_y_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f - (prog2 * 0.2f);
|
||||
logo2_rotation_ = prog2 * 0.1f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::ROTATE_SPIRAL:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
float ease_t1 = easeInOutQuad(prog1);
|
||||
logo1_scale_ = 1.0f - (ease_t1 * 0.7f);
|
||||
logo1_rotation_ = prog1 * 6.28f;
|
||||
logo1_squash_y_ = 1.0f;
|
||||
logo1_stretch_x_ = 1.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
float ease_t2 = easeInOutQuad(prog2);
|
||||
logo2_scale_ = 1.0f - (ease_t2 * 0.7f);
|
||||
logo2_rotation_ = prog2 * 6.28f;
|
||||
logo2_squash_y_ = 1.0f;
|
||||
logo2_stretch_x_ = 1.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppLogoAnimationType::BOUNCE_SQUASH:
|
||||
{
|
||||
float prog1 = std::min(1.0f, fade_progress_logo1);
|
||||
if (prog1 < 0.2f) {
|
||||
float squash_t = prog1 / 0.2f;
|
||||
logo1_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo1_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (prog1 - 0.2f) / 0.8f;
|
||||
logo1_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo1_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo1_scale_ = 1.0f + (prog1 * 0.3f);
|
||||
logo1_rotation_ = 0.0f;
|
||||
|
||||
float prog2 = std::min(1.0f, fade_progress_logo2);
|
||||
if (prog2 < 0.2f) {
|
||||
float squash_t = prog2 / 0.2f;
|
||||
logo2_squash_y_ = 1.0f - (squash_t * 0.3f);
|
||||
logo2_stretch_x_ = 1.0f + (squash_t * 0.2f);
|
||||
} else {
|
||||
float jump_t = (prog2 - 0.2f) / 0.8f;
|
||||
logo2_squash_y_ = 0.7f + (jump_t * 0.5f);
|
||||
logo2_stretch_x_ = 1.2f - (jump_t * 0.2f);
|
||||
}
|
||||
logo2_scale_ = 1.0f + (prog2 * 0.3f);
|
||||
logo2_rotation_ = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogo::render() {
|
||||
// Renderizar si NO está en estado HIDDEN (incluye FADE_IN, VISIBLE, FADE_OUT)
|
||||
if (state_ != AppLogoState::HIDDEN) {
|
||||
// Renderizar LOGO1 primero (fondo), luego LOGO2 (encima)
|
||||
renderWithGeometry(1);
|
||||
renderWithGeometry(2);
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogo::updateScreenSize(int screen_width, int screen_height) {
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
// ========================================================================
|
||||
// Detectar si coincide con resolución nativa o base, cambiar texturas
|
||||
// ========================================================================
|
||||
bool is_native = (screen_width == native_screen_width_ && screen_height == native_screen_height_);
|
||||
|
||||
if (is_native) {
|
||||
// Cambiar a texturas nativas (F4 fullscreen)
|
||||
logo1_current_texture_ = logo1_native_texture_;
|
||||
logo1_current_width_ = logo1_native_width_;
|
||||
logo1_current_height_ = logo1_native_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_native_texture_;
|
||||
logo2_current_width_ = logo2_native_width_;
|
||||
logo2_current_height_ = logo2_native_height_;
|
||||
|
||||
std::cout << "AppLogo: Cambiado a texturas NATIVAS" << std::endl;
|
||||
} else {
|
||||
// Cambiar a texturas base (ventana redimensionable)
|
||||
logo1_current_texture_ = logo1_base_texture_;
|
||||
logo1_current_width_ = logo1_base_width_;
|
||||
logo1_current_height_ = logo1_base_height_;
|
||||
|
||||
logo2_current_texture_ = logo2_base_texture_;
|
||||
logo2_current_width_ = logo2_base_width_;
|
||||
logo2_current_height_ = logo2_base_height_;
|
||||
|
||||
std::cout << "AppLogo: Cambiado a texturas BASE" << std::endl;
|
||||
}
|
||||
|
||||
// Nota: No es necesario recalcular escalas porque las texturas están pre-escaladas
|
||||
// al tamaño exacto de pantalla. Solo renderizamos al 100% (o con deformaciones de animación).
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Funciones de easing para animaciones
|
||||
// ============================================================================
|
||||
|
||||
float AppLogo::easeOutElastic(float t) {
|
||||
// Elastic easing out: bounce elástico al final
|
||||
const float c4 = (2.0f * 3.14159f) / 3.0f;
|
||||
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
|
||||
return powf(2.0f, -10.0f * t) * sinf((t * 10.0f - 0.75f) * c4) + 1.0f;
|
||||
}
|
||||
|
||||
float AppLogo::easeOutBack(float t) {
|
||||
// Back easing out: overshoot suave al final
|
||||
const float c1 = 1.70158f;
|
||||
const float c3 = c1 + 1.0f;
|
||||
|
||||
return 1.0f + c3 * powf(t - 1.0f, 3.0f) + c1 * powf(t - 1.0f, 2.0f);
|
||||
}
|
||||
|
||||
float AppLogo::easeOutBounce(float t) {
|
||||
// Bounce easing out: rebotes decrecientes (para BOUNCE_SQUASH)
|
||||
const float n1 = 7.5625f;
|
||||
const float d1 = 2.75f;
|
||||
|
||||
if (t < 1.0f / d1) {
|
||||
return n1 * t * t;
|
||||
} else if (t < 2.0f / d1) {
|
||||
t -= 1.5f / d1;
|
||||
return n1 * t * t + 0.75f;
|
||||
} else if (t < 2.5f / d1) {
|
||||
t -= 2.25f / d1;
|
||||
return n1 * t * t + 0.9375f;
|
||||
} else {
|
||||
t -= 2.625f / d1;
|
||||
return n1 * t * t + 0.984375f;
|
||||
}
|
||||
}
|
||||
|
||||
float AppLogo::easeInOutQuad(float t) {
|
||||
// Quadratic easing in/out: aceleración suave (para ROTATE_SPIRAL)
|
||||
if (t < 0.5f) {
|
||||
return 2.0f * t * t;
|
||||
} else {
|
||||
return 1.0f - powf(-2.0f * t + 2.0f, 2.0f) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Función auxiliar para aleatorización
|
||||
// ============================================================================
|
||||
|
||||
AppLogoAnimationType AppLogo::getRandomAnimation() {
|
||||
// Generar número aleatorio entre 0 y 3 (4 tipos de animación)
|
||||
int random_value = rand() % 4;
|
||||
|
||||
switch (random_value) {
|
||||
case 0:
|
||||
return AppLogoAnimationType::ZOOM_ONLY;
|
||||
case 1:
|
||||
return AppLogoAnimationType::ELASTIC_STICK;
|
||||
case 2:
|
||||
return AppLogoAnimationType::ROTATE_SPIRAL;
|
||||
case 3:
|
||||
default:
|
||||
return AppLogoAnimationType::BOUNCE_SQUASH;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Renderizado con geometría (para todos los logos, con deformaciones)
|
||||
// ============================================================================
|
||||
|
||||
void AppLogo::renderWithGeometry(int logo_index) {
|
||||
if (!renderer_) return;
|
||||
|
||||
// Seleccionar variables según el logo_index (1 = logo1, 2 = logo2)
|
||||
SDL_Texture* texture;
|
||||
int base_width, base_height;
|
||||
float scale, squash_y, stretch_x, rotation;
|
||||
|
||||
if (logo_index == 1) {
|
||||
if (!logo1_current_texture_) return;
|
||||
texture = logo1_current_texture_;
|
||||
base_width = logo1_current_width_;
|
||||
base_height = logo1_current_height_;
|
||||
scale = logo1_scale_;
|
||||
squash_y = logo1_squash_y_;
|
||||
stretch_x = logo1_stretch_x_;
|
||||
rotation = logo1_rotation_;
|
||||
} else if (logo_index == 2) {
|
||||
if (!logo2_current_texture_) return;
|
||||
texture = logo2_current_texture_;
|
||||
base_width = logo2_current_width_;
|
||||
base_height = logo2_current_height_;
|
||||
scale = logo2_scale_;
|
||||
squash_y = logo2_squash_y_;
|
||||
stretch_x = logo2_stretch_x_;
|
||||
rotation = logo2_rotation_;
|
||||
} else {
|
||||
return; // Índice inválido
|
||||
}
|
||||
|
||||
// Aplicar alpha específico de cada logo (con retraso para logo2)
|
||||
int alpha = (logo_index == 1) ? logo1_alpha_ : logo2_alpha_;
|
||||
float alpha_normalized = static_cast<float>(alpha) / 255.0f; // Convertir 0-255 → 0.0-1.0
|
||||
// NO usar SDL_SetTextureAlphaMod - aplicar alpha directamente a vértices
|
||||
|
||||
// Calcular padding desde bordes derecho e inferior
|
||||
float padding_x = screen_width_ * APPLOGO_PADDING_PERCENT;
|
||||
float padding_y = screen_height_ * APPLOGO_PADDING_PERCENT;
|
||||
|
||||
// Calcular esquina BASE (sin escala) para obtener centro FIJO
|
||||
// Esto asegura que el centro no se mueva cuando cambia scale/squash/stretch
|
||||
float corner_x_base = screen_width_ - base_width - padding_x;
|
||||
float corner_y_base = screen_height_ - base_height - padding_y;
|
||||
|
||||
// Centro FIJO del logo (no cambia con scale/squash/stretch)
|
||||
float center_x = corner_x_base + (base_width / 2.0f);
|
||||
float center_y = corner_y_base + (base_height / 2.0f);
|
||||
|
||||
// Calcular tamaño ESCALADO (para vértices)
|
||||
// (base_width y base_height ya están pre-escalados al tamaño correcto de pantalla)
|
||||
float width = base_width * scale * stretch_x;
|
||||
float height = base_height * scale * squash_y;
|
||||
|
||||
// Pre-calcular seno y coseno de rotación
|
||||
float cos_rot = cosf(rotation);
|
||||
float sin_rot = sinf(rotation);
|
||||
|
||||
// Crear 4 vértices del quad (centrado en center_x, center_y)
|
||||
SDL_Vertex vertices[4];
|
||||
|
||||
// Offset desde el centro
|
||||
float half_w = width / 2.0f;
|
||||
float half_h = height / 2.0f;
|
||||
|
||||
// Vértice superior izquierdo (rotado)
|
||||
{
|
||||
float local_x = -half_w;
|
||||
float local_y = -half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[0].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[0].tex_coord = {0.0f, 0.0f};
|
||||
vertices[0].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice superior derecho (rotado)
|
||||
{
|
||||
float local_x = half_w;
|
||||
float local_y = -half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[1].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[1].tex_coord = {1.0f, 0.0f};
|
||||
vertices[1].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice inferior derecho (rotado)
|
||||
{
|
||||
float local_x = half_w;
|
||||
float local_y = half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[2].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[2].tex_coord = {1.0f, 1.0f};
|
||||
vertices[2].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Vértice inferior izquierdo (rotado)
|
||||
{
|
||||
float local_x = -half_w;
|
||||
float local_y = half_h;
|
||||
float rotated_x = local_x * cos_rot - local_y * sin_rot;
|
||||
float rotated_y = local_x * sin_rot + local_y * cos_rot;
|
||||
vertices[3].position = {center_x + rotated_x, center_y + rotated_y};
|
||||
vertices[3].tex_coord = {0.0f, 1.0f};
|
||||
vertices[3].color = {1.0f, 1.0f, 1.0f, alpha_normalized}; // Alpha aplicado al vértice
|
||||
}
|
||||
|
||||
// Índices para 2 triángulos
|
||||
int indices[6] = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
// Renderizar con la textura del logo correspondiente
|
||||
SDL_RenderGeometry(renderer_, texture, vertices, 4, indices, 6);
|
||||
}
|
||||
117
source/app_logo.hpp
Normal file
117
source/app_logo.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_render.h> // for SDL_Renderer
|
||||
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
|
||||
#include "defines.hpp" // for AppMode
|
||||
|
||||
class Texture;
|
||||
class Sprite;
|
||||
|
||||
// Estados de la máquina de estados del logo
|
||||
enum class AppLogoState {
|
||||
HIDDEN, // Logo oculto, esperando APPLOGO_DISPLAY_INTERVAL
|
||||
FADE_IN, // Apareciendo (alpha 0 → 255)
|
||||
VISIBLE, // Completamente visible, esperando APPLOGO_DISPLAY_DURATION
|
||||
FADE_OUT // Desapareciendo (alpha 255 → 0)
|
||||
};
|
||||
|
||||
// Tipo de animación de entrada/salida
|
||||
enum class AppLogoAnimationType {
|
||||
ZOOM_ONLY, // A: Solo zoom simple (120% → 100% → 120%)
|
||||
ELASTIC_STICK, // B: Zoom + deformación elástica tipo "pegatina"
|
||||
ROTATE_SPIRAL, // C: Rotación en espiral (entra girando, sale girando)
|
||||
BOUNCE_SQUASH // D: Rebote con aplastamiento (cae rebotando, salta)
|
||||
};
|
||||
|
||||
class AppLogo {
|
||||
public:
|
||||
AppLogo() = default;
|
||||
~AppLogo(); // Necesario para liberar las 4 texturas SDL
|
||||
|
||||
// Inicializar textura y sprite del logo
|
||||
bool initialize(SDL_Renderer* renderer, int screen_width, int screen_height);
|
||||
|
||||
// Actualizar temporizadores y estado de visibilidad
|
||||
void update(float delta_time, AppMode current_mode);
|
||||
|
||||
// Renderizar logo si está visible
|
||||
void render();
|
||||
|
||||
// Actualizar tamaño de pantalla (reposicionar logo)
|
||||
void updateScreenSize(int screen_width, int screen_height);
|
||||
|
||||
private:
|
||||
// ====================================================================
|
||||
// Texturas pre-escaladas (4 texturas: 2 logos × 2 resoluciones)
|
||||
// ====================================================================
|
||||
SDL_Texture* logo1_base_texture_ = nullptr; // Logo1 para resolución base
|
||||
SDL_Texture* logo1_native_texture_ = nullptr; // Logo1 para resolución nativa (F4)
|
||||
SDL_Texture* logo2_base_texture_ = nullptr; // Logo2 para resolución base
|
||||
SDL_Texture* logo2_native_texture_ = nullptr; // Logo2 para resolución nativa (F4)
|
||||
|
||||
// Dimensiones pre-calculadas para cada textura
|
||||
int logo1_base_width_ = 0, logo1_base_height_ = 0;
|
||||
int logo1_native_width_ = 0, logo1_native_height_ = 0;
|
||||
int logo2_base_width_ = 0, logo2_base_height_ = 0;
|
||||
int logo2_native_width_ = 0, logo2_native_height_ = 0;
|
||||
|
||||
// Texturas actualmente en uso (apuntan a base o native según resolución)
|
||||
SDL_Texture* logo1_current_texture_ = nullptr;
|
||||
SDL_Texture* logo2_current_texture_ = nullptr;
|
||||
int logo1_current_width_ = 0, logo1_current_height_ = 0;
|
||||
int logo2_current_width_ = 0, logo2_current_height_ = 0;
|
||||
|
||||
// Resoluciones conocidas
|
||||
int base_screen_width_ = 0, base_screen_height_ = 0; // Resolución inicial
|
||||
int native_screen_width_ = 0, native_screen_height_ = 0; // Resolución nativa (F4)
|
||||
|
||||
// ====================================================================
|
||||
// Variables COMPARTIDAS (sincronización de ambos logos)
|
||||
// ====================================================================
|
||||
AppLogoState state_ = AppLogoState::HIDDEN; // Estado actual de la máquina de estados
|
||||
float timer_ = 0.0f; // Contador de tiempo para estado actual
|
||||
|
||||
// Alpha INDEPENDIENTE para cada logo (Logo 2 con retraso)
|
||||
int logo1_alpha_ = 0; // Alpha de Logo 1 (0-255)
|
||||
int logo2_alpha_ = 0; // Alpha de Logo 2 (0-255, con retraso)
|
||||
|
||||
// Animación COMPARTIDA (misma para ambos logos, misma entrada y salida)
|
||||
AppLogoAnimationType current_animation_ = AppLogoAnimationType::ZOOM_ONLY;
|
||||
|
||||
// Variables de deformación INDEPENDIENTES para logo1
|
||||
float logo1_scale_ = 1.0f; // Escala actual de logo1 (1.0 = 100%)
|
||||
float logo1_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo1
|
||||
float logo1_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo1
|
||||
float logo1_rotation_ = 0.0f; // Rotación en radianes logo1
|
||||
|
||||
// Variables de deformación INDEPENDIENTES para logo2
|
||||
float logo2_scale_ = 1.0f; // Escala actual de logo2 (1.0 = 100%)
|
||||
float logo2_squash_y_ = 1.0f; // Factor de aplastamiento vertical logo2
|
||||
float logo2_stretch_x_ = 1.0f; // Factor de estiramiento horizontal logo2
|
||||
float logo2_rotation_ = 0.0f; // Rotación en radianes logo2
|
||||
|
||||
int screen_width_ = 0; // Ancho de pantalla (para centrar)
|
||||
int screen_height_ = 0; // Alto de pantalla (para centrar)
|
||||
|
||||
// Tamaño base del logo (calculado una vez)
|
||||
float base_width_ = 0.0f;
|
||||
float base_height_ = 0.0f;
|
||||
|
||||
// SDL renderer (necesario para renderizado con geometría)
|
||||
SDL_Renderer* renderer_ = nullptr;
|
||||
|
||||
// Métodos privados auxiliares
|
||||
void updateLogoPosition(); // Centrar ambos logos en pantalla (superpuestos)
|
||||
void renderWithGeometry(int logo_index); // Renderizar logo con vértices deformados (1 o 2)
|
||||
|
||||
// Funciones de easing
|
||||
float easeOutElastic(float t); // Elastic bounce out
|
||||
float easeOutBack(float t); // Overshoot out
|
||||
float easeOutBounce(float t); // Bounce easing (para BOUNCE_SQUASH)
|
||||
float easeInOutQuad(float t); // Quadratic easing (para ROTATE_SPIRAL)
|
||||
|
||||
// Función auxiliar para elegir animación aleatoria
|
||||
AppLogoAnimationType getRandomAnimation();
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "ball.h"
|
||||
#include "ball.hpp"
|
||||
|
||||
#include <stdlib.h> // for rand
|
||||
|
||||
#include <cmath> // for fabs
|
||||
|
||||
#include "defines.h" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
||||
#include "defines.hpp" // for Color, SCREEN_HEIGHT, GRAVITY_FORCE
|
||||
class Texture;
|
||||
|
||||
// Función auxiliar para generar pérdida aleatoria en rebotes
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
|
||||
#include "defines.h" // for Color
|
||||
#include "external/sprite.h" // for Sprite
|
||||
#include "defines.hpp" // for Color
|
||||
#include "external/sprite.hpp" // for Sprite
|
||||
class Texture;
|
||||
|
||||
class Ball {
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "boid_manager.h"
|
||||
#include "boid_manager.hpp"
|
||||
|
||||
#include <algorithm> // for std::min, std::max
|
||||
#include <cmath> // for sqrt, atan2
|
||||
|
||||
#include "../ball.h" // for Ball
|
||||
#include "../engine.h" // for Engine (si se necesita)
|
||||
#include "../scene/scene_manager.h" // for SceneManager
|
||||
#include "../state/state_manager.h" // for StateManager
|
||||
#include "../ui/ui_manager.h" // for UIManager
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "engine.hpp" // for Engine (si se necesita)
|
||||
#include "scene/scene_manager.hpp" // for SceneManager
|
||||
#include "state/state_manager.hpp" // for StateManager
|
||||
#include "ui/ui_manager.hpp" // for UIManager
|
||||
|
||||
BoidManager::BoidManager()
|
||||
: engine_(nullptr)
|
||||
@@ -17,7 +17,18 @@ BoidManager::BoidManager()
|
||||
, screen_width_(0)
|
||||
, screen_height_(0)
|
||||
, boids_active_(false)
|
||||
, spatial_grid_(800, 600, BOID_GRID_CELL_SIZE) { // Tamaño por defecto, se actualiza en initialize()
|
||||
, 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() {
|
||||
@@ -57,9 +68,9 @@ void BoidManager::activateBoids() {
|
||||
float vx, vy;
|
||||
ball->getVelocity(vx, vy);
|
||||
if (vx == 0.0f && vy == 0.0f) {
|
||||
// Velocidad aleatoria entre -1 y 1
|
||||
vx = (rand() % 200 - 100) / 100.0f;
|
||||
vy = (rand() % 200 - 100) / 100.0f;
|
||||
// Velocidad aleatoria entre -60 y +60 px/s (time-based)
|
||||
vx = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||
vy = ((rand() % 200 - 100) / 100.0f) * 60.0f;
|
||||
ball->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
@@ -118,14 +129,14 @@ void BoidManager::update(float delta_time) {
|
||||
limitSpeed(ball.get());
|
||||
}
|
||||
|
||||
// Actualizar posiciones con velocidades resultantes
|
||||
// Actualizar posiciones con velocidades resultantes (time-based)
|
||||
for (auto& ball : balls) {
|
||||
float vx, vy;
|
||||
ball->getVelocity(vx, vy);
|
||||
|
||||
SDL_FRect pos = ball->getPosition();
|
||||
pos.x += vx;
|
||||
pos.y += vy;
|
||||
pos.x += vx * delta_time; // time-based
|
||||
pos.y += vy * delta_time;
|
||||
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
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 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)
|
||||
// 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_y += (dy / distance) * separation_strength;
|
||||
count++;
|
||||
@@ -177,8 +188,8 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||
// Aplicar fuerza de separación
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
vx += steer_x * BOID_SEPARATION_WEIGHT * delta_time;
|
||||
vy += steer_y * BOID_SEPARATION_WEIGHT * delta_time;
|
||||
vx += steer_x * separation_weight_ * delta_time;
|
||||
vy += steer_y * separation_weight_ * delta_time;
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
if (other == boid) continue;
|
||||
@@ -207,7 +218,7 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < BOID_ALIGNMENT_RADIUS) {
|
||||
if (distance < alignment_radius_) {
|
||||
float other_vx, other_vy;
|
||||
other->getVelocity(other_vx, other_vy);
|
||||
avg_vx += other_vx;
|
||||
@@ -224,14 +235,14 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
// Steering hacia la velocidad promedio
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
float steer_x = (avg_vx - vx) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
||||
float steer_y = (avg_vy - vy) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
||||
float steer_x = (avg_vx - vx) * alignment_weight_ * delta_time;
|
||||
float steer_y = (avg_vy - vy) * alignment_weight_ * delta_time;
|
||||
|
||||
// Limitar fuerza máxima de steering
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > BOID_MAX_FORCE) {
|
||||
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
||||
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
vx += steer_x;
|
||||
@@ -251,7 +262,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, BOID_COHESION_RADIUS);
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, cohesion_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue;
|
||||
@@ -264,7 +275,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
float dy = center_y - other_y;
|
||||
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_y += other_y;
|
||||
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)
|
||||
if (distance_to_center > 0.1f) {
|
||||
// Normalizar vector dirección (fuerza independiente de distancia)
|
||||
float steer_x = (dx_to_center / distance_to_center) * BOID_COHESION_WEIGHT * delta_time;
|
||||
float steer_y = (dy_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) * cohesion_weight_ * delta_time;
|
||||
|
||||
// Limitar fuerza máxima de steering
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > BOID_MAX_FORCE) {
|
||||
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
||||
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
float vx, vy;
|
||||
@@ -304,32 +315,69 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
}
|
||||
|
||||
void BoidManager::applyBoundaries(Ball* boid) {
|
||||
// Mantener boids dentro de los límites de la pantalla
|
||||
// Comportamiento "wrapping" (teletransporte al otro lado)
|
||||
// NUEVA IMPLEMENTACIÓN: Bordes como obstáculos (repulsión en lugar de wrapping)
|
||||
// Cuando un boid se acerca a un borde, se aplica una fuerza alejándolo
|
||||
SDL_FRect pos = boid->getPosition();
|
||||
float center_x = pos.x + pos.w / 2.0f;
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
bool wrapped = false;
|
||||
float steer_x = 0.0f;
|
||||
float steer_y = 0.0f;
|
||||
|
||||
if (center_x < 0) {
|
||||
pos.x = screen_width_ - pos.w / 2.0f;
|
||||
wrapped = true;
|
||||
} else if (center_x > screen_width_) {
|
||||
pos.x = -pos.w / 2.0f;
|
||||
wrapped = true;
|
||||
// Borde izquierdo (x < boundary_margin_)
|
||||
if (center_x < boundary_margin_) {
|
||||
float distance = center_x; // Distancia al borde (0 = colisión)
|
||||
if (distance < boundary_margin_) {
|
||||
// Fuerza proporcional a cercanía: 0% en margen, 100% en colisión
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x += repulsion_strength; // Empujar hacia la derecha
|
||||
}
|
||||
}
|
||||
|
||||
if (center_y < 0) {
|
||||
pos.y = screen_height_ - pos.h / 2.0f;
|
||||
wrapped = true;
|
||||
} else if (center_y > screen_height_) {
|
||||
pos.y = -pos.h / 2.0f;
|
||||
wrapped = true;
|
||||
// Borde derecho (x > screen_width_ - boundary_margin_)
|
||||
if (center_x > screen_width_ - boundary_margin_) {
|
||||
float distance = screen_width_ - center_x;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x -= repulsion_strength; // Empujar hacia la izquierda
|
||||
}
|
||||
}
|
||||
|
||||
if (wrapped) {
|
||||
boid->setPosition(pos.x, pos.y);
|
||||
// Borde superior (y < boundary_margin_)
|
||||
if (center_y < boundary_margin_) {
|
||||
float distance = center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y += repulsion_strength; // Empujar hacia abajo
|
||||
}
|
||||
}
|
||||
|
||||
// Borde inferior (y > screen_height_ - boundary_margin_)
|
||||
if (center_y > screen_height_ - boundary_margin_) {
|
||||
float distance = screen_height_ - center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y -= repulsion_strength; // Empujar hacia arriba
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar fuerza de repulsión si hay alguna
|
||||
if (steer_x != 0.0f || steer_y != 0.0f) {
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
|
||||
// Normalizar fuerza de repulsión (para que todas las direcciones tengan la misma intensidad)
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > 0.0f) {
|
||||
steer_x /= steer_mag;
|
||||
steer_y /= steer_mag;
|
||||
}
|
||||
|
||||
// Aplicar aceleración de repulsión (time-based)
|
||||
// boundary_weight_ es más fuerte que separation para garantizar que no escapen
|
||||
vx += steer_x * boundary_weight_ * (1.0f / 60.0f); // Simular delta_time fijo para independencia
|
||||
vy += steer_y * boundary_weight_ * (1.0f / 60.0f);
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,16 +389,16 @@ void BoidManager::limitSpeed(Ball* boid) {
|
||||
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;
|
||||
if (speed > max_speed_) {
|
||||
vx = (vx / speed) * max_speed_;
|
||||
vy = (vy / speed) * max_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
|
||||
// FASE 1.2: Aplicar velocidad mínima (evitar boids estáticos)
|
||||
if (speed > 0.0f && speed < BOID_MIN_SPEED) {
|
||||
vx = (vx / speed) * BOID_MIN_SPEED;
|
||||
vy = (vy / speed) * BOID_MIN_SPEED;
|
||||
if (speed > 0.0f && speed < min_speed_) {
|
||||
vx = (vx / speed) * min_speed_;
|
||||
vy = (vy / speed) * min_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
|
||||
#include "../defines.h" // for SimulationMode, AppMode
|
||||
#include "../spatial_grid.h" // for SpatialGrid
|
||||
#include "defines.hpp" // for SimulationMode, AppMode
|
||||
#include "spatial_grid.hpp" // for SpatialGrid
|
||||
|
||||
// Forward declarations
|
||||
class Engine;
|
||||
@@ -103,10 +103,24 @@ class BoidManager {
|
||||
// FASE 2: Grid reutilizable para búsquedas de vecinos
|
||||
SpatialGrid spatial_grid_;
|
||||
|
||||
// === Parámetros ajustables en runtime (inicializados con valores de defines.h) ===
|
||||
// Permite modificar comportamiento sin recompilar (para tweaking/debug visual)
|
||||
float separation_radius_; // Radio de separación (evitar colisiones)
|
||||
float alignment_radius_; // Radio de alineación (matching de velocidad)
|
||||
float cohesion_radius_; // Radio de cohesión (centro de masa)
|
||||
float separation_weight_; // Peso fuerza de separación (aceleración px/s²)
|
||||
float alignment_weight_; // Peso fuerza de alineación (steering proporcional)
|
||||
float cohesion_weight_; // Peso fuerza de cohesión (aceleración px/s²)
|
||||
float max_speed_; // Velocidad máxima (px/s)
|
||||
float min_speed_; // Velocidad mínima (px/s)
|
||||
float max_force_; // Fuerza máxima de steering (px/s)
|
||||
float boundary_margin_; // Margen para repulsión de bordes (px)
|
||||
float boundary_weight_; // Peso fuerza de repulsión de bordes (aceleración px/s²)
|
||||
|
||||
// Métodos privados para las reglas de Reynolds
|
||||
void applySeparation(Ball* boid, float delta_time);
|
||||
void applyAlignment(Ball* boid, float delta_time);
|
||||
void applyCohesion(Ball* boid, float delta_time);
|
||||
void applyBoundaries(Ball* boid); // Mantener boids dentro de pantalla
|
||||
void applyBoundaries(Ball* boid); // Repulsión de bordes (ya no wrapping)
|
||||
void limitSpeed(Ball* boid); // Limitar velocidad máxima
|
||||
};
|
||||
@@ -8,9 +8,9 @@
|
||||
constexpr char WINDOW_CAPTION[] = "ViBe3 Physics (JailDesigner 2025)";
|
||||
|
||||
// Resolución por defecto (usada si no se especifica en CLI)
|
||||
constexpr int DEFAULT_SCREEN_WIDTH = 320; // Ancho lógico por defecto (si no hay -w)
|
||||
constexpr int DEFAULT_SCREEN_HEIGHT = 240; // Alto lógico por defecto (si no hay -h)
|
||||
constexpr int DEFAULT_WINDOW_ZOOM = 3; // Zoom inicial de ventana (1x = sin zoom)
|
||||
constexpr int DEFAULT_SCREEN_WIDTH = 1280; // Ancho lógico por defecto (si no hay -w)
|
||||
constexpr int DEFAULT_SCREEN_HEIGHT = 720; // Alto lógico por defecto (si no hay -h)
|
||||
constexpr int DEFAULT_WINDOW_ZOOM = 1; // Zoom inicial de ventana (1x = sin zoom)
|
||||
|
||||
// Configuración de zoom dinámico de ventana
|
||||
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
|
||||
@@ -22,17 +22,16 @@ constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones
|
||||
constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²)
|
||||
|
||||
// Configuración de interfaz
|
||||
constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms) - OBSOLETO, usar NOTIFICATION_DURATION
|
||||
constexpr float THEME_TRANSITION_DURATION = 0.5f; // Duración de transiciones LERP entre temas (segundos)
|
||||
|
||||
// Configuración de notificaciones (sistema Notifier)
|
||||
constexpr int TEXT_ABSOLUTE_SIZE = 12; // Tamaño fuente base en píxeles físicos (múltiplo de 12px, tamaño nativo de la fuente)
|
||||
constexpr Uint64 NOTIFICATION_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (ms)
|
||||
constexpr Uint64 NOTIFICATION_FADE_TIME = 200; // Duración animación salida (ms)
|
||||
constexpr float NOTIFICATION_BG_ALPHA = 0.7f; // Opacidad fondo semitransparente (0.0-1.0)
|
||||
constexpr int NOTIFICATION_PADDING = 10; // Padding interno del fondo (píxeles físicos)
|
||||
constexpr int NOTIFICATION_TOP_MARGIN = 20; // Margen superior desde borde pantalla (píxeles físicos)
|
||||
constexpr int TEXT_ABSOLUTE_SIZE = 12; // Tamaño fuente base en píxeles físicos (múltiplo de 12px, tamaño nativo de la fuente)
|
||||
constexpr Uint64 NOTIFICATION_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (ms)
|
||||
constexpr Uint64 NOTIFICATION_FADE_TIME = 200; // Duración animación salida (ms)
|
||||
constexpr float NOTIFICATION_BG_ALPHA = 0.7f; // Opacidad fondo semitransparente (0.0-1.0)
|
||||
constexpr int NOTIFICATION_PADDING = 10; // Padding interno del fondo (píxeles físicos)
|
||||
constexpr int NOTIFICATION_TOP_MARGIN = 20; // Margen superior desde borde pantalla (píxeles físicos)
|
||||
|
||||
// Configuración de pérdida aleatoria en rebotes
|
||||
constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas
|
||||
@@ -60,12 +59,12 @@ struct Color {
|
||||
|
||||
// Estructura de tema de colores estático
|
||||
struct ThemeColors {
|
||||
const char* name_en; // Nombre en inglés (para debug)
|
||||
const char* name_es; // Nombre en español (para display)
|
||||
int text_color_r, text_color_g, text_color_b; // Color del texto del tema
|
||||
float bg_top_r, bg_top_g, bg_top_b;
|
||||
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
||||
std::vector<Color> ball_colors;
|
||||
const char* name_en; // Nombre en inglés (para debug)
|
||||
const char* name_es; // Nombre en español (para display)
|
||||
int text_color_r, text_color_g, text_color_b; // Color del texto del tema
|
||||
float bg_top_r, bg_top_g, bg_top_b;
|
||||
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
||||
std::vector<Color> ball_colors;
|
||||
};
|
||||
|
||||
// Estructura para keyframe de tema dinámico
|
||||
@@ -99,21 +98,21 @@ enum class GravityDirection {
|
||||
// Enum para temas de colores (seleccionables con teclado numérico y Shift+Numpad)
|
||||
// Todos los temas usan ahora sistema dinámico de keyframes
|
||||
enum class ColorTheme {
|
||||
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
||||
OCEAN = 1, // Azules, turquesas, blancos (estático: 1 keyframe)
|
||||
NEON = 2, // Cian, magenta, verde lima, amarillo vibrante (estático: 1 keyframe)
|
||||
FOREST = 3, // Verdes, marrones, amarillos otoño (estático: 1 keyframe)
|
||||
RGB = 4, // RGB puros y subdivisiones matemáticas - fondo blanco (estático: 1 keyframe)
|
||||
MONOCHROME = 5, // Fondo negro degradado, sprites blancos monocromáticos (estático: 1 keyframe)
|
||||
LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe)
|
||||
CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (estático: 1 keyframe)
|
||||
EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe)
|
||||
SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo)
|
||||
OCEAN_WAVES = 10, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo)
|
||||
NEON_PULSE = 11, // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong)
|
||||
FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo)
|
||||
AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s ciclo)
|
||||
VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo)
|
||||
SUNSET = 0, // Naranjas, rojos, amarillos, rosas (estático: 1 keyframe)
|
||||
OCEAN = 1, // Azules, turquesas, blancos (estático: 1 keyframe)
|
||||
NEON = 2, // Cian, magenta, verde lima, amarillo vibrante (estático: 1 keyframe)
|
||||
FOREST = 3, // Verdes, marrones, amarillos otoño (estático: 1 keyframe)
|
||||
RGB = 4, // RGB puros y subdivisiones matemáticas - fondo blanco (estático: 1 keyframe)
|
||||
MONOCHROME = 5, // Fondo negro degradado, sprites blancos monocromáticos (estático: 1 keyframe)
|
||||
LAVENDER = 6, // Degradado violeta-azul, pelotas amarillo dorado (estático: 1 keyframe)
|
||||
CRIMSON = 7, // Fondo negro-rojo, pelotas rojas uniformes (estático: 1 keyframe)
|
||||
EMERALD = 8, // Fondo negro-verde, pelotas verdes uniformes (estático: 1 keyframe)
|
||||
SUNRISE = 9, // Amanecer: Noche → Alba → Día (animado: 4 keyframes, 12s ciclo)
|
||||
OCEAN_WAVES = 10, // Olas oceánicas: Azul oscuro ↔ Turquesa (animado: 3 keyframes, 8s ciclo)
|
||||
NEON_PULSE = 11, // Pulso neón: Negro ↔ Neón vibrante (animado: 3 keyframes, 3s ping-pong)
|
||||
FIRE = 12, // Fuego vivo: Brasas → Llamas → Inferno (animado: 4 keyframes, 10s ciclo)
|
||||
AURORA = 13, // Aurora boreal: Verde → Violeta → Cian (animado: 4 keyframes, 14s ciclo)
|
||||
VOLCANIC = 14 // Erupción volcánica: Ceniza → Erupción → Lava (animado: 4 keyframes, 12s ciclo)
|
||||
};
|
||||
|
||||
// Enum para tipo de figura 3D
|
||||
@@ -282,27 +281,42 @@ constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO = 5; // 5% probabilidad en D
|
||||
constexpr int LOGO_JUMP_PROBABILITY_FROM_DEMO_LITE = 3; // 3% probabilidad en DEMO LITE (aún más raro)
|
||||
|
||||
// Sistema de espera de flips en LOGO MODE (camino alternativo de decisión)
|
||||
constexpr int LOGO_FLIP_WAIT_MIN = 1; // Mínimo de flips a esperar antes de cambiar a PHYSICS
|
||||
constexpr int LOGO_FLIP_WAIT_MAX = 3; // Máximo de flips a esperar
|
||||
constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progreso de flip para trigger
|
||||
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
||||
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
||||
constexpr int LOGO_FLIP_WAIT_MIN = 1; // Mínimo de flips a esperar antes de cambiar a PHYSICS
|
||||
constexpr int LOGO_FLIP_WAIT_MAX = 3; // Máximo de flips a esperar
|
||||
constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progreso de flip para trigger
|
||||
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
||||
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
||||
|
||||
// Configuración de AppLogo (logo periódico en pantalla)
|
||||
constexpr float APPLOGO_DISPLAY_INTERVAL = 90.0f; // Intervalo entre apariciones del logo (segundos)
|
||||
constexpr float APPLOGO_DISPLAY_DURATION = 30.0f; // Duración de visibilidad del logo (segundos)
|
||||
constexpr float APPLOGO_ANIMATION_DURATION = 0.5f; // Duración de animación entrada/salida (segundos)
|
||||
constexpr float APPLOGO_HEIGHT_PERCENT = 0.4f; // Altura del logo = 40% de la altura de pantalla
|
||||
constexpr float APPLOGO_PADDING_PERCENT = 0.05f; // Padding desde esquina inferior-derecha = 10%
|
||||
constexpr float APPLOGO_LOGO2_DELAY = 0.25f; // Retraso de Logo 2 respecto a Logo 1 (segundos)
|
||||
|
||||
// Configuración de Modo BOIDS (comportamiento de enjambre)
|
||||
// FASE 1.1 REVISADA: Parámetros ajustados tras detectar cohesión mal normalizada
|
||||
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
||||
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
||||
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
||||
constexpr float BOID_SEPARATION_WEIGHT = 1.5f; // Peso de separación
|
||||
constexpr float BOID_ALIGNMENT_WEIGHT = 1.0f; // Peso de alineación
|
||||
constexpr float BOID_COHESION_WEIGHT = 0.001f; // Peso de cohesión (MICRO - 1000x menor por falta de normalización)
|
||||
constexpr float BOID_MAX_SPEED = 2.5f; // Velocidad máxima (píxeles/frame - REDUCIDA)
|
||||
constexpr float BOID_MAX_FORCE = 0.05f; // Fuerza máxima de steering (REDUCIDA para evitar aceleración excesiva)
|
||||
constexpr float BOID_MIN_SPEED = 0.3f; // Velocidad mínima (evita boids estáticos)
|
||||
// TIME-BASED CONVERSION (frame-based → time-based):
|
||||
// - Radios: sin cambios (píxeles)
|
||||
// - Velocidades (MAX_SPEED, MIN_SPEED): ×60 (px/frame → px/s)
|
||||
// - Aceleraciones puras (SEPARATION, COHESION): ×60² = ×3600 (px/frame² → px/s²)
|
||||
// - Steering proporcional (ALIGNMENT): ×60 (proporcional a velocidad)
|
||||
// - Límite velocidad (MAX_FORCE): ×60 (px/frame → px/s)
|
||||
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
||||
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
||||
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
||||
constexpr float BOID_SEPARATION_WEIGHT = 5400.0f; // Aceleración de separación (px/s²) [era 1.5 × 3600]
|
||||
constexpr float BOID_ALIGNMENT_WEIGHT = 60.0f; // Steering de alineación (proporcional) [era 1.0 × 60]
|
||||
constexpr float BOID_COHESION_WEIGHT = 3.6f; // Aceleración de cohesión (px/s²) [era 0.001 × 3600]
|
||||
constexpr float BOID_MAX_SPEED = 150.0f; // Velocidad máxima (px/s) [era 2.5 × 60]
|
||||
constexpr float BOID_MAX_FORCE = 3.0f; // Fuerza máxima de steering (px/s) [era 0.05 × 60]
|
||||
constexpr float BOID_MIN_SPEED = 18.0f; // Velocidad mínima (px/s) [era 0.3 × 60]
|
||||
constexpr float BOID_BOUNDARY_MARGIN = 50.0f; // Distancia a borde para activar repulsión (píxeles)
|
||||
constexpr float BOID_BOUNDARY_WEIGHT = 7200.0f; // Aceleración de repulsión de bordes (px/s²) [más fuerte que separation]
|
||||
|
||||
// FASE 2: Spatial Hash Grid para optimización O(n²) → O(n)
|
||||
constexpr float BOID_GRID_CELL_SIZE = 100.0f; // Tamaño de celda del grid (píxeles)
|
||||
// Debe ser ≥ BOID_COHESION_RADIUS para funcionar correctamente
|
||||
// Debe ser ≥ BOID_COHESION_RADIUS para funcionar correctamente
|
||||
|
||||
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_events.h> // for SDL_Event, SDL_PollEvent
|
||||
@@ -17,27 +17,29 @@
|
||||
#include <iostream> // for cout
|
||||
#include <string> // for string
|
||||
|
||||
#include "resource_manager.hpp" // for ResourceManager
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // for GetModuleFileName
|
||||
#endif
|
||||
|
||||
#include "ball.h" // for Ball
|
||||
#include "external/mouse.h" // for Mouse namespace
|
||||
#include "external/texture.h" // for Texture
|
||||
#include "shapes/atom_shape.h" // for AtomShape
|
||||
#include "shapes/cube_shape.h" // for CubeShape
|
||||
#include "shapes/cylinder_shape.h" // for CylinderShape
|
||||
#include "shapes/helix_shape.h" // for HelixShape
|
||||
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
||||
#include "shapes/lissajous_shape.h" // for LissajousShape
|
||||
#include "shapes/png_shape.h" // for PNGShape
|
||||
#include "shapes/sphere_shape.h" // for SphereShape
|
||||
#include "shapes/torus_shape.h" // for TorusShape
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "external/mouse.hpp" // for Mouse namespace
|
||||
#include "external/texture.hpp" // for Texture
|
||||
#include "shapes/atom_shape.hpp" // for AtomShape
|
||||
#include "shapes/cube_shape.hpp" // for CubeShape
|
||||
#include "shapes/cylinder_shape.hpp" // for CylinderShape
|
||||
#include "shapes/helix_shape.hpp" // for HelixShape
|
||||
#include "shapes/icosahedron_shape.hpp" // for IcosahedronShape
|
||||
#include "shapes/lissajous_shape.hpp" // for LissajousShape
|
||||
#include "shapes/png_shape.hpp" // for PNGShape
|
||||
#include "shapes/sphere_shape.hpp" // for SphereShape
|
||||
#include "shapes/torus_shape.hpp" // for TorusShape
|
||||
|
||||
// getExecutableDirectory() ya está definido en defines.h como inline
|
||||
|
||||
// Implementación de métodos públicos
|
||||
bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMode initial_mode) {
|
||||
bool success = true;
|
||||
|
||||
// Obtener resolución de pantalla para validación
|
||||
@@ -164,9 +166,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback: cargar texturas desde pack usando la lista del ResourcePack
|
||||
if (Texture::isPackLoaded()) {
|
||||
auto pack_resources = Texture::getPackResourceList();
|
||||
// Fallback: cargar texturas desde pack usando la lista del ResourceManager
|
||||
if (ResourceManager::isPackLoaded()) {
|
||||
auto pack_resources = ResourceManager::getResourceList();
|
||||
|
||||
// Filtrar solo los recursos en balls/ con extensión .png
|
||||
for (const auto& resource : pack_resources) {
|
||||
@@ -235,7 +237,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
// Inicializar UIManager (HUD, FPS, notificaciones)
|
||||
// NOTA: Debe llamarse DESPUÉS de calcular physical_window_* y ThemeManager
|
||||
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)
|
||||
shape_manager_ = std::make_unique<ShapeManager>();
|
||||
@@ -246,6 +250,22 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
state_manager_ = std::make_unique<StateManager>();
|
||||
state_manager_->initialize(this); // Callback al Engine
|
||||
|
||||
// Establecer modo inicial si no es SANDBOX (default)
|
||||
// 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)
|
||||
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||
current_screen_width_, current_screen_height_);
|
||||
@@ -254,6 +274,14 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
||||
boid_manager_ = std::make_unique<BoidManager>();
|
||||
boid_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -334,6 +362,11 @@ void Engine::update() {
|
||||
|
||||
// Actualizar transiciones de temas (delegado a ThemeManager)
|
||||
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 ===
|
||||
@@ -343,7 +376,7 @@ void Engine::handleGravityToggle() {
|
||||
// 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"
|
||||
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
|
||||
// La gravedad ya está desactivada por BoidManager::activateBoids() y se mantiene al salir
|
||||
showNotificationForAction("Modo Física - Gravedad Off");
|
||||
@@ -364,18 +397,19 @@ void Engine::handleGravityToggle() {
|
||||
}
|
||||
|
||||
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) {
|
||||
toggleBoidsMode(); // Esto cambia a PHYSICS y activa gravedad
|
||||
// Continuar para aplicar la dirección de gravedad
|
||||
current_mode_ = SimulationMode::PHYSICS;
|
||||
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
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
else if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeModeInternal(); // Desactivar figura (activa gravedad automáticamente)
|
||||
} else {
|
||||
scene_manager_->enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||
}
|
||||
|
||||
scene_manager_->changeGravityDirection(direction);
|
||||
showNotificationForAction(notification_text);
|
||||
}
|
||||
@@ -435,11 +469,11 @@ void Engine::toggleDepthZoom() {
|
||||
}
|
||||
|
||||
// Boids (comportamiento de enjambre)
|
||||
void Engine::toggleBoidsMode() {
|
||||
void Engine::toggleBoidsMode(bool force_gravity_on) {
|
||||
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;
|
||||
boid_manager_->deactivateBoids();
|
||||
boid_manager_->deactivateBoids(force_gravity_on); // Pasar parámetro para control preciso
|
||||
} else {
|
||||
// Entrar al modo boids (desde PHYSICS o SHAPE)
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
@@ -522,6 +556,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);
|
||||
}
|
||||
|
||||
@@ -710,6 +749,11 @@ void Engine::render() {
|
||||
active_shape_.get(), shape_convergence_,
|
||||
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_);
|
||||
}
|
||||
|
||||
@@ -744,6 +788,12 @@ void Engine::toggleFullscreen() {
|
||||
fullscreen_enabled_ = !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
|
||||
updatePhysicalWindowSize();
|
||||
}
|
||||
@@ -784,6 +834,22 @@ void Engine::toggleRealFullscreen() {
|
||||
|
||||
// Actualizar tamaño de pantalla para boids (wrapping boundaries)
|
||||
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);
|
||||
}
|
||||
@@ -806,6 +872,22 @@ void Engine::toggleRealFullscreen() {
|
||||
// Reinicar la escena con resolución original
|
||||
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1331,6 +1413,18 @@ void Engine::executeDemoAction(bool is_lite) {
|
||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||
int new_scenario = valid_scenarios[rand() % 5];
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1412,21 +1506,7 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
|
||||
} else {
|
||||
// DEMO COMPLETO: Randomizar TODO
|
||||
|
||||
// 1. Escenario (excluir índices 0, 6, 7)
|
||||
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
|
||||
// 1. Física o Figura (decidir PRIMERO antes de cambiar escenario)
|
||||
if (rand() % 2 == 0) {
|
||||
// Modo física
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
@@ -1435,20 +1515,83 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
|
||||
} else {
|
||||
// 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};
|
||||
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) {
|
||||
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;
|
||||
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 (excluir índices 0, 6, 7) - AHORA con current_mode_ ya establecido correctamente
|
||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||
int new_scenario = valid_scenarios[rand() % 5];
|
||||
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);
|
||||
scene_manager_->changeGravityDirection(new_direction);
|
||||
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
||||
@@ -1545,6 +1688,15 @@ void Engine::executeExitLogoMode() {
|
||||
clampShapeScale();
|
||||
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)
|
||||
if (active_shape_) {
|
||||
PNGShape* png_shape = dynamic_cast<PNGShape*>(active_shape_.get());
|
||||
|
||||
@@ -10,22 +10,23 @@
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ball.h" // for Ball
|
||||
#include "boids_mgr/boid_manager.h" // for BoidManager
|
||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||
#include "external/texture.h" // for Texture
|
||||
#include "input/input_handler.h" // for InputHandler
|
||||
#include "scene/scene_manager.h" // for SceneManager
|
||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||
#include "shapes_mgr/shape_manager.h" // for ShapeManager
|
||||
#include "state/state_manager.h" // for StateManager
|
||||
#include "theme_manager.h" // for ThemeManager
|
||||
#include "ui/ui_manager.h" // for UIManager
|
||||
#include "app_logo.hpp" // for AppLogo
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "boids_mgr/boid_manager.hpp" // for BoidManager
|
||||
#include "defines.hpp" // for GravityDirection, ColorTheme, ShapeType
|
||||
#include "external/texture.hpp" // for Texture
|
||||
#include "input/input_handler.hpp" // for InputHandler
|
||||
#include "scene/scene_manager.hpp" // for SceneManager
|
||||
#include "shapes/shape.hpp" // for Shape (interfaz polimórfica)
|
||||
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
|
||||
#include "state/state_manager.hpp" // for StateManager
|
||||
#include "theme_manager.hpp" // for ThemeManager
|
||||
#include "ui/ui_manager.hpp" // for UIManager
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
// Interfaz pública principal
|
||||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false);
|
||||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
|
||||
void run();
|
||||
void shutdown();
|
||||
|
||||
@@ -49,7 +50,7 @@ class Engine {
|
||||
void toggleDepthZoom();
|
||||
|
||||
// Boids (comportamiento de enjambre)
|
||||
void toggleBoidsMode();
|
||||
void toggleBoidsMode(bool force_gravity_on = true);
|
||||
|
||||
// Temas de colores
|
||||
void cycleTheme(bool forward);
|
||||
@@ -105,6 +106,7 @@ class Engine {
|
||||
std::unique_ptr<BoidManager> boid_manager_; // Gestión de comportamiento boids
|
||||
std::unique_ptr<StateManager> state_manager_; // Gestión de estados (DEMO/LOGO)
|
||||
std::unique_ptr<UIManager> ui_manager_; // Gestión de UI (HUD, FPS, notificaciones)
|
||||
std::unique_ptr<AppLogo> app_logo_; // Gestión de logo periódico en pantalla
|
||||
|
||||
// Recursos SDL
|
||||
SDL_Window* window_ = nullptr;
|
||||
@@ -160,7 +162,6 @@ class Engine {
|
||||
// Sistema de Modo DEMO (auto-play) y LOGO
|
||||
// NOTA: Engine mantiene estado de implementación para callbacks performLogoAction()
|
||||
// StateManager coordina los triggers y timers, Engine ejecuta las acciones
|
||||
AppMode previous_app_mode_ = AppMode::SANDBOX; // Modo previo antes de entrar a LOGO
|
||||
float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción
|
||||
float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos)
|
||||
|
||||
78
source/external/dbgtxt.h
vendored
78
source/external/dbgtxt.h
vendored
@@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace {
|
||||
SDL_Texture* dbg_tex = nullptr;
|
||||
SDL_Renderer* dbg_ren = nullptr;
|
||||
} // namespace
|
||||
|
||||
inline void dbg_init(SDL_Renderer* renderer) {
|
||||
dbg_ren = renderer;
|
||||
Uint8 font[448] = {0x42, 0x4D, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x18, 0xF3, 0x83, 0x83, 0xCF, 0x83, 0x87, 0x00, 0x00, 0xF3, 0x39, 0x39, 0xCF, 0x79, 0xF3, 0x00, 0x00, 0x01, 0xF9, 0x39, 0xCF, 0x61, 0xF9, 0x00, 0x00, 0x33, 0xF9, 0x03, 0xE7, 0x87, 0x81, 0x00, 0x00, 0x93, 0x03, 0x3F, 0xF3, 0x1B, 0x39, 0x00, 0x00, 0xC3, 0x3F, 0x9F, 0x39, 0x3B, 0x39, 0x00, 0x41, 0xE3, 0x03, 0xC3, 0x01, 0x87, 0x83, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0x01, 0xC7, 0x81, 0x01, 0x83, 0x00, 0x00, 0xE7, 0x1F, 0x9B, 0xE7, 0x1F, 0x39, 0x00, 0x00, 0xE7, 0x8F, 0x39, 0xE7, 0x87, 0xF9, 0x00, 0x00, 0xC3, 0xC7, 0x39, 0xE7, 0xC3, 0xC3, 0x00, 0x00, 0x99, 0xE3, 0x39, 0xE7, 0xF1, 0xE7, 0x00, 0x00, 0x99, 0xF1, 0xB3, 0xC7, 0x39, 0xF3, 0x00, 0x00, 0x99, 0x01, 0xC7, 0xE7, 0x83, 0x81, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x83, 0xE7, 0x83, 0xEF, 0x39, 0x39, 0x00, 0x00, 0x39, 0xE7, 0x39, 0xC7, 0x11, 0x11, 0x00, 0x00, 0xF9, 0xE7, 0x39, 0x83, 0x01, 0x83, 0x00, 0x00, 0x83, 0xE7, 0x39, 0x11, 0x01, 0xC7, 0x00, 0x00, 0x3F, 0xE7, 0x39, 0x39, 0x29, 0x83, 0x00, 0x00, 0x33, 0xE7, 0x39, 0x39, 0x39, 0x11, 0x00, 0x00, 0x87, 0x81, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x39, 0x39, 0x83, 0x3F, 0x85, 0x31, 0x00, 0x00, 0x39, 0x31, 0x39, 0x3F, 0x33, 0x23, 0x00, 0x00, 0x29, 0x21, 0x39, 0x03, 0x21, 0x07, 0x00, 0x00, 0x01, 0x01, 0x39, 0x39, 0x39, 0x31, 0x00, 0x00, 0x01, 0x09, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x11, 0x19, 0x39, 0x39, 0x39, 0x39, 0x00, 0x00, 0x39, 0x39, 0x83, 0x03, 0x83, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xC1, 0x39, 0x81, 0x83, 0x31, 0x01, 0x00, 0x00, 0x99, 0x39, 0xE7, 0x39, 0x23, 0x3F, 0x00, 0x00, 0x39, 0x39, 0xE7, 0xF9, 0x07, 0x3F, 0x00, 0x00, 0x31, 0x01, 0xE7, 0xF9, 0x0F, 0x3F, 0x00, 0x00, 0x3F, 0x39, 0xE7, 0xF9, 0x27, 0x3F, 0x00, 0x00, 0x9F, 0x39, 0xE7, 0xF9, 0x33, 0x3F, 0x00, 0x00, 0xC1, 0x39, 0x81, 0xF9, 0x39, 0x3F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x39, 0x03, 0xC3, 0x07, 0x01, 0x3F, 0x00, 0x00, 0x39, 0x39, 0x99, 0x33, 0x3F, 0x3F, 0x00, 0x00, 0x01, 0x39, 0x3F, 0x39, 0x3F, 0x3F, 0x00, 0x00, 0x39, 0x03, 0x3F, 0x39, 0x03, 0x03, 0x00, 0x00, 0x39, 0x39, 0x3F, 0x39, 0x3F, 0x3F, 0x00, 0x00, 0x93, 0x39, 0x99, 0x33, 0x3F, 0x3F, 0x00, 0x00, 0xC7, 0x03, 0xC3, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
// Cargar surface del bitmap font
|
||||
SDL_Surface* font_surface = SDL_LoadBMP_IO(SDL_IOFromMem(font, 448), 1);
|
||||
if (font_surface != nullptr) {
|
||||
// Crear una nueva surface de 32 bits con canal alpha
|
||||
SDL_Surface* rgba_surface = SDL_CreateSurface(font_surface->w, font_surface->h, SDL_PIXELFORMAT_RGBA8888);
|
||||
if (rgba_surface != nullptr) {
|
||||
// Obtener píxeles de ambas surfaces
|
||||
Uint8* src_pixels = (Uint8*)font_surface->pixels;
|
||||
Uint32* dst_pixels = (Uint32*)rgba_surface->pixels;
|
||||
|
||||
int width = font_surface->w;
|
||||
int height = font_surface->h;
|
||||
|
||||
// Procesar cada píxel
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int byte_index = y * font_surface->pitch + (x / 8);
|
||||
int bit_index = 7 - (x % 8);
|
||||
|
||||
// Extraer bit del bitmap monocromo
|
||||
bool is_white = (src_pixels[byte_index] >> bit_index) & 1;
|
||||
|
||||
if (is_white) // Fondo blanco original -> transparente
|
||||
{
|
||||
dst_pixels[y * width + x] = 0x00000000; // Transparente
|
||||
} else // Texto negro original -> blanco opaco
|
||||
{
|
||||
dst_pixels[y * width + x] = 0xFFFFFFFF; // Blanco opaco
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbg_tex = SDL_CreateTextureFromSurface(dbg_ren, rgba_surface);
|
||||
SDL_DestroySurface(rgba_surface);
|
||||
}
|
||||
SDL_DestroySurface(font_surface);
|
||||
}
|
||||
|
||||
// Configurar filtro nearest neighbor para píxel perfect del texto
|
||||
if (dbg_tex != nullptr) {
|
||||
SDL_SetTextureScaleMode(dbg_tex, SDL_SCALEMODE_NEAREST);
|
||||
// Configurar blend mode para transparencia normal
|
||||
SDL_SetTextureBlendMode(dbg_tex, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
inline void dbg_print(int x, int y, const char* text, Uint8 r, Uint8 g, Uint8 b) {
|
||||
int cc = 0;
|
||||
SDL_SetTextureColorMod(dbg_tex, r, g, b);
|
||||
SDL_FRect src = {0, 0, 8, 8};
|
||||
SDL_FRect dst = {static_cast<float>(x), static_cast<float>(y), 8, 8};
|
||||
while (text[cc] != 0) {
|
||||
if (text[cc] != 32) {
|
||||
if (text[cc] >= 65) {
|
||||
src.x = ((text[cc] - 65) % 6) * 8;
|
||||
src.y = ((text[cc] - 65) / 6) * 8;
|
||||
} else {
|
||||
src.x = ((text[cc] - 22) % 6) * 8;
|
||||
src.y = ((text[cc] - 22) / 6) * 8;
|
||||
}
|
||||
|
||||
SDL_RenderTexture(dbg_ren, dbg_tex, &src, &dst);
|
||||
}
|
||||
cc++;
|
||||
dst.x += 8;
|
||||
}
|
||||
}
|
||||
2
source/external/mouse.cpp
vendored
2
source/external/mouse.cpp
vendored
@@ -1,4 +1,4 @@
|
||||
#include "mouse.h"
|
||||
#include "mouse.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_ShowCursor
|
||||
|
||||
|
||||
4
source/external/sprite.cpp
vendored
4
source/external/sprite.cpp
vendored
@@ -1,6 +1,6 @@
|
||||
#include "sprite.h"
|
||||
#include "sprite.hpp"
|
||||
|
||||
#include "texture.h" // for Texture
|
||||
#include "texture.hpp" // for Texture
|
||||
|
||||
// Constructor
|
||||
Sprite::Sprite(std::shared_ptr<Texture> texture)
|
||||
|
||||
10630
source/external/stb_image_resize2.h
vendored
Normal file
10630
source/external/stb_image_resize2.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
85
source/external/texture.cpp
vendored
85
source/external/texture.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "texture.h"
|
||||
#include "texture.hpp"
|
||||
|
||||
#include <SDL3/SDL_error.h> // Para SDL_GetError
|
||||
#include <SDL3/SDL_log.h> // Para SDL_Log
|
||||
@@ -12,38 +12,7 @@
|
||||
#include <string> // Para operator<<, string
|
||||
|
||||
#include "stb_image.h" // Para stbi_failure_reason, stbi_image_free
|
||||
#include "../resource_pack.h" // Sistema de empaquetado de recursos
|
||||
|
||||
// Instancia global de ResourcePack (se inicializa al primer uso)
|
||||
static ResourcePack* g_resourcePack = nullptr;
|
||||
|
||||
// Inicializar el sistema de recursos (llamar desde main antes de cargar texturas)
|
||||
void Texture::initResourceSystem(const std::string& packFilePath) {
|
||||
if (g_resourcePack == nullptr) {
|
||||
g_resourcePack = new ResourcePack();
|
||||
if (!g_resourcePack->loadPack(packFilePath)) {
|
||||
// Si falla, borrar instancia (usará fallback a disco)
|
||||
delete g_resourcePack;
|
||||
g_resourcePack = nullptr;
|
||||
std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl;
|
||||
} else {
|
||||
std::cout << "resources.pack cargado (" << g_resourcePack->getResourceCount() << " recursos)" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener lista de recursos disponibles en el pack
|
||||
std::vector<std::string> Texture::getPackResourceList() {
|
||||
if (g_resourcePack != nullptr) {
|
||||
return g_resourcePack->getResourceList();
|
||||
}
|
||||
return std::vector<std::string>(); // Vacío si no hay pack
|
||||
}
|
||||
|
||||
// Verificar si el pack está cargado
|
||||
bool Texture::isPackLoaded() {
|
||||
return g_resourcePack != nullptr;
|
||||
}
|
||||
#include "resource_manager.hpp" // Sistema de empaquetado de recursos centralizado
|
||||
|
||||
Texture::Texture(SDL_Renderer *renderer)
|
||||
: renderer_(renderer),
|
||||
@@ -70,30 +39,29 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
||||
int width, height, orig_format;
|
||||
unsigned char *data = nullptr;
|
||||
|
||||
// 1. Intentar cargar desde pack (si está inicializado)
|
||||
if (g_resourcePack != nullptr) {
|
||||
ResourcePack::ResourceData packData = g_resourcePack->loadResource(file_path);
|
||||
if (packData.data != nullptr) {
|
||||
// Descodificar imagen desde memoria usando stb_image
|
||||
data = stbi_load_from_memory(packData.data, static_cast<int>(packData.size),
|
||||
&width, &height, &orig_format, req_format);
|
||||
delete[] packData.data; // Liberar buffer temporal del pack
|
||||
// 1. Intentar cargar desde ResourceManager (pack o disco)
|
||||
unsigned char* resourceData = nullptr;
|
||||
size_t resourceSize = 0;
|
||||
|
||||
if (data != nullptr) {
|
||||
if (ResourceManager::loadResource(file_path, resourceData, resourceSize)) {
|
||||
// Descodificar imagen desde memoria usando stb_image
|
||||
data = stbi_load_from_memory(resourceData, static_cast<int>(resourceSize),
|
||||
&width, &height, &orig_format, req_format);
|
||||
delete[] resourceData; // Liberar buffer temporal
|
||||
|
||||
if (data != nullptr) {
|
||||
if (ResourceManager::isPackLoaded()) {
|
||||
std::cout << "Imagen cargada desde pack: " << filename.c_str() << std::endl;
|
||||
} else {
|
||||
std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: cargar desde disco (modo desarrollo)
|
||||
// 2. Si todo falla, error
|
||||
if (data == nullptr) {
|
||||
data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
||||
if (data == nullptr) {
|
||||
SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
|
||||
exit(1);
|
||||
} else {
|
||||
std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl;
|
||||
}
|
||||
SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int pitch;
|
||||
@@ -128,6 +96,9 @@ bool Texture::loadFromFile(const std::string &file_path) {
|
||||
|
||||
// Configurar filtro nearest neighbor para píxel perfect
|
||||
SDL_SetTextureScaleMode(new_texture, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
// Habilitar alpha blending para transparencias
|
||||
SDL_SetTextureBlendMode(new_texture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
// Destruye la superficie cargada
|
||||
@@ -169,3 +140,17 @@ int Texture::getHeight() {
|
||||
void Texture::setColor(int r, int g, int b) {
|
||||
SDL_SetTextureColorMod(texture_, r, g, b);
|
||||
}
|
||||
|
||||
// Modula el alpha de la textura
|
||||
void Texture::setAlpha(int alpha) {
|
||||
if (texture_ != nullptr) {
|
||||
SDL_SetTextureAlphaMod(texture_, static_cast<Uint8>(alpha));
|
||||
}
|
||||
}
|
||||
|
||||
// Configurar modo de escalado
|
||||
void Texture::setScaleMode(SDL_ScaleMode mode) {
|
||||
if (texture_ != nullptr) {
|
||||
SDL_SetTextureScaleMode(texture_, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,6 @@ class Texture {
|
||||
int height_;
|
||||
|
||||
public:
|
||||
// Sistema de recursos empaquetados (inicializar desde main)
|
||||
static void initResourceSystem(const std::string& packFilePath);
|
||||
static std::vector<std::string> getPackResourceList();
|
||||
static bool isPackLoaded();
|
||||
|
||||
// Inicializa las variables
|
||||
explicit Texture(SDL_Renderer *renderer);
|
||||
Texture(SDL_Renderer *renderer, const std::string &file_path);
|
||||
@@ -44,6 +39,12 @@ class Texture {
|
||||
// Modula el color de la textura
|
||||
void setColor(int r, int g, int b);
|
||||
|
||||
// Modula el alpha (transparencia) de la textura
|
||||
void setAlpha(int alpha);
|
||||
|
||||
// Configurar modo de escalado (NEAREST para pixel art, LINEAR para suavizado)
|
||||
void setScaleMode(SDL_ScaleMode mode);
|
||||
|
||||
// Getter para batch rendering
|
||||
SDL_Texture *getSDLTexture() const { return texture_; }
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "input_handler.h"
|
||||
#include "input_handler.hpp"
|
||||
|
||||
#include <SDL3/SDL_keycode.h> // for SDL_Keycode
|
||||
#include <string> // for std::string, std::to_string
|
||||
|
||||
#include "../engine.h" // for Engine
|
||||
#include "../external/mouse.h" // for Mouse namespace
|
||||
#include "engine.hpp" // for Engine
|
||||
#include "external/mouse.hpp" // for Mouse namespace
|
||||
|
||||
bool InputHandler::processEvents(Engine& engine) {
|
||||
SDL_Event event;
|
||||
|
||||
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 <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
|
||||
|
||||
@@ -13,13 +16,17 @@ void printHelp() {
|
||||
std::cout << " -z, --zoom <n> Zoom de ventana (default: 3)\n";
|
||||
std::cout << " -f, --fullscreen Modo pantalla completa (F3 - letterbox)\n";
|
||||
std::cout << " -F, --real-fullscreen Modo pantalla completa real (F4 - nativo)\n";
|
||||
std::cout << " -m, --mode <mode> Modo inicial: sandbox, demo, demo-lite, logo (default: sandbox)\n";
|
||||
std::cout << " --help Mostrar esta ayuda\n\n";
|
||||
std::cout << "Ejemplos:\n";
|
||||
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
|
||||
std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 (auto)\n";
|
||||
std::cout << " vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n";
|
||||
std::cout << " vibe3_physics -f # Fullscreen letterbox (F3)\n";
|
||||
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n\n";
|
||||
std::cout << " vibe3_physics -F # Fullscreen real (F4 - resolución nativa)\n";
|
||||
std::cout << " vibe3_physics --mode demo # Arrancar en modo DEMO (auto-play)\n";
|
||||
std::cout << " vibe3_physics -m demo-lite # Arrancar en modo DEMO_LITE (solo física)\n";
|
||||
std::cout << " vibe3_physics -F --mode logo # Fullscreen + modo LOGO (easter egg)\n\n";
|
||||
std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n";
|
||||
}
|
||||
|
||||
@@ -29,6 +36,7 @@ int main(int argc, char* argv[]) {
|
||||
int zoom = 0;
|
||||
bool fullscreen = false;
|
||||
bool real_fullscreen = false;
|
||||
AppMode initial_mode = AppMode::SANDBOX; // Modo inicial (default: SANDBOX)
|
||||
|
||||
// Parsear argumentos
|
||||
for (int i = 1; i < argc; i++) {
|
||||
@@ -38,8 +46,8 @@ int main(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--width") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
width = atoi(argv[++i]);
|
||||
if (width < 640) {
|
||||
std::cerr << "Error: Ancho mínimo es 640px\n";
|
||||
if (width < 320) {
|
||||
std::cerr << "Error: Ancho mínimo es 320\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@@ -49,8 +57,8 @@ int main(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--height") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
height = atoi(argv[++i]);
|
||||
if (height < 480) {
|
||||
std::cerr << "Error: Alto mínimo es 480px\n";
|
||||
if (height < 240) {
|
||||
std::cerr << "Error: Alto mínimo es 240\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@@ -72,6 +80,25 @@ int main(int argc, char* argv[]) {
|
||||
fullscreen = true;
|
||||
} else if (strcmp(argv[i], "-F") == 0 || strcmp(argv[i], "--real-fullscreen") == 0) {
|
||||
real_fullscreen = true;
|
||||
} else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--mode") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
std::string mode_str = argv[++i];
|
||||
if (mode_str == "sandbox") {
|
||||
initial_mode = AppMode::SANDBOX;
|
||||
} else if (mode_str == "demo") {
|
||||
initial_mode = AppMode::DEMO;
|
||||
} else if (mode_str == "demo-lite") {
|
||||
initial_mode = AppMode::DEMO_LITE;
|
||||
} else if (mode_str == "logo") {
|
||||
initial_mode = AppMode::LOGO;
|
||||
} else {
|
||||
std::cerr << "Error: Modo '" << mode_str << "' no válido. Usa: sandbox, demo, demo-lite, logo\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: -m/--mode requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
|
||||
printHelp();
|
||||
@@ -82,11 +109,11 @@ int main(int argc, char* argv[]) {
|
||||
// Inicializar sistema de recursos empaquetados (intentar cargar resources.pack)
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
std::string pack_path = resources_dir + "/resources.pack";
|
||||
Texture::initResourceSystem(pack_path);
|
||||
ResourceManager::init(pack_path);
|
||||
|
||||
Engine engine;
|
||||
|
||||
if (!engine.initialize(width, height, zoom, fullscreen)) {
|
||||
if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) {
|
||||
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
91
source/resource_manager.cpp
Normal file
91
source/resource_manager.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "resource_manager.hpp"
|
||||
#include "resource_pack.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
// Inicializar el puntero estático
|
||||
ResourcePack* ResourceManager::resourcePack_ = nullptr;
|
||||
|
||||
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() {
|
||||
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. Intentar cargar desde pack (si está disponible)
|
||||
if (resourcePack_ != nullptr) {
|
||||
ResourcePack::ResourceData packData = resourcePack_->loadResource(resourcePath);
|
||||
if (packData.data != nullptr) {
|
||||
data = packData.data;
|
||||
size = packData.size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: cargar desde disco
|
||||
std::ifstream file(resourcePath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
// Intentar con "data/" como prefijo si no se encontró
|
||||
std::string dataPath = "data/" + resourcePath;
|
||||
file.open(dataPath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener tamaño del archivo
|
||||
size = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Alocar buffer y leer
|
||||
data = new unsigned char[size];
|
||||
file.read(reinterpret_cast<char*>(data), size);
|
||||
file.close();
|
||||
|
||||
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;
|
||||
}
|
||||
82
source/resource_manager.hpp
Normal file
82
source/resource_manager.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#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_;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "resource_pack.h"
|
||||
#include "resource_pack.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef RESOURCE_PACK_H
|
||||
#define RESOURCE_PACK_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
@@ -64,5 +63,3 @@ private:
|
||||
uint32_t calculateChecksum(const unsigned char* data, size_t size);
|
||||
std::string normalizePath(const std::string& path);
|
||||
};
|
||||
|
||||
#endif // RESOURCE_PACK_H
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "scene_manager.h"
|
||||
#include "scene_manager.hpp"
|
||||
|
||||
#include <cstdlib> // for rand
|
||||
|
||||
#include "../defines.h" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
|
||||
#include "../external/texture.h" // for Texture
|
||||
#include "../theme_manager.h" // for ThemeManager
|
||||
#include "defines.hpp" // for BALL_COUNT_SCENARIOS, GRAVITY_MASS_MIN, etc
|
||||
#include "external/texture.hpp" // for Texture
|
||||
#include "theme_manager.hpp" // for ThemeManager
|
||||
|
||||
SceneManager::SceneManager(int screen_width, int screen_height)
|
||||
: current_gravity_(GravityDirection::DOWN)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <memory> // for unique_ptr, shared_ptr
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "../ball.h" // for Ball
|
||||
#include "../defines.h" // for GravityDirection
|
||||
#include "ball.hpp" // for Ball
|
||||
#include "defines.hpp" // for GravityDirection
|
||||
|
||||
// Forward declarations
|
||||
class Texture;
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "atom_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "atom_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Átomo con núcleo central y órbitas electrónicas
|
||||
// Comportamiento: Núcleo estático + electrones orbitando en planos inclinados
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "cube_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "cube_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
#include <vector>
|
||||
|
||||
// Figura: Cubo 3D rotante
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "cylinder_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "cylinder_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
#include <cstdlib> // Para rand()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Cilindro 3D rotante
|
||||
// Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "helix_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "helix_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void HelixShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Espiral helicoidal 3D con distribución uniforme
|
||||
// Comportamiento: Rotación en eje Y + animación de fase vertical
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "icosahedron_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "icosahedron_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Icosaedro 3D (D20, poliedro regular de 20 caras)
|
||||
// Comportamiento: 12 vértices distribuidos uniformemente con rotación triple
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "lissajous_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "lissajous_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include <cmath>
|
||||
|
||||
void LissajousShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
#include "shape.hpp"
|
||||
|
||||
// Figura: Curva de Lissajous 3D
|
||||
// Comportamiento: Curva paramétrica 3D con rotación global y animación de fase
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "png_shape.h"
|
||||
#include "../defines.h"
|
||||
#include "../external/stb_image.h"
|
||||
#include "png_shape.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "external/stb_image.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user