diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLTexture.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLTexture.h index 1b4be8b6..ab0b9ba3 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLTexture.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLTexture.h @@ -2,30 +2,77 @@ #include #include +#include namespace XCEngine { namespace RHI { +enum class OpenGLTextureType { + Texture1D, + Texture2D, + Texture2DArray, + Texture3D, + TextureCube, + TextureCubeArray +}; + +enum class OpenGLFormat { + R8, + RG8, + RGBA8, + RGBA16F, + RGBA32F, + Depth24Stencil8, + Depth32F, + CompressedDXT1, + CompressedDXT5 +}; + +enum class OpenGLInternalFormat { + R8 = 1, + RG8 = 2, + RGBA8 = 4, + RGBA16F = 11, + RGBA32F = 16, + Depth24Stencil8 = 38, + Depth32F = 31, + CompressedDXT1 = 21, + CompressedDXT5 = 22 +}; + class OpenGLTexture { public: OpenGLTexture(); ~OpenGLTexture(); + bool Initialize(OpenGLTextureType type, int width, int height, int depth, int mipLevels, OpenGLFormat format, const void* data = nullptr); bool Initialize2D(int width, int height, int channels, const void* data, bool generateMipmap = true); + bool InitializeCubeMap(int size, int mipLevels, OpenGLFormat format, const void* data = nullptr); bool LoadFromFile(const char* path, bool flipVertical = true); void Shutdown(); void Bind(int slot = 0) const; void Unbind() const; + void BindImage(int slot, bool read, bool write) const; + + void GenerateMipmap(); + void SetFiltering(int minFilter, int magFilter); + void SetWrapping(int wrapS, int wrapT, int wrapR = -1); unsigned int GetID() const { return m_texture; } + OpenGLTextureType GetType() const { return m_type; } int GetWidth() const { return m_width; } int GetHeight() const { return m_height; } + int GetDepth() const { return m_depth; } + int GetMipLevels() const { return m_mipLevels; } private: unsigned int m_texture; + OpenGLTextureType m_type; int m_width; int m_height; + int m_depth; + int m_mipLevels; int m_channels; }; diff --git a/engine/src/RHI/OpenGL/OpenGLTexture.cpp b/engine/src/RHI/OpenGL/OpenGLTexture.cpp index 70ceecd7..9b934fdc 100644 --- a/engine/src/RHI/OpenGL/OpenGLTexture.cpp +++ b/engine/src/RHI/OpenGL/OpenGLTexture.cpp @@ -8,10 +8,54 @@ namespace XCEngine { namespace RHI { +static unsigned int ToGLTextureTarget(OpenGLTextureType type) { + switch (type) { + case OpenGLTextureType::Texture1D: return GL_TEXTURE_1D; + case OpenGLTextureType::Texture2D: return GL_TEXTURE_2D; + case OpenGLTextureType::Texture2DArray: return GL_TEXTURE_2D_ARRAY; + case OpenGLTextureType::Texture3D: return GL_TEXTURE_3D; + case OpenGLTextureType::TextureCube: return GL_TEXTURE_CUBE_MAP; + case OpenGLTextureType::TextureCubeArray: return GL_TEXTURE_CUBE_MAP_ARRAY; + default: return GL_TEXTURE_2D; + } +} + +static void ToGLFormat(OpenGLFormat fmt, unsigned int& internalFormat, unsigned int& glFormat, unsigned int& glType) { + switch (fmt) { + case OpenGLFormat::R8: + internalFormat = GL_R8; glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; + break; + case OpenGLFormat::RG8: + internalFormat = GL_RG8; glFormat = GL_RG; glType = GL_UNSIGNED_BYTE; + break; + case OpenGLFormat::RGBA8: + internalFormat = GL_RGBA8; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; + break; + case OpenGLFormat::RGBA16F: + internalFormat = GL_RGBA16F; glFormat = GL_RGBA; glType = GL_HALF_FLOAT; + break; + case OpenGLFormat::RGBA32F: + internalFormat = GL_RGBA32F; glFormat = GL_RGBA; glType = GL_FLOAT; + break; + case OpenGLFormat::Depth24Stencil8: + internalFormat = GL_DEPTH24_STENCIL8; glFormat = GL_DEPTH_STENCIL; glType = GL_UNSIGNED_INT_24_8; + break; + case OpenGLFormat::Depth32F: + internalFormat = GL_DEPTH_COMPONENT32F; glFormat = GL_DEPTH_COMPONENT; glType = GL_FLOAT; + break; + default: + internalFormat = GL_RGBA8; glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; + break; + } +} + OpenGLTexture::OpenGLTexture() : m_texture(0) + , m_type(OpenGLTextureType::Texture2D) , m_width(0) , m_height(0) + , m_depth(0) + , m_mipLevels(1) , m_channels(0) { } @@ -19,41 +63,110 @@ OpenGLTexture::~OpenGLTexture() { Shutdown(); } +bool OpenGLTexture::Initialize(OpenGLTextureType type, int width, int height, int depth, int mipLevels, OpenGLFormat format, const void* data) { + m_type = type; + m_width = width; + m_height = height; + m_depth = depth; + m_mipLevels = mipLevels; + + unsigned int target = ToGLTextureTarget(type); + unsigned int internalFormat, glFormat, glType; + ToGLFormat(format, internalFormat, glFormat, glType); + + glGenTextures(1, &m_texture); + glBindTexture(target, m_texture); + + if (type == OpenGLTextureType::TextureCube) { + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, width, height, 0, glFormat, glType, data); + } + } else if (type == OpenGLTextureType::Texture1D) { + glTexImage1D(GL_TEXTURE_1D, 0, internalFormat, width, 0, glFormat, glType, data); + } else if (type == OpenGLTextureType::Texture3D) { + glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0, glFormat, glType, data); + } else { + glTexImage2D(target, 0, internalFormat, width, height, 0, glFormat, glType, data); + } + + if (mipLevels > 1) { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glBindTexture(target, 0); + return true; +} + bool OpenGLTexture::Initialize2D(int width, int height, int channels, const void* data, bool generateMipmap) { + m_channels = channels; + m_type = OpenGLTextureType::Texture2D; + m_width = width; + m_height = height; + m_depth = 1; + m_mipLevels = generateMipmap ? 0 : 1; + glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_2D, m_texture); - + GLenum format = (channels == 4) ? GL_RGBA : GL_RGB; glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); - + if (generateMipmap) { glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - m_width = width; - m_height = height; - m_channels = channels; - + glBindTexture(GL_TEXTURE_2D, 0); return true; } +bool OpenGLTexture::InitializeCubeMap(int size, int mipLevels, OpenGLFormat format, const void* data) { + m_type = OpenGLTextureType::TextureCube; + m_width = size; + m_height = size; + m_depth = 1; + m_mipLevels = mipLevels; + + unsigned int internalFormat, glFormat, glType; + ToGLFormat(format, internalFormat, glFormat, glType); + + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_texture); + + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, size, size, 0, glFormat, glType, data); + } + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, mipLevels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + return true; +} + bool OpenGLTexture::LoadFromFile(const char* path, bool flipVertical) { stbi_set_flip_vertically_on_load(flipVertical ? 1 : 0); - + unsigned char* data = stbi_load(path, &m_width, &m_height, &m_channels, 0); if (!data) { std::cout << "Failed to load texture: " << path << std::endl; return false; } - + bool result = Initialize2D(m_width, m_height, m_channels, data, true); stbi_image_free(data); return result; @@ -67,12 +180,46 @@ void OpenGLTexture::Shutdown() { } void OpenGLTexture::Bind(int slot) const { + unsigned int target = ToGLTextureTarget(m_type); glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(target, m_texture); } void OpenGLTexture::Unbind() const { - glBindTexture(GL_TEXTURE_2D, 0); + unsigned int target = ToGLTextureTarget(m_type); + glBindTexture(target, 0); +} + +void OpenGLTexture::BindImage(int slot, bool read, bool write) const { + unsigned int target = ToGLTextureTarget(m_type); + GLenum access = (read && write) ? GL_READ_WRITE : (read ? GL_READ_ONLY : GL_WRITE_ONLY); + glBindImageTexture(slot, m_texture, 0, GL_FALSE, 0, access, GL_RGBA8); +} + +void OpenGLTexture::GenerateMipmap() { + unsigned int target = ToGLTextureTarget(m_type); + glBindTexture(target, m_texture); + glGenerateMipmap(target); + glBindTexture(target, 0); +} + +void OpenGLTexture::SetFiltering(int minFilter, int magFilter) { + unsigned int target = ToGLTextureTarget(m_type); + glBindTexture(target, m_texture); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter); + glBindTexture(target, 0); +} + +void OpenGLTexture::SetWrapping(int wrapS, int wrapT, int wrapR) { + unsigned int target = ToGLTextureTarget(m_type); + glBindTexture(target, m_texture); + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapT); + if (wrapR >= 0 && (m_type == OpenGLTextureType::Texture3D || m_type == OpenGLTextureType::TextureCube)) { + glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapR); + } + glBindTexture(target, 0); } } // namespace RHI