Extra2D/include/graphics/texture_atlas.h

185 lines
5.0 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 <core/color.h>
#include <core/math_types.h>
#include <core/types.h>
#include <graphics/texture.h>
#include <graphics/opengl/gl_texture.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
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 getWidth() const { return width_; }
int getHeight() 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