#include "jail_shader.h" #include // Para SDL_FPoint #include // Para SDL_bool #include // Para strncmp #include // Para basic_ostream, operator<<, endl, cout #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 shaderType) { if (source.empty()) { throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío."); } // Crear identificador del shader GLuint resultado = glCreateShader(shaderType); // Agregar una directiva según el tipo de shader std::string directiva = (shaderType == GL_VERTEX_SHADER) ? "#define VERTEX\n" : "#define FRAGMENT\n"; const char *sources[2] = {directiva.c_str(), source.c_str()}; // Especificar el código fuente del shader glShaderSource(resultado, 2, sources, nullptr); // Compilar el shader glCompileShader(resultado); // Verificar si la compilación fue exitosa GLint compiladoCorrectamente = GL_FALSE; glGetShaderiv(resultado, GL_COMPILE_STATUS, &compiladoCorrectamente); if (compiladoCorrectamente != GL_TRUE) { std::cout << "Error en la compilación del shader (" << resultado << ")!" << std::endl; GLint longitudLog; glGetShaderiv(resultado, GL_INFO_LOG_LENGTH, &longitudLog); if (longitudLog > 0) { std::vector log(longitudLog); glGetShaderInfoLog(resultado, longitudLog, &longitudLog, log.data()); std::cout << "Registro de compilación del shader: " << log.data() << std::endl; } glDeleteShader(resultado); resultado = 0; } return resultado; } // Función para compilar un programa de shaders (vertex y fragment) a partir de std::string GLuint compileProgram(const std::string &vertexShaderSource, const std::string &fragmentShaderSource) { GLuint idPrograma = glCreateProgram(); // Si el fragment shader está vacío, reutilizamos el código del vertex shader GLuint idShaderVertice = compileShader(vertexShaderSource, GL_VERTEX_SHADER); GLuint idShaderFragmento = compileShader(fragmentShaderSource.empty() ? vertexShaderSource : fragmentShaderSource, GL_FRAGMENT_SHADER); if (idShaderVertice && idShaderFragmento) { // Asociar los shaders al programa glAttachShader(idPrograma, idShaderVertice); glAttachShader(idPrograma, idShaderFragmento); glLinkProgram(idPrograma); glValidateProgram(idPrograma); // Verificar el estado del enlace GLint longitudLog; glGetProgramiv(idPrograma, GL_INFO_LOG_LENGTH, &longitudLog); if (longitudLog > 0) { std::vector log(longitudLog); glGetProgramInfoLog(idPrograma, longitudLog, &longitudLog, log.data()); std::cout << "Registro de información del programa:" << std::endl << log.data() << std::endl; } } if (idShaderVertice) { glDeleteShader(idShaderVertice); } if (idShaderFragmento) { glDeleteShader(idShaderFragmento); } return idPrograma; } bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader) { shader::win = ventana; shader::renderer = SDL_GetRenderer(ventana); shader::backBuffer = texturaBackBuffer; SDL_GetWindowSize(ventana, &win_size.x, &win_size.y); //int acceso; SDL_GetTextureSize(texturaBackBuffer, &tex_size.x, &tex_size.y); // SDL_QueryTexture(texturaBackBuffer, nullptr, &acceso, &tex_size.x, &tex_size.y); /*if (acceso != SDL_TEXTUREACCESS_TARGET) { throw std::runtime_error("ERROR FATAL: La textura debe tener definido SDL_TEXTUREACCESS_TARGET."); }*/ const auto RENDER_NAME = SDL_GetRendererName(renderer); // Verificar que el renderer sea OpenGL if (!strncmp(RENDER_NAME, "opengl", 6)) { #ifndef __APPLE__ if (!initGLExtensions()) { std::cout << "ADVERTENCIA: No se han podido inicializar las extensiones de OpenGL." << std::endl; usingOpenGL = false; return false; } #endif // Compilar el programa de shaders utilizando std::string programId = compileProgram(vertexShader, fragmentShader); } else { std::cout << "ADVERTENCIA: El driver del renderer no es OpenGL." << std::endl; 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); // SDL_GL_BindTexture(backBuffer, nullptr, nullptr); 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); } } }