#include "texture.h" #include #include #include #include bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { assert(texture); assert(desc); glGenTextures(1, &texture->id); if (!texture->id) { log_error("glGenTextures() failed"); return false; } texture->target = to_GL_dimension(desc->dimension); glBindTexture(texture->target, texture->id); // glTexStorageXD const int levels = desc->mipmaps ? max(max(log2(desc->width), log2(desc->height)), log2(desc->depth)) + 1 : 1; const GLenum internal_format = to_GL_internal_format(desc->format); switch (texture->target) { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: glTexStorage2D( texture->target, levels, internal_format, desc->width, desc->height); break; default: FAIL("Unhandled texture dimension"); gfx_del_texture(texture); return false; } texture->format = to_GL_format(desc->format); texture->type = to_GL_type(desc->format); texture->width = desc->width; texture->height = desc->height; gfx_update_texture(texture, &desc->data); // gfx_update_texture() unbinds the texture at the end, so re-bind it here. glBindTexture(texture->target, texture->id); // Mipmaps. if (desc->mipmaps) { glGenerateMipmap(texture->target); } // Texture filtering. const bool linear = desc->filtering == LinearFiltering; GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (linear ? GL_LINEAR : GL_NEAREST); GLenum mag = linear ? GL_LINEAR : GL_NEAREST; glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); // Texture wrapping. GLenum wrap = GL_INVALID_ENUM; switch (desc->wrap) { case Repeat: wrap = GL_REPEAT; break; case ClampToEdge: wrap = GL_CLAMP_TO_EDGE; break; } glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); glBindTexture(texture->target, 0); return true; } void gfx_del_texture(Texture* texture) { assert(texture); if (texture->id) { glDeleteTextures(1, &texture->id); texture->id = 0; } } void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { assert(texture); assert(desc); glBindTexture(texture->target, texture->id); // glTexSubImageXD switch (texture->target) { case GL_TEXTURE_2D: if (desc->pixels) { glTexSubImage2D( GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, /*yoffset=*/0, texture->width, texture->height, texture->format, texture->type, desc->pixels); } break; case GL_TEXTURE_CUBE_MAP: for (int i = 0; i < 6; ++i) { const void* pixels = *(&desc->cubemap.pixels_pos_x + i); if (pixels) { glTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, /*level=*/0, /*xoffset=*/0, /*yoffset=*/0, texture->width, texture->height, texture->format, texture->type, pixels); } } break; default: FAIL("Unhandled texture dimension"); break; } glBindTexture(texture->target, 0); } GLenum to_GL_dimension(TextureDimension dim) { switch (dim) { case Texture2D: return GL_TEXTURE_2D; case TextureCubeMap: return GL_TEXTURE_CUBE_MAP; default: FAIL("Unhandled TextureDimension"); return GL_INVALID_ENUM; } } GLenum to_GL_internal_format(TextureFormat format) { switch (format) { case TextureDepth: return GL_DEPTH_COMPONENT; case TextureRG16: return GL_RG16; case TextureRG16F: return GL_RG16F; case TextureRGB8: return GL_RGB8; case TextureR11G11B10F: return GL_R11F_G11F_B10F; case TextureRGBA8: return GL_RGBA8; case TextureSRGB8: return GL_SRGB8; case TextureSRGBA8: return GL_SRGB8_ALPHA8; default: FAIL("Unhandled TextureFormat"); return GL_INVALID_ENUM; } } GLenum to_GL_format(TextureFormat format) { switch (format) { case TextureDepth: return GL_DEPTH_COMPONENT; case TextureRG16: case TextureRG16F: return GL_RG; case TextureRGB8: case TextureR11G11B10F: case TextureSRGB8: return GL_RGB; case TextureRGBA8: case TextureSRGBA8: return GL_RGBA; default: FAIL("Unhandled TextureFormat"); return GL_INVALID_ENUM; } } GLenum to_GL_type(TextureFormat format) { switch (format) { case TextureDepth: case TextureRG16F: case TextureR11G11B10F: return GL_FLOAT; case TextureRG16: case TextureRGB8: case TextureRGBA8: case TextureSRGB8: case TextureSRGBA8: return GL_UNSIGNED_BYTE; default: FAIL("Unhandled TextureFormat"); return GL_INVALID_ENUM; } } GLenum to_GL_cubemap_face(CubemapFace face) { switch (face) { case CubemapFacePosX: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; case CubemapFaceNegX: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; case CubemapFacePosY: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; case CubemapFaceNegY: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; case CubemapFacePosZ: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; case CubemapFaceNegZ: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; default: FAIL("Unhandled CubemapFace"); return GL_INVALID_ENUM; } }