#include "jail_shader.h" #include // para SDL_Point #include // para NULL, free, malloc, exit #include // para strncmp #include // para basic_ostream, char_traits, operator<< #ifdef __APPLE__ #include "CoreFoundation/CoreFoundation.h" #include #if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #include #else // NOT ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #include #endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 #else // NOT __APPLE__ #include #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_Point tex_size = {320, 256}; bool usingOpenGL; #ifndef __APPLE__ // I'm avoiding the use of GLEW or some extensions handler, but that // doesn't mean you should... 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 GLuint compileShader(const char *source, GLuint shaderType) { // Create ID for shader GLuint result = glCreateShader(shaderType); // Add define depending on shader type const char *sources[2] = {shaderType == GL_VERTEX_SHADER ? "#define VERTEX\n" : "#define FRAGMENT\n", source}; // Define shader text glShaderSource(result, 2, sources, nullptr); // Compile shader glCompileShader(result); // Check vertex shader for errors GLint shaderCompiled = GL_FALSE; glGetShaderiv(result, GL_COMPILE_STATUS, &shaderCompiled); if (shaderCompiled != GL_TRUE) { std::cout << "Error en la compilación: " << result << "!" << std::endl; GLint logLength; glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { GLchar *log = (GLchar *)malloc(logLength); glGetShaderInfoLog(result, logLength, &logLength, log); std::cout << "Shader compile log:" << log << std::endl; free(log); } glDeleteShader(result); result = 0; } return result; } GLuint compileProgram(const char *vertexShaderSource, const char *fragmentShaderSource) { GLuint programId = 0; GLuint vtxShaderId, fragShaderId; programId = glCreateProgram(); vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER); fragShaderId = compileShader(fragmentShaderSource ? fragmentShaderSource : vertexShaderSource, GL_FRAGMENT_SHADER); if (vtxShaderId && fragShaderId) { // Associate shader with program glAttachShader(programId, vtxShaderId); glAttachShader(programId, fragShaderId); glLinkProgram(programId); glValidateProgram(programId); // Check the status of the compile/link GLint logLen; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen); if (logLen > 0) { char *log = (char *)malloc(logLen * sizeof(char)); // Show any errors as appropriate glGetProgramInfoLog(programId, logLen, &logLen, log); std::cout << "Prog Info Log: " << std::endl << log << std::endl; free(log); } } if (vtxShaderId) { glDeleteShader(vtxShaderId); } if (fragShaderId) { glDeleteShader(fragShaderId); } return programId; } const bool init(SDL_Window *win, SDL_Texture *backBuffer, const char *vertexShader, const char *fragmentShader) { shader::win = win; shader::renderer = SDL_GetRenderer(win); shader::backBuffer = backBuffer; SDL_GetWindowSize(win, &win_size.x, &win_size.y); int access; SDL_QueryTexture(backBuffer, nullptr, &access, &tex_size.x, &tex_size.y); if (access != SDL_TEXTUREACCESS_TARGET) { throw std::runtime_error("ERROR FATAL: La textura debe tener SDL_TEXTUREACCESS_TARGET definido."); } SDL_RendererInfo rendererInfo; SDL_GetRendererInfo(renderer, &rendererInfo); if (!strncmp(rendererInfo.name, "opengl", 6)) { #ifndef __APPLE__ if (!initGLExtensions()) { std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl; usingOpenGL = false; return false; } #endif // Compilar el shader y dejarlo listo para usar. programId = compileProgram(vertexShader, fragmentShader); } else { std::cout << "WARNING: 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_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_RenderGetLogicalSize(renderer, &logicalW, &logicalH); if (logicalW == 0 || logicalH == 0) { logicalW = win_size.x; logicalH = win_size.y; } // Calcula el viewport para preservar la relación de aspecto (letterboxing) float windowAspect = static_cast(win_size.x) / win_size.y; float logicalAspect = static_cast(logicalW) / logicalH; int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y; if (windowAspect > logicalAspect) { // La ventana es más ancha que el espacio lógico: viewportW = static_cast(logicalAspect * win_size.y); viewportX = (win_size.x - viewportW) / 2; } else { // La ventana es más alta que el espacio lógico: viewportH = static_cast(win_size.x / logicalAspect); viewportY = (win_size.y - viewportH) / 2; } glViewport(viewportX, viewportY, viewportW, viewportH); // Configura la proyección ortográfica usando el espacio lógico glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Se usa glOrtho para definir el espacio lógico: // el 0 está en la esquina superior izquierda y logicalH en la inferior. glOrtho(0, static_cast(logicalW), static_cast(logicalH), 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Dibuja el quad usando las nuevas coordenadas de textura: // Se corrigen la inversión vertical y la espejada horizontal. glBegin(GL_TRIANGLE_STRIP); // Vértice superior izquierdo: posición (0, 0) glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // Vértice superior derecho: posición (logicalW, 0) glTexCoord2f(1.0f, 1.0f); glVertex2f(static_cast(logicalW), 0.0f); // Vértice inferior izquierdo: posición (0, logicalH) glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, static_cast(logicalH)); // Vértice inferior derecho: posición (logicalW, logicalH) glTexCoord2f(1.0f, 0.0f); glVertex2f(static_cast(logicalW), static_cast(logicalH)); glEnd(); SDL_GL_SwapWindow(win); if (programId != 0) { glUseProgram(oldProgramId); } } else { SDL_RenderCopy(renderer, backBuffer, nullptr, nullptr); SDL_RenderPresent(renderer); } } }