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
|