248 lines
6.2 KiB
C++
248 lines
6.2 KiB
C++
#pragma once
|
||
|
||
#include <renderer/texture.h>
|
||
#include <types/math/rect.h>
|
||
#include <types/ptr/intrusive_ptr.h>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <unordered_map>
|
||
#include <memory>
|
||
|
||
// 使用 stb_rect_pack 进行矩形打包
|
||
#define STB_RECT_PACK_IMPLEMENTATION
|
||
#include <stb/stb_rect_pack.h>
|
||
|
||
namespace extra2d {
|
||
|
||
/**
|
||
* @brief 图集区域
|
||
*
|
||
* 存储子图在图集中的位置和 UV 坐标
|
||
*/
|
||
struct AtlasRegion {
|
||
int x; // 在图集中的 X 坐标(像素)
|
||
int y; // 在图集中的 Y 坐标(像素)
|
||
int width; // 宽度(像素)
|
||
int height; // 高度(像素)
|
||
|
||
/**
|
||
* @brief 获取归一化 UV 坐标
|
||
* @param atlasWidth 图集宽度
|
||
* @param atlasHeight 图集高度
|
||
* @return UV 矩形 (x, y, width, height)
|
||
*/
|
||
Rect getUVRect(int atlasWidth, int atlasHeight) const {
|
||
return Rect(
|
||
static_cast<float>(x) / atlasWidth,
|
||
static_cast<float>(y) / atlasHeight,
|
||
static_cast<float>(width) / atlasWidth,
|
||
static_cast<float>(height) / atlasHeight
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @brief 获取归一化 UV 坐标(翻转 Y 轴)
|
||
* @param atlasWidth 图集宽度
|
||
* @param atlasHeight 图集高度
|
||
* @return UV 矩形 (x, y, width, height),Y 轴翻转
|
||
*/
|
||
Rect getUVRectFlipped(int atlasWidth, int atlasHeight) const {
|
||
float u = static_cast<float>(x) / atlasWidth;
|
||
float v = static_cast<float>(atlasHeight - y - height) / atlasHeight;
|
||
float w = static_cast<float>(width) / atlasWidth;
|
||
float h = static_cast<float>(height) / atlasHeight;
|
||
return Rect(u, v, w, h);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @brief 纹理图集
|
||
*
|
||
* 使用 stb_rect_pack 将多个小纹理打包到一个大纹理中
|
||
* 减少纹理切换,提高批处理效率
|
||
*/
|
||
class TextureAtlas : public RefCounted {
|
||
public:
|
||
/**
|
||
* @brief 默认构造函数
|
||
*/
|
||
TextureAtlas();
|
||
|
||
/**
|
||
* @brief 析构函数
|
||
*/
|
||
~TextureAtlas();
|
||
|
||
// 禁止拷贝
|
||
TextureAtlas(const TextureAtlas&) = delete;
|
||
TextureAtlas& operator=(const TextureAtlas&) = delete;
|
||
|
||
// 允许移动
|
||
TextureAtlas(TextureAtlas&& other) noexcept;
|
||
TextureAtlas& operator=(TextureAtlas&& other) noexcept;
|
||
|
||
/**
|
||
* @brief 初始化图集
|
||
* @param width 图集宽度(必须是 2 的幂)
|
||
* @param height 图集高度(必须是 2 的幂)
|
||
* @return 初始化是否成功
|
||
*/
|
||
bool initialize(int width, int height);
|
||
|
||
/**
|
||
* @brief 关闭图集
|
||
*/
|
||
void shutdown();
|
||
|
||
/**
|
||
* @brief 添加纹理到图集
|
||
* @param name 纹理名称(用于后续查询)
|
||
* @param texture 纹理指针
|
||
* @return 是否成功添加
|
||
*/
|
||
bool addTexture(const std::string& name, Ptr<Texture> texture);
|
||
|
||
/**
|
||
* @brief 从内存添加纹理数据
|
||
* @param name 纹理名称
|
||
* @param data 像素数据
|
||
* @param width 纹理宽度
|
||
* @param height 纹理高度
|
||
* @param format 像素格式
|
||
* @return 是否成功添加
|
||
*/
|
||
bool addTextureData(const std::string& name, const uint8_t* data,
|
||
int width, int height, TextureFormat format);
|
||
|
||
/**
|
||
* @brief 完成打包并生成图集纹理
|
||
* @return 生成是否成功
|
||
*/
|
||
bool finalize();
|
||
|
||
/**
|
||
* @brief 获取图集纹理
|
||
* @return 图集纹理指针
|
||
*/
|
||
Ptr<Texture> getAtlasTexture() const { return atlasTexture_; }
|
||
|
||
/**
|
||
* @brief 获取子图区域
|
||
* @param name 纹理名称
|
||
* @return 区域信息,不存在返回 nullptr
|
||
*/
|
||
const AtlasRegion* getRegion(const std::string& name) const;
|
||
|
||
/**
|
||
* @brief 获取子图的 UV 坐标
|
||
* @param name 纹理名称
|
||
* @return UV 矩形,不存在返回 (0,0,1,1)
|
||
*/
|
||
Rect getUVRect(const std::string& name) const;
|
||
|
||
/**
|
||
* @brief 检查是否包含指定纹理
|
||
* @param name 纹理名称
|
||
* @return 是否包含
|
||
*/
|
||
bool hasTexture(const std::string& name) const;
|
||
|
||
/**
|
||
* @brief 获取图集宽度
|
||
*/
|
||
int getWidth() const { return width_; }
|
||
|
||
/**
|
||
* @brief 获取图集高度
|
||
*/
|
||
int getHeight() const { return height_; }
|
||
|
||
/**
|
||
* @brief 获取已用空间百分比
|
||
*/
|
||
float getUsageRatio() const;
|
||
|
||
/**
|
||
* @brief 获取已添加纹理数量
|
||
*/
|
||
size_t getTextureCount() const { return regions_.size(); }
|
||
|
||
/**
|
||
* @brief 检查是否已最终化
|
||
*/
|
||
bool isFinalized() const { return finalized_; }
|
||
|
||
private:
|
||
// 待打包的矩形信息
|
||
struct PendingTexture {
|
||
std::string name;
|
||
int width;
|
||
int height;
|
||
std::vector<uint8_t> data;
|
||
TextureFormat format;
|
||
};
|
||
|
||
int width_ = 0;
|
||
int height_ = 0;
|
||
Ptr<Texture> atlasTexture_;
|
||
std::unordered_map<std::string, AtlasRegion> regions_;
|
||
std::vector<PendingTexture> pendingTextures_;
|
||
|
||
// stb_rect_pack 上下文
|
||
std::unique_ptr<stbrp_context> packContext_;
|
||
std::vector<stbrp_node> packNodes_;
|
||
|
||
bool finalized_ = false;
|
||
|
||
/**
|
||
* @brief 将像素数据复制到图集
|
||
*/
|
||
void copyTextureData(const PendingTexture& tex, const AtlasRegion& region);
|
||
};
|
||
|
||
/**
|
||
* @brief 图集构建器
|
||
*
|
||
* 辅助构建纹理图集的工具类
|
||
*/
|
||
class AtlasBuilder {
|
||
public:
|
||
/**
|
||
* @brief 设置目标图集大小
|
||
* @param width 宽度
|
||
* @param height 高度
|
||
*/
|
||
void setSize(int width, int height) {
|
||
width_ = width;
|
||
height_ = height;
|
||
}
|
||
|
||
/**
|
||
* @brief 添加纹理
|
||
* @param name 纹理名称
|
||
* @param texture 纹理
|
||
*/
|
||
void addTexture(const std::string& name, Ptr<Texture> texture) {
|
||
textures_.push_back({name, texture});
|
||
}
|
||
|
||
/**
|
||
* @brief 构建图集
|
||
* @return 图集指针,失败返回 nullptr
|
||
*/
|
||
Ptr<TextureAtlas> build();
|
||
|
||
/**
|
||
* @brief 自动选择最佳图集大小并构建
|
||
* @return 图集指针,失败返回 nullptr
|
||
*/
|
||
Ptr<TextureAtlas> buildAuto();
|
||
|
||
private:
|
||
int width_ = 2048;
|
||
int height_ = 2048;
|
||
std::vector<std::pair<std::string, Ptr<Texture>>> textures_;
|
||
};
|
||
|
||
} // namespace extra2d
|