|
|
|
@@ -13,9 +13,9 @@
|
|
|
|
#include <cstdlib> // for rand, srand
|
|
|
|
#include <cstdlib> // for rand, srand
|
|
|
|
#include <cstring> // for strlen
|
|
|
|
#include <cstring> // for strlen
|
|
|
|
#include <ctime> // for time
|
|
|
|
#include <ctime> // for time
|
|
|
|
|
|
|
|
#include <filesystem> // for path operations
|
|
|
|
#include <iostream> // for cout
|
|
|
|
#include <iostream> // for cout
|
|
|
|
#include <string> // for string
|
|
|
|
#include <string> // for string
|
|
|
|
#include <filesystem> // for path operations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h> // for GetModuleFileName
|
|
|
|
#include <windows.h> // for GetModuleFileName
|
|
|
|
@@ -24,15 +24,15 @@
|
|
|
|
#include "ball.h" // for Ball
|
|
|
|
#include "ball.h" // for Ball
|
|
|
|
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
|
|
|
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
|
|
|
#include "external/texture.h" // for Texture
|
|
|
|
#include "external/texture.h" // for Texture
|
|
|
|
#include "shapes/sphere_shape.h" // for SphereShape
|
|
|
|
|
|
|
|
#include "shapes/cube_shape.h" // for CubeShape
|
|
|
|
|
|
|
|
#include "shapes/helix_shape.h" // for HelixShape
|
|
|
|
|
|
|
|
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
|
|
|
|
|
|
|
#include "shapes/torus_shape.h" // for TorusShape
|
|
|
|
|
|
|
|
#include "shapes/cylinder_shape.h" // for CylinderShape
|
|
|
|
|
|
|
|
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
|
|
|
|
|
|
|
#include "shapes/atom_shape.h" // for AtomShape
|
|
|
|
#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/png_shape.h" // for PNGShape
|
|
|
|
#include "shapes/png_shape.h" // for PNGShape
|
|
|
|
|
|
|
|
#include "shapes/sphere_shape.h" // for SphereShape
|
|
|
|
|
|
|
|
#include "shapes/torus_shape.h" // for TorusShape
|
|
|
|
|
|
|
|
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
|
|
|
|
|
|
|
|
|
|
|
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
|
|
|
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
|
|
|
std::string getExecutableDirectory() {
|
|
|
|
std::string getExecutableDirectory() {
|
|
|
|
@@ -48,25 +48,64 @@ std::string getExecutableDirectory() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Implementación de métodos públicos
|
|
|
|
// Implementación de métodos públicos
|
|
|
|
bool Engine::initialize(int width, int height, bool fullscreen) {
|
|
|
|
bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|
|
|
bool success = true;
|
|
|
|
bool success = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Usar parámetros o valores por defecto
|
|
|
|
// Obtener resolución de pantalla para validación
|
|
|
|
int window_width = (width > 0) ? width : SCREEN_WIDTH * WINDOW_ZOOM;
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
|
|
int window_height = (height > 0) ? height : SCREEN_HEIGHT * WINDOW_ZOOM;
|
|
|
|
std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl;
|
|
|
|
int logical_width = (width > 0) ? width : SCREEN_WIDTH;
|
|
|
|
return false;
|
|
|
|
int logical_height = (height > 0) ? height : SCREEN_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Guardar resolución base (configurada por CLI)
|
|
|
|
int num_displays = 0;
|
|
|
|
|
|
|
|
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
|
|
|
|
|
|
|
const auto *dm = (displays && num_displays > 0) ? SDL_GetCurrentDisplayMode(displays[0]) : nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int screen_w = dm ? dm->w : 1920; // Fallback si falla
|
|
|
|
|
|
|
|
int screen_h = dm ? dm->h - WINDOW_DECORATION_HEIGHT : 1080;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (displays) SDL_free(displays);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Usar parámetros o valores por defecto
|
|
|
|
|
|
|
|
int logical_width = (width > 0) ? width : DEFAULT_SCREEN_WIDTH;
|
|
|
|
|
|
|
|
int logical_height = (height > 0) ? height : DEFAULT_SCREEN_HEIGHT;
|
|
|
|
|
|
|
|
int window_zoom = (zoom > 0) ? zoom : DEFAULT_WINDOW_ZOOM;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// VALIDACIÓN 1: Si resolución > pantalla → reset a default
|
|
|
|
|
|
|
|
if (logical_width > screen_w || logical_height > screen_h) {
|
|
|
|
|
|
|
|
std::cout << "Advertencia: Resolución " << logical_width << "x" << logical_height
|
|
|
|
|
|
|
|
<< " excede pantalla " << screen_w << "x" << screen_h
|
|
|
|
|
|
|
|
<< ". Usando default " << DEFAULT_SCREEN_WIDTH << "x" << DEFAULT_SCREEN_HEIGHT << "\n";
|
|
|
|
|
|
|
|
logical_width = DEFAULT_SCREEN_WIDTH;
|
|
|
|
|
|
|
|
logical_height = DEFAULT_SCREEN_HEIGHT;
|
|
|
|
|
|
|
|
window_zoom = DEFAULT_WINDOW_ZOOM; // Reset zoom también
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// VALIDACIÓN 2: Calcular max_zoom y ajustar si es necesario
|
|
|
|
|
|
|
|
int max_zoom = std::min(screen_w / logical_width, screen_h / logical_height);
|
|
|
|
|
|
|
|
if (window_zoom > max_zoom) {
|
|
|
|
|
|
|
|
std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom
|
|
|
|
|
|
|
|
<< " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n";
|
|
|
|
|
|
|
|
window_zoom = max_zoom;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Si se especificaron parámetros CLI y zoom no se especificó, usar zoom=1
|
|
|
|
|
|
|
|
if ((width > 0 || height > 0) && zoom == 0) {
|
|
|
|
|
|
|
|
window_zoom = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular tamaño de ventana
|
|
|
|
|
|
|
|
int window_width = logical_width * window_zoom;
|
|
|
|
|
|
|
|
int window_height = logical_height * window_zoom;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Guardar resolución base (configurada por CLI o default)
|
|
|
|
base_screen_width_ = logical_width;
|
|
|
|
base_screen_width_ = logical_width;
|
|
|
|
base_screen_height_ = logical_height;
|
|
|
|
base_screen_height_ = logical_height;
|
|
|
|
current_screen_width_ = logical_width;
|
|
|
|
current_screen_width_ = logical_width;
|
|
|
|
current_screen_height_ = logical_height;
|
|
|
|
current_screen_height_ = logical_height;
|
|
|
|
|
|
|
|
|
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
|
|
// SDL ya inicializado arriba para validación
|
|
|
|
std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl;
|
|
|
|
{
|
|
|
|
success = false;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Crear ventana principal (fullscreen si se especifica)
|
|
|
|
// Crear ventana principal (fullscreen si se especifica)
|
|
|
|
Uint32 window_flags = SDL_WINDOW_OPENGL;
|
|
|
|
Uint32 window_flags = SDL_WINDOW_OPENGL;
|
|
|
|
if (fullscreen) {
|
|
|
|
if (fullscreen) {
|
|
|
|
@@ -613,8 +652,7 @@ void Engine::render() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
|
|
|
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
|
|
|
std::sort(render_order.begin(), render_order.end(),
|
|
|
|
std::sort(render_order.begin(), render_order.end(), [this](size_t a, size_t b) {
|
|
|
|
[this](size_t a, size_t b) {
|
|
|
|
|
|
|
|
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
|
|
|
return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
@@ -626,8 +664,7 @@ void Engine::render() {
|
|
|
|
float depth_scale = balls_[idx]->getDepthScale();
|
|
|
|
float depth_scale = balls_[idx]->getDepthScale();
|
|
|
|
|
|
|
|
|
|
|
|
// Mapear brightness de 0-1 a rango MIN-MAX
|
|
|
|
// Mapear brightness de 0-1 a rango MIN-MAX
|
|
|
|
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness *
|
|
|
|
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
|
|
|
(ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Aplicar factor de brillo al color
|
|
|
|
// Aplicar factor de brillo al color
|
|
|
|
int r_mod = static_cast<int>(color.r * brightness_factor);
|
|
|
|
int r_mod = static_cast<int>(color.r * brightness_factor);
|
|
|
|
@@ -903,7 +940,7 @@ void Engine::toggleRealFullscreen() {
|
|
|
|
|
|
|
|
|
|
|
|
// Restaurar ventana normal
|
|
|
|
// Restaurar ventana normal
|
|
|
|
SDL_SetWindowFullscreen(window_, false);
|
|
|
|
SDL_SetWindowFullscreen(window_, false);
|
|
|
|
SDL_SetWindowSize(window_, base_screen_width_ * WINDOW_ZOOM, base_screen_height_ * WINDOW_ZOOM);
|
|
|
|
SDL_SetWindowSize(window_, base_screen_width_ * DEFAULT_WINDOW_ZOOM, base_screen_height_ * DEFAULT_WINDOW_ZOOM);
|
|
|
|
|
|
|
|
|
|
|
|
// Restaurar presentación lógica base
|
|
|
|
// Restaurar presentación lógica base
|
|
|
|
SDL_SetRenderLogicalPresentation(renderer_, base_screen_width_, base_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
|
|
|
SDL_SetRenderLogicalPresentation(renderer_, base_screen_width_, base_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
|
|
|
@@ -966,11 +1003,16 @@ void Engine::toggleIntegerScaling() {
|
|
|
|
|
|
|
|
|
|
|
|
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
|
|
|
|
std::string Engine::gravityDirectionToString(GravityDirection direction) const {
|
|
|
|
switch (direction) {
|
|
|
|
switch (direction) {
|
|
|
|
case GravityDirection::DOWN: return "DOWN";
|
|
|
|
case GravityDirection::DOWN:
|
|
|
|
case GravityDirection::UP: return "UP";
|
|
|
|
return "DOWN";
|
|
|
|
case GravityDirection::LEFT: return "LEFT";
|
|
|
|
case GravityDirection::UP:
|
|
|
|
case GravityDirection::RIGHT: return "RIGHT";
|
|
|
|
return "UP";
|
|
|
|
default: return "UNKNOWN";
|
|
|
|
case GravityDirection::LEFT:
|
|
|
|
|
|
|
|
return "LEFT";
|
|
|
|
|
|
|
|
case GravityDirection::RIGHT:
|
|
|
|
|
|
|
|
return "RIGHT";
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1100,7 +1142,7 @@ int Engine::calculateMaxWindowZoom() const {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular zoom máximo usando la fórmula de Coffee Crisis
|
|
|
|
// Calcular zoom máximo usando la fórmula de Coffee Crisis
|
|
|
|
const int MAX_ZOOM = std::min(dm->w / SCREEN_WIDTH, (dm->h - WINDOW_DECORATION_HEIGHT) / SCREEN_HEIGHT);
|
|
|
|
const int MAX_ZOOM = std::min(dm->w / base_screen_width_, (dm->h - WINDOW_DECORATION_HEIGHT) / base_screen_height_);
|
|
|
|
|
|
|
|
|
|
|
|
SDL_free(displays);
|
|
|
|
SDL_free(displays);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1120,12 +1162,12 @@ void Engine::setWindowZoom(int new_zoom) {
|
|
|
|
// Obtener posición actual del centro de la ventana
|
|
|
|
// Obtener posición actual del centro de la ventana
|
|
|
|
int current_x, current_y;
|
|
|
|
int current_x, current_y;
|
|
|
|
SDL_GetWindowPosition(window_, ¤t_x, ¤t_y);
|
|
|
|
SDL_GetWindowPosition(window_, ¤t_x, ¤t_y);
|
|
|
|
int current_center_x = current_x + (SCREEN_WIDTH * current_window_zoom_) / 2;
|
|
|
|
int current_center_x = current_x + (base_screen_width_ * current_window_zoom_) / 2;
|
|
|
|
int current_center_y = current_y + (SCREEN_HEIGHT * current_window_zoom_) / 2;
|
|
|
|
int current_center_y = current_y + (base_screen_height_ * current_window_zoom_) / 2;
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular nuevo tamaño
|
|
|
|
// Calcular nuevo tamaño
|
|
|
|
int new_width = SCREEN_WIDTH * new_zoom;
|
|
|
|
int new_width = base_screen_width_ * new_zoom;
|
|
|
|
int new_height = SCREEN_HEIGHT * new_zoom;
|
|
|
|
int new_height = base_screen_height_ * new_zoom;
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular nueva posición (centrada en el punto actual)
|
|
|
|
// Calcular nueva posición (centrada en el punto actual)
|
|
|
|
int new_x = current_center_x - new_width / 2;
|
|
|
|
int new_x = current_center_x - new_width / 2;
|
|
|
|
@@ -1162,46 +1204,77 @@ void Engine::zoomOut() {
|
|
|
|
void Engine::initializeThemes() {
|
|
|
|
void Engine::initializeThemes() {
|
|
|
|
// SUNSET: Naranjas, rojos, amarillos, rosas (8 colores)
|
|
|
|
// SUNSET: Naranjas, rojos, amarillos, rosas (8 colores)
|
|
|
|
themes_[0] = {
|
|
|
|
themes_[0] = {
|
|
|
|
"SUNSET", "ATARDECER", // Nombres (inglés, español)
|
|
|
|
"SUNSET",
|
|
|
|
255, 140, 60, // Color texto: naranja cálido
|
|
|
|
"ATARDECER", // Nombres (inglés, español)
|
|
|
|
180.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo superior (naranja suave)
|
|
|
|
255,
|
|
|
|
40.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior (púrpura oscuro)
|
|
|
|
140,
|
|
|
|
{{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}}
|
|
|
|
60, // Color texto: naranja cálido
|
|
|
|
};
|
|
|
|
180.0f / 255.0f,
|
|
|
|
|
|
|
|
140.0f / 255.0f,
|
|
|
|
|
|
|
|
100.0f / 255.0f, // Fondo superior (naranja suave)
|
|
|
|
|
|
|
|
40.0f / 255.0f,
|
|
|
|
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
60.0f / 255.0f, // Fondo inferior (púrpura oscuro)
|
|
|
|
|
|
|
|
{{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}}};
|
|
|
|
|
|
|
|
|
|
|
|
// OCEAN: Azules, turquesas, blancos (8 colores)
|
|
|
|
// OCEAN: Azules, turquesas, blancos (8 colores)
|
|
|
|
themes_[1] = {
|
|
|
|
themes_[1] = {
|
|
|
|
"OCEAN", "OCEANO", // Nombres (inglés, español)
|
|
|
|
"OCEAN",
|
|
|
|
80, 200, 255, // Color texto: azul océano
|
|
|
|
"OCEANO", // Nombres (inglés, español)
|
|
|
|
100.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo superior (azul cielo)
|
|
|
|
80,
|
|
|
|
20.0f / 255.0f, 40.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior (azul marino)
|
|
|
|
200,
|
|
|
|
{{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}}
|
|
|
|
255, // Color texto: azul océano
|
|
|
|
};
|
|
|
|
100.0f / 255.0f,
|
|
|
|
|
|
|
|
150.0f / 255.0f,
|
|
|
|
|
|
|
|
200.0f / 255.0f, // Fondo superior (azul cielo)
|
|
|
|
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
40.0f / 255.0f,
|
|
|
|
|
|
|
|
80.0f / 255.0f, // Fondo inferior (azul marino)
|
|
|
|
|
|
|
|
{{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}}};
|
|
|
|
|
|
|
|
|
|
|
|
// NEON: Cian, magenta, verde lima, amarillo vibrante (8 colores)
|
|
|
|
// NEON: Cian, magenta, verde lima, amarillo vibrante (8 colores)
|
|
|
|
themes_[2] = {
|
|
|
|
themes_[2] = {
|
|
|
|
"NEON", "NEON", // Nombres (inglés, español)
|
|
|
|
"NEON",
|
|
|
|
255, 60, 255, // Color texto: magenta brillante
|
|
|
|
"NEON", // Nombres (inglés, español)
|
|
|
|
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior (negro azulado)
|
|
|
|
255,
|
|
|
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro)
|
|
|
|
60,
|
|
|
|
{{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}}
|
|
|
|
255, // Color texto: magenta brillante
|
|
|
|
};
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
40.0f / 255.0f, // Fondo superior (negro azulado)
|
|
|
|
|
|
|
|
0.0f / 255.0f,
|
|
|
|
|
|
|
|
0.0f / 255.0f,
|
|
|
|
|
|
|
|
0.0f / 255.0f, // Fondo inferior (negro)
|
|
|
|
|
|
|
|
{{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}}};
|
|
|
|
|
|
|
|
|
|
|
|
// FOREST: Verdes, marrones, amarillos otoño (8 colores)
|
|
|
|
// FOREST: Verdes, marrones, amarillos otoño (8 colores)
|
|
|
|
themes_[3] = {
|
|
|
|
themes_[3] = {
|
|
|
|
"FOREST", "BOSQUE", // Nombres (inglés, español)
|
|
|
|
"FOREST",
|
|
|
|
100, 255, 100, // Color texto: verde natural
|
|
|
|
"BOSQUE", // Nombres (inglés, español)
|
|
|
|
144.0f / 255.0f, 238.0f / 255.0f, 144.0f / 255.0f, // Fondo superior (verde claro)
|
|
|
|
100,
|
|
|
|
101.0f / 255.0f, 67.0f / 255.0f, 33.0f / 255.0f, // Fondo inferior (marrón tierra)
|
|
|
|
255,
|
|
|
|
{{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}}
|
|
|
|
100, // Color texto: verde natural
|
|
|
|
};
|
|
|
|
144.0f / 255.0f,
|
|
|
|
|
|
|
|
238.0f / 255.0f,
|
|
|
|
|
|
|
|
144.0f / 255.0f, // Fondo superior (verde claro)
|
|
|
|
|
|
|
|
101.0f / 255.0f,
|
|
|
|
|
|
|
|
67.0f / 255.0f,
|
|
|
|
|
|
|
|
33.0f / 255.0f, // Fondo inferior (marrón tierra)
|
|
|
|
|
|
|
|
{{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}}};
|
|
|
|
|
|
|
|
|
|
|
|
// RGB: Círculo cromático con 24 puntos (cada 15°) - Ultra precisión matemática
|
|
|
|
// RGB: Círculo cromático con 24 puntos (cada 15°) - Ultra precisión matemática
|
|
|
|
themes_[4] = {
|
|
|
|
themes_[4] = {
|
|
|
|
"RGB", "RGB", // Nombres (inglés, español)
|
|
|
|
"RGB",
|
|
|
|
100, 100, 100, // Color texto: gris oscuro (contraste con fondo blanco)
|
|
|
|
"RGB", // Nombres (inglés, español)
|
|
|
|
1.0f, 1.0f, 1.0f, // Fondo superior (blanco puro)
|
|
|
|
100,
|
|
|
|
1.0f, 1.0f, 1.0f, // Fondo inferior (blanco puro) - sin degradado
|
|
|
|
100,
|
|
|
|
|
|
|
|
100, // Color texto: gris oscuro (contraste con fondo blanco)
|
|
|
|
|
|
|
|
1.0f,
|
|
|
|
|
|
|
|
1.0f,
|
|
|
|
|
|
|
|
1.0f, // Fondo superior (blanco puro)
|
|
|
|
|
|
|
|
1.0f,
|
|
|
|
|
|
|
|
1.0f,
|
|
|
|
|
|
|
|
1.0f, // Fondo inferior (blanco puro) - sin degradado
|
|
|
|
{
|
|
|
|
{
|
|
|
|
{255, 0, 0}, // 0° - Rojo puro
|
|
|
|
{255, 0, 0}, // 0° - Rojo puro
|
|
|
|
{255, 64, 0}, // 15° - Rojo-Naranja
|
|
|
|
{255, 64, 0}, // 15° - Rojo-Naranja
|
|
|
|
@@ -1227,15 +1300,21 @@ void Engine::initializeThemes() {
|
|
|
|
{255, 0, 191}, // 315° - Magenta-Rojo claro
|
|
|
|
{255, 0, 191}, // 315° - Magenta-Rojo claro
|
|
|
|
{255, 0, 128}, // 330° - Magenta-Rojo
|
|
|
|
{255, 0, 128}, // 330° - Magenta-Rojo
|
|
|
|
{255, 0, 64} // 345° - Magenta claro-Rojo
|
|
|
|
{255, 0, 64} // 345° - Magenta claro-Rojo
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MONOCHROME: Fondo negro degradado, sprites blancos monocromáticos (8 tonos grises)
|
|
|
|
// MONOCHROME: Fondo negro degradado, sprites blancos monocromáticos (8 tonos grises)
|
|
|
|
themes_[5] = {
|
|
|
|
themes_[5] = {
|
|
|
|
"MONOCHROME", "MONOCROMO", // Nombres (inglés, español)
|
|
|
|
"MONOCHROME",
|
|
|
|
200, 200, 200, // Color texto: gris claro
|
|
|
|
"MONOCROMO", // Nombres (inglés, español)
|
|
|
|
20.0f / 255.0f, 20.0f / 255.0f, 20.0f / 255.0f, // Fondo superior (gris muy oscuro)
|
|
|
|
200,
|
|
|
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro)
|
|
|
|
200,
|
|
|
|
|
|
|
|
200, // Color texto: gris claro
|
|
|
|
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
20.0f / 255.0f,
|
|
|
|
|
|
|
|
20.0f / 255.0f, // Fondo superior (gris muy oscuro)
|
|
|
|
|
|
|
|
0.0f / 255.0f,
|
|
|
|
|
|
|
|
0.0f / 255.0f,
|
|
|
|
|
|
|
|
0.0f / 255.0f, // Fondo inferior (negro)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
{255, 255, 255}, // Blanco puro - todas las pelotas del mismo color
|
|
|
|
{255, 255, 255}, // Blanco puro - todas las pelotas del mismo color
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
@@ -1244,9 +1323,7 @@ void Engine::initializeThemes() {
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255},
|
|
|
|
{255, 255, 255}
|
|
|
|
{255, 255, 255}}};
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Engine::startThemeTransition(ColorTheme new_theme) {
|
|
|
|
void Engine::startThemeTransition(ColorTheme new_theme) {
|
|
|
|
@@ -1284,8 +1361,7 @@ Color Engine::getInterpolatedColor(size_t ball_index) const {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.r), static_cast<float>(target_color.r), transition_progress_)),
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.r), static_cast<float>(target_color.r), transition_progress_)),
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.g), static_cast<float>(target_color.g), transition_progress_)),
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.g), static_cast<float>(target_color.g), transition_progress_)),
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.b), static_cast<float>(target_color.b), transition_progress_))
|
|
|
|
static_cast<Uint8>(lerp(static_cast<float>(current_color.b), static_cast<float>(target_color.b), transition_progress_))};
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sistema de Modo DEMO (auto-play)
|
|
|
|
// Sistema de Modo DEMO (auto-play)
|
|
|
|
@@ -1321,9 +1397,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|
|
|
|
|
|
|
|
|
|
|
if (is_lite) {
|
|
|
|
if (is_lite) {
|
|
|
|
// DEMO LITE: Solo física/figuras
|
|
|
|
// DEMO LITE: Solo física/figuras
|
|
|
|
TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE
|
|
|
|
TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE + DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS + DEMO_LITE_WEIGHT_IMPULSE;
|
|
|
|
+ DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS
|
|
|
|
|
|
|
|
+ DEMO_LITE_WEIGHT_IMPULSE;
|
|
|
|
|
|
|
|
random_value = rand() % TOTAL_WEIGHT;
|
|
|
|
random_value = rand() % TOTAL_WEIGHT;
|
|
|
|
|
|
|
|
|
|
|
|
// Cambiar dirección gravedad (25%)
|
|
|
|
// Cambiar dirección gravedad (25%)
|
|
|
|
@@ -1344,9 +1418,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|
|
|
// Activar figura 3D (25%)
|
|
|
|
// Activar figura 3D (25%)
|
|
|
|
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
|
|
|
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
|
|
|
if (random_value < accumulated_weight) {
|
|
|
|
if (random_value < accumulated_weight) {
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
|
|
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
|
|
|
|
|
|
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
|
|
|
|
|
|
int shape_index = rand() % 9;
|
|
|
|
int shape_index = rand() % 9;
|
|
|
|
activateShape(shapes[shape_index]);
|
|
|
|
activateShape(shapes[shape_index]);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
@@ -1368,10 +1440,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// DEMO COMPLETO: Todas las acciones
|
|
|
|
// DEMO COMPLETO: Todas las acciones
|
|
|
|
TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE
|
|
|
|
TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE + DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
|
|
|
+ DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME
|
|
|
|
|
|
|
|
+ DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM
|
|
|
|
|
|
|
|
+ DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
|
|
|
|
|
|
|
random_value = rand() % TOTAL_WEIGHT;
|
|
|
|
random_value = rand() % TOTAL_WEIGHT;
|
|
|
|
|
|
|
|
|
|
|
|
// Cambiar dirección gravedad (10%)
|
|
|
|
// Cambiar dirección gravedad (10%)
|
|
|
|
@@ -1392,9 +1461,7 @@ void Engine::performDemoAction(bool is_lite) {
|
|
|
|
// Activar figura 3D (20%)
|
|
|
|
// Activar figura 3D (20%)
|
|
|
|
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
|
|
|
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
|
|
|
if (random_value < accumulated_weight) {
|
|
|
|
if (random_value < accumulated_weight) {
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
|
|
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
|
|
|
|
|
|
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
|
|
|
|
|
|
|
int shape_index = rand() % 9;
|
|
|
|
int shape_index = rand() % 9;
|
|
|
|
activateShape(shapes[shape_index]);
|
|
|
|
activateShape(shapes[shape_index]);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
@@ -1489,9 +1556,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
|
|
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
|
|
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
|
|
|
|
|
|
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
|
|
|
|
|
|
activateShape(shapes[rand() % 8]);
|
|
|
|
activateShape(shapes[rand() % 8]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1527,9 +1592,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
|
|
|
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
|
|
|
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
|
|
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
|
|
|
|
|
|
|
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
|
|
|
|
|
|
|
activateShape(shapes[rand() % 8]);
|
|
|
|
activateShape(shapes[rand() % 8]);
|
|
|
|
|
|
|
|
|
|
|
|
// 5. Profundidad (solo si estamos en figura)
|
|
|
|
// 5. Profundidad (solo si estamos en figura)
|
|
|
|
@@ -1780,9 +1843,7 @@ void Engine::updateShape() {
|
|
|
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
|
|
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
|
|
|
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
|
|
|
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
|
|
|
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
|
|
|
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
|
|
|
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_,
|
|
|
|
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_, SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
|
|
|
SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR,
|
|
|
|
|
|
|
|
SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular brillo según profundidad Z para renderizado
|
|
|
|
// Calcular brillo según profundidad Z para renderizado
|
|
|
|
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
|
|
|
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
|
|
|
|