Refactor code structure for improved readability and maintainability
This commit is contained in:
253
src/texture.cpp
Normal file
253
src/texture.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "texture.h"
|
||||
|
||||
#include "stb_image.h"
|
||||
#include <stdexcept>
|
||||
|
||||
struct TextureFormat {
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
};
|
||||
|
||||
// TextureFormatType => TextureFormat mapping
|
||||
static const TextureFormat textureFormatMap[] = {
|
||||
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE }, // R8
|
||||
{ GL_RG8, GL_RG, GL_UNSIGNED_BYTE }, // RG8
|
||||
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE }, // RGB8
|
||||
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // RGBA8
|
||||
{ GL_R16F, GL_RED, GL_HALF_FLOAT }, // R16F
|
||||
{ GL_RG16F, GL_RG, GL_HALF_FLOAT }, // RG16F
|
||||
{ GL_RGB16F, GL_RGB, GL_HALF_FLOAT }, // RGB16F
|
||||
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, // RGBA16F
|
||||
{ GL_R32F, GL_RED, GL_FLOAT }, // R32F
|
||||
{ GL_RG32F, GL_RG, GL_FLOAT }, // RG32F
|
||||
{ GL_RGB32F, GL_RGB, GL_FLOAT }, // RGB32F
|
||||
{ GL_RGBA32F, GL_RGBA, GL_FLOAT } // RGBA32F
|
||||
};
|
||||
|
||||
void Texture::Bind() const
|
||||
{
|
||||
glBindTexture(m_target, m_id);
|
||||
}
|
||||
|
||||
void Texture::Unbind() const
|
||||
{
|
||||
glBindTexture(m_target, 0);
|
||||
}
|
||||
|
||||
void Texture::GenerateMipmaps() const
|
||||
{
|
||||
glGenerateMipmap(m_target);
|
||||
}
|
||||
|
||||
static GLenum ToGLFilter(TextureFilter filter)
|
||||
{
|
||||
switch (filter) {
|
||||
case TextureFilter::Nearest: return GL_NEAREST;
|
||||
case TextureFilter::Linear: return GL_LINEAR;
|
||||
case TextureFilter::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST;
|
||||
case TextureFilter::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST;
|
||||
case TextureFilter::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR;
|
||||
case TextureFilter::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR;
|
||||
}
|
||||
return GL_LINEAR;
|
||||
}
|
||||
|
||||
static GLenum ToGLWrap(TextureWrap wrap)
|
||||
{
|
||||
switch (wrap) {
|
||||
case TextureWrap::Repeat: return GL_REPEAT;
|
||||
case TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT;
|
||||
case TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE;
|
||||
case TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER;
|
||||
}
|
||||
return GL_REPEAT;
|
||||
}
|
||||
|
||||
void Texture::SetFilter(TextureFilter min, TextureFilter mag) const
|
||||
{
|
||||
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, ToGLFilter(min));
|
||||
glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, ToGLFilter(mag));
|
||||
}
|
||||
|
||||
void Texture::SetWrap(TextureWrap s, TextureWrap t) const
|
||||
{
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_S, ToGLWrap(s));
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_T, ToGLWrap(t));
|
||||
}
|
||||
|
||||
void Texture::SetWrap(TextureWrap s, TextureWrap t, TextureWrap r) const
|
||||
{
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_S, ToGLWrap(s));
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_T, ToGLWrap(t));
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_R, ToGLWrap(r));
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture1D> Texture::CreateTexture(uint32_t width, TextureFormatType format)
|
||||
{
|
||||
auto tex = std::make_unique<Texture1D>();
|
||||
tex->m_width = width;
|
||||
tex->Bind();
|
||||
tex->LoadData(format, nullptr);
|
||||
tex->Unbind();
|
||||
return tex;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture2D> Texture::CreateTexture(uint32_t width, uint32_t height, TextureFormatType format)
|
||||
{
|
||||
auto tex = std::make_unique<Texture2D>();
|
||||
tex->m_width = width;
|
||||
tex->m_height = height;
|
||||
tex->Bind();
|
||||
tex->LoadData(format, nullptr);
|
||||
tex->Unbind();
|
||||
return tex;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture3D> Texture::CreateTexture(uint32_t width, uint32_t height, uint32_t depth, TextureFormatType format)
|
||||
{
|
||||
auto tex = std::make_unique<Texture3D>();
|
||||
tex->m_width = width;
|
||||
tex->m_height = height;
|
||||
tex->m_depth = depth;
|
||||
tex->Bind();
|
||||
tex->LoadData(format, nullptr);
|
||||
tex->Unbind();
|
||||
return tex;
|
||||
}
|
||||
|
||||
std::unique_ptr<TextureCubeMap> Texture::CreateCubeMap(uint32_t size, TextureFormatType format)
|
||||
{
|
||||
auto tex = std::make_unique<TextureCubeMap>();
|
||||
tex->m_size = size;
|
||||
tex->Bind();
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
tex->LoadFaceData(static_cast<TextureCubeMap::Face>(i), format, nullptr);
|
||||
}
|
||||
tex->Unbind();
|
||||
return tex;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture2D> Texture::LoadFromFile(const std::string &filepath)
|
||||
{
|
||||
int width, height, channels;
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
stbi_uc* data = stbi_load(filepath.c_str(), &width, &height, &channels, 0);
|
||||
if (!data) {
|
||||
throw std::runtime_error("Failed to load texture: " + filepath);
|
||||
}
|
||||
|
||||
TextureFormatType format;
|
||||
switch (channels) {
|
||||
case 1: format = TextureFormatType::R8; break;
|
||||
case 2: format = TextureFormatType::RG8; break;
|
||||
case 3: format = TextureFormatType::RGB8; break;
|
||||
case 4: format = TextureFormatType::RGBA8; break;
|
||||
default:
|
||||
stbi_image_free(data);
|
||||
throw std::runtime_error("Unsupported number of channels in texture: " + filepath);
|
||||
}
|
||||
|
||||
auto tex = CreateTexture(width, height, format);
|
||||
tex->Bind();
|
||||
tex->LoadData(format, data);
|
||||
tex->Unbind();
|
||||
|
||||
stbi_image_free(data);
|
||||
return tex;
|
||||
}
|
||||
|
||||
Texture::Texture(GLenum target)
|
||||
: m_target(target)
|
||||
{
|
||||
glGenTextures(1, &m_id);
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCubeMap::LoadFaceData(Face face, TextureFormatType format, const void* data) const
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + static_cast<GLenum>(face),
|
||||
0,
|
||||
fmt.internalFormat,
|
||||
m_size, m_size, 0,
|
||||
fmt.format, fmt.type,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
void TextureCubeMap::LoadFaceDataFromFile(Face face, const std::string &filepath) const
|
||||
{
|
||||
int width, height, channels;
|
||||
stbi_set_flip_vertically_on_load(false); // Cube maps typically expect unflipped images
|
||||
stbi_uc* data = stbi_load(filepath.c_str(), &width, &height, &channels, 0);
|
||||
if (!data) {
|
||||
throw std::runtime_error("Failed to load cube map face texture: " + filepath);
|
||||
}
|
||||
|
||||
if (width != m_size || height != m_size) {
|
||||
stbi_image_free(data);
|
||||
throw std::runtime_error("Cube map face texture size mismatch: " + filepath);
|
||||
}
|
||||
|
||||
TextureFormatType format;
|
||||
switch (channels) {
|
||||
case 1: format = TextureFormatType::R8; break;
|
||||
case 2: format = TextureFormatType::RG8; break;
|
||||
case 3: format = TextureFormatType::RGB8; break;
|
||||
case 4: format = TextureFormatType::RGBA8; break;
|
||||
default:
|
||||
stbi_image_free(data);
|
||||
throw std::runtime_error("Unsupported number of channels in cube map face texture: " + filepath);
|
||||
}
|
||||
|
||||
LoadFaceData(face, format, data);
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
void Texture1D::LoadData(TextureFormatType format, const void *data) const
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
glTexImage1D(
|
||||
m_target,
|
||||
0,
|
||||
fmt.internalFormat,
|
||||
m_width, 0,
|
||||
fmt.format, fmt.type,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
void Texture2D::LoadData(TextureFormatType format, const void *data) const
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
glTexImage2D(
|
||||
m_target,
|
||||
0,
|
||||
fmt.internalFormat,
|
||||
m_width, m_height, 0,
|
||||
fmt.format, fmt.type,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
void Texture3D::LoadData(TextureFormatType format, const void *data) const
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
glTexImage3D(
|
||||
m_target,
|
||||
0,
|
||||
fmt.internalFormat,
|
||||
m_width, m_height, m_depth, 0,
|
||||
fmt.format, fmt.type,
|
||||
data
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user