2026-03-02 00:25:14 +08:00
|
|
|
|
#include <renderer/texture.h>
|
2026-03-03 02:16:29 +08:00
|
|
|
|
#include <renderer/rhi_module.h>
|
2026-03-02 00:25:14 +08:00
|
|
|
|
#include <utils/logger.h>
|
|
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
|
|
|
|
#include <stb/stb_image.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace extra2d {
|
|
|
|
|
|
|
|
|
|
|
|
Texture::Texture() = default;
|
|
|
|
|
|
|
|
|
|
|
|
Texture::~Texture() {
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// RHI 纹理句柄是轻量级句柄,不需要显式释放
|
|
|
|
|
|
// 实际的纹理资源由 RHI 设备管理
|
|
|
|
|
|
handle_ = TextureHandle();
|
2026-03-02 00:25:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 释放旧纹理
|
|
|
|
|
|
handle_ = TextureHandle();
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
|
|
|
|
|
width_ = width;
|
|
|
|
|
|
height_ = height;
|
|
|
|
|
|
format_ = format;
|
|
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 获取 RHI 设备
|
|
|
|
|
|
auto* rhiModule = RHIModule::get();
|
|
|
|
|
|
if (!rhiModule) {
|
|
|
|
|
|
E2D_LOG_ERROR("RHIModule not available");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
auto* device = rhiModule->getDevice();
|
|
|
|
|
|
if (!device) {
|
|
|
|
|
|
E2D_LOG_ERROR("RHIDevice not available");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 创建纹理描述
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 上传纹理数据
|
|
|
|
|
|
if (data) {
|
|
|
|
|
|
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
|
|
|
|
|
// 这里假设 update 接受数据指针
|
|
|
|
|
|
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
|
|
|
|
|
// 生成 mipmap
|
|
|
|
|
|
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 获取纹理句柄
|
|
|
|
|
|
handle_ = TextureHandle(texture.release());
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Texture::create(int width, int height, TextureFormat format) {
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 释放旧纹理
|
|
|
|
|
|
handle_ = TextureHandle();
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
|
|
|
|
|
width_ = width;
|
|
|
|
|
|
height_ = height;
|
|
|
|
|
|
format_ = format;
|
|
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 获取 RHI 设备
|
|
|
|
|
|
auto* rhiModule = RHIModule::get();
|
|
|
|
|
|
if (!rhiModule) {
|
|
|
|
|
|
E2D_LOG_ERROR("RHIModule not available");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
auto* device = rhiModule->getDevice();
|
|
|
|
|
|
if (!device) {
|
|
|
|
|
|
E2D_LOG_ERROR("RHIDevice not available");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 创建纹理描述
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 获取纹理句柄
|
|
|
|
|
|
handle_ = TextureHandle(texture.release());
|
2026-03-02 00:25:14 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 19:32:23 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
|
// 辅助函数:获取每个像素的字节数
|
|
|
|
|
|
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;
|
2026-03-02 00:25:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace extra2d
|