#include "jail_shader.h" #include // Para SDL_LogCategory, SDL_LogError, SDL_LogWarn #include // Para SDL_FPoint, SDL_Point #include // Para strncmp #include // Para runtime_error #include // Para vector #ifdef __APPLE__ #include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS #include // Para OpenGL en macOS #if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #include // Para OpenGL 3 en macOS #else // NO ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #include // Para OpenGL (compatibilidad) en macOS #endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #else // SI NO ES __APPLE__ #include // Para GLuint, glTexCoord2f, glVertex2f, GLfloat #endif // __APPLE__ namespace shader { SDL_Window *win = nullptr; SDL_Renderer *renderer = nullptr; GLuint programId = 0; SDL_Texture *backBuffer = nullptr; SDL_Point win_size = {320 * 4, 256 * 4}; SDL_FPoint tex_size = {320, 256}; bool usingOpenGL = false; #ifndef __APPLE__ // Declaración de funciones de extensión de OpenGL (evitando GLEW) PFNGLCREATESHADERPROC glCreateShader; PFNGLSHADERSOURCEPROC glShaderSource; PFNGLCOMPILESHADERPROC glCompileShader; PFNGLGETSHADERIVPROC glGetShaderiv; PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; PFNGLDELETESHADERPROC glDeleteShader; PFNGLATTACHSHADERPROC glAttachShader; PFNGLCREATEPROGRAMPROC glCreateProgram; PFNGLLINKPROGRAMPROC glLinkProgram; PFNGLVALIDATEPROGRAMPROC glValidateProgram; PFNGLGETPROGRAMIVPROC glGetProgramiv; PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; PFNGLUSEPROGRAMPROC glUseProgram; bool 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"); return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && glUseProgram; } #endif // Función para compilar un shader a partir de un std::string GLuint compileShader(const std::string &source, GLuint shader_type) { if (source.empty()) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR FATAL: El código fuente del shader está vacío."); throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío."); } // Crear identificador del shader GLuint shader_id = glCreateShader(shader_type); // Agregar una directiva según el tipo de shader std::string directive = (shader_type == GL_VERTEX_SHADER) ? "#define VERTEX\n" : "#define FRAGMENT\n"; const char *sources[2] = {directive.c_str(), source.c_str()}; // Especificar el código fuente del shader glShaderSource(shader_id, 2, sources, nullptr); // Compilar el shader glCompileShader(shader_id); // Verificar si la compilación fue exitosa GLint compiled_ok = GL_FALSE; glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled_ok); if (compiled_ok != GL_TRUE) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en la compilación del shader (%d)!", shader_id); 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, "Registro de compilación del shader: %s", log.data()); } glDeleteShader(shader_id); shader_id = 0; } return shader_id; } // Función para compilar un programa de shaders (vertex y fragment) a partir de std::string GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source) { GLuint program_id = glCreateProgram(); // Si el fragment shader está vacío, reutilizamos el código del vertex shader GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER); GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER); if (vertex_shader_id && fragment_shader_id) { // Asociar los shaders al programa glAttachShader(program_id, vertex_shader_id); glAttachShader(program_id, fragment_shader_id); glLinkProgram(program_id); glValidateProgram(program_id); // Verificar el estado del enlace GLint log_length; glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { std::vector log(log_length); glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data()); } } if (vertex_shader_id) { glDeleteShader(vertex_shader_id); } if (fragment_shader_id) { glDeleteShader(fragment_shader_id); } return program_id; } bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) { shader::win = window; shader::renderer = SDL_GetRenderer(window); shader::backBuffer = back_buffer_texture; SDL_GetWindowSize(window, &win_size.x, &win_size.y); SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y); const auto render_name = SDL_GetRendererName(renderer); // Verificar que el renderer sea OpenGL if (!strncmp(render_name, "opengl", 6)) { #ifndef __APPLE__ if (!initGLExtensions()) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: No se han podido inicializar las extensiones de OpenGL."); usingOpenGL = false; return false; } #endif // Compilar el programa de shaders utilizando std::string programId = compileProgram(vertex_shader, fragment_shader); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL."); usingOpenGL = false; return false; } usingOpenGL = true; return true; } void render() { GLint oldProgramId; // Establece el color de fondo SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetRenderTarget(renderer, nullptr); SDL_RenderClear(renderer); if (usingOpenGL) { SDL_GetTextureProperties(backBuffer); glBindTexture(GL_TEXTURE_2D, 1); if (programId != 0) { glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); glUseProgram(programId); } // Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize int logicalW, logicalH; SDL_RendererLogicalPresentation mode; SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH, &mode); if (logicalW == 0 || logicalH == 0) { logicalW = win_size.x; logicalH = win_size.y; } // Cálculo del viewport int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y; const bool USE_INTEGER_SCALE = mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; if (USE_INTEGER_SCALE) { // Calcula el factor de escalado entero máximo que se puede aplicar int scaleX = win_size.x / logicalW; int scaleY = win_size.y / logicalH; int scale = (scaleX < scaleY ? scaleX : scaleY); if (scale < 1) { scale = 1; } viewportW = logicalW * scale; viewportH = logicalH * scale; viewportX = (win_size.x - viewportW) / 2; viewportY = (win_size.y - viewportH) / 2; } else { // Letterboxing: preserva la relación de aspecto usando una escala flotante float windowAspect = static_cast(win_size.x) / win_size.y; float logicalAspect = static_cast(logicalW) / logicalH; if (windowAspect > logicalAspect) { viewportW = static_cast(logicalAspect * win_size.y); viewportX = (win_size.x - viewportW) / 2; } else { viewportH = static_cast(win_size.x / logicalAspect); viewportY = (win_size.y - viewportH) / 2; } } glViewport(viewportX, viewportY, viewportW, viewportH); // Configurar la proyección ortográfica usando el espacio lógico glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Queremos que el origen esté en la esquina superior izquierda del espacio lógico. glOrtho(0, static_cast(logicalW), static_cast(logicalH), 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Dibuja el quad con las coordenadas ajustadas. // Se asignan las coordenadas de textura "normales" para que no quede espejado horizontalmente, // y se mantiene el flip vertical para que la imagen no aparezca volteada. glBegin(GL_TRIANGLE_STRIP); // Vértice superior izquierdo glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // Vértice superior derecho glTexCoord2f(1.0f, 1.0f); glVertex2f(static_cast(logicalW), 0.0f); // Vértice inferior izquierdo glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, static_cast(logicalH)); // Vértice inferior derecho glTexCoord2f(1.0f, 0.0f); glVertex2f(static_cast(logicalW), static_cast(logicalH)); glEnd(); SDL_GL_SwapWindow(win); if (programId != 0) { glUseProgram(oldProgramId); } } else { SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr); SDL_RenderPresent(renderer); } } }