From d81f0c1e451324cd73eff6a4af5144dfd8c47dac Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Fri, 27 Feb 2026 18:52:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(graphics):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E7=BA=B9=E7=90=86=E5=9B=BE=E9=9B=86=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=AE=9E=E7=8E=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/graphics/texture_atlas.h | 185 --------------------- src/graphics/texture_atlas.cpp | 272 ------------------------------- 2 files changed, 457 deletions(-) delete mode 100644 include/graphics/texture_atlas.h delete mode 100644 src/graphics/texture_atlas.cpp diff --git a/include/graphics/texture_atlas.h b/include/graphics/texture_atlas.h deleted file mode 100644 index 51ecc78..0000000 --- a/include/graphics/texture_atlas.h +++ /dev/null @@ -1,185 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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); - - // 获取图集纹理 - IntrusivePtr 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_; - IntrusivePtr texture_; - std::unordered_map entries_; - - // 矩形打包数据 - struct PackNode { - int x, y, width, height; - bool used; - std::unique_ptr left; - std::unique_ptr right; - - PackNode(int x_, int y_, int w, int h) - : x(x_), y(y_), width(w), height(h), used(false) {} - }; - - std::unique_ptr 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> &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> pages_; - std::unordered_map 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 diff --git a/src/graphics/texture_atlas.cpp b/src/graphics/texture_atlas.cpp deleted file mode 100644 index 5af8a7f..0000000 --- a/src/graphics/texture_atlas.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include -#include -#include - - -namespace extra2d { - -// ============================================================================ -// TextureAtlasPage 实现 -// ============================================================================ - -TextureAtlasPage::TextureAtlasPage(int width, int height) - : width_(width), height_(height), isFull_(false), usedArea_(0) { - // 创建空白纹理 - std::vector emptyData(width * height * 4, 0); - texture_ = makeRef(width, height, emptyData.data(), 4); - - // 初始化矩形打包根节点 - root_ = std::make_unique(0, 0, width, height); - - E2D_LOG_INFO("Created texture atlas page: {}x{}", width, height); -} - -TextureAtlasPage::~TextureAtlasPage() = default; - -bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth, - int texHeight, const uint8_t *pixels, - Rect &outUvRect) { - if (isFull_) { - return false; - } - - // 添加边距 - int paddedWidth = texWidth + 2 * PADDING; - int paddedHeight = texHeight + 2 * PADDING; - - // 如果纹理太大,无法放入 - if (paddedWidth > width_ || paddedHeight > height_) { - return false; - } - - // 尝试插入 - PackNode *node = insert(root_.get(), paddedWidth, paddedHeight); - if (node == nullptr) { - // 无法放入,标记为满 - isFull_ = true; - return false; - } - - // 写入像素数据(跳过边距区域) - writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight, - pixels); - - // 创建条目 - AtlasEntry entry; - entry.name = name; - entry.originalSize = - Vec2(static_cast(texWidth), static_cast(texHeight)); - entry.padding = PADDING; - - // 计算 UV 坐标(考虑边距) - float u1 = static_cast(node->x + PADDING) / width_; - float v1 = static_cast(node->y + PADDING) / height_; - float u2 = static_cast(node->x + PADDING + texWidth) / width_; - float v2 = static_cast(node->y + PADDING + texHeight) / height_; - - entry.uvRect = Rect(u1, v1, u2 - u1, v2 - v1); - outUvRect = entry.uvRect; - - entries_[name] = std::move(entry); - usedArea_ += paddedWidth * paddedHeight; - - E2D_LOG_DEBUG("Added texture '{}' to atlas: {}x{} at ({}, {})", name, - texWidth, texHeight, node->x, node->y); - - return true; -} - -TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width, - int height) { - if (node == nullptr) { - return nullptr; - } - - // 如果节点已被使用,尝试子节点 - if (node->used) { - PackNode *result = insert(node->left.get(), width, height); - if (result != nullptr) { - return result; - } - return insert(node->right.get(), width, height); - } - - // 检查是否适合 - if (width > node->width || height > node->height) { - return nullptr; - } - - // 如果刚好合适,使用此节点 - if (width == node->width && height == node->height) { - node->used = true; - return node; - } - - // 需要分割节点 - int dw = node->width - width; - int dh = node->height - height; - - if (dw > dh) { - // 水平分割 - node->left = - std::make_unique(node->x, node->y, width, node->height); - node->right = - std::make_unique(node->x + width, node->y, dw, node->height); - } else { - // 垂直分割 - node->left = - std::make_unique(node->x, node->y, node->width, height); - node->right = - std::make_unique(node->x, node->y + height, node->width, dh); - } - - // 递归插入到左子节点 - return insert(node->left.get(), width, height); -} - -void TextureAtlasPage::writePixels(int x, int y, int w, int h, - const uint8_t *pixels) { - if (texture_ == nullptr || pixels == nullptr) { - return; - } - - // 使用 glTexSubImage2D 更新纹理数据 - GLuint texID = static_cast( - reinterpret_cast(texture_->getNativeHandle())); - - glBindTexture(GL_TEXTURE_2D, texID); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, - pixels); - glBindTexture(GL_TEXTURE_2D, 0); -} - -const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const { - auto it = entries_.find(name); - if (it != entries_.end()) { - return &it->second; - } - return nullptr; -} - -float TextureAtlasPage::getUsageRatio() const { - return static_cast(usedArea_) / (width_ * height_); -} - -// ============================================================================ -// TextureAtlas 实现 -// ============================================================================ - -TextureAtlas::TextureAtlas() - : pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256), - enabled_(true), initialized_(false) {} - -TextureAtlas::~TextureAtlas() = default; - -void TextureAtlas::init(int pageSize) { - pageSize_ = pageSize; - initialized_ = true; - E2D_LOG_INFO("TextureAtlas initialized with page size: {}", pageSize); -} - -bool TextureAtlas::addTexture(const std::string &name, int width, int height, - const uint8_t *pixels) { - if (!enabled_ || !initialized_) { - return false; - } - - // 检查是否已存在 - if (contains(name)) { - return true; - } - - // 检查纹理大小 - if (width > sizeThreshold_ || height > sizeThreshold_) { - E2D_LOG_DEBUG("Texture '{}' too large for atlas ({}x{} > {}), skipping", - name, width, height, sizeThreshold_); - return false; - } - - // 尝试添加到现有页面 - Rect uvRect; - for (auto &page : pages_) { - if (page->tryAddTexture(name, width, height, pixels, uvRect)) { - entryToPage_[name] = page.get(); - return true; - } - } - - // 创建新页面 - auto newPage = std::make_unique(pageSize_, pageSize_); - if (newPage->tryAddTexture(name, width, height, pixels, uvRect)) { - entryToPage_[name] = newPage.get(); - pages_.push_back(std::move(newPage)); - return true; - } - - E2D_LOG_WARN("Failed to add texture '{}' to atlas", name); - return false; -} - -bool TextureAtlas::contains(const std::string &name) const { - return entryToPage_.find(name) != entryToPage_.end(); -} - -const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - return it->second->getTexture().get(); - } - return nullptr; -} - -Rect TextureAtlas::getUVRect(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - const AtlasEntry *entry = it->second->getEntry(name); - if (entry != nullptr) { - return entry->uvRect; - } - } - return Rect(0, 0, 1, 1); // 默认 UV -} - -Vec2 TextureAtlas::getOriginalSize(const std::string &name) const { - auto it = entryToPage_.find(name); - if (it != entryToPage_.end()) { - const AtlasEntry *entry = it->second->getEntry(name); - if (entry != nullptr) { - return entry->originalSize; - } - } - return Vec2(0, 0); -} - -float TextureAtlas::getTotalUsageRatio() const { - if (pages_.empty()) { - return 0.0f; - } - - float total = 0.0f; - for (const auto &page : pages_) { - total += page->getUsageRatio(); - } - return total / pages_.size(); -} - -void TextureAtlas::clear() { - pages_.clear(); - entryToPage_.clear(); - E2D_LOG_INFO("TextureAtlas cleared"); -} - -// ============================================================================ -// TextureAtlasManager 单例实现 -// ============================================================================ - -TextureAtlasManager &TextureAtlasManager::getInstance() { - static TextureAtlasManager instance; - return instance; -} - -} // namespace extra2d