#include "sdl_renderer.h" #include #include #include #include namespace vibe4 { SDLRenderer::SDLRenderer() = default; SDLRenderer::~SDLRenderer() { shutdown(); } bool SDLRenderer::initialize(SDL_Window* window, int width, int height) { window_ = window; screen_width_ = width; screen_height_ = height; // Crear renderer SDL con aceleración por hardware renderer_ = SDL_CreateRenderer(window, nullptr); if (!renderer_) { std::cout << "¡No se pudo crear el renderer SDL! Error: " << SDL_GetError() << std::endl; return false; } // Configurar el renderer SDL_SetRenderLogicalPresentation(renderer_, width, height, SDL_LOGICAL_PRESENTATION_LETTERBOX); // Reservar espacio para el batch rendering batch_vertices_.reserve(50000 * 4); // 4 vértices por sprite batch_indices_.reserve(50000 * 6); // 6 índices por sprite std::cout << "SDLRenderer inicializado exitosamente" << std::endl; return true; } void SDLRenderer::shutdown() { if (sprite_texture_) { SDL_DestroyTexture(sprite_texture_); sprite_texture_ = nullptr; } if (renderer_) { SDL_DestroyRenderer(renderer_); renderer_ = nullptr; } } bool SDLRenderer::beginFrame() { if (!renderer_) return false; // Limpiar el frame SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255); SDL_RenderClear(renderer_); // Limpiar batch de la frame anterior clearBatch(); return true; } void SDLRenderer::endFrame() { // Renderizar todo el batch acumulado renderBatch(); } void SDLRenderer::present() { if (renderer_) { SDL_RenderPresent(renderer_); } } void SDLRenderer::renderGradientBackground( float top_r, float top_g, float top_b, float bottom_r, float bottom_g, float bottom_b) { if (!renderer_) return; // Crear gradiente suave usando SDL_RenderGeometry con 4 vértices SDL_Vertex bg_vertices[4]; // 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; 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, sprite.r, sprite.g, sprite.b); } } void SDLRenderer::addSpriteToBatch(float x, float y, float w, float h, float r, float g, float b) { // Aplicar efectos CRT a los colores si están habilitados float final_r = r, final_g = g, final_b = b; if (crt_enabled_) { applyCRTEffectsToColor(final_r, final_g, final_b, x, y); } // Normalizar colores (0-255 -> 0-1) como en el original final_r /= 255.0f; final_g /= 255.0f; final_b /= 255.0f; // Crear índice base para este sprite int base_index = static_cast(batch_vertices_.size()); // Añadir 4 vértices (quad) - SDL_Vertex usa RGB sin alpha SDL_Vertex v1, v2, v3, v4; v1.position = {x, y}; v1.tex_coord = {0, 0}; v1.color = {final_r, final_g, final_b}; v2.position = {x + w, y}; v2.tex_coord = {1, 0}; v2.color = {final_r, final_g, final_b}; v3.position = {x + w, y + h}; v3.tex_coord = {1, 1}; v3.color = {final_r, final_g, final_b}; v4.position = {x, y + h}; v4.tex_coord = {0, 1}; v4.color = {final_r, final_g, final_b}; batch_vertices_.push_back(v1); batch_vertices_.push_back(v2); batch_vertices_.push_back(v3); batch_vertices_.push_back(v4); // Añadir 6 índices (2 triángulos) batch_indices_.insert(batch_indices_.end(), { base_index, base_index + 1, base_index + 2, // Primer triángulo base_index, base_index + 2, base_index + 3 // Segundo triángulo }); } void SDLRenderer::clearBatch() { batch_vertices_.clear(); batch_indices_.clear(); } void SDLRenderer::renderBatch() { if (batch_vertices_.empty() || !renderer_) return; // 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())); } void SDLRenderer::applyCRTEffectsToColor(float& r, float& g, float& b, float x, float y) { // Simulación básica de efectos CRT sin shaders reales // Scanlines básicos if (crt_params_.enable_scanlines) { float scanline = std::sin(y * 3.14159f * 2.0f / 2.0f); // 2 píxeles por scanline float scanline_factor = crt_params_.scanline_intensity + (1.0f - crt_params_.scanline_intensity) * (scanline * 0.5f + 0.5f); r *= scanline_factor; g *= scanline_factor; b *= scanline_factor; } // Efecto de máscara básico (simulando píxeles RGB) if (crt_params_.mask_brightness < 1.0f) { int pixel_x = static_cast(x) % 3; float mask_factor = crt_params_.mask_brightness; switch (pixel_x) { case 0: g *= mask_factor; b *= mask_factor; break; // Pixel rojo case 1: r *= mask_factor; b *= mask_factor; break; // Pixel verde case 2: r *= mask_factor; g *= mask_factor; break; // Pixel azul } } // Bloom básico (intensificar colores brillantes) if (crt_params_.enable_bloom && crt_params_.bloom_factor > 1.0f) { float brightness = (r + g + b) / 3.0f; if (brightness > 0.7f) { float bloom_strength = (brightness - 0.7f) * (crt_params_.bloom_factor - 1.0f); r += bloom_strength; g += bloom_strength; b += bloom_strength; } } // Clamp valores r = std::min(255.0f, std::max(0.0f, r)); g = std::min(255.0f, std::max(0.0f, g)); b = std::min(255.0f, std::max(0.0f, b)); } void SDLRenderer::setCRTParams(const CRTParams& params) { crt_params_ = params; } void SDLRenderer::enableCRT(bool enable) { crt_enabled_ = enable; } void SDLRenderer::setVSync(bool enable) { vsync_enabled_ = enable; if (renderer_) { SDL_SetRenderVSync(renderer_, enable ? 1 : 0); } } void SDLRenderer::resize(int width, int height) { screen_width_ = width; screen_height_ = height; if (renderer_) { SDL_SetRenderLogicalPresentation(renderer_, width, height, SDL_LOGICAL_PRESENTATION_LETTERBOX); } } } // namespace vibe4