diff --git a/source/backends/sdl_renderer.cpp b/source/backends/sdl_renderer.cpp index cbb610f..f8c707d 100644 --- a/source/backends/sdl_renderer.cpp +++ b/source/backends/sdl_renderer.cpp @@ -78,40 +78,52 @@ void SDLRenderer::renderGradientBackground( if (!renderer_) return; - // Crear gradiente usando múltiples rectángulos - const int gradient_steps = 32; - float step_height = static_cast(screen_height_) / gradient_steps; + // Crear gradiente suave usando SDL_RenderGeometry con 4 vértices + SDL_Vertex bg_vertices[4]; - for (int i = 0; i < gradient_steps; ++i) { - float t = static_cast(i) / (gradient_steps - 1); + // Aplicar efectos CRT si están habilitados + float final_top_r = top_r, final_top_g = top_g, final_top_b = top_b; + float final_bottom_r = bottom_r, final_bottom_g = bottom_g, final_bottom_b = bottom_b; - // Interpolar colores - float r = top_r + (bottom_r - top_r) * t; - float g = top_g + (bottom_g - top_g) * t; - float b = top_b + (bottom_b - top_b) * t; - - // Aplicar efectos CRT básicos si están habilitados - if (crt_enabled_) { - applyCRTEffectsToColor(r, g, b, 0, i * step_height); - } - - SDL_SetRenderDrawColor(renderer_, - static_cast(r * 255), - static_cast(g * 255), - static_cast(b * 255), 255); - - SDL_FRect rect = { - 0, i * step_height, - static_cast(screen_width_), step_height + 1 - }; - SDL_RenderFillRect(renderer_, &rect); + if (crt_enabled_) { + applyCRTEffectsToColor(final_top_r, final_top_g, final_top_b, 0, 0); + applyCRTEffectsToColor(final_bottom_r, final_bottom_g, final_bottom_b, 0, screen_height_); } + + // Vértice superior izquierdo + bg_vertices[0].position = {0, 0}; + bg_vertices[0].tex_coord = {0.0f, 0.0f}; + bg_vertices[0].color = {final_top_r, final_top_g, final_top_b}; + + // Vértice superior derecho + bg_vertices[1].position = {static_cast(screen_width_), 0}; + bg_vertices[1].tex_coord = {1.0f, 0.0f}; + bg_vertices[1].color = {final_top_r, final_top_g, final_top_b}; + + // Vértice inferior derecho + bg_vertices[2].position = {static_cast(screen_width_), static_cast(screen_height_)}; + bg_vertices[2].tex_coord = {1.0f, 1.0f}; + bg_vertices[2].color = {final_bottom_r, final_bottom_g, final_bottom_b}; + + // Vértice inferior izquierdo + bg_vertices[3].position = {0, static_cast(screen_height_)}; + bg_vertices[3].tex_coord = {0.0f, 1.0f}; + bg_vertices[3].color = {final_bottom_r, final_bottom_g, final_bottom_b}; + + // Índices para 2 triángulos + int bg_indices[6] = {0, 1, 2, 2, 3, 0}; + + // Renderizar gradiente suave sin textura + SDL_RenderGeometry(renderer_, nullptr, bg_vertices, 4, bg_indices, 6); } void SDLRenderer::renderSpriteBatch( const std::vector& sprites, void* texture_data) { + // Guardar la textura para usar en renderBatch() + sprite_texture_ = static_cast(texture_data); + // Agregar todos los sprites al batch for (const auto& sprite : sprites) { addSpriteToBatch(sprite.x, sprite.y, sprite.w, sprite.h, @@ -127,7 +139,7 @@ void SDLRenderer::addSpriteToBatch(float x, float y, float w, float h, applyCRTEffectsToColor(final_r, final_g, final_b, x, y); } - // Normalizar colores (0-255 -> 0-1) + // Normalizar colores (0-255 -> 0-1) como en el original final_r /= 255.0f; final_g /= 255.0f; final_b /= 255.0f; @@ -174,8 +186,8 @@ void SDLRenderer::clearBatch() { void SDLRenderer::renderBatch() { if (batch_vertices_.empty() || !renderer_) return; - // Renderizar todo el batch en una sola llamada - SDL_RenderGeometry(renderer_, nullptr, + // Renderizar todo el batch con la textura + SDL_RenderGeometry(renderer_, sprite_texture_, batch_vertices_.data(), static_cast(batch_vertices_.size()), batch_indices_.data(), static_cast(batch_indices_.size())); } diff --git a/source/backends/sdl_renderer.h b/source/backends/sdl_renderer.h index 5777e9e..06864f9 100644 --- a/source/backends/sdl_renderer.h +++ b/source/backends/sdl_renderer.h @@ -39,6 +39,9 @@ public: void setVSync(bool enable) override; void resize(int width, int height) override; + // Getter específico para compatibilidad con dbgtxt.h + SDL_Renderer* getSDLRenderer() const { return renderer_; } + private: SDL_Renderer* renderer_ = nullptr; SDL_Texture* sprite_texture_ = nullptr; diff --git a/source/engine.cpp b/source/engine.cpp index 159d8c5..6243952 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -39,7 +39,7 @@ bool Engine::initialize() { // Crear y configurar el window manager window_manager_ = std::make_unique(); - if (!window_manager_->initialize(WINDOW_CAPTION, SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_ZOOM)) { + if (!window_manager_->initialize(WINDOW_CAPTION, SCREEN_WIDTH, SCREEN_HEIGHT, 1)) { std::cout << "¡No se pudo inicializar el WindowManager!" << std::endl; return false; } @@ -65,9 +65,19 @@ bool Engine::initialize() { // Inicializar otros componentes srand(static_cast(time(nullptr))); - // TODO: Cargar datos de textura para sprites - // En una implementación completa, cargaríamos la textura aquí - texture_data_ = nullptr; + // Cargar textura para sprites e inicializar debug text + auto* sdl_renderer = window_manager_->getSDLRenderer(); + if (sdl_renderer) { + // Construir ruta absoluta a la imagen + std::string exe_dir = getExecutableDirectory(); + std::string texture_path = exe_dir + "/data/ball.png"; + texture_ = std::make_shared(sdl_renderer, texture_path); + + // Inicializar sistema de texto debug + dbg_init(sdl_renderer); + } else { + std::cout << "¡Advertencia: No se pudo obtener SDL_Renderer para texturas!" << std::endl; + } initializeThemes(); initBalls(scenario_); @@ -92,8 +102,7 @@ void Engine::shutdown() { window_manager_.reset(); } - // Limpiar datos de textura si los hay - texture_data_ = nullptr; + // La textura se limpia automáticamente con shared_ptr } // Métodos privados - esqueleto básico por ahora @@ -292,32 +301,36 @@ void Engine::handleEvents() { } void Engine::render() { - // Renderizar fondo degradado en lugar de color sólido + if (!window_manager_) return; + + // PASO 1: Activar renderizado a textura lógica + if (!window_manager_->setRenderTarget()) { + return; + } + + // PASO 2: Renderizar fondo degradado a la textura renderGradientBackground(); - // Usar el nuevo sistema de renderizado - auto* renderer = window_manager_ ? window_manager_->getRenderer() : nullptr; - if (!renderer) return; + // PASO 3: Limpiar batches del frame anterior + batch_vertices_.clear(); + batch_indices_.clear(); - // Comenzar frame de renderizado - renderer->beginFrame(); - - // Limpiar batch del frame anterior - clearSpriteBatch(); - - // Recopilar datos de todas las bolas para batch rendering + // PASO 4: Recopilar datos de todas las bolas para batch rendering for (auto &ball : balls_) { SDL_FRect pos = ball->getPosition(); Color color = ball->getColor(); addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b); } - // Renderizar batch completo - renderSpriteBatch(); - - // Finalizar frame - renderer->endFrame(); + // PASO 5: Renderizar todas las bolas a la textura + auto* sdl_renderer = window_manager_->getSDLRenderer(); + if (!batch_vertices_.empty() && sdl_renderer && texture_) { + SDL_RenderGeometry(sdl_renderer, texture_->getSDLTexture(), + batch_vertices_.data(), static_cast(batch_vertices_.size()), + batch_indices_.data(), static_cast(batch_indices_.size())); + } + // PASO 5.5: Renderizar texto a la textura (antes de presentar) if (show_text_) { // Colores acordes a cada tema (para texto del número de pelotas y nombre del tema) int theme_colors[][3] = { @@ -384,10 +397,8 @@ void Engine::render() { dbg_print(8, 64, theme_text.c_str(), 255, 255, 128); // Amarillo claro para tema } - // Presentar frame final - if (renderer) { - renderer->present(); - } + // PASO 6: Presentar la textura final con zoom/postprocesado + window_manager_->presentFrame(); } void Engine::initBalls(int value) { @@ -412,7 +423,7 @@ void Engine::initBalls(int value) { const Color COLOR = theme.ball_colors[color_index]; // Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada) float mass_factor = GRAVITY_MASS_MIN + (rand() % 1000) / 1000.0f * (GRAVITY_MASS_MAX - GRAVITY_MASS_MIN); - balls_.emplace_back(std::make_unique(X, VX, VY, COLOR, nullptr, current_screen_width_, current_screen_height_, current_gravity_, mass_factor)); + balls_.emplace_back(std::make_unique(X, VX, VY, COLOR, texture_, current_screen_width_, current_screen_height_, current_gravity_, mass_factor)); } setText(); // Actualiza el texto } @@ -555,53 +566,41 @@ std::string Engine::gravityDirectionToString(GravityDirection direction) const { } void Engine::renderGradientBackground() { - // Crear quad de pantalla completa con degradado - SDL_Vertex bg_vertices[4]; - - // Obtener colores del tema actual ThemeColors &theme = themes_[static_cast(current_theme_)]; - float top_r = theme.bg_top_r; - float top_g = theme.bg_top_g; - float top_b = theme.bg_top_b; + auto* sdl_renderer = window_manager_ ? window_manager_->getSDLRenderer() : nullptr; + if (!sdl_renderer) { + return; + } - float bottom_r = theme.bg_bottom_r; - float bottom_g = theme.bg_bottom_g; - float bottom_b = theme.bg_bottom_b; + // Crear gradiente suave usando SDL_RenderGeometry como en la implementación original + SDL_Vertex bg_vertices[4]; // Vértice superior izquierdo bg_vertices[0].position = {0, 0}; bg_vertices[0].tex_coord = {0.0f, 0.0f}; - bg_vertices[0].color = {top_r, top_g, top_b, 1.0f}; + bg_vertices[0].color = {theme.bg_top_r, theme.bg_top_g, theme.bg_top_b, 1.0f}; // Vértice superior derecho - bg_vertices[1].position = {static_cast(current_screen_width_), 0}; + bg_vertices[1].position = {static_cast(SCREEN_WIDTH), 0}; bg_vertices[1].tex_coord = {1.0f, 0.0f}; - bg_vertices[1].color = {top_r, top_g, top_b, 1.0f}; + bg_vertices[1].color = {theme.bg_top_r, theme.bg_top_g, theme.bg_top_b, 1.0f}; // Vértice inferior derecho - bg_vertices[2].position = {static_cast(current_screen_width_), static_cast(current_screen_height_)}; + bg_vertices[2].position = {static_cast(SCREEN_WIDTH), static_cast(SCREEN_HEIGHT)}; bg_vertices[2].tex_coord = {1.0f, 1.0f}; - bg_vertices[2].color = {bottom_r, bottom_g, bottom_b, 1.0f}; + bg_vertices[2].color = {theme.bg_bottom_r, theme.bg_bottom_g, theme.bg_bottom_b, 1.0f}; // Vértice inferior izquierdo - bg_vertices[3].position = {0, static_cast(current_screen_height_)}; + bg_vertices[3].position = {0, static_cast(SCREEN_HEIGHT)}; bg_vertices[3].tex_coord = {0.0f, 1.0f}; - bg_vertices[3].color = {bottom_r, bottom_g, bottom_b, 1.0f}; + bg_vertices[3].color = {theme.bg_bottom_r, theme.bg_bottom_g, theme.bg_bottom_b, 1.0f}; // Índices para 2 triángulos int bg_indices[6] = {0, 1, 2, 2, 3, 0}; - // TODO: Migrar renderizado de fondo degradado al nuevo sistema - // SDL_RenderGeometry(renderer_, nullptr, bg_vertices, 4, bg_indices, 6); - - auto* renderer = window_manager_ ? window_manager_->getRenderer() : nullptr; - if (renderer) { - renderer->renderGradientBackground( - theme.bg_top_r, theme.bg_top_g, theme.bg_top_b, - theme.bg_bottom_r, theme.bg_bottom_g, theme.bg_bottom_b - ); - } + // Renderizar gradiente sin textura (nullptr) + SDL_RenderGeometry(sdl_renderer, nullptr, bg_vertices, 4, bg_indices, 6); } // Método addSpriteToBatch antiguo eliminado - ahora se usa el del nuevo sistema @@ -867,20 +866,53 @@ void Engine::clearSpriteBatch() { void Engine::renderSpriteBatch() { auto* renderer = window_manager_ ? window_manager_->getRenderer() : nullptr; - if (renderer && !sprite_batch_.empty()) { - renderer->renderSpriteBatch(sprite_batch_, texture_data_); + if (renderer && !sprite_batch_.empty() && texture_) { + void* texture_data = static_cast(texture_->getSDLTexture()); + renderer->renderSpriteBatch(sprite_batch_, texture_data); } } void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b) { - vibe4::SpriteData sprite; - sprite.x = x; - sprite.y = y; - sprite.w = w; - sprite.h = h; - sprite.r = static_cast(r); - sprite.g = static_cast(g); - sprite.b = static_cast(b); + int vertex_index = static_cast(batch_vertices_.size()); - sprite_batch_.push_back(sprite); + // Crear 4 vértices para el quad (2 triángulos) + SDL_Vertex vertices[4]; + + // Convertir colores de int (0-255) a float (0.0-1.0) + float rf = r / 255.0f; + float gf = g / 255.0f; + float bf = b / 255.0f; + + // Vértice superior izquierdo + vertices[0].position = {x, y}; + vertices[0].tex_coord = {0.0f, 0.0f}; + vertices[0].color = {rf, gf, bf, 1.0f}; + + // Vértice superior derecho + vertices[1].position = {x + w, y}; + vertices[1].tex_coord = {1.0f, 0.0f}; + vertices[1].color = {rf, gf, bf, 1.0f}; + + // Vértice inferior derecho + vertices[2].position = {x + w, y + h}; + vertices[2].tex_coord = {1.0f, 1.0f}; + vertices[2].color = {rf, gf, bf, 1.0f}; + + // Vértice inferior izquierdo + vertices[3].position = {x, y + h}; + vertices[3].tex_coord = {0.0f, 1.0f}; + vertices[3].color = {rf, gf, bf, 1.0f}; + + // Añadir vértices al batch + for (int i = 0; i < 4; i++) { + batch_vertices_.push_back(vertices[i]); + } + + // Añadir índices para 2 triángulos + batch_indices_.push_back(vertex_index + 0); + batch_indices_.push_back(vertex_index + 1); + batch_indices_.push_back(vertex_index + 2); + batch_indices_.push_back(vertex_index + 2); + batch_indices_.push_back(vertex_index + 3); + batch_indices_.push_back(vertex_index + 0); } \ No newline at end of file diff --git a/source/engine.h b/source/engine.h index d1fb6ac..c870dce 100644 --- a/source/engine.h +++ b/source/engine.h @@ -2,6 +2,7 @@ #include // for SDL_Event #include // for Uint64 +#include // for SDL_Vertex #include // for array #include // for unique_ptr, shared_ptr @@ -12,6 +13,7 @@ #include "ball.h" // for Ball #include "window_manager.h" // for WindowManager #include "backends/renderer_interface.h" // for CRTParams, SpriteData +#include "external/texture.h" // for Texture class Engine { public: @@ -23,7 +25,7 @@ public: private: // Sistema de renderizado std::unique_ptr window_manager_; - void* texture_data_ = nullptr; // Datos de textura para sprites + std::shared_ptr texture_ = nullptr; // Textura para sprites // Estado del simulador std::vector> balls_; @@ -82,6 +84,10 @@ private: // Batch rendering std::vector sprite_batch_; + // Batch rendering directo (como el original) + std::vector batch_vertices_; + std::vector batch_indices_; + // Métodos principales del loop void calculateDeltaTime(); void update(); diff --git a/source/window_manager.cpp b/source/window_manager.cpp index 83c1bf3..a1b01bc 100644 --- a/source/window_manager.cpp +++ b/source/window_manager.cpp @@ -37,8 +37,8 @@ bool WindowManager::initialize(const char* title, int width, int height, int zoo return false; } - // Crear ventana SDL - if (!createSDLWindow(title, width * zoom, height * zoom)) { + // Crear ventana SDL (sin multiplicar por zoom - trabajamos nativo) + if (!createSDLWindow(title, width, height)) { return false; } @@ -57,11 +57,28 @@ bool WindowManager::initialize(const char* title, int width, int height, int zoo return false; } + // Crear textura de renderizado para postprocesado + auto* sdl_renderer = getSDLRenderer(); + if (sdl_renderer) { + render_texture_ = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, width, height); + if (!render_texture_) { + std::cout << "¡No se pudo crear la textura de renderizado! Error: " << SDL_GetError() << std::endl; + return false; + } + std::cout << "Textura de renderizado creada: " << width << "x" << height << std::endl; + } + std::cout << "Backend de renderizado inicializado: " << renderer_->getBackendName() << std::endl; return true; } void WindowManager::shutdown() { + if (render_texture_) { + SDL_DestroyTexture(render_texture_); + render_texture_ = nullptr; + } + if (renderer_) { renderer_->shutdown(); renderer_.reset(); @@ -234,4 +251,52 @@ const char* WindowManager::getBackendName() const { return renderer_ ? renderer_->getBackendName() : "None"; } +SDL_Renderer* WindowManager::getSDLRenderer() const { + // Solo funciona si el backend activo es SDL + if (renderer_ && renderer_->getBackendType() == BackendType::SDL) { + auto* sdl_renderer = static_cast(renderer_.get()); + return sdl_renderer->getSDLRenderer(); + } + return nullptr; +} + +bool WindowManager::setRenderTarget() { + auto* sdl_renderer = getSDLRenderer(); + if (!sdl_renderer || !render_texture_) { + return false; + } + + // Cambiar el render target a nuestra textura + if (!SDL_SetRenderTarget(sdl_renderer, render_texture_)) { + std::cout << "¡No se pudo establecer render target! Error: " << SDL_GetError() << std::endl; + return false; + } + + // Limpiar la textura de renderizado + SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(sdl_renderer); + + return true; +} + +void WindowManager::presentFrame() { + auto* sdl_renderer = getSDLRenderer(); + if (!sdl_renderer || !render_texture_) { + return; + } + + // Volver al render target por defecto (la ventana) + SDL_SetRenderTarget(sdl_renderer, nullptr); + + // Limpiar la pantalla + SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(sdl_renderer); + + // Copiar la textura de renderizado a la pantalla 1:1 (sin zoom) + SDL_RenderTexture(sdl_renderer, render_texture_, nullptr, nullptr); + + // Presentar el frame final + SDL_RenderPresent(sdl_renderer); +} + } // namespace vibe4 \ No newline at end of file diff --git a/source/window_manager.h b/source/window_manager.h index 3b7d13b..b6b1c17 100644 --- a/source/window_manager.h +++ b/source/window_manager.h @@ -2,6 +2,7 @@ #include // for SDL_Window #include // for SDL_Event +#include // for SDL_Renderer #include // for unique_ptr #include // for string @@ -22,6 +23,13 @@ public: SDL_Window* getWindow() const { return window_; } RendererInterface* getRenderer() const { return renderer_.get(); } + // Getter específico para compatibilidad con dbgtxt.h + SDL_Renderer* getSDLRenderer() const; + + // Sistema de render-to-texture para postprocesado + bool setRenderTarget(); // Activa renderizado a textura lógica + void presentFrame(); // Presenta textura final con zoom/efectos + // Control de ventana void setTitle(const char* title); bool setFullscreen(bool enable); @@ -49,6 +57,9 @@ private: SDL_Window* window_ = nullptr; std::unique_ptr renderer_; + // Sistema de render-to-texture + SDL_Texture* render_texture_ = nullptr; // Textura de resolución lógica + // Estado de la ventana int logical_width_ = 0; int logical_height_ = 0;