Refactor code structure for improved readability and maintainability

This commit is contained in:
Diego Lopes
2026-03-18 00:25:31 -04:00
commit e538df3673
34 changed files with 9243 additions and 0 deletions

253
src/texture.cpp Normal file
View 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
);
}