Extra2D/include/renderer/texture_atlas.h

248 lines
6.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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