#include "opengl_shader.hpp" #include #include #include #include #include "ui/logger.hpp" // Para Loger namespace Rendering { OpenGLShader::~OpenGLShader() { cleanup(); } #ifndef __APPLE__ bool OpenGLShader::initGLExtensions() { glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation"); glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f"); glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glGenVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)SDL_GL_GetProcAddress("glBindVertexArray"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glDeleteVertexArrays"); glGenBuffers = (PFNGLGENBUFFERSPROC)SDL_GL_GetProcAddress("glGenBuffers"); glBindBuffer = (PFNGLBINDBUFFERPROC)SDL_GL_GetProcAddress("glBindBuffer"); glBufferData = (PFNGLBUFFERDATAPROC)SDL_GL_GetProcAddress("glBufferData"); glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteBuffers"); glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer"); glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray"); return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && glUseProgram && glDeleteProgram && glGetUniformLocation && glUniform2f && glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays && glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers && glVertexAttribPointer && glEnableVertexAttribArray; } #endif void OpenGLShader::checkGLError(const char* operation) { GLenum error = glGetError(); if (error != GL_NO_ERROR) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error OpenGL en %s: 0x%x", operation, error); } } GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type) { if (source.empty()) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: El código fuente del shader está vacío"); return 0; } GLuint shader_id = glCreateShader(shader_type); if (shader_id == 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear shader"); checkGLError("glCreateShader"); return 0; } const char* sources[1] = {source.c_str()}; glShaderSource(shader_id, 1, sources, nullptr); checkGLError("glShaderSource"); glCompileShader(shader_id); checkGLError("glCompileShader"); // Verificar compilación GLint compiled = GL_FALSE; glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled); if (compiled != GL_TRUE) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en compilación del shader"); GLint log_length; glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { std::vector log(log_length); glGetShaderInfoLog(shader_id, log_length, &log_length, log.data()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Log de compilación: %s", log.data()); } glDeleteShader(shader_id); return 0; } return shader_id; } GLuint OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) { GLuint program = glCreateProgram(); if (program == 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear programa de shaders"); return 0; } glAttachShader(program, vertex_shader); checkGLError("glAttachShader(vertex)"); glAttachShader(program, fragment_shader); checkGLError("glAttachShader(fragment)"); glLinkProgram(program); checkGLError("glLinkProgram"); // Verificar enlace GLint linked = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linked); if (linked != GL_TRUE) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar programa"); GLint log_length; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { std::vector log(log_length); glGetProgramInfoLog(program, log_length, &log_length, log.data()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Log de enlace: %s", log.data()); } glDeleteProgram(program); return 0; } glValidateProgram(program); checkGLError("glValidateProgram"); return program; } void OpenGLShader::createQuadGeometry() { // Datos del quad: posición (x, y) + coordenadas de textura (u, v) // Formato: x, y, u, v float vertices[] = { // Posición // TexCoords -1.0F, -1.0F, 0.0F, 0.0F, // Inferior izquierda 1.0F, -1.0F, 1.0F, 0.0F, // Inferior derecha 1.0F, 1.0F, 1.0F, 1.0F, // Superior derecha -1.0F, 1.0F, 0.0F, 1.0F // Superior izquierda }; // Índices para dibujar el quad con dos triángulos unsigned int indices[] = { 0, 1, 2, // Primer triángulo 2, 3, 0 // Segundo triángulo }; // Generar y configurar VAO glGenVertexArrays(1, &vao_); glBindVertexArray(vao_); checkGLError("glBindVertexArray"); // Generar y configurar VBO glGenBuffers(1, &vbo_); glBindBuffer(GL_ARRAY_BUFFER, vbo_); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); checkGLError("glBufferData(VBO)"); // Generar y configurar EBO glGenBuffers(1, &ebo_); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); checkGLError("glBufferData(EBO)"); // Atributo 0: Posición (2 floats) glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); checkGLError("glVertexAttribPointer(position)"); // Atributo 1: Coordenadas de textura (2 floats) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); glEnableVertexAttribArray(1); checkGLError("glVertexAttribPointer(texcoord)"); // Desvincular glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } GLuint OpenGLShader::getTextureID(SDL_Texture* texture) { if (!texture) return 1; SDL_PropertiesID props = SDL_GetTextureProperties(texture); GLuint texture_id = 0; // Intentar obtener ID de textura OpenGL texture_id = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr); if (texture_id == 0) { texture_id = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr); } if (texture_id == 0) { texture_id = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", 1); } if (texture_id == 0) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "No se pudo obtener ID de textura OpenGL, usando 1 por defecto"); texture_id = 1; } return texture_id; } bool OpenGLShader::init(SDL_Window* window, SDL_Texture* texture, const std::string& vertex_source, const std::string& fragment_source) { window_ = window; back_buffer_ = texture; renderer_ = SDL_GetRenderer(window); if (!renderer_) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer"); return false; } // Obtener tamaños SDL_GetWindowSize(window_, &window_width_, &window_height_); SDL_GetTextureSize(back_buffer_, &texture_width_, &texture_height_); /* SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Inicializando shaders: ventana=%dx%d, textura=%.0fx%.0f", window_width_, window_height_, texture_width_, texture_height_); */ Logger::info( "Inicializando shaders: ventana=" + std::to_string(window_width_) + "x" + std::to_string(window_height_) + ", textura=" + std::to_string(static_cast(texture_width_)) + "x" + std::to_string(static_cast(texture_height_))); // Verificar que es OpenGL const char* renderer_name = SDL_GetRendererName(renderer_); if (!renderer_name || strncmp(renderer_name, "opengl", 6) != 0) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Renderer no es OpenGL: %s", renderer_name ? renderer_name : "unknown"); return false; } #ifndef __APPLE__ // Inicializar extensiones OpenGL en Windows/Linux if (!initGLExtensions()) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al inicializar extensiones OpenGL"); return false; } #endif // Limpiar shader anterior si existe if (program_id_ != 0) { glDeleteProgram(program_id_); program_id_ = 0; } // Compilar shaders GLuint vertex_shader = compileShader(vertex_source, GL_VERTEX_SHADER); GLuint fragment_shader = compileShader(fragment_source, GL_FRAGMENT_SHADER); if (vertex_shader == 0 || fragment_shader == 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al compilar shaders"); if (vertex_shader != 0) glDeleteShader(vertex_shader); if (fragment_shader != 0) glDeleteShader(fragment_shader); return false; } // Enlazar programa program_id_ = linkProgram(vertex_shader, fragment_shader); // Limpiar shaders (ya no necesarios tras el enlace) glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); if (program_id_ == 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear programa de shaders"); return false; } // Crear geometría del quad createQuadGeometry(); // Obtener ubicación del uniform TextureSize glUseProgram(program_id_); texture_size_location_ = glGetUniformLocation(program_id_, "TextureSize"); if (texture_size_location_ != -1) { /* SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Configurando TextureSize uniform: %.0fx%.0f", texture_width_, texture_height_); */ Logger::info( "Configurando TextureSize uniform: " + std::to_string(static_cast(texture_width_)) + "x" + std::to_string(static_cast(texture_height_))); glUniform2f(texture_size_location_, texture_width_, texture_height_); checkGLError("glUniform2f(TextureSize)"); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Uniform 'TextureSize' no encontrado en shader"); } glUseProgram(0); is_initialized_ = true; /* SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** OpenGL 3.3 Shader Backend inicializado correctamente"); */ Logger::info("OpenGL 3.3 Shader Backend inicializado correctamente"); return true; } void OpenGLShader::render() { if (!is_initialized_ || program_id_ == 0) { // Fallback: renderizado SDL normal SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255); SDL_SetRenderTarget(renderer_, nullptr); SDL_RenderClear(renderer_); SDL_RenderTexture(renderer_, back_buffer_, nullptr, nullptr); SDL_RenderPresent(renderer_); return; } // Obtener tamaño actual de ventana (puede haber cambiado) int current_width, current_height; SDL_GetWindowSize(window_, ¤t_width, ¤t_height); // Guardar estados OpenGL GLint old_program; glGetIntegerv(GL_CURRENT_PROGRAM, &old_program); GLint old_viewport[4]; glGetIntegerv(GL_VIEWPORT, old_viewport); GLboolean was_texture_enabled = glIsEnabled(GL_TEXTURE_2D); GLint old_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture); GLint old_vao; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); // Preparar renderizado SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255); SDL_SetRenderTarget(renderer_, nullptr); SDL_RenderClear(renderer_); // Obtener y bindear textura GLuint texture_id = getTextureID(back_buffer_); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture_id); checkGLError("glBindTexture"); // Usar nuestro programa glUseProgram(program_id_); checkGLError("glUseProgram"); // Configurar viewport (obtener tamaño lógico de SDL) int logical_w, logical_h; SDL_RendererLogicalPresentation mode; SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &mode); if (logical_w == 0 || logical_h == 0) { logical_w = current_width; logical_h = current_height; } // Calcular viewport considerando aspect ratio int viewport_x = 0, viewport_y = 0; int viewport_w = current_width, viewport_h = current_height; if (mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) { int scale_x = current_width / logical_w; int scale_y = current_height / logical_h; int scale = (scale_x < scale_y) ? scale_x : scale_y; if (scale < 1) scale = 1; viewport_w = logical_w * scale; viewport_h = logical_h * scale; viewport_x = (current_width - viewport_w) / 2; viewport_y = (current_height - viewport_h) / 2; } else { float window_aspect = static_cast(current_width) / current_height; float logical_aspect = static_cast(logical_w) / logical_h; if (window_aspect > logical_aspect) { viewport_w = static_cast(logical_aspect * current_height); viewport_x = (current_width - viewport_w) / 2; } else { viewport_h = static_cast(current_width / logical_aspect); viewport_y = (current_height - viewport_h) / 2; } } glViewport(viewport_x, viewport_y, viewport_w, viewport_h); checkGLError("glViewport"); // Dibujar quad usando VAO glBindVertexArray(vao_); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); checkGLError("glDrawElements"); // Presentar SDL_GL_SwapWindow(window_); // Restaurar estados OpenGL glUseProgram(old_program); glBindTexture(GL_TEXTURE_2D, old_texture); if (!was_texture_enabled) { glDisable(GL_TEXTURE_2D); } glBindVertexArray(old_vao); glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); } void OpenGLShader::setTextureSize(float width, float height) { if (!is_initialized_ || program_id_ == 0) { return; } texture_width_ = width; texture_height_ = height; GLint old_program; glGetIntegerv(GL_CURRENT_PROGRAM, &old_program); glUseProgram(program_id_); if (texture_size_location_ != -1) { glUniform2f(texture_size_location_, width, height); checkGLError("glUniform2f(TextureSize)"); } glUseProgram(old_program); } void OpenGLShader::cleanup() { if (vao_ != 0) { glDeleteVertexArrays(1, &vao_); vao_ = 0; } if (vbo_ != 0) { glDeleteBuffers(1, &vbo_); vbo_ = 0; } if (ebo_ != 0) { glDeleteBuffers(1, &ebo_); ebo_ = 0; } if (program_id_ != 0) { glDeleteProgram(program_id_); program_id_ = 0; } is_initialized_ = false; window_ = nullptr; renderer_ = nullptr; back_buffer_ = nullptr; } } // namespace Rendering