Extra2D/src/renderer/texture.cpp

156 lines
4.3 KiB
C++
Raw Normal View History

#include <renderer/texture.h>
#include <utils/logger.h>
#include <glad/glad.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
namespace extra2d {
// OpenGL 格式转换函数
static GLint getTextureInternalFormat(TextureFormat format) {
switch (format) {
case TextureFormat::RGBA8: return GL_RGBA8;
case TextureFormat::RGB8: return GL_RGB8;
case TextureFormat::RGBA4: return GL_RGBA4;
case TextureFormat::R8: return GL_R8;
case TextureFormat::RG8: return GL_RG8;
default: return GL_RGBA8;
}
}
static GLenum getTextureFormat(TextureFormat format) {
switch (format) {
case TextureFormat::RGBA8:
case TextureFormat::RGBA4:
return GL_RGBA;
case TextureFormat::RGB8:
return GL_RGB;
case TextureFormat::R8:
return GL_RED;
case TextureFormat::RG8:
return GL_RG;
default:
return GL_RGBA;
}
}
Texture::Texture() = default;
Texture::~Texture() {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
}
bool Texture::loadFromFile(const std::string& path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_LOG_ERROR("Failed to load texture: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1: format = TextureFormat::R8; break;
case 2: format = TextureFormat::RG8; break;
case 3: format = TextureFormat::RGB8; break;
case 4: format = TextureFormat::RGBA8; break;
default: format = TextureFormat::RGBA8; break;
}
bool result = loadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_LOG_DEBUG("Texture loaded: {} ({}x{})", path, width_, height_);
}
return result;
}
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
width_ = width;
height_ = height;
format_ = format;
// 创建纹理
glGenTextures(1, &texture_);
glBindTexture(GL_TEXTURE_2D, texture_);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 上传纹理数据
glTexImage2D(GL_TEXTURE_2D, 0,
getTextureInternalFormat(format_),
width_, height_, 0,
getTextureFormat(format_),
GL_UNSIGNED_BYTE, data);
// 生成 mipmap
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
bool Texture::create(int width, int height, TextureFormat format) {
if (texture_ != 0) {
glDeleteTextures(1, &texture_);
texture_ = 0;
}
width_ = width;
height_ = height;
format_ = format;
// 创建纹理
glGenTextures(1, &texture_);
glBindTexture(GL_TEXTURE_2D, texture_);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 分配纹理存储(无数据)
glTexImage2D(GL_TEXTURE_2D, 0,
getTextureInternalFormat(format_),
width_, height_, 0,
getTextureFormat(format_),
GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void Texture::bind(uint32_t slot) const {
if (texture_ != 0) {
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, texture_);
}
}
void Texture::unbind() const {
glBindTexture(GL_TEXTURE_2D, 0);
}
} // namespace extra2d