Extra2D/include/renderer/texture_atlas.h

248 lines
6.2 KiB
C
Raw Normal View History

#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