232 lines
6.3 KiB
C++
232 lines
6.3 KiB
C++
#include <renderer/texture.h>
|
||
#include <renderer/rhi_module.h>
|
||
#include <utils/logger.h>
|
||
#define STB_IMAGE_IMPLEMENTATION
|
||
#include <stb/stb_image.h>
|
||
|
||
namespace extra2d {
|
||
|
||
Texture::Texture() = default;
|
||
|
||
Texture::~Texture() {
|
||
// RHI 纹理句柄是轻量级句柄,不需要显式释放
|
||
// 实际的纹理资源由 RHI 设备管理
|
||
handle_ = TextureHandle();
|
||
}
|
||
|
||
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) {
|
||
// 释放旧纹理
|
||
handle_ = TextureHandle();
|
||
|
||
width_ = width;
|
||
height_ = height;
|
||
format_ = format;
|
||
|
||
// 获取 RHI 设备
|
||
auto* rhiModule = RHIModule::get();
|
||
if (!rhiModule) {
|
||
E2D_LOG_ERROR("RHIModule not available");
|
||
return false;
|
||
}
|
||
|
||
auto* device = rhiModule->getDevice();
|
||
if (!device) {
|
||
E2D_LOG_ERROR("RHIDevice not available");
|
||
return false;
|
||
}
|
||
|
||
// 创建纹理描述
|
||
TextureDesc desc;
|
||
desc.width = static_cast<uint32_t>(width);
|
||
desc.height = static_cast<uint32_t>(height);
|
||
desc.format = format;
|
||
desc.mipLevels = 1; // 初始创建 1 层,后续生成 mipmap
|
||
|
||
// 创建 RHI 纹理
|
||
auto texture = device->createTexture(desc);
|
||
if (!texture) {
|
||
E2D_LOG_ERROR("Failed to create RHI texture");
|
||
return false;
|
||
}
|
||
|
||
// 上传纹理数据
|
||
if (data) {
|
||
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
||
// 这里假设 update 接受数据指针
|
||
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||
// 生成 mipmap
|
||
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
||
}
|
||
|
||
// 获取纹理句柄
|
||
handle_ = TextureHandle(texture.release());
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Texture::create(int width, int height, TextureFormat format) {
|
||
// 释放旧纹理
|
||
handle_ = TextureHandle();
|
||
|
||
width_ = width;
|
||
height_ = height;
|
||
format_ = format;
|
||
|
||
// 获取 RHI 设备
|
||
auto* rhiModule = RHIModule::get();
|
||
if (!rhiModule) {
|
||
E2D_LOG_ERROR("RHIModule not available");
|
||
return false;
|
||
}
|
||
|
||
auto* device = rhiModule->getDevice();
|
||
if (!device) {
|
||
E2D_LOG_ERROR("RHIDevice not available");
|
||
return false;
|
||
}
|
||
|
||
// 创建纹理描述
|
||
TextureDesc desc;
|
||
desc.width = static_cast<uint32_t>(width);
|
||
desc.height = static_cast<uint32_t>(height);
|
||
desc.format = format;
|
||
desc.mipLevels = 1; // 不生成 mipmap
|
||
|
||
// 创建 RHI 纹理
|
||
auto texture = device->createTexture(desc);
|
||
if (!texture) {
|
||
E2D_LOG_ERROR("Failed to create RHI texture");
|
||
return false;
|
||
}
|
||
|
||
// 获取纹理句柄
|
||
handle_ = TextureHandle(texture.release());
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Texture::reloadFromFile(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 reload 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 = reloadFromMemory(data, width_, height_, format);
|
||
stbi_image_free(data);
|
||
|
||
if (result) {
|
||
E2D_LOG_INFO("Texture reloaded: {} ({}x{})", path, width_, height_);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
||
// 更新尺寸和格式
|
||
width_ = width;
|
||
height_ = height;
|
||
format_ = format;
|
||
|
||
// 获取 RHI 设备
|
||
auto* rhiModule = RHIModule::get();
|
||
if (!rhiModule) {
|
||
E2D_LOG_ERROR("RHIModule not available");
|
||
return false;
|
||
}
|
||
|
||
auto* device = rhiModule->getDevice();
|
||
if (!device) {
|
||
E2D_LOG_ERROR("RHIDevice not available");
|
||
return false;
|
||
}
|
||
|
||
// 创建新的纹理描述
|
||
TextureDesc desc;
|
||
desc.width = static_cast<uint32_t>(width);
|
||
desc.height = static_cast<uint32_t>(height);
|
||
desc.format = format;
|
||
desc.mipLevels = 1;
|
||
|
||
// 创建新的 RHI 纹理
|
||
auto texture = device->createTexture(desc);
|
||
if (!texture) {
|
||
E2D_LOG_ERROR("Failed to create RHI texture during reload");
|
||
return false;
|
||
}
|
||
|
||
// 上传纹理数据
|
||
if (data) {
|
||
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||
}
|
||
|
||
// 替换旧的纹理句柄
|
||
handle_ = TextureHandle(texture.release());
|
||
|
||
return true;
|
||
}
|
||
|
||
// 辅助函数:获取每个像素的字节数
|
||
uint32_t Texture::getBytesPerPixel(TextureFormat format) {
|
||
switch (format) {
|
||
case TextureFormat::R8: return 1;
|
||
case TextureFormat::RG8: return 2;
|
||
case TextureFormat::RGB8: return 3;
|
||
case TextureFormat::RGBA8:
|
||
case TextureFormat::RGBA8_SRGB: return 4;
|
||
case TextureFormat::Depth16: return 2;
|
||
case TextureFormat::Depth24: return 3;
|
||
case TextureFormat::Depth32F: return 4;
|
||
case TextureFormat::Depth24Stencil8: return 4;
|
||
case TextureFormat::Depth32FStencil8: return 5;
|
||
default: return 4;
|
||
}
|
||
}
|
||
|
||
} // namespace extra2d
|