185 lines
4.9 KiB
C++
185 lines
4.9 KiB
C++
#pragma once
|
||
|
||
#include <core/color.h>
|
||
#include <core/math_types.h>
|
||
#include <core/types.h>
|
||
#include <graphics/opengl/gl_texture.h>
|
||
#include <graphics/texture.h>
|
||
#include <memory>
|
||
#include <string>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
|
||
namespace extra2d {
|
||
|
||
// ============================================================================
|
||
// 纹理图集 - 自动将小纹理合并到大图集以减少 DrawCall
|
||
// ============================================================================
|
||
|
||
/**
|
||
* @brief 图集中的单个纹理条目
|
||
*/
|
||
struct AtlasEntry {
|
||
std::string name; // 原始纹理名称/路径
|
||
Rect uvRect; // 在图集中的 UV 坐标范围
|
||
Vec2 originalSize; // 原始纹理尺寸
|
||
uint32_t padding; // 边距(用于避免纹理 bleeding)
|
||
|
||
AtlasEntry() : uvRect(), originalSize(), padding(2) {}
|
||
};
|
||
|
||
/**
|
||
* @brief 纹理图集页面
|
||
* 当单个图集放不下时,创建多个页面
|
||
*/
|
||
class TextureAtlasPage {
|
||
public:
|
||
static constexpr int DEFAULT_SIZE = 2048;
|
||
static constexpr int MAX_SIZE = 4096;
|
||
static constexpr int MIN_TEXTURE_SIZE = 32; // 小于此大小的纹理才考虑合并
|
||
static constexpr int PADDING = 2; // 纹理间边距
|
||
|
||
TextureAtlasPage(int width = DEFAULT_SIZE, int height = DEFAULT_SIZE);
|
||
~TextureAtlasPage();
|
||
|
||
// 尝试添加纹理到图集
|
||
// 返回是否成功,如果成功则输出 uvRect
|
||
bool tryAddTexture(const std::string &name, int texWidth, int texHeight,
|
||
const uint8_t *pixels, Rect &outUvRect);
|
||
|
||
// 获取图集纹理
|
||
Ptr<Texture> getTexture() const { return texture_; }
|
||
|
||
// 获取条目
|
||
const AtlasEntry *getEntry(const std::string &name) const;
|
||
|
||
// 获取使用率
|
||
float getUsageRatio() const;
|
||
|
||
// 获取尺寸
|
||
int width() const { return width_; }
|
||
int height() const { return height_; }
|
||
|
||
// 是否已满
|
||
bool isFull() const { return isFull_; }
|
||
|
||
private:
|
||
int width_, height_;
|
||
Ptr<Texture> texture_;
|
||
std::unordered_map<std::string, AtlasEntry> entries_;
|
||
|
||
// 矩形打包数据
|
||
struct PackNode {
|
||
int x, y, width, height;
|
||
bool used;
|
||
std::unique_ptr<PackNode> left;
|
||
std::unique_ptr<PackNode> right;
|
||
|
||
PackNode(int x_, int y_, int w, int h)
|
||
: x(x_), y(y_), width(w), height(h), used(false) {}
|
||
};
|
||
|
||
std::unique_ptr<PackNode> root_;
|
||
bool isFull_;
|
||
int usedArea_;
|
||
|
||
// 递归插入
|
||
PackNode *insert(PackNode *node, int width, int height);
|
||
void writePixels(int x, int y, int w, int h, const uint8_t *pixels);
|
||
};
|
||
|
||
/**
|
||
* @brief 纹理图集管理器
|
||
* 自动管理多个图集页面,提供统一的纹理查询接口
|
||
*/
|
||
class TextureAtlas {
|
||
public:
|
||
TextureAtlas();
|
||
~TextureAtlas();
|
||
|
||
// 初始化
|
||
void init(int pageSize = TextureAtlasPage::DEFAULT_SIZE);
|
||
|
||
// 添加纹理到图集
|
||
// 如果纹理太大,返回 false,应该作为独立纹理加载
|
||
bool addTexture(const std::string &name, int width, int height,
|
||
const uint8_t *pixels);
|
||
|
||
// 查询纹理是否在图集中
|
||
bool contains(const std::string &name) const;
|
||
|
||
// 获取纹理在图集中的信息
|
||
// 返回图集纹理和 UV 坐标
|
||
const Texture *getAtlasTexture(const std::string &name) const;
|
||
Rect getUVRect(const std::string &name) const;
|
||
|
||
// 获取原始纹理尺寸
|
||
Vec2 getOriginalSize(const std::string &name) const;
|
||
|
||
// 获取所有图集页面
|
||
const std::vector<std::unique_ptr<TextureAtlasPage>> &getPages() const {
|
||
return pages_;
|
||
}
|
||
|
||
// 获取总使用率
|
||
float getTotalUsageRatio() const;
|
||
|
||
// 清空所有图集
|
||
void clear();
|
||
|
||
// 设置是否启用自动图集
|
||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||
bool isEnabled() const { return enabled_; }
|
||
|
||
// 设置纹理大小阈值(小于此大小的纹理才进入图集)
|
||
void setSizeThreshold(int threshold) { sizeThreshold_ = threshold; }
|
||
int getSizeThreshold() const { return sizeThreshold_; }
|
||
|
||
private:
|
||
std::vector<std::unique_ptr<TextureAtlasPage>> pages_;
|
||
std::unordered_map<std::string, TextureAtlasPage *> entryToPage_;
|
||
|
||
int pageSize_;
|
||
int sizeThreshold_;
|
||
bool enabled_;
|
||
bool initialized_;
|
||
};
|
||
|
||
/**
|
||
* @brief 全局图集管理器(单例)
|
||
*/
|
||
class TextureAtlasManager {
|
||
public:
|
||
static TextureAtlasManager &getInstance();
|
||
|
||
// 获取主图集
|
||
TextureAtlas &getAtlas() { return atlas_; }
|
||
|
||
// 快捷方法
|
||
bool addTexture(const std::string &name, int width, int height,
|
||
const uint8_t *pixels) {
|
||
return atlas_.addTexture(name, width, height, pixels);
|
||
}
|
||
|
||
bool contains(const std::string &name) const { return atlas_.contains(name); }
|
||
|
||
const Texture *getAtlasTexture(const std::string &name) const {
|
||
return atlas_.getAtlasTexture(name);
|
||
}
|
||
|
||
Rect getUVRect(const std::string &name) const {
|
||
return atlas_.getUVRect(name);
|
||
}
|
||
|
||
private:
|
||
TextureAtlasManager() = default;
|
||
~TextureAtlasManager() = default;
|
||
|
||
TextureAtlasManager(const TextureAtlasManager &) = delete;
|
||
TextureAtlasManager &operator=(const TextureAtlasManager &) = delete;
|
||
|
||
TextureAtlas atlas_;
|
||
};
|
||
|
||
} // namespace extra2d
|