14 Commits

Author SHA1 Message Date
fa285519b2 Fix: Corregir creación de DMG eliminando prefijo rw.* y conflictos
- Corregir nombre de archivos DMG usando TARGET_NAME en lugar de TARGET_FILE
- Agregar limpieza de volúmenes montados antes de crear DMG
- Eliminar creación manual de enlace Applications (create-dmg lo hace con --app-drop-link)
- Mejorar manejo de errores eliminando || true y agregando verificación de éxito
- Limpiar archivos temporales rw.*.dmg después de crear DMG

Esto resuelve el problema donde el DMG final tenía prefijo "rw.XXXXX" y no se
podía abrir correctamente debido a conflictos con volúmenes montados y doble
creación del enlace a Applications.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 11:03:31 +02:00
8285a8fafe Mejorar creación de DMG en macOS con create-dmg y posicionamiento de iconos
- Añadir instalación automática de create-dmg vía Homebrew si no está presente
- Reemplazar hdiutil por create-dmg para generar DMG con diseño profesional
- Configurar ventana de DMG: 720x300px con iconos de 96x96px
- Posicionar iconos centrados: Applications, .app, LICENSE, README.md
- Aplicar mejoras a ambas versiones: Intel y Apple Silicon
- Eliminar referencia obsoleta a tmp.dmg

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 10:31:25 +02:00
1a555e03f7 Fix: Mejorar escalado de fuentes para resoluciones altas y actualizar .gitignore
Problemas resueltos:
- Texto demasiado pequeño en resoluciones 2K/4K (1440p mostraba 36-41px)
- Archivos generados (resources.pack, *.zip) siendo versionados
- Carpetas temporales de empaquetado sin ignorar

Cambios realizados:

1. UIManager: Escalado más agresivo para resoluciones altas
   - Proporción mejorada: 1/40 → 1/26 (incremento ~35%)
   - Límite máximo: 36px → 72px
   - Resultados: 1080p→42px, 1440p→55px, 2160p→72px
   - Resoluciones bajas sin cambios (10-18px)

