Compare commits
5 Commits
f06123feff
...
2026-03-18
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aa4a1227e | |||
| 02fdcd4113 | |||
| 7db9e46f95 | |||
| ff6aaef7c6 | |||
| 8e2e681b2c |
@@ -74,7 +74,16 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
|
||||
|
||||
// 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) {
|
||||
if (max_zoom < 1) {
|
||||
// Resolució lògica no cap en pantalla ni a zoom=1: escalar-la per fer-la càpida
|
||||
float scale = std::min(static_cast<float>(screen_w) / logical_width,
|
||||
static_cast<float>(screen_h) / logical_height);
|
||||
logical_width = std::max(320, static_cast<int>(logical_width * scale));
|
||||
logical_height = std::max(240, static_cast<int>(logical_height * scale));
|
||||
window_zoom = 1;
|
||||
std::cout << "Advertencia: Resolución no cabe en pantalla. Ajustando a "
|
||||
<< logical_width << "x" << logical_height << "\n";
|
||||
} else if (window_zoom > max_zoom) {
|
||||
std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom
|
||||
<< " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n";
|
||||
window_zoom = max_zoom;
|
||||
@@ -288,7 +297,10 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod
|
||||
}
|
||||
|
||||
// Benchmark de rendimiento (determina max_auto_scenario_ para modos automáticos)
|
||||
runPerformanceBenchmark();
|
||||
if (!skip_benchmark_)
|
||||
runPerformanceBenchmark();
|
||||
else if (custom_scenario_enabled_)
|
||||
custom_auto_available_ = true; // benchmark omitido: confiar en que el hardware lo soporta
|
||||
|
||||
// Precalentar caché: shapes PNG (evitar I/O en primera activación de PNG_SHAPE)
|
||||
{
|
||||
@@ -554,6 +566,21 @@ void Engine::switchTexture() {
|
||||
switchTextureInternal(true); // Mostrar notificación en modo manual
|
||||
}
|
||||
|
||||
// Control manual del benchmark (--skip-benchmark, --max-balls)
|
||||
void Engine::setSkipBenchmark() {
|
||||
skip_benchmark_ = true;
|
||||
}
|
||||
|
||||
void Engine::setMaxBallsOverride(int n) {
|
||||
skip_benchmark_ = true;
|
||||
int best = DEMO_AUTO_MIN_SCENARIO;
|
||||
for (int i = DEMO_AUTO_MIN_SCENARIO; i <= DEMO_AUTO_MAX_SCENARIO; ++i) {
|
||||
if (BALL_COUNT_SCENARIOS[i] <= n) best = i;
|
||||
else break;
|
||||
}
|
||||
max_auto_scenario_ = best;
|
||||
}
|
||||
|
||||
// Escenario custom (--custom-balls)
|
||||
void Engine::setCustomScenario(int balls) {
|
||||
custom_scenario_balls_ = balls;
|
||||
@@ -691,34 +718,31 @@ void Engine::render() {
|
||||
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
||||
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
||||
|
||||
// Crear vector de índices para ordenamiento
|
||||
std::vector<size_t> render_order;
|
||||
render_order.reserve(balls.size());
|
||||
// Bucket sort per profunditat Z (O(N) vs O(N log N))
|
||||
for (size_t i = 0; i < balls.size(); i++) {
|
||||
render_order.push_back(i);
|
||||
int b = static_cast<int>(balls[i]->getDepthBrightness() * (DEPTH_SORT_BUCKETS - 1));
|
||||
depth_buckets_[std::clamp(b, 0, DEPTH_SORT_BUCKETS - 1)].push_back(i);
|
||||
}
|
||||
|
||||
// Ordenar índices por profundidad Z (menor primero = fondo primero)
|
||||
std::sort(render_order.begin(), render_order.end(), [&balls](size_t a, size_t b) {
|
||||
return balls[a]->getDepthBrightness() < balls[b]->getDepthBrightness();
|
||||
});
|
||||
// Renderizar en orden de profundidad (bucket 0 = fons, bucket 255 = davant)
|
||||
for (int b = 0; b < DEPTH_SORT_BUCKETS; b++) {
|
||||
for (size_t idx : depth_buckets_[b]) {
|
||||
SDL_FRect pos = balls[idx]->getPosition();
|
||||
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||
float brightness = balls[idx]->getDepthBrightness();
|
||||
float depth_scale = balls[idx]->getDepthScale();
|
||||
|
||||
// Renderizar en orden de profundidad (fondo → frente)
|
||||
for (size_t idx : render_order) {
|
||||
SDL_FRect pos = balls[idx]->getPosition();
|
||||
Color color = theme_manager_->getInterpolatedColor(idx); // Usar color interpolado (LERP)
|
||||
float brightness = balls[idx]->getDepthBrightness();
|
||||
float depth_scale = balls[idx]->getDepthScale();
|
||||
// Mapear brightness de 0-1 a rango MIN-MAX
|
||||
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
||||
|
||||
// Mapear brightness de 0-1 a rango MIN-MAX
|
||||
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f;
|
||||
// Aplicar factor de brillo al color
|
||||
int r_mod = static_cast<int>(color.r * brightness_factor);
|
||||
int g_mod = static_cast<int>(color.g * brightness_factor);
|
||||
int b_mod = static_cast<int>(color.b * brightness_factor);
|
||||
|
||||
// Aplicar factor de brillo al color
|
||||
int r_mod = static_cast<int>(color.r * brightness_factor);
|
||||
int g_mod = static_cast<int>(color.g * brightness_factor);
|
||||
int b_mod = static_cast<int>(color.b * brightness_factor);
|
||||
|
||||
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod, depth_scale);
|
||||
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod, depth_scale);
|
||||
}
|
||||
depth_buckets_[b].clear(); // netejar per al proper frame
|
||||
}
|
||||
} else {
|
||||
// MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
|
||||
@@ -1667,17 +1691,22 @@ void Engine::runPerformanceBenchmark() {
|
||||
const int BENCH_DURATION_MS = 600;
|
||||
const int WARMUP_FRAMES = 5;
|
||||
|
||||
SimulationMode original_mode = current_mode_;
|
||||
|
||||
auto restore = [&]() {
|
||||
SDL_SetRenderVSync(renderer_, vsync_enabled_ ? 1 : 0);
|
||||
SDL_ShowWindow(window_);
|
||||
scene_manager_->changeScenario(0, current_mode_);
|
||||
current_mode_ = original_mode;
|
||||
active_shape_.reset();
|
||||
scene_manager_->changeScenario(0, original_mode);
|
||||
last_frame_time_ = 0;
|
||||
};
|
||||
|
||||
// Test escenario custom (independiente de max_auto_scenario_)
|
||||
custom_auto_available_ = false;
|
||||
if (custom_scenario_enabled_) {
|
||||
scene_manager_->changeScenario(CUSTOM_SCENARIO_IDX, current_mode_);
|
||||
scene_manager_->changeScenario(CUSTOM_SCENARIO_IDX, SimulationMode::SHAPE);
|
||||
activateShapeInternal(ShapeType::SPHERE);
|
||||
last_frame_time_ = 0;
|
||||
for (int w = 0; w < WARMUP_FRAMES; ++w) {
|
||||
calculateDeltaTime();
|
||||
@@ -1700,7 +1729,8 @@ void Engine::runPerformanceBenchmark() {
|
||||
|
||||
// Probar de más pesado a más ligero
|
||||
for (int idx = DEMO_AUTO_MAX_SCENARIO; idx >= DEMO_AUTO_MIN_SCENARIO; --idx) {
|
||||
scene_manager_->changeScenario(idx, current_mode_);
|
||||
scene_manager_->changeScenario(idx, SimulationMode::SHAPE);
|
||||
activateShapeInternal(ShapeType::SPHERE);
|
||||
|
||||
// Warmup: estabilizar física y pipeline GPU
|
||||
last_frame_time_ = 0;
|
||||
|
||||
@@ -81,6 +81,10 @@ class Engine {
|
||||
bool isCustomAutoAvailable() const { return custom_auto_available_; }
|
||||
int getCustomScenarioBalls() const { return custom_scenario_balls_; }
|
||||
|
||||
// Control manual del benchmark (--skip-benchmark, --max-balls)
|
||||
void setSkipBenchmark();
|
||||
void setMaxBallsOverride(int n);
|
||||
|
||||
// Notificaciones (público para InputHandler)
|
||||
void showNotificationForAction(const std::string& text);
|
||||
|
||||
@@ -189,6 +193,7 @@ class Engine {
|
||||
int custom_scenario_balls_ = 0;
|
||||
bool custom_scenario_enabled_ = false;
|
||||
bool custom_auto_available_ = false;
|
||||
bool skip_benchmark_ = false;
|
||||
|
||||
// Sistema de convergencia para LOGO MODE (escala con resolución)
|
||||
// Usado por performLogoAction() para detectar cuando las bolas forman el logo
|
||||
@@ -218,6 +223,10 @@ class Engine {
|
||||
std::vector<SDL_Vertex> batch_vertices_;
|
||||
std::vector<int> batch_indices_;
|
||||
|
||||
// Bucket sort per z-ordering (SHAPE mode)
|
||||
static constexpr int DEPTH_SORT_BUCKETS = 256;
|
||||
std::array<std::vector<size_t>, DEPTH_SORT_BUCKETS> depth_buckets_;
|
||||
|
||||
// Configuración del sistema de texto (constantes configurables)
|
||||
static constexpr const char* TEXT_FONT_PATH = "data/fonts/determination.ttf";
|
||||
static constexpr int TEXT_BASE_SIZE = 24; // Tamaño base para 240p
|
||||
|
||||
@@ -19,6 +19,8 @@ void printHelp() {
|
||||
std::cout << " -k, --kiosk Modo kiosko (F4 fijo, sin ESC, sin zoom)\n";
|
||||
std::cout << " -m, --mode <mode> Modo inicial: sandbox, demo, demo-lite, logo (default: sandbox)\n";
|
||||
std::cout << " --custom-balls <n> Activa escenario custom (tecla 9) con N pelotas\n";
|
||||
std::cout << " --skip-benchmark Salta el benchmark y usa el máximo de bolas (50000)\n";
|
||||
std::cout << " --max-balls <n> Limita el máximo de bolas en modos DEMO/DEMO_LITE\n";
|
||||
std::cout << " --help Mostrar esta ayuda\n\n";
|
||||
std::cout << "Ejemplos:\n";
|
||||
std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n";
|
||||
@@ -41,6 +43,8 @@ int main(int argc, char* argv[]) {
|
||||
bool fullscreen = false;
|
||||
bool real_fullscreen = false;
|
||||
bool kiosk_mode = false;
|
||||
bool skip_benchmark = false;
|
||||
int max_balls_override = 0;
|
||||
AppMode initial_mode = AppMode::SANDBOX; // Modo inicial (default: SANDBOX)
|
||||
|
||||
// Parsear argumentos
|
||||
@@ -118,6 +122,20 @@ int main(int argc, char* argv[]) {
|
||||
std::cerr << "Error: --custom-balls requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(argv[i], "--skip-benchmark") == 0) {
|
||||
skip_benchmark = true;
|
||||
} else if (strcmp(argv[i], "--max-balls") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
int n = atoi(argv[++i]);
|
||||
if (n < 1) {
|
||||
std::cerr << "Error: --max-balls requiere un valor >= 1\n";
|
||||
return -1;
|
||||
}
|
||||
max_balls_override = n;
|
||||
} else {
|
||||
std::cerr << "Error: --max-balls requiere un valor\n";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: Opción desconocida '" << argv[i] << "'\n";
|
||||
printHelp();
|
||||
@@ -135,6 +153,11 @@ int main(int argc, char* argv[]) {
|
||||
if (custom_balls > 0)
|
||||
engine.setCustomScenario(custom_balls); // pre-init: asigna campos antes del benchmark
|
||||
|
||||
if (max_balls_override > 0)
|
||||
engine.setMaxBallsOverride(max_balls_override);
|
||||
else if (skip_benchmark)
|
||||
engine.setSkipBenchmark();
|
||||
|
||||
if (!engine.initialize(width, height, zoom, fullscreen, initial_mode)) {
|
||||
std::cout << "¡Error al inicializar el engine!" << std::endl;
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user