#include "gl_texture_load.h" #include "../../vendor/stb/stb_image.h" #include #include namespace fn { namespace { // Error thread-local — cada thread mantiene su propio mensaje. thread_local std::string g_last_error; bool ends_with_ci(const char* s, const char* suffix) { if (!s || !suffix) return false; size_t ls = std::strlen(s); size_t lf = std::strlen(suffix); if (lf > ls) return false; const char* a = s + ls - lf; for (size_t i = 0; i < lf; i++) { char ca = a[i]; char cb = suffix[i]; if (ca >= 'A' && ca <= 'Z') ca = char(ca - 'A' + 'a'); if (cb >= 'A' && cb <= 'Z') cb = char(cb - 'A' + 'a'); if (ca != cb) return false; } return true; } GLuint upload_ldr(const unsigned char* pixels, int w, int h, bool srgb) { GLuint id = 0; glGenTextures(1, &id); if (id == 0) return 0; glBindTexture(GL_TEXTURE_2D, id); GLint internal = srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8; glTexImage2D(GL_TEXTURE_2D, 0, internal, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); return id; } GLuint upload_hdr(const float* pixels, int w, int h) { GLuint id = 0; glGenTextures(1, &id); if (id == 0) return 0; glBindTexture(GL_TEXTURE_2D, id); // GL_RGBA16F + GL_FLOAT — GL 3.0+. En GL 2.1 no hay GL_RGBA16F; aceptamos // la limitacion (driver moderno). glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_FLOAT, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); return id; } void set_error(const char* msg) { g_last_error = msg ? msg : "unknown error"; } } // namespace GlTexture gl_texture_load(const char* path, bool flip_y, bool srgb) { GlTexture tex{}; if (!path || !*path) { set_error("gl_texture_load: empty path"); return tex; } stbi_set_flip_vertically_on_load(flip_y ? 1 : 0); if (ends_with_ci(path, ".hdr")) { int w = 0, h = 0, ch = 0; float* px = stbi_loadf(path, &w, &h, &ch, 4); if (!px) { set_error(stbi_failure_reason()); return tex; } tex.id = upload_hdr(px, w, h); tex.w = w; tex.h = h; tex.channels = ch; stbi_image_free(px); if (tex.id == 0) set_error("glGenTextures returned 0"); else g_last_error.clear(); return tex; } int w = 0, h = 0, ch = 0; unsigned char* px = stbi_load(path, &w, &h, &ch, 4); // forzamos RGBA if (!px) { set_error(stbi_failure_reason()); return tex; } tex.id = upload_ldr(px, w, h, srgb); tex.w = w; tex.h = h; tex.channels = ch; stbi_image_free(px); if (tex.id == 0) set_error("glGenTextures returned 0"); else g_last_error.clear(); return tex; } GlTexture gl_texture_load_from_memory(const unsigned char* data, int size, bool flip_y, bool srgb) { GlTexture tex{}; if (!data || size <= 0) { set_error("gl_texture_load_from_memory: empty buffer"); return tex; } stbi_set_flip_vertically_on_load(flip_y ? 1 : 0); int w = 0, h = 0, ch = 0; unsigned char* px = stbi_load_from_memory(data, size, &w, &h, &ch, 4); if (!px) { set_error(stbi_failure_reason()); return tex; } tex.id = upload_ldr(px, w, h, srgb); tex.w = w; tex.h = h; tex.channels = ch; stbi_image_free(px); if (tex.id == 0) set_error("glGenTextures returned 0"); else g_last_error.clear(); return tex; } void gl_texture_destroy(GlTexture& tex) { if (tex.id != 0) { glDeleteTextures(1, &tex.id); tex.id = 0; } tex.w = tex.h = tex.channels = 0; } const char* gl_texture_last_error() { return g_last_error.c_str(); } void gl_texture_bind_uniform(GLuint program, const char* name, const GlTexture& tex, int unit) { if (program == 0 || !name || !tex.ok()) return; glActiveTexture(GL_TEXTURE0 + unit); glBindTexture(GL_TEXTURE_2D, tex.id); GLint loc = glGetUniformLocation(program, name); if (loc >= 0) glUniform1i(loc, unit); } } // namespace fn