512 lines
17 KiB
C++
512 lines
17 KiB
C++
#include "opengl_shader.hpp"
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
#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<char> 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<char> 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<int>(texture_width_)) +
|
|
"x" +
|
|
std::to_string(static_cast<int>(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<int>(texture_width_)) +
|
|
"x" +
|
|
std::to_string(static_cast<int>(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<float>(current_width) / current_height;
|
|
float logical_aspect = static_cast<float>(logical_w) / logical_h;
|
|
|
|
if (window_aspect > logical_aspect) {
|
|
viewport_w = static_cast<int>(logical_aspect * current_height);
|
|
viewport_x = (current_width - viewport_w) / 2;
|
|
} else {
|
|
viewport_h = static_cast<int>(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
|