#include #include #include #include namespace extra2d { // ======================================== // TextureAtlas 实现 // ======================================== TextureAtlas::TextureAtlas() = default; TextureAtlas::~TextureAtlas() { shutdown(); } TextureAtlas::TextureAtlas(TextureAtlas&& other) noexcept : width_(other.width_) , height_(other.height_) , atlasTexture_(std::move(other.atlasTexture_)) , regions_(std::move(other.regions_)) , pendingTextures_(std::move(other.pendingTextures_)) , packContext_(std::move(other.packContext_)) , packNodes_(std::move(other.packNodes_)) , finalized_(other.finalized_) { other.width_ = 0; other.height_ = 0; other.finalized_ = false; } TextureAtlas& TextureAtlas::operator=(TextureAtlas&& other) noexcept { if (this != &other) { shutdown(); width_ = other.width_; height_ = other.height_; atlasTexture_ = std::move(other.atlasTexture_); regions_ = std::move(other.regions_); pendingTextures_ = std::move(other.pendingTextures_); packContext_ = std::move(other.packContext_); packNodes_ = std::move(other.packNodes_); finalized_ = other.finalized_; other.width_ = 0; other.height_ = 0; other.finalized_ = false; } return *this; } bool TextureAtlas::initialize(int width, int height) { shutdown(); if (width <= 0 || height <= 0) { E2D_LOG_ERROR("TextureAtlas::initialize: Invalid size {}x{}", width, height); return false; } width_ = width; height_ = height; // 初始化 stb_rect_pack packContext_ = std::make_unique(); packNodes_.resize(width); stbrp_init_target(packContext_.get(), width, height, packNodes_.data(), width); E2D_LOG_DEBUG("TextureAtlas initialized: {}x{}", width, height); return true; } void TextureAtlas::shutdown() { atlasTexture_.reset(); regions_.clear(); pendingTextures_.clear(); packContext_.reset(); packNodes_.clear(); width_ = 0; height_ = 0; finalized_ = false; } bool TextureAtlas::addTexture(const std::string& name, Ptr texture) { if (finalized_) { E2D_LOG_WARN("TextureAtlas::addTexture: Cannot add texture to finalized atlas"); return false; } if (!texture) { E2D_LOG_WARN("TextureAtlas::addTexture: Texture is null"); return false; } if (regions_.find(name) != regions_.end()) { E2D_LOG_WARN("TextureAtlas::addTexture: Texture '{}' already exists", name); return false; } // 获取纹理尺寸 int texWidth = static_cast(texture->getWidth()); int texHeight = static_cast(texture->getHeight()); if (texWidth <= 0 || texHeight <= 0) { E2D_LOG_WARN("TextureAtlas::addTexture: Invalid texture size {}x{}", texWidth, texHeight); return false; } // 检查纹理是否适合图集 if (texWidth > width_ || texHeight > height_) { E2D_LOG_WARN("TextureAtlas::addTexture: Texture {}x{} too large for atlas {}x{}", texWidth, texHeight, width_, height_); return false; } // 创建待处理纹理信息 PendingTexture pending; pending.name = name; pending.width = texWidth; pending.height = texHeight; pending.format = texture->getFormat(); // 获取纹理数据 size_t dataSize = texWidth * texHeight * 4; // 假设 RGBA pending.data.resize(dataSize); // TODO: 实现 Texture::getData() 方法来读取像素数据 // 暂时使用占位数据 std::fill(pending.data.begin(), pending.data.end(), 255); pendingTextures_.push_back(std::move(pending)); E2D_LOG_DEBUG("TextureAtlas: Added texture '{}' ({}x{})", name, texWidth, texHeight); return true; } bool TextureAtlas::addTextureData(const std::string& name, const uint8_t* data, int width, int height, TextureFormat format) { if (finalized_) { E2D_LOG_WARN("TextureAtlas::addTextureData: Cannot add texture to finalized atlas"); return false; } if (!data || width <= 0 || height <= 0) { E2D_LOG_WARN("TextureAtlas::addTextureData: Invalid parameters"); return false; } if (regions_.find(name) != regions_.end()) { E2D_LOG_WARN("TextureAtlas::addTextureData: Texture '{}' already exists", name); return false; } if (width > width_ || height > height_) { E2D_LOG_WARN("TextureAtlas::addTextureData: Texture {}x{} too large for atlas {}x{}", width, height, width_, height_); return false; } PendingTexture pending; pending.name = name; pending.width = width; pending.height = height; pending.format = format; // 复制像素数据 int channels = (format == TextureFormat::RGBA8) ? 4 : 3; size_t dataSize = width * height * channels; pending.data.resize(dataSize); std::memcpy(pending.data.data(), data, dataSize); pendingTextures_.push_back(std::move(pending)); E2D_LOG_DEBUG("TextureAtlas: Added texture data '{}' ({}x{})", name, width, height); return true; } bool TextureAtlas::finalize() { if (finalized_) { return true; } if (pendingTextures_.empty()) { E2D_LOG_WARN("TextureAtlas::finalize: No textures to pack"); return false; } // 准备矩形数组 std::vector rects; rects.reserve(pendingTextures_.size()); for (size_t i = 0; i < pendingTextures_.size(); ++i) { stbrp_rect rect; rect.id = static_cast(i); rect.w = pendingTextures_[i].width; rect.h = pendingTextures_[i].height; rect.x = 0; rect.y = 0; rect.was_packed = 0; rects.push_back(rect); } // 执行打包 int result = stbrp_pack_rects(packContext_.get(), rects.data(), static_cast(rects.size())); if (!result) { E2D_LOG_ERROR("TextureAtlas::finalize: Failed to pack all textures"); return false; } // 创建图集纹理数据 std::vector atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景 // 处理打包结果 for (const auto& rect : rects) { if (!rect.was_packed) { E2D_LOG_WARN("TextureAtlas::finalize: Texture {} not packed", rect.id); continue; } const auto& pending = pendingTextures_[rect.id]; // 创建区域信息 AtlasRegion region; region.x = rect.x; region.y = rect.y; region.width = rect.w; region.height = rect.h; regions_[pending.name] = region; // 复制像素数据到图集 // TODO: 实现正确的像素格式转换 // 目前假设所有纹理都是 RGBA8 for (int y = 0; y < pending.height; ++y) { for (int x = 0; x < pending.width; ++x) { int srcIdx = (y * pending.width + x) * 4; int dstIdx = ((rect.y + y) * width_ + (rect.x + x)) * 4; if (srcIdx + 3 < static_cast(pending.data.size()) && dstIdx + 3 < static_cast(atlasData.size())) { atlasData[dstIdx + 0] = pending.data[srcIdx + 0]; atlasData[dstIdx + 1] = pending.data[srcIdx + 1]; atlasData[dstIdx + 2] = pending.data[srcIdx + 2]; atlasData[dstIdx + 3] = pending.data[srcIdx + 3]; } } } E2D_LOG_DEBUG("TextureAtlas: Packed '{}' at ({}, {}) size {}x{}", pending.name, rect.x, rect.y, rect.w, rect.h); } // 创建图集纹理 atlasTexture_ = makePtr(); if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_, TextureFormat::RGBA8)) { E2D_LOG_ERROR("TextureAtlas::finalize: Failed to create atlas texture"); return false; } // 清理待处理列表 pendingTextures_.clear(); finalized_ = true; E2D_LOG_INFO("TextureAtlas finalized: {} textures packed into {}x{} atlas ({}% usage)", regions_.size(), width_, height_, static_cast(getUsageRatio() * 100)); return true; } const AtlasRegion* TextureAtlas::getRegion(const std::string& name) const { auto it = regions_.find(name); if (it != regions_.end()) { return &it->second; } return nullptr; } Rect TextureAtlas::getUVRect(const std::string& name) const { const AtlasRegion* region = getRegion(name); if (region) { return region->getUVRect(width_, height_); } return Rect(0.0f, 0.0f, 1.0f, 1.0f); } bool TextureAtlas::hasTexture(const std::string& name) const { return regions_.find(name) != regions_.end(); } float TextureAtlas::getUsageRatio() const { if (!finalized_ || regions_.empty()) { return 0.0f; } int totalArea = 0; for (const auto& pair : regions_) { totalArea += pair.second.width * pair.second.height; } return static_cast(totalArea) / (width_ * height_); } // ======================================== // AtlasBuilder 实现 // ======================================== Ptr AtlasBuilder::build() { if (textures_.empty()) { E2D_LOG_WARN("AtlasBuilder::build: No textures to build"); return nullptr; } auto atlas = makePtr(); if (!atlas->initialize(width_, height_)) { return nullptr; } for (const auto& pair : textures_) { atlas->addTexture(pair.first, pair.second); } if (!atlas->finalize()) { return nullptr; } return atlas; } Ptr AtlasBuilder::buildAuto() { if (textures_.empty()) { E2D_LOG_WARN("AtlasBuilder::buildAuto: No textures to build"); return nullptr; } // 计算总面积 int totalArea = 0; int maxWidth = 0; int maxHeight = 0; for (const auto& pair : textures_) { if (pair.second) { int w = static_cast(pair.second->getWidth()); int h = static_cast(pair.second->getHeight()); totalArea += w * h; maxWidth = std::max(maxWidth, w); maxHeight = std::max(maxHeight, h); } } // 选择合适的大小(2 的幂) int atlasSize = 64; while (atlasSize < maxWidth || atlasSize < maxHeight || atlasSize * atlasSize < totalArea * 1.5f) { atlasSize *= 2; if (atlasSize > 8192) { E2D_LOG_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas"); return nullptr; } } width_ = atlasSize; height_ = atlasSize; return build(); } } // namespace extra2d