diff --git a/source/engine.cpp b/source/engine.cpp index 31cac64..86699da 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -203,21 +203,8 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) { srand(static_cast(time(nullptr))); - // Calcular tamaño de fuente escalado según resolución - // Usa las constantes configurables de la clase - int font_size = (base_screen_height_ * TEXT_BASE_SIZE) / 240; - - // Inicializar TextRenderer para display (texto centrado) - if (!text_renderer_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING)) { - SDL_Log("Error al inicializar TextRenderer (display)"); - return false; - } - - // Inicializar TextRenderer para debug (HUD) - if (!text_renderer_debug_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING)) { - SDL_Log("Error al inicializar TextRenderer (debug)"); - return false; - } + // Calcular tamaño físico de ventana y tamaño de fuente absoluto + updatePhysicalWindowSize(); // Inicializar ThemeManager theme_manager_ = std::make_unique(); @@ -883,6 +870,10 @@ void Engine::render() { SDL_RenderGeometry(renderer_, texture_->getSDLTexture(), batch_vertices_.data(), static_cast(batch_vertices_.size()), batch_indices_.data(), static_cast(batch_indices_.size())); } + // Calcular factores de escala lógica → física para texto absoluto + float text_scale_x = static_cast(physical_window_width_) / static_cast(current_screen_width_); + float text_scale_y = static_cast(physical_window_height_) / static_cast(current_screen_height_); + if (show_text_) { // Obtener datos del tema actual (delegado a ThemeManager) int text_color_r, text_color_g, text_color_b; @@ -894,7 +885,7 @@ void Engine::render() { int margin = 8; // Texto del número de pelotas con color del tema - text_renderer_.print(text_pos_, margin, text_.c_str(), text_color_r, text_color_g, text_color_b); + text_renderer_.printPhysical(text_pos_, margin, text_.c_str(), text_color_r, text_color_g, text_color_b, text_scale_x, text_scale_y); // Mostrar nombre del tema en castellano debajo del número de pelotas // (solo si text_ NO es ya el nombre del tema, para evitar duplicación) @@ -904,7 +895,7 @@ void Engine::render() { int theme_y = margin + line_height; // Espaciado dinámico // Texto del nombre del tema con el mismo color - text_renderer_.print(theme_x, theme_y, theme_name_es, text_color_r, text_color_g, text_color_b); + text_renderer_.printPhysical(theme_x, theme_y, theme_name_es, text_color_r, text_color_g, text_color_b, text_scale_x, text_scale_y); } } @@ -918,10 +909,10 @@ void Engine::render() { // Mostrar contador de FPS en esquina superior derecha int fps_text_width = text_renderer_debug_.getTextWidth(fps_text_.c_str()); int fps_x = current_screen_width_ - fps_text_width - margin; - text_renderer_debug_.print(fps_x, current_y, fps_text_.c_str(), 255, 255, 0); // Amarillo + text_renderer_debug_.printPhysical(fps_x, current_y, fps_text_.c_str(), 255, 255, 0, text_scale_x, text_scale_y); // Amarillo // Mostrar estado V-Sync en esquina superior izquierda - text_renderer_debug_.print(margin, current_y, vsync_text_.c_str(), 0, 255, 255); // Cian + text_renderer_debug_.printPhysical(margin, current_y, vsync_text_.c_str(), 0, 255, 255, text_scale_x, text_scale_y); // Cian current_y += line_height; // Debug: Mostrar valores de la primera pelota (si existe) @@ -929,35 +920,35 @@ void Engine::render() { // Línea 1: Gravedad int grav_int = static_cast(balls_[0]->getGravityForce()); std::string grav_text = "Gravedad: " + std::to_string(grav_int); - text_renderer_debug_.print(margin, current_y, grav_text.c_str(), 255, 0, 255); // Magenta + text_renderer_debug_.printPhysical(margin, current_y, grav_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta current_y += line_height; // Línea 2: Velocidad Y int vy_int = static_cast(balls_[0]->getVelocityY()); std::string vy_text = "Velocidad Y: " + std::to_string(vy_int); - text_renderer_debug_.print(margin, current_y, vy_text.c_str(), 255, 0, 255); // Magenta + text_renderer_debug_.printPhysical(margin, current_y, vy_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta current_y += line_height; // Línea 3: Estado superficie std::string surface_text = balls_[0]->isOnSurface() ? "Superficie: Sí" : "Superficie: No"; - text_renderer_debug_.print(margin, current_y, surface_text.c_str(), 255, 0, 255); // Magenta + text_renderer_debug_.printPhysical(margin, current_y, surface_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta current_y += line_height; // Línea 4: Coeficiente de rebote (loss) float loss_val = balls_[0]->getLossCoefficient(); std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4); - text_renderer_debug_.print(margin, current_y, loss_text.c_str(), 255, 0, 255); // Magenta + text_renderer_debug_.printPhysical(margin, current_y, loss_text.c_str(), 255, 0, 255, text_scale_x, text_scale_y); // Magenta current_y += line_height; // Línea 5: Dirección de gravedad std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(current_gravity_); - text_renderer_debug_.print(margin, current_y, gravity_dir_text.c_str(), 255, 255, 0); // Amarillo + text_renderer_debug_.printPhysical(margin, current_y, gravity_dir_text.c_str(), 255, 255, 0, text_scale_x, text_scale_y); // Amarillo current_y += line_height; } // Debug: Mostrar tema actual (delegado a ThemeManager) std::string theme_text = std::string("Tema: ") + theme_manager_->getCurrentThemeNameEN(); - text_renderer_debug_.print(margin, current_y, theme_text.c_str(), 255, 255, 128); // Amarillo claro + text_renderer_debug_.printPhysical(margin, current_y, theme_text.c_str(), 255, 255, 128, text_scale_x, text_scale_y); // Amarillo claro current_y += line_height; // Debug: Mostrar modo de simulación actual @@ -969,14 +960,14 @@ void Engine::render() { } else { mode_text = "Modo: Forma"; } - text_renderer_debug_.print(margin, current_y, mode_text.c_str(), 0, 255, 128); // Verde claro + text_renderer_debug_.printPhysical(margin, current_y, mode_text.c_str(), 0, 255, 128, text_scale_x, text_scale_y); // Verde claro current_y += line_height; // Debug: Mostrar convergencia en modo LOGO (solo cuando está activo) if (current_app_mode_ == AppMode::LOGO && current_mode_ == SimulationMode::SHAPE) { int convergence_percent = static_cast(shape_convergence_ * 100.0f); std::string convergence_text = "Convergencia: " + std::to_string(convergence_percent) + "%"; - text_renderer_debug_.print(margin, current_y, convergence_text.c_str(), 255, 128, 0); // Naranja + text_renderer_debug_.printPhysical(margin, current_y, convergence_text.c_str(), 255, 128, 0, text_scale_x, text_scale_y); // Naranja current_y += line_height; } @@ -987,17 +978,17 @@ void Engine::render() { const char* logo_text = "Modo Logo"; int logo_text_width = text_renderer_debug_.getTextWidth(logo_text); int logo_x = (current_screen_width_ - logo_text_width) / 2; - text_renderer_debug_.print(logo_x, fixed_y, logo_text, 255, 128, 0); // Naranja + text_renderer_debug_.printPhysical(logo_x, fixed_y, logo_text, 255, 128, 0, text_scale_x, text_scale_y); // Naranja } else if (current_app_mode_ == AppMode::DEMO) { const char* demo_text = "Modo Demo"; int demo_text_width = text_renderer_debug_.getTextWidth(demo_text); int demo_x = (current_screen_width_ - demo_text_width) / 2; - text_renderer_debug_.print(demo_x, fixed_y, demo_text, 255, 165, 0); // Naranja + text_renderer_debug_.printPhysical(demo_x, fixed_y, demo_text, 255, 165, 0, text_scale_x, text_scale_y); // Naranja } else if (current_app_mode_ == AppMode::DEMO_LITE) { const char* lite_text = "Modo Demo Lite"; int lite_text_width = text_renderer_debug_.getTextWidth(lite_text); int lite_x = (current_screen_width_ - lite_text_width) / 2; - text_renderer_debug_.print(lite_x, fixed_y, lite_text, 255, 200, 0); // Amarillo-naranja + text_renderer_debug_.printPhysical(lite_x, fixed_y, lite_text, 255, 200, 0, text_scale_x, text_scale_y); // Amarillo-naranja } } @@ -1174,6 +1165,9 @@ void Engine::toggleRealFullscreen() { // Actualizar presentación lógica del renderizador SDL_SetRenderLogicalPresentation(renderer_, current_screen_width_, current_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + // Actualizar tamaño físico de ventana y fuentes + updatePhysicalWindowSize(); + // Reinicar la escena con nueva resolución initBalls(scenario_); } @@ -1191,6 +1185,9 @@ void Engine::toggleRealFullscreen() { // Restaurar presentación lógica base SDL_SetRenderLogicalPresentation(renderer_, base_screen_width_, base_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + // Actualizar tamaño físico de ventana y fuentes + updatePhysicalWindowSize(); + // Reinicar la escena con resolución original initBalls(scenario_); } @@ -1376,6 +1373,9 @@ void Engine::setWindowZoom(int new_zoom) { 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() { @@ -1386,6 +1386,31 @@ void Engine::zoomOut() { setWindowZoom(current_window_zoom_ - 1); } +void Engine::updatePhysicalWindowSize() { + if (real_fullscreen_enabled_) { + // En fullscreen real, usar resolución del display + physical_window_width_ = current_screen_width_; + physical_window_height_ = current_screen_height_; + } else { + // En modo ventana, obtener tamaño real de la ventana (lógica * zoom) + int window_w = 0, window_h = 0; + SDL_GetWindowSize(window_, &window_w, &window_h); + physical_window_width_ = window_w; + physical_window_height_ = window_h; + } + + // Recalcular tamaño de fuente basado en altura física + // Referencia: 8px a 1440p (monitor del usuario) + int font_size = (physical_window_height_ * TEXT_BASE_SIZE) / 1440; + if (font_size < 6) font_size = 6; // Tamaño mínimo legible + + // Reinicializar TextRenderers con nuevo tamaño de fuente + text_renderer_.cleanup(); + text_renderer_debug_.cleanup(); + text_renderer_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING); + text_renderer_debug_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING); +} + // ============================================================================ // Sistema de gestión de estados (MANUAL/DEMO/DEMO_LITE/LOGO) // ============================================================================ diff --git a/source/engine.h b/source/engine.h index 6570aec..c1207c2 100644 --- a/source/engine.h +++ b/source/engine.h @@ -83,6 +83,10 @@ class Engine { int current_screen_width_ = DEFAULT_SCREEN_WIDTH; int current_screen_height_ = DEFAULT_SCREEN_HEIGHT; + // Resolución física real de ventana/pantalla (para texto absoluto) + int physical_window_width_ = DEFAULT_SCREEN_WIDTH; + int physical_window_height_ = DEFAULT_SCREEN_HEIGHT; + // Sistema de temas (delegado a ThemeManager) std::unique_ptr theme_manager_; int theme_page_ = 0; // Página actual de temas (0 o 1) para acceso por Numpad @@ -124,7 +128,7 @@ class Engine { // 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 = 8; // Tamaño base para 240p + static constexpr int TEXT_BASE_SIZE = 24; // Tamaño base para 240p static constexpr bool TEXT_ANTIALIASING = true; // true = suavizado, false = píxeles nítidos // Métodos principales del loop @@ -171,6 +175,7 @@ class Engine { void setWindowZoom(int new_zoom); void zoomIn(); void zoomOut(); + void updatePhysicalWindowSize(); // Actualizar tamaño físico real de ventana // Rendering void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f); diff --git a/source/text/textrenderer.cpp b/source/text/textrenderer.cpp index 92c0862..80df977 100644 --- a/source/text/textrenderer.cpp +++ b/source/text/textrenderer.cpp @@ -90,6 +90,68 @@ void TextRenderer::print(int x, int y, const std::string& text, uint8_t r, uint8 print(x, y, text.c_str(), r, g, b); } +void TextRenderer::printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) { + if (!isInitialized() || text == nullptr || text[0] == '\0') { + return; + } + + // Convertir coordenadas lógicas a físicas + int physical_x = static_cast(logical_x * scale_x); + int physical_y = static_cast(logical_y * scale_y); + + // Crear superficie con el texto renderizado + SDL_Color color = {r, g, b, 255}; + SDL_Surface* text_surface = nullptr; + + if (use_antialiasing_) { + text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color); + } else { + text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color); + } + + if (text_surface == nullptr) { + SDL_Log("Error al renderizar texto: %s", SDL_GetError()); + return; + } + + // Crear textura desde la superficie + SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface); + + if (text_texture == nullptr) { + SDL_Log("Error al crear textura: %s", SDL_GetError()); + SDL_DestroySurface(text_surface); + return; + } + + // Renderizar en coordenadas físicas (bypass presentación lógica) + // Usar SDL_RenderTexture con coordenadas absolutas de ventana + SDL_FRect dest_rect; + dest_rect.x = static_cast(physical_x); + dest_rect.y = static_cast(physical_y); + dest_rect.w = static_cast(text_surface->w); + dest_rect.h = static_cast(text_surface->h); + + // Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos + int logical_w = 0, logical_h = 0; + SDL_RendererLogicalPresentation presentation_mode; + SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode); + + // Renderizar sin presentación lógica (coordenadas absolutas) + SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED); + SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect); + + // Restaurar presentación lógica + SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode); + + // Limpiar recursos + SDL_DestroyTexture(text_texture); + SDL_DestroySurface(text_surface); +} + +void TextRenderer::printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y) { + printPhysical(logical_x, logical_y, text.c_str(), r, g, b, scale_x, scale_y); +} + int TextRenderer::getTextWidth(const char* text) { if (!isInitialized() || text == nullptr) { return 0; diff --git a/source/text/textrenderer.h b/source/text/textrenderer.h index 7bbb5c4..ad8eab4 100644 --- a/source/text/textrenderer.h +++ b/source/text/textrenderer.h @@ -19,7 +19,11 @@ public: void print(int x, int y, const char* text, uint8_t r, uint8_t g, uint8_t b); void print(int x, int y, const std::string& text, uint8_t r, uint8_t g, uint8_t b); - // Obtiene el ancho de un texto renderizado + // Renderiza texto en coordenadas lógicas, pero convierte a físicas para tamaño absoluto + void printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y); + void printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y); + + // Obtiene el ancho de un texto renderizado (en píxeles lógicos para compatibilidad) int getTextWidth(const char* text); // Obtiene la altura de la fuente