2. .gitignore: Excluir archivos generados y temporales
   - resources.pack (archivo empaquetado)
   - Archivos de distribución (*.zip, *.dmg, *.tar.gz, *.AppImage)
   - Carpetas temporales (vibe3_release/, Frameworks/)
   - Binarios de herramientas (tools/*.exe)

3. defines.hpp: Resolución por defecto actualizada
   - 640x360 (zoom 2x) → 1280x720 (zoom 1x)

Resultado:
- Texto significativamente más legible en pantallas grandes
- Repositorio limpio sin archivos generados

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 10:22:25 +02:00
af3ed6c2b3 Fix: Ajustar dimensionamiento de HelpOverlay para resoluciones bajas
Problemas resueltos:
- En 640x360, el overlay generaba textura enorme con letras grandes
- El cálculo de font size usaba dimensiones físicas (con zoom aplicado)
  en lugar de dimensiones lógicas (resolución interna)
- No había límite máximo de ancho para el overlay
- Padding fijo de 25px era excesivo en pantallas pequeñas

Cambios realizados:

1. UIManager: Usar dimensiones lógicas para calcular font size
   - Nuevo parámetro logical_width/logical_height en initialize()
   - calculateFontSize() ahora usa altura lógica sin zoom
   - Escalado híbrido: proporcional en extremos, escalonado en rango medio
   - Para 640x360: 10px (antes 18px con zoom 2x)
   - Para 640x480: 12px (antes 24px con zoom 2x)

2. HelpOverlay: Agregar límites máximos de dimensiones
   - Box width limitado al 95% del ancho físico
   - Box height limitado al 90% de la altura física
   - Padding dinámico: 25px para >=600px, escalado para menores
   - Para 360px altura: padding de 15px (antes 25px fijo)

3. Engine: Pasar dimensiones lógicas a UIManager
   - initialize() ahora recibe current_screen_width/height

Resultado:
- 640x360: Overlay compacto con fuente 10px que cabe en pantalla
- 640x480: Overlay con fuente 12px (tamaño apropiado)
- Tamaño de fuente consistente independiente del zoom de ventana

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 14:23:59 +02:00
a9d7b66e83 Refactorizar estilo del proyecto: .h → .hpp, #pragma once, includes desde raíz
Modernizar convenciones de código C++ aplicando las siguientes directivas:

## Cambios principales

**1. Renombrar headers (.h → .hpp)**
- 36 archivos renombrados a extensión .hpp (estándar C++)
- Mantenidos como .h: stb_image.h, stb_image_resize2.h (librerías C externas)

**2. Modernizar include guards (#ifndef → #pragma once)**
- resource_manager.hpp: #ifndef RESOURCE_MANAGER_H → #pragma once
- resource_pack.hpp: #ifndef RESOURCE_PACK_H → #pragma once
- spatial_grid.hpp: #ifndef SPATIAL_GRID_H → #pragma once

**3. Sistema de includes desde raíz del proyecto**
- CMakeLists.txt: añadido include_directories(${CMAKE_SOURCE_DIR}/source)
- Eliminadas rutas relativas (../) en todos los includes
- Includes ahora usan rutas absolutas desde source/

**Antes:**
```cpp
#include "../defines.h"
#include "../text/textrenderer.h"
```

**Ahora:**
```cpp
#include "defines.hpp"
#include "text/textrenderer.hpp"
```

## Archivos afectados

- 1 archivo CMakeLists.txt modificado
- 36 archivos renombrados (.h → .hpp)
- 32 archivos .cpp actualizados (includes)
- 36 archivos .hpp actualizados (includes + guards)
- 1 archivo tools/ actualizado

**Total: 70 archivos modificados**

## Verificación

 Proyecto compila sin errores
 Todas las rutas de includes correctas
 Include guards modernizados
 Librerías externas C mantienen extensión .h

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 13:49:58 +02:00
a929df6b73 Fix: Corregir inicialización de figuras en modo DEMO
Solucionar bug donde las pelotas aparecían en el centro sin formar
la figura geométrica al entrar en modo DEMO con SimulationMode::SHAPE.

## Problema
Al randomizar el estado en modo DEMO, si se elegía una figura:
1. Se configuraba el modo SHAPE
2. Se llamaba a changeScenario() que creaba pelotas en el centro
3. NO se llamaba a generateShape() para calcular los puntos de la figura
4. Resultado: pelotas amontonadas en el centro sin formar figura

## Solución
Reordenar operaciones en executeRandomizeOnDemoStart():
1. Decidir PRIMERO el modo (PHYSICS o SHAPE) antes de changeScenario
2. Si SHAPE: configurar figura manualmente sin generar puntos
3. Llamar a changeScenario() con el modo ya establecido
4. Después de changeScenario(), generar figura y activar atracción

Cambios adicionales:
- Arreglar warning de formato %zu en textrenderer.cpp (MinGW)
- Usar %lu con cast para size_t en logs

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 13:22:03 +02:00
3f027d953c Eliminados warnings en textrenderer.cpp 2025-10-23 12:26:52 +02:00
1354ed82d2 Fix: Corregir carga de fuentes desde ResourceManager
Problema:
- Las fuentes TTF no se renderizaban (error "Text has zero width")
- Ocurría tanto al cargar desde resources.pack como desde disco
- El buffer de memoria se liberaba inmediatamente después de crear
  el SDL_IOStream, pero SDL_ttf necesita acceder a esos datos
  durante toda la vida de la fuente

Solución:
- Añadido campo font_data_buffer_ para mantener los datos en memoria
- Modificado init() y reinitialize() para NO liberar el buffer
  inmediatamente después de cargar la fuente
- Modificado cleanup() para liberar el buffer cuando se cierre la fuente
- Añadidos logs de debug para confirmar la carga desde ResourceManager

Archivos modificados:
- source/text/textrenderer.h: Añadido campo font_data_buffer_
- source/text/textrenderer.cpp: Correcciones en init(), reinitialize()
  y cleanup()

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 12:15:54 +02:00
2fd6d99a61 Añadir sistema de Makefiles para herramienta de empaquetado de recursos
- Crear tools/Makefile con soporte multiplataforma (Windows/Linux/macOS)
- Añadir targets: pack_tool, resource_pack, test_pack, clean, help
- Mejorar Makefile raíz con target force_resource_pack
- Integrar regeneración automática de resources.pack en todos los releases
- Los releases siempre generan un resources.pack actualizado

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 09:48:57 +02:00
2fa1684f01 Refactorizar sistema de recursos: crear ResourceManager centralizado
- Crear ResourceManager singleton para gestión centralizada de recursos
- Separar lógica de ResourcePack de la clase Texture
- Adaptar TextRenderer para cargar fuentes TTF desde pack
- Adaptar LogoScaler para cargar imágenes PNG desde pack
- Actualizar main.cpp y engine.cpp para usar ResourceManager
- Regenerar resources.pack con fuentes y logos incluidos

Fixes:
- Resuelve error de carga de fuentes desde disco
- Resuelve error de carga de logos (can't fopen)
- Implementa fallback automático a disco si no existe pack
- Todas las clases ahora pueden cargar recursos desde pack

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 09:16:18 +02:00
41c76316ef Actualizar README.md 2025-10-19 17:39:35 +02:00
ce50a29019 Eliminado código DEPRECATED de ui_manager 2025-10-19 15:02:13 +02:00
f25cb96a91 Correciones en Makefile 2025-10-19 09:46:55 +02:00
d73781be9f Añadido vibe3.res 2025-10-19 09:29:26 +02:00
76 changed files with 876 additions and 395 deletions

17
.gitignore vendored
View File

@@ -94,3 +94,20 @@ Thumbs.db
# Claude Code
.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

View File

@@ -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})

133
Makefile
View File

@@ -42,8 +42,8 @@ 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
@@ -67,36 +67,44 @@ APP_SOURCES := $(filter-out source/main_old.cpp, $(APP_SOURCES))
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 -lSDL3_ttf
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..."
@@ -113,6 +121,13 @@ resources.pack: $(PACK_TOOL) $(DATA_FILES)
$(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,7 +146,7 @@ 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'
@@ -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"
@@ -193,8 +217,8 @@ macos_release: resources.pack
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
@@ -203,11 +227,28 @@ ifdef ENABLE_MACOS_X86_64
# 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
@@ -216,11 +257,28 @@ endif
# 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
@@ -235,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)"
@@ -260,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)"
@@ -364,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)"
@@ -389,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
@@ -428,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"

View File

@@ -1,6 +1,8 @@
# ViBe3 Physics - Simulador de Sprites con Física Avanzada
**ViBe3 Physics** es una demo experimental de **vibe-coding** que implementa **físicas avanzadas** con sistema de delta time independiente del framerate. Utiliza **SDL3** para renderizado optimizado con batch geometry de hasta 100,000 sprites simultáneos.
**ViBe3 Physics** es una demo experimental de **vibe-coding** que implementa **físicas avanzadas** con sistema de delta time independiente del framerate. Utiliza **SDL3** para renderizado optimizado con batch geometry de hasta 50,000 sprites simultáneos.
![ViBe3 Physics - Gameplay](https://php.sustancia.synology.me/images/vibe3_physics/captura_vibe3_physics_1.png)
El nombre refleja su propósito: **ViBe** (vibe-coding experimental) + **Physics** (nuevas físicas experimentales). La demo sirve como sandbox para probar bucles de juego con timing independiente, comportamientos emergentes (boids), figuras 3D procedurales y efectos demoscene.
@@ -154,6 +156,8 @@ Controlan el comportamiento físico de las pelotas:
ViBe3 Physics incluye **15 temas visuales** organizados en **2 páginas** (9 estáticos + 1 dinámico en Página 1, 5 dinámicos en Página 2). Los temas dinámicos tienen animación de colores en tiempo real.
![ViBe3 Physics - Gameplay](https://php.sustancia.synology.me/images/vibe3_physics/captura_vibe3_physics_2.png)
### Controles de Temas
- **`B`**: Ciclar entre TODOS los temas (adelante)
@@ -407,8 +411,8 @@ vibe3_physics [opciones]
### Validaciones
- **Ancho mínimo**: 640px
- **Alto mínimo**: 480px
- **Ancho mínimo**: 320px
- **Alto mínimo**: 240px
- **Zoom automático**: Si resolución > pantalla, usa default
- **Zoom máximo**: Ajustado según pantalla disponible

BIN
release/vibe3.res Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,12 @@
#include "app_logo.h"
#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.h" // for LogoScaler
#include "defines.h" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
#include "logo_scaler.hpp" // for LogoScaler
#include "defines.hpp" // for APPLOGO_HEIGHT_PERCENT, getResourcesDirectory
// ============================================================================
// Destructor - Liberar las 4 texturas SDL

View File

@@ -4,7 +4,7 @@
#include <memory> // for unique_ptr, shared_ptr
#include "defines.h" // for AppMode
#include "defines.hpp" // for AppMode
class Texture;
class Sprite;

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,7 +22,6 @@ 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)

View File

@@ -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,22 +17,24 @@
#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
@@ -164,9 +166,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
}
}
} 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, AppMod
// 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>();
@@ -1502,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) {
@@ -1525,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

View File

@@ -10,18 +10,18 @@
#include <string> // for string
#include <vector> // for vector
#include "app_logo.h" // for AppLogo
#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:

View File

@@ -1,4 +1,4 @@
#include "mouse.h"
#include "mouse.hpp"
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_ShowCursor

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -1,5 +1,5 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "logo_scaler.h"
#include "logo_scaler.hpp"
#include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL3/SDL_log.h> // Para SDL_Log
@@ -13,6 +13,7 @@
#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
@@ -51,10 +52,22 @@ bool LogoScaler::detectNativeResolution(int& native_width, int& native_height) {
unsigned char* LogoScaler::loadAndScale(const std::string& path,
int target_width, int target_height,
int& out_width, int& out_height) {
// 1. Cargar imagen original con stb_image
// 1. Intentar cargar imagen desde ResourceManager (pack o disco)
int orig_width, orig_height, orig_channels;
unsigned char* orig_data = stbi_load(path.c_str(), &orig_width, &orig_height, &orig_channels, STBI_rgb_alpha);
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;

View File

@@ -1,8 +1,9 @@
#include <iostream>
#include <cstring>
#include <string>
#include "engine.h"
#include "defines.h"
#include "engine.hpp"
#include "defines.hpp"
#include "resource_manager.hpp"
// getExecutableDirectory() ya está definido en defines.h como inline
@@ -45,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 {
@@ -56,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 {
@@ -108,7 +109,7 @@ 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;

View 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;
}

View 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_;
};

View File

@@ -1,4 +1,4 @@
#include "resource_pack.h"
#include "resource_pack.hpp"
#include <algorithm>
#include <cstring>

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -1,6 +1,6 @@
#pragma once
#include "shape.h"
#include "shape.hpp"
#include <vector>
// Figura: Cubo 3D rotante

View File

@@ -1,5 +1,5 @@
#include "cylinder_shape.h"
#include "../defines.h"
#include "cylinder_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include <cstdlib> // Para rand()

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -1,5 +1,5 @@
#include "icosahedron_shape.h"
#include "../defines.h"
#include "icosahedron_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include <vector>

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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>

View File

@@ -1,7 +1,7 @@
#pragma once
#include "shape.h"
#include "../defines.h" // Para PNG_IDLE_TIME_MIN/MAX constantes
#include "shape.hpp"
#include "defines.hpp" // Para PNG_IDLE_TIME_MIN/MAX constantes
#include <vector>
#include <cstdlib> // Para rand()

View File

@@ -1,5 +1,5 @@
#include "sphere_shape.h"
#include "../defines.h"
#include "sphere_shape.hpp"
#include "defines.hpp"
#include <cmath>
void SphereShape::generatePoints(int num_points, float screen_width, float screen_height) {

View File

@@ -1,6 +1,6 @@
#pragma once
#include "shape.h"
#include "shape.hpp"
// Figura: Esfera 3D con distribución uniforme (Fibonacci Sphere Algorithm)
// Comportamiento: Rotación dual en ejes X e Y

View File

@@ -1,5 +1,5 @@
#include "torus_shape.h"
#include "../defines.h"
#include "torus_shape.hpp"
#include "defines.hpp"
#include <cmath>
void TorusShape::generatePoints(int num_points, float screen_width, float screen_height) {

View File

@@ -1,6 +1,6 @@
#pragma once
#include "shape.h"
#include "shape.hpp"
// Figura: Torus/Toroide 3D (donut/rosquilla)
// Comportamiento: Superficie toroidal con rotación triple (X, Y, Z)

View File

@@ -1,25 +1,25 @@
#include "shape_manager.h"
#include "shape_manager.hpp"
#include <algorithm> // for std::min, std::max
#include <cstdlib> // for rand
#include <string> // for std::string
#include "../ball.h" // for Ball
#include "../defines.h" // for constantes
#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 "defines.hpp" // for constantes
#include "scene/scene_manager.hpp" // for SceneManager
#include "state/state_manager.hpp" // for StateManager
#include "ui/ui_manager.hpp" // for UIManager
// Includes de todas las shapes (necesario para creación polimórfica)
#include "../shapes/atom_shape.h"
#include "../shapes/cube_shape.h"
#include "../shapes/cylinder_shape.h"
#include "../shapes/helix_shape.h"
#include "../shapes/icosahedron_shape.h"
#include "../shapes/lissajous_shape.h"
#include "../shapes/png_shape.h"
#include "../shapes/sphere_shape.h"
#include "../shapes/torus_shape.h"
#include "shapes/atom_shape.hpp"
#include "shapes/cube_shape.hpp"
#include "shapes/cylinder_shape.hpp"
#include "shapes/helix_shape.hpp"
#include "shapes/icosahedron_shape.hpp"
#include "shapes/lissajous_shape.hpp"
#include "shapes/png_shape.hpp"
#include "shapes/sphere_shape.hpp"
#include "shapes/torus_shape.hpp"
ShapeManager::ShapeManager()
: engine_(nullptr)

View File

@@ -2,8 +2,8 @@
#include <memory> // for unique_ptr
#include "../defines.h" // for SimulationMode, ShapeType
#include "../shapes/shape.h" // for Shape base class
#include "defines.hpp" // for SimulationMode, ShapeType
#include "shapes/shape.hpp" // for Shape base class
// Forward declarations
class Engine;

View File

@@ -1,9 +1,9 @@
#include "spatial_grid.h"
#include "spatial_grid.hpp"
#include <algorithm> // for std::max, std::min
#include <cmath> // for std::floor, std::ceil
#include "ball.h" // for Ball
#include "ball.hpp" // for Ball
SpatialGrid::SpatialGrid(int world_width, int world_height, float cell_size)
: world_width_(world_width)

View File

@@ -1,5 +1,4 @@
#ifndef SPATIAL_GRID_H
#define SPATIAL_GRID_H
#pragma once
#include <unordered_map>
#include <vector>
@@ -70,5 +69,3 @@ private:
// Usamos unordered_map para O(1) lookup
std::unordered_map<int, std::vector<Ball*>> cells_;
};
#endif // SPATIAL_GRID_H

View File

@@ -1,10 +1,10 @@
#include "state_manager.h"
#include "state_manager.hpp"
#include <cstdlib> // for rand
#include "../defines.h" // for constantes DEMO/LOGO
#include "../engine.h" // for Engine (callbacks)
#include "../shapes/png_shape.h" // for PNGShape flip detection
#include "defines.hpp" // for constantes DEMO/LOGO
#include "engine.hpp" // for Engine (callbacks)
#include "shapes/png_shape.hpp" // for PNGShape flip detection
StateManager::StateManager()
: engine_(nullptr)

View File

@@ -3,7 +3,7 @@
#include <SDL3/SDL_stdinc.h> // for Uint64
#include <cstddef> // for size_t
#include "../defines.h" // for AppMode, ShapeType, GravityDirection
#include "defines.hpp" // for AppMode, ShapeType, GravityDirection
// Forward declarations
class Engine;

View File

@@ -1,8 +1,9 @@
#include "textrenderer.h"
#include "textrenderer.hpp"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include "resource_manager.hpp"
TextRenderer::TextRenderer() : renderer_(nullptr), font_(nullptr), font_size_(0), use_antialiasing_(true) {
TextRenderer::TextRenderer() : renderer_(nullptr), font_(nullptr), font_size_(0), use_antialiasing_(true), font_data_buffer_(nullptr) {
}
TextRenderer::~TextRenderer() {
@@ -23,7 +24,34 @@ bool TextRenderer::init(SDL_Renderer* renderer, const char* font_path, int font_
}
}
// Cargar la fuente
// Intentar cargar la fuente desde ResourceManager (pack o disco)
unsigned char* fontData = nullptr;
size_t fontDataSize = 0;
if (ResourceManager::loadResource(font_path, fontData, fontDataSize)) {
// Crear SDL_IOStream desde memoria
SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
if (fontIO != nullptr) {
// Cargar fuente desde IOStream
font_ = TTF_OpenFontIO(fontIO, true, font_size); // true = cerrar stream automáticamente
if (font_ == nullptr) {
SDL_Log("Error al cargar fuente desde memoria '%s': %s", font_path, SDL_GetError());
delete[] fontData; // Liberar solo si falla la carga
return false;
}
// CRÍTICO: NO eliminar fontData aquí - SDL_ttf necesita estos datos en memoria
// mientras la fuente esté abierta. Se liberará en cleanup()
font_data_buffer_ = fontData;
SDL_Log("Fuente cargada desde ResourceManager: %s (%lu bytes)", font_path, (unsigned long)fontDataSize);
return true;
} else {
delete[] fontData;
}
}
// Fallback final: intentar cargar directamente desde disco (por si falla ResourceManager)
font_ = TTF_OpenFont(font_path, font_size);
if (font_ == nullptr) {
SDL_Log("Error al cargar fuente '%s': %s", font_path, SDL_GetError());
@@ -45,13 +73,43 @@ bool TextRenderer::reinitialize(int new_font_size) {
return true;
}
// Cerrar fuente actual
// Cerrar fuente actual y liberar buffer previo
if (font_ != nullptr) {
TTF_CloseFont(font_);
font_ = nullptr;
}
if (font_data_buffer_ != nullptr) {
delete[] font_data_buffer_;
font_data_buffer_ = nullptr;
}
// Cargar fuente con nuevo tamaño
// Intentar cargar la fuente desde ResourceManager con el nuevo tamaño
unsigned char* fontData = nullptr;
size_t fontDataSize = 0;
if (ResourceManager::loadResource(font_path_, fontData, fontDataSize)) {
SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast<size_t>(fontDataSize));
if (fontIO != nullptr) {
font_ = TTF_OpenFontIO(fontIO, true, new_font_size);
if (font_ == nullptr) {
SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
font_path_.c_str(), new_font_size, SDL_GetError());
delete[] fontData; // Liberar solo si falla
return false;
}
// Mantener buffer en memoria (NO eliminar)
font_data_buffer_ = fontData;
font_size_ = new_font_size;
SDL_Log("Fuente recargada desde ResourceManager: %s (tamaño %d)", font_path_.c_str(), new_font_size);
return true;
} else {
delete[] fontData;
}
}
// Fallback: cargar directamente desde disco
font_ = TTF_OpenFont(font_path_.c_str(), new_font_size);
if (font_ == nullptr) {
SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s",
@@ -59,9 +117,7 @@ bool TextRenderer::reinitialize(int new_font_size) {
return false;
}
// Actualizar tamaño almacenado
font_size_ = new_font_size;
return true;
}
@@ -70,6 +126,10 @@ void TextRenderer::cleanup() {
TTF_CloseFont(font_);
font_ = nullptr;
}
if (font_data_buffer_ != nullptr) {
delete[] font_data_buffer_;
font_data_buffer_ = nullptr;
}
renderer_ = nullptr;
}

View File

@@ -50,4 +50,5 @@ private:
int font_size_;
bool use_antialiasing_;
std::string font_path_; // Almacenar ruta para reinitialize()
unsigned char* font_data_buffer_; // Buffer de datos de fuente (mantener en memoria mientras esté abierta)
};

View File

@@ -1,7 +1,7 @@
#include "theme_manager.h"
#include "theme_manager.hpp"
#include "themes/static_theme.h"
#include "themes/dynamic_theme.h"
#include "themes/static_theme.hpp"
#include "themes/dynamic_theme.hpp"
// ============================================================================
// INICIALIZACIÓN

View File

@@ -3,10 +3,10 @@
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "ball.h" // for Ball class
#include "defines.h" // for Color, ColorTheme
#include "themes/theme.h" // for Theme interface
#include "themes/theme_snapshot.h" // for ThemeSnapshot
#include "ball.hpp" // for Ball class
#include "defines.hpp" // for Color, ColorTheme
#include "themes/theme.hpp" // for Theme interface
#include "themes/theme_snapshot.hpp" // for ThemeSnapshot
/**
* ThemeManager: Gestiona el sistema de temas visuales (unificado, estáticos y dinámicos)

View File

@@ -1,4 +1,4 @@
#include "dynamic_theme.h"
#include "dynamic_theme.hpp"
#include <algorithm> // for std::min
DynamicTheme::DynamicTheme(const char* name_en, const char* name_es,

View File

@@ -1,6 +1,6 @@
#pragma once
#include "theme.h"
#include "theme.hpp"
#include <string>
// Forward declaration (estructura definida en defines.h)

View File

@@ -1,4 +1,4 @@
#include "static_theme.h"
#include "static_theme.hpp"
StaticTheme::StaticTheme(const char* name_en, const char* name_es,
int text_r, int text_g, int text_b,

View File

@@ -1,6 +1,6 @@
#pragma once
#include "theme.h"
#include "theme.hpp"
#include <string>
/**

View File

@@ -1,7 +1,7 @@
#pragma once
#include <vector>
#include "../defines.h" // for Color, ThemeKeyframe
#include "defines.hpp" // for Color, ThemeKeyframe
/**
* Theme: Interfaz polimórfica para todos los temas (estáticos y dinámicos)

View File

@@ -2,7 +2,7 @@
#include <string>
#include <vector>
#include "../defines.h" // for Color
#include "defines.hpp" // for Color
/**
* ThemeSnapshot: "Fotografía" del estado de un tema en un momento dado

View File

@@ -1,9 +1,9 @@
#include "help_overlay.h"
#include "help_overlay.hpp"
#include <algorithm> // for std::min
#include "../text/textrenderer.h"
#include "../theme_manager.h"
#include "text/textrenderer.hpp"
#include "theme_manager.hpp"
HelpOverlay::HelpOverlay()
: renderer_(nullptr),
@@ -28,7 +28,7 @@ HelpOverlay::HelpOverlay()
// COLUMNA 1: SIMULACIÓN
{"SIMULACIÓN", ""},
{"1-8", "Escenarios (10 a 50,000 pelotas)"},
{"F", "Toggle Física Última Figura"},
{"F", "Toggle Física - Última Figura"},
{"B", "Modo Boids (enjambre)"},
{"ESPACIO", "Impulso contra gravedad"},
{"G", "Toggle Gravedad ON/OFF"},
@@ -151,7 +151,8 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
}
int line_height = text_renderer_->getTextHeight();
int padding = 25;
// Padding dinámico basado en altura física: 25px para >= 600px, escalado proporcionalmente para menores
int padding = (physical_height_ >= 600) ? 25 : std::max(10, physical_height_ / 24);
// Calcular ancho máximo por columna
int max_col1_width = 0;
@@ -234,11 +235,11 @@ void HelpOverlay::calculateBoxDimensions() {
int text_width, text_height;
calculateTextDimensions(text_width, text_height);
// Usar directamente el ancho y altura calculados según el contenido
box_width_ = text_width;
// Aplicar límites máximos: 95% ancho, 90% altura
int max_width = static_cast<int>(physical_width_ * 0.95f);
int max_height = static_cast<int>(physical_height_ * 0.90f);
// Altura: 90% de altura física o altura calculada, el que sea menor
int max_height = static_cast<int>(physical_height_ * 0.9f);
box_width_ = std::min(text_width, max_width);
box_height_ = std::min(text_height, max_height);
// Centrar en pantalla
@@ -333,7 +334,8 @@ void HelpOverlay::rebuildCachedTexture() {
// Configuración de espaciado
int line_height = text_renderer_->getTextHeight();
int padding = 25;
// Padding dinámico basado en altura física: 25px para >= 600px, escalado proporcionalmente para menores
int padding = (physical_height_ >= 600) ? 25 : std::max(10, physical_height_ / 24);
int current_x = padding; // Coordenadas relativas a la textura (0,0)
int current_y = padding;

View File

@@ -1,8 +1,8 @@
#include "notifier.h"
#include "../text/textrenderer.h"
#include "../theme_manager.h"
#include "../defines.h"
#include "../utils/easing_functions.h"
#include "notifier.hpp"
#include "text/textrenderer.hpp"
#include "theme_manager.hpp"
#include "defines.hpp"
#include "utils/easing_functions.hpp"
#include <SDL3/SDL.h>
// ============================================================================

View File

@@ -1,17 +1,17 @@
#include "ui_manager.h"
#include "ui_manager.hpp"
#include <SDL3/SDL.h>
#include <string>
#include "../ball.h" // for Ball
#include "../defines.h" // for TEXT_DURATION, NOTIFICATION_DURATION, AppMode, SimulationMode
#include "../engine.h" // for Engine (info de sistema)
#include "../scene/scene_manager.h" // for SceneManager
#include "../shapes/shape.h" // for Shape
#include "../text/textrenderer.h" // for TextRenderer
#include "../theme_manager.h" // for ThemeManager
#include "notifier.h" // for Notifier
#include "help_overlay.h" // for HelpOverlay
#include "ball.hpp" // for Ball
#include "defines.hpp" // for TEXT_DURATION, NOTIFICATION_DURATION, AppMode, SimulationMode
#include "engine.hpp" // for Engine (info de sistema)
#include "scene/scene_manager.hpp" // for SceneManager
#include "shapes/shape.hpp" // for Shape
#include "text/textrenderer.hpp" // for TextRenderer
#include "theme_manager.hpp" // for ThemeManager
#include "notifier.hpp" // for Notifier
#include "help_overlay.hpp" // for HelpOverlay
// ============================================================================
// HELPER: Obtener viewport en coordenadas físicas (no lógicas)
@@ -39,16 +39,11 @@ static SDL_Rect getPhysicalViewport(SDL_Renderer* renderer) {
}
UIManager::UIManager()
: text_renderer_(nullptr)
, text_renderer_debug_(nullptr)
: text_renderer_debug_(nullptr)
, text_renderer_notifier_(nullptr)
, notifier_(nullptr)
, help_overlay_(nullptr)
, show_debug_(false)
, show_text_(true)
, text_()
, text_pos_(0)
, text_init_time_(0)
, fps_last_time_(0)
, fps_frame_count_(0)
, fps_current_(0)
@@ -58,12 +53,13 @@ UIManager::UIManager()
, theme_manager_(nullptr)
, physical_window_width_(0)
, physical_window_height_(0)
, logical_window_width_(0)
, logical_window_height_(0)
, current_font_size_(18) { // Tamaño por defecto (medium)
}
UIManager::~UIManager() {
// Limpieza: Los objetos creados con new deben ser eliminados
delete text_renderer_;
delete text_renderer_debug_;
delete text_renderer_notifier_;
delete notifier_;
@@ -71,22 +67,23 @@ UIManager::~UIManager() {
}
void UIManager::initialize(SDL_Renderer* renderer, ThemeManager* theme_manager,
int physical_width, int physical_height) {
int physical_width, int physical_height,
int logical_width, int logical_height) {
renderer_ = renderer;
theme_manager_ = theme_manager;
physical_window_width_ = physical_width;
physical_window_height_ = physical_height;
logical_window_width_ = logical_width;
logical_window_height_ = logical_height;
// Calcular tamaño de fuente apropiado según dimensiones físicas
current_font_size_ = calculateFontSize(physical_width, physical_height);
// Calcular tamaño de fuente apropiado según dimensiones LÓGICAS (sin zoom)
current_font_size_ = calculateFontSize(logical_height);
// Crear renderers de texto
text_renderer_ = new TextRenderer();
text_renderer_debug_ = new TextRenderer();
text_renderer_notifier_ = new TextRenderer();
// Inicializar renderers con tamaño dinámico
text_renderer_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true);
text_renderer_debug_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true);
text_renderer_notifier_->init(renderer, "data/fonts/FunnelSans-Regular.ttf", current_font_size_, true);
@@ -115,11 +112,6 @@ void UIManager::update(Uint64 current_time, float delta_time) {
fps_text_ = "fps: " + std::to_string(fps_current_);
}
// Actualizar texto obsoleto (DEPRECATED)
if (show_text_) {
show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION);
}
// Actualizar sistema de notificaciones
notifier_->update(current_time);
}
@@ -138,11 +130,6 @@ void UIManager::render(SDL_Renderer* renderer,
physical_window_width_ = physical_width;
physical_window_height_ = physical_height;
// Renderizar texto obsoleto centrado (DEPRECATED - mantener temporalmente)
if (show_text_) {
renderObsoleteText(current_screen_width);
}
// Renderizar debug HUD si está activo
if (show_debug_) {
renderDebugHUD(engine, scene_manager, current_mode, current_app_mode,
@@ -183,17 +170,15 @@ void UIManager::updatePhysicalWindowSize(int width, int height) {
physical_window_width_ = width;
physical_window_height_ = height;
// Calcular nuevo tamaño de fuente apropiado
int new_font_size = calculateFontSize(width, height);
// Calcular nuevo tamaño de fuente apropiado basado en altura LÓGICA
// (las dimensiones lógicas no cambian con zoom, solo con cambios explícitos de resolución)
int new_font_size = calculateFontSize(logical_window_height_);
// Si el tamaño cambió, reinicializar todos los text renderers
if (new_font_size != current_font_size_) {
current_font_size_ = new_font_size;
// Reinicializar text renderers con nuevo tamaño
if (text_renderer_) {
text_renderer_->reinitialize(current_font_size_);
}
if (text_renderer_debug_) {
text_renderer_debug_->reinitialize(current_font_size_);
}
@@ -211,13 +196,6 @@ void UIManager::updatePhysicalWindowSize(int width, int height) {
notifier_->updateWindowSize(width, height);
}
void UIManager::setTextObsolete(const std::string& text, int pos, int current_screen_width) {
text_ = text;
text_pos_ = pos;
text_init_time_ = SDL_GetTicks();
show_text_ = true;
}
// === Métodos privados ===
void UIManager::renderDebugHUD(const Engine* engine,
@@ -424,27 +402,6 @@ void UIManager::renderDebugHUD(const Engine* engine,
}
}
void UIManager::renderObsoleteText(int current_screen_width) {
// DEPRECATED: Sistema antiguo de texto centrado
// Mantener por compatibilidad temporal hasta migrar todo a Notifier
// Calcular escala dinámica basada en resolución física
float text_scale_x = static_cast<float>(physical_window_width_) / 426.0f;
float text_scale_y = static_cast<float>(physical_window_height_) / 240.0f;
// Obtener color del tema actual (LERP interpolado)
int margin = 8;
Color text_color = theme_manager_->getInterpolatedColor(0);
int text_color_r = text_color.r;
int text_color_g = text_color.g;
int text_color_b = text_color.b;
// Renderizar texto centrado usando coordenadas físicas
text_renderer_->printPhysical(text_pos_, margin, text_.c_str(),
text_color_r, text_color_g, text_color_b,
text_scale_x, text_scale_y);
}
std::string UIManager::gravityDirectionToString(int direction) const {
switch (direction) {
case 0: return "Abajo"; // DOWN
@@ -455,20 +412,37 @@ std::string UIManager::gravityDirectionToString(int direction) const {
}
}
int UIManager::calculateFontSize(int physical_width, int physical_height) const {
// Calcular área física de la ventana
int area = physical_width * physical_height;
int UIManager::calculateFontSize(int logical_height) const {
// Escalado híbrido basado en ALTURA LÓGICA (resolución interna, sin zoom)
// Esto asegura que el tamaño de fuente sea consistente independientemente del zoom de ventana
// - Proporcional en extremos (muy bajo/alto)
// - Escalonado en rango medio (estabilidad)
// Stepped scaling con 3 tamaños:
// - SMALL: < 800x600 (480,000 pixels) → 14px
// - MEDIUM: 800x600 a 1920x1080 (2,073,600 pixels) → 18px
// - LARGE: > 1920x1080 → 24px
int font_size = 14; // Default fallback
if (area < 480000) {
return 14; // Ventanas pequeñas
} else if (area < 2073600) {
return 18; // Ventanas medianas (default)
if (logical_height < 300) {
// Rango bajo: proporcional (240px→9.6, 280px→11.2)
font_size = logical_height / 25;
} else if (logical_height < 380) {
// Rango muy bajo (300-379px) → 10px (crítico para 640x360)
font_size = 10;
} else if (logical_height < 500) {
// Rango medio-bajo (380-499px) → 12px
font_size = 12;
} else if (logical_height < 700) {
// Rango medio (500-699px) → 14px
font_size = 14;
} else if (logical_height < 900) {
// Rango medio-alto (700-899px) → 18px
font_size = 18;
} else {
return 24; // Ventanas grandes
// Rango alto: proporcional (1080px→42, 1440px→55, 2160px→72)
font_size = logical_height / 26;
}
// Aplicar límites: mínimo 9px, máximo 72px
if (font_size < 9) font_size = 9;
if (font_size > 72) font_size = 72;
return font_size;
}

View File

@@ -46,9 +46,12 @@ class UIManager {
* @param theme_manager Gestor de temas (para colores)
* @param physical_width Ancho físico de ventana (píxeles reales)
* @param physical_height Alto físico de ventana (píxeles reales)
* @param logical_width Ancho lógico (resolución interna)
* @param logical_height Alto lógico (resolución interna)
*/
void initialize(SDL_Renderer* renderer, ThemeManager* theme_manager,
int physical_width, int physical_height);
int physical_width, int physical_height,
int logical_width, int logical_height);
/**
* @brief Actualiza UI (FPS counter, notificaciones, texto obsoleto)
@@ -111,14 +114,6 @@ class UIManager {
*/
void updatePhysicalWindowSize(int width, int height);
/**
* @brief Establece texto obsoleto (DEPRECATED - usar Notifier en su lugar)
* @param text Texto a mostrar
* @param pos Posición X del texto
* @param current_screen_width Ancho de pantalla (para cálculos)
*/
void setTextObsolete(const std::string& text, int pos, int current_screen_width);
// === Getters ===
/**
@@ -131,11 +126,6 @@ class UIManager {
*/
int getCurrentFPS() const { return fps_current_; }
/**
* @brief Verifica si texto obsoleto está visible
*/
bool isTextObsoleteVisible() const { return show_text_; }
private:
/**
* @brief Renderiza HUD de debug (solo si show_debug_ == true)
@@ -153,12 +143,6 @@ class UIManager {
const Shape* active_shape,
float shape_convergence);
/**
* @brief Renderiza texto obsoleto centrado (DEPRECATED)
* @param current_screen_width Ancho lógico de pantalla
*/
void renderObsoleteText(int current_screen_width);
/**
* @brief Convierte dirección de gravedad a string
* @param direction Dirección como int (cast de GravityDirection)
@@ -167,15 +151,13 @@ class UIManager {
std::string gravityDirectionToString(int direction) const;
/**
* @brief Calcula tamaño de fuente apropiado según dimensiones físicas
* @param physical_width Ancho físico de ventana
* @param physical_height Alto físico de ventana
* @return Tamaño de fuente (14px/18px/24px)
* @brief Calcula tamaño de fuente apropiado según dimensiones lógicas
* @param logical_height Alto lógico (resolución interna, sin zoom)
* @return Tamaño de fuente (9-72px)
*/
int calculateFontSize(int physical_width, int physical_height) const;
int calculateFontSize(int logical_height) const;
// === Recursos de renderizado ===
TextRenderer* text_renderer_; // Texto obsoleto (DEPRECATED)
TextRenderer* text_renderer_debug_; // HUD de debug
TextRenderer* text_renderer_notifier_; // Notificaciones
Notifier* notifier_; // Sistema de notificaciones
@@ -183,12 +165,6 @@ class UIManager {
// === Estado de UI ===
bool show_debug_; // HUD de debug activo (tecla F12)
bool show_text_; // Texto obsoleto visible (DEPRECATED)
// === Sistema de texto obsoleto (DEPRECATED) ===
std::string text_; // Texto a mostrar
int text_pos_; // Posición X del texto
Uint64 text_init_time_; // Tiempo de inicio de texto
// === Sistema de FPS ===
Uint64 fps_last_time_; // Último tiempo de actualización de FPS
@@ -202,7 +178,9 @@ class UIManager {
ThemeManager* theme_manager_; // Gestor de temas (para colores)
int physical_window_width_; // Ancho físico de ventana (píxeles reales)
int physical_window_height_; // Alto físico de ventana (píxeles reales)
int logical_window_width_; // Ancho lógico (resolución interna)
int logical_window_height_; // Alto lógico (resolución interna)
// === Sistema de escalado dinámico de texto ===
int current_font_size_; // Tamaño de fuente actual (14/18/24)
int current_font_size_; // Tamaño de fuente actual (9-72px)
};

187
tools/Makefile Normal file
View File

@@ -0,0 +1,187 @@
# ============================================================================
# ViBe3 Physics - Resource Packer Tool Makefile
# ============================================================================
# Directorios
DIR_ROOT := $(dir $(abspath $(dir $(MAKEFILE_LIST))))
DIR_SOURCES := $(DIR_ROOT)source/
DIR_TOOLS := $(DIR_ROOT)tools/
DIR_DATA := $(DIR_ROOT)data/
# Archivos fuente
PACK_SOURCES := $(DIR_TOOLS)pack_resources.cpp $(DIR_SOURCES)resource_pack.cpp
PACK_INCLUDES := -I$(DIR_ROOT)
# Compilador y flags
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Os -ffunction-sections -fdata-sections
# Variables específicas por sistema operativo
ifeq ($(OS),Windows_NT)
# Windows
PACK_TOOL := pack_resources.exe
RESOURCE_PACK := ../resources.pack
TEST_PACK := test_resources.pack
LDFLAGS := -Wl,--gc-sections -static-libstdc++ -static-libgcc
RMFILE := del /Q
MKDIR := mkdir
FixPath = $(subst /,\,$1)
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
# Linux
PACK_TOOL := pack_resources
RESOURCE_PACK := ../resources.pack
TEST_PACK := test_resources.pack
LDFLAGS := -Wl,--gc-sections
RMFILE := rm -f
MKDIR := mkdir -p
FixPath = $1
endif
ifeq ($(UNAME_S),Darwin)
# macOS
PACK_TOOL := pack_resources
RESOURCE_PACK := ../resources.pack
TEST_PACK := test_resources.pack
LDFLAGS :=
RMFILE := rm -f
MKDIR := mkdir -p
FixPath = $1
endif
endif
# Detectar todos los archivos en data/ como dependencias
DATA_FILES := $(shell find ../data -type f 2>/dev/null)
# ============================================================================
# Targets principales
# ============================================================================
.PHONY: all pack_tool resource_pack test_pack clean help
# Target por defecto: compilar herramienta
all: pack_tool
# Compilar herramienta de empaquetado
pack_tool: $(PACK_TOOL)
$(PACK_TOOL): $(PACK_SOURCES)
@echo "=========================================="
@echo "Compilando herramienta de empaquetado..."
@echo "=========================================="
$(CXX) $(CXXFLAGS) $(PACK_INCLUDES) $(PACK_SOURCES) $(LDFLAGS) -o $(PACK_TOOL)
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo "✓ Herramienta compilada: $(PACK_TOOL)"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
# Crear pack de recursos final
resource_pack: $(PACK_TOOL)
@echo "=========================================="
@echo "Generando resources.pack..."
@echo "=========================================="
@echo "Directorio de datos: ../data"
@echo "Archivo de salida: $(RESOURCE_PACK)"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
./$(PACK_TOOL) ../data $(RESOURCE_PACK)
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo "✓ Pack de recursos creado exitosamente"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
# Crear pack de recursos de prueba
test_pack: $(PACK_TOOL)
@echo "=========================================="
@echo "Generando pack de prueba..."
@echo "=========================================="
@echo "Directorio de datos: ../data"
@echo "Archivo de salida: $(TEST_PACK)"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
./$(PACK_TOOL) ../data $(TEST_PACK)
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo "✓ Pack de prueba creado: $(TEST_PACK)"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
# Limpiar archivos generados
clean:
@echo "Limpiando archivos generados..."
ifeq ($(OS),Windows_NT)
@if exist "$(PACK_TOOL)" $(RMFILE) "$(PACK_TOOL)"
@if exist "$(TEST_PACK)" $(RMFILE) "$(TEST_PACK)"
else
@$(RMFILE) $(PACK_TOOL) $(TEST_PACK)
endif
@echo "✓ Limpieza completada"
# Mostrar ayuda
help:
@echo "=========================================="
@echo "ViBe3 Physics - Resource Packer Makefile"
@echo "=========================================="
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo "Comandos disponibles:"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo " make - Compilar herramienta (equivalente a 'make pack_tool')"
@echo " make pack_tool - Compilar herramienta de empaquetado"
@echo " make resource_pack - Crear ../resources.pack desde ../data"
@echo " make test_pack - Crear pack de prueba (test_resources.pack)"
@echo " make clean - Limpiar archivos generados"
@echo " make help - Mostrar esta ayuda"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo "Ejemplos de uso:"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif
@echo " cd tools"
@echo " make # Compilar herramienta"
@echo " make resource_pack # Crear pack de recursos"
@echo " ./$(PACK_TOOL) --help # Ver ayuda de la herramienta"
ifeq ($(OS),Windows_NT)
@echo.
else
@echo ""
endif

View File

@@ -1,4 +1,4 @@
#include "../source/resource_pack.h"
#include "../source/resource_pack.hpp"
#include <iostream>
#include <filesystem>