From 1d2e9c503569e662c7e995763f0ec25b3108b17b Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 20 Mar 2026 22:35:40 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20F7/F8=20redimensionan=20campo=20l=C3=B3?= =?UTF-8?q?gico,=20F1/F2=20muestran=20notificaci=C3=B3n=20de=20zoom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - F7/F8: nuevo setFieldScale() cambia resolución lógica en pasos del 10% (mín 50%, máx limitado por pantalla), reinicia escena como F4 - F1/F2: muestran notificación "Zoom X%" al cambiar escala de ventana - Ventana física = lógico × zoom en todo momento; resizeWindowCentered() unifica el cálculo de posición leyendo el tamaño real con SDL_GetWindowSize - PostFXUniforms::time renombrado a screen_height; scanlines usan la altura lógica actual en lugar del 720 hardcodeado — F1/F2 escalan las scanlines visualmente, F7/F8 las mantienen a 1 franja por píxel lógico - Eliminados logs de debug de calculateMaxWindowScale y setWindowScale Co-Authored-By: Claude Sonnet 4.6 --- shaders/postfx.frag | 4 +- source/defines.hpp | 12 ++- source/engine.cpp | 163 ++++++++++++++++++++------------- source/engine.hpp | 18 +++- source/gpu/gpu_pipeline.cpp | 4 +- source/gpu/gpu_pipeline.hpp | 2 +- source/input/input_handler.cpp | 11 +++ 7 files changed, 135 insertions(+), 79 deletions(-) diff --git a/shaders/postfx.frag b/shaders/postfx.frag index 57d301b..64add04 100644 --- a/shaders/postfx.frag +++ b/shaders/postfx.frag @@ -6,7 +6,7 @@ layout(set=3, binding=0) uniform PostFXUniforms { float vignette_strength; float chroma_strength; float scanline_strength; - float time; + float screen_height; } u; void main() { float ca = u.chroma_strength * 0.005; @@ -15,7 +15,7 @@ void main() { color.g = texture(scene, v_uv).g; color.b = texture(scene, v_uv - vec2( ca, 0.0)).b; color.a = texture(scene, v_uv).a; - float scan = 0.85 + 0.15 * sin(v_uv.y * 3.14159265 * 720.0); + float scan = 0.85 + 0.15 * sin(v_uv.y * 3.14159265 * u.screen_height); color.rgb *= mix(1.0, scan, u.scanline_strength); vec2 d = v_uv - vec2(0.5, 0.5); float vignette = 1.0 - dot(d, d) * u.vignette_strength; diff --git a/source/defines.hpp b/source/defines.hpp index 9ddf9ce..e43281c 100644 --- a/source/defines.hpp +++ b/source/defines.hpp @@ -5,19 +5,23 @@ #include // for std::vector in DynamicThemeKeyframe/DynamicTheme // Configuración de ventana y pantalla -constexpr char WINDOW_CAPTION[] = "ViBe3 Physics (JailDesigner 2025)"; +constexpr char WINDOW_CAPTION[] = "© 2025 ViBe3 Physics — JailDesigner"; // Resolución por defecto (usada si no se especifica en CLI) 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) -constexpr int WINDOW_ZOOM_MAX = 10; // Zoom máximo teórico (3200x2400) +// Configuración de zoom dinámico de ventana (legacy, solo usado en initialize()) +constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (legacy, para validación inicial) +constexpr int WINDOW_ZOOM_MAX = 10; // Zoom máximo teórico (legacy) constexpr int WINDOW_DESKTOP_MARGIN = 10; // Margen mínimo con bordes del escritorio constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones del SO +// Configuración de escala de ventana por pasos (F1/F2) +constexpr float WINDOW_SCALE_STEP = 0.1f; // Incremento/decremento por pulsación (10%) +constexpr float WINDOW_SCALE_MIN = 0.5f; // Escala mínima (50% de la resolución base) + // Configuración de física constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²) diff --git a/source/engine.cpp b/source/engine.cpp index cca3df7..b548ca6 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -84,8 +84,8 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod window_zoom = 1; } - // Guardar zoom calculado ANTES de crear la ventana (para F1/F2/F3/F4) - current_window_zoom_ = window_zoom; + // Guardar escala inicial (siempre 1.0 salvo que CLI haya pedido zoom > 1) + current_window_scale_ = static_cast(window_zoom); // Calcular tamaño de ventana int window_width = logical_width * window_zoom; @@ -427,7 +427,7 @@ void Engine::calculateDeltaTime() { void Engine::update() { // Accumulate time for PostFX uniforms - postfx_uniforms_.time += delta_time_; + postfx_uniforms_.screen_height = static_cast(current_screen_height_); // Actualizar visibilidad del cursor (auto-ocultar tras inactividad) Mouse::updateCursorVisibility(); @@ -953,7 +953,9 @@ void Engine::toggleFullscreen() { // Si acabamos de salir de fullscreen, restaurar tamaño de ventana if (!fullscreen_enabled_) { - SDL_SetWindowSize(window_, base_screen_width_ * current_window_zoom_, base_screen_height_ * current_window_zoom_); + int restore_w = static_cast(std::round(base_screen_width_ * current_window_scale_)); + int restore_h = static_cast(std::round(base_screen_height_ * current_window_scale_)); + SDL_SetWindowSize(window_, restore_w, restore_h); SDL_SetWindowPosition(window_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } @@ -1018,10 +1020,13 @@ void Engine::toggleRealFullscreen() { // Volver a resolución base (configurada por CLI o default) current_screen_width_ = base_screen_width_; current_screen_height_ = base_screen_height_; + current_field_scale_ = 1.0f; // Resetear escala de campo al salir de fullscreen real - // Restaurar ventana normal con el zoom actual (no hardcoded) + // Restaurar ventana normal con la escala actual SDL_SetWindowFullscreen(window_, false); - SDL_SetWindowSize(window_, base_screen_width_ * current_window_zoom_, base_screen_height_ * current_window_zoom_); + int restore_w = static_cast(std::round(base_screen_width_ * current_window_scale_)); + int restore_h = static_cast(std::round(base_screen_height_ * current_window_scale_)); + SDL_SetWindowSize(window_, restore_w, restore_h); SDL_SetWindowPosition(window_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); // Recrear render target offscreen con resolución base @@ -1148,85 +1153,112 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, static_cast(current_screen_height_)); } -// Sistema de zoom dinámico -int Engine::calculateMaxWindowZoom() const { - // Obtener información del display usando el método de Coffee Crisis - int num_displays = 0; - SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); - if (displays == nullptr || num_displays == 0) { - return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener +// Sistema de escala de ventana (pasos del 10%) +float Engine::calculateMaxWindowScale() const { + SDL_Rect bounds; + if (!SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &bounds)) { // bool: false = error + return WINDOW_SCALE_MIN; // Fallback solo si falla de verdad } - - // Obtener el modo de display actual - const auto* dm = SDL_GetCurrentDisplayMode(displays[0]); - if (dm == nullptr) { - SDL_free(displays); - return WINDOW_ZOOM_MIN; - } - - // Calcular zoom máximo usando la fórmula de Coffee Crisis - const int MAX_ZOOM = std::min(dm->w / base_screen_width_, (dm->h - WINDOW_DECORATION_HEIGHT) / base_screen_height_); - - SDL_free(displays); - - // Aplicar límites - return std::max(WINDOW_ZOOM_MIN, std::min(MAX_ZOOM, WINDOW_ZOOM_MAX)); + float max_by_w = static_cast(bounds.w - 2 * WINDOW_DESKTOP_MARGIN) / base_screen_width_; + float max_by_h = static_cast(bounds.h - 2 * WINDOW_DESKTOP_MARGIN - WINDOW_DECORATION_HEIGHT) / base_screen_height_; + float result = std::max(WINDOW_SCALE_MIN, std::min(max_by_w, max_by_h)); + return result; } -void Engine::setWindowZoom(int new_zoom) { - // Validar zoom - int max_zoom = calculateMaxWindowZoom(); - new_zoom = std::max(WINDOW_ZOOM_MIN, std::min(new_zoom, max_zoom)); +// Redimensiona la ventana física manteniéndo su centro, con clamping a pantalla. +static void resizeWindowCentered(SDL_Window* window, int new_w, int new_h) { + int cur_x, cur_y, cur_w, cur_h; + SDL_GetWindowPosition(window, &cur_x, &cur_y); + SDL_GetWindowSize(window, &cur_w, &cur_h); - if (new_zoom == current_window_zoom_) { - return; // No hay cambio + int new_x = cur_x + (cur_w - new_w) / 2; + int new_y = cur_y + (cur_h - new_h) / 2; + + SDL_Rect bounds; + if (SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &bounds)) { + new_x = std::max(WINDOW_DESKTOP_MARGIN, + std::min(new_x, bounds.w - new_w - WINDOW_DESKTOP_MARGIN)); + new_y = std::max(WINDOW_DESKTOP_MARGIN, + std::min(new_y, bounds.h - new_h - WINDOW_DESKTOP_MARGIN - WINDOW_DECORATION_HEIGHT)); } - // Obtener posición actual del centro de la ventana - int current_x, current_y; - SDL_GetWindowPosition(window_, ¤t_x, ¤t_y); - int current_center_x = current_x + (base_screen_width_ * current_window_zoom_) / 2; - int current_center_y = current_y + (base_screen_height_ * current_window_zoom_) / 2; + SDL_SetWindowSize(window, new_w, new_h); + SDL_SetWindowPosition(window, new_x, new_y); +} - // Calcular nuevo tamaño - int new_width = base_screen_width_ * new_zoom; - int new_height = base_screen_height_ * new_zoom; +void Engine::setWindowScale(float new_scale) { + float max_scale = calculateMaxWindowScale(); + new_scale = std::max(WINDOW_SCALE_MIN, std::min(new_scale, max_scale)); + new_scale = std::round(new_scale * 10.0f) / 10.0f; - // Calcular nueva posición (centrada en el punto actual) - int new_x = current_center_x - new_width / 2; - int new_y = current_center_y - new_height / 2; + if (new_scale == current_window_scale_) return; - // Obtener límites del escritorio para no salirse - SDL_Rect display_bounds; - if (SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &display_bounds) == 0) { - // Aplicar márgenes - int min_x = WINDOW_DESKTOP_MARGIN; - int min_y = WINDOW_DESKTOP_MARGIN; - int max_x = display_bounds.w - new_width - WINDOW_DESKTOP_MARGIN; - int max_y = display_bounds.h - new_height - WINDOW_DESKTOP_MARGIN - WINDOW_DECORATION_HEIGHT; + int new_width = static_cast(std::round(current_screen_width_ * new_scale)); + int new_height = static_cast(std::round(current_screen_height_ * new_scale)); - // Limitar posición - new_x = std::max(min_x, std::min(new_x, max_x)); - new_y = std::max(min_y, std::min(new_y, max_y)); - } + resizeWindowCentered(window_, new_width, new_height); + current_window_scale_ = new_scale; - // Aplicar cambios - SDL_SetWindowSize(window_, new_width, new_height); - SDL_SetWindowPosition(window_, new_x, new_y); - current_window_zoom_ = new_zoom; - - // Actualizar tamaño físico de ventana y fuentes updatePhysicalWindowSize(); } void Engine::zoomIn() { - setWindowZoom(current_window_zoom_ + 1); + float prev = current_window_scale_; + setWindowScale(current_window_scale_ + WINDOW_SCALE_STEP); + if (current_window_scale_ != prev) { + char buf[32]; + std::snprintf(buf, sizeof(buf), "Zoom %.0f%%", current_window_scale_ * 100.0f); + showNotificationForAction(buf); + } } void Engine::zoomOut() { - setWindowZoom(current_window_zoom_ - 1); + float prev = current_window_scale_; + setWindowScale(current_window_scale_ - WINDOW_SCALE_STEP); + if (current_window_scale_ != prev) { + char buf[32]; + std::snprintf(buf, sizeof(buf), "Zoom %.0f%%", current_window_scale_ * 100.0f); + showNotificationForAction(buf); + } } +void Engine::setFieldScale(float new_scale) { + float max_scale = calculateMaxWindowScale(); + new_scale = std::max(WINDOW_SCALE_MIN, std::min(new_scale, max_scale)); + new_scale = std::round(new_scale * 10.0f) / 10.0f; + if (new_scale == current_field_scale_) return; + + current_field_scale_ = new_scale; + current_screen_width_ = static_cast(std::round(base_screen_width_ * new_scale)); + current_screen_height_ = static_cast(std::round(base_screen_height_ * new_scale)); + + // Ajustar ventana física: campo lógico × zoom actual, manteniendo centro + int phys_w = static_cast(std::round(current_screen_width_ * current_window_scale_)); + int phys_h = static_cast(std::round(current_screen_height_ * current_window_scale_)); + resizeWindowCentered(window_, phys_w, phys_h); + + // Recrear render target con nueva resolución lógica + recreateOffscreenTexture(); + updatePhysicalWindowSize(); + + // Reiniciar escena (igual que F4) + scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_); + scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_); + boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_); + shape_manager_->updateScreenSize(current_screen_width_, current_screen_height_); + if (app_logo_) app_logo_->updateScreenSize(current_screen_width_, current_screen_height_); + if (current_mode_ == SimulationMode::SHAPE) { + generateShape(); + scene_manager_->enableShapeAttractionAll(true); + } + + showNotificationForAction("Campo " + std::to_string(current_screen_width_) + + " x " + std::to_string(current_screen_height_)); +} + +void Engine::fieldSizeUp() { setFieldScale(current_field_scale_ + WINDOW_SCALE_STEP); } +void Engine::fieldSizeDown() { setFieldScale(current_field_scale_ - WINDOW_SCALE_STEP); } + void Engine::updatePhysicalWindowSize() { if (real_fullscreen_enabled_) { // En fullscreen real (F4), usar resolución del display @@ -1254,6 +1286,7 @@ void Engine::updatePhysicalWindowSize() { // Notificar a UIManager del cambio de tamaño (delegado) ui_manager_->updatePhysicalWindowSize(physical_window_width_, physical_window_height_); + } // ============================================================================ diff --git a/source/engine.hpp b/source/engine.hpp index 390256d..8b90e45 100644 --- a/source/engine.hpp +++ b/source/engine.hpp @@ -76,6 +76,11 @@ class Engine { void toggleRealFullscreen(); void toggleIntegerScaling(); + // Campo de juego (tamaño lógico + físico) + void fieldSizeUp(); + void fieldSizeDown(); + void setFieldScale(float new_scale); + // PostFX presets void handlePostFXCycle(); void handlePostFXToggle(); @@ -187,8 +192,11 @@ class Engine { float postfx_override_vignette_ = -1.f; // -1 = sin override float postfx_override_chroma_ = -1.f; - // Sistema de zoom dinámico - int current_window_zoom_ = DEFAULT_WINDOW_ZOOM; + // Sistema de escala de ventana + float current_window_scale_ = 1.0f; + + // Escala del campo de juego lógico (F7/F8) + float current_field_scale_ = 1.0f; // V-Sync y fullscreen bool vsync_enabled_ = true; @@ -242,9 +250,9 @@ class Engine { // Sistema de cambio de sprites dinámico void switchTextureInternal(bool show_notification); - // Sistema de zoom dinámico - int calculateMaxWindowZoom() const; - void setWindowZoom(int new_zoom); + // Sistema de escala de ventana + float calculateMaxWindowScale() const; + void setWindowScale(float new_scale); void zoomIn(); void zoomOut(); void updatePhysicalWindowSize(); diff --git a/source/gpu/gpu_pipeline.cpp b/source/gpu/gpu_pipeline.cpp index 618d19f..753be43 100644 --- a/source/gpu/gpu_pipeline.cpp +++ b/source/gpu/gpu_pipeline.cpp @@ -117,7 +117,7 @@ struct PostFXUniforms { float vignette_strength; float chroma_strength; float scanline_strength; - float time; + float screen_height; }; fragment float4 postfx_fs(PostVOut in [[stage_in]], @@ -133,7 +133,7 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]], color.a = scene.sample(samp, in.uv ).a; // Scanlines: horizontal sine-wave at ~360 lines (one dark band per 2 px at 720p) - float scan = 0.85 + 0.15 * sin(in.uv.y * 3.14159265 * 720.0); + float scan = 0.85 + 0.15 * sin(in.uv.y * 3.14159265 * u.screen_height); color.rgb *= mix(1.0, scan, u.scanline_strength); // Vignette: radial edge darkening diff --git a/source/gpu/gpu_pipeline.hpp b/source/gpu/gpu_pipeline.hpp index 7c154ce..8be09ab 100644 --- a/source/gpu/gpu_pipeline.hpp +++ b/source/gpu/gpu_pipeline.hpp @@ -11,7 +11,7 @@ struct PostFXUniforms { float vignette_strength; // 0 = none, 0.8 = default subtle float chroma_strength; // 0 = off, 0.2 = default chromatic aberration float scanline_strength; // 0 = off, 1 = full scanlines - float time; // accumulated seconds (for future animations) + float screen_height; // logical render target height (px), for resolution-independent scanlines }; // ============================================================================ diff --git a/source/input/input_handler.cpp b/source/input/input_handler.cpp index 1d4c536..b090897 100644 --- a/source/input/input_handler.cpp +++ b/source/input/input_handler.cpp @@ -264,6 +264,17 @@ bool InputHandler::processEvents(Engine& engine) { engine.toggleIntegerScaling(); break; + // Redimensionar campo de juego (tamaño lógico + físico) + case SDLK_F7: + if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT); + else engine.fieldSizeDown(); + break; + + case SDLK_F8: + if (engine.isKioskMode()) engine.showNotificationForAction(KIOSK_NOTIFICATION_TEXT); + else engine.fieldSizeUp(); + break; + // Toggle Modo DEMO COMPLETO (auto-play) o Pausar tema dinámico (Shift+D) case SDLK_D: // Shift+D = Pausar tema dinámico