// // MaterialAssetUtil.h:材质资产编辑共用工具(Inspector / Material Editor // 面板共享)。 // // 单一事实来源(原 InspectorPanel.cpp 匿名 ns 实现上提): // - .material 文件枚举缓存(inline 变量跨 TU 唯一——面板各自创建 // Override 后缓存失效即时可见) // - Create Override:内建材质快照 → assets/materials/*.material → // GUID 登记 → 同步加载 → 挂实体覆盖表 // #ifndef HELLOVULKAN_MATERIALASSETUTIL_H #define HELLOVULKAN_MATERIALASSETUTIL_H #include "AssetKind.h" // EditorAsset::IsTexturePath(纹理扩展名单一事实来源) #include #include #include #include #include #include #include #include #include #include namespace EditorMaterialUtil { namespace fs = std::filesystem; // .material 文件枚举(会话级缓存 + 脏标)。inline 变量 = 所有 TU // 共享同一份(Create Override 新建文件后置脏重扫) inline std::vector s_MaterialFiles; inline bool s_MaterialFilesDirty = true; inline void InvalidateMaterialFileList() { s_MaterialFilesDirty = true; } // 递归收集 assets/ 下纹理文件(材质编辑器纹理槽选择器;纹理文件 // 不被编辑器创建,无需脏标)。会话级缓存 inline const std::vector& AssetTextureFiles() { static std::vector s_Files; static bool s_Cached = false; if (!s_Cached) { s_Cached = true; if (fs::exists("assets")) { for (const auto& entry : fs::recursive_directory_iterator("assets")) { if (!entry.is_regular_file()) { continue; } if (EditorAsset::IsTexturePath(entry.path())) { s_Files.push_back(entry.path().generic_string()); } } std::sort(s_Files.begin(), s_Files.end()); } } return s_Files; } inline const std::vector& AssetMaterialFiles() { if (s_MaterialFilesDirty) { s_MaterialFilesDirty = false; s_MaterialFiles.clear(); if (fs::exists("assets")) { for (const auto& entry : fs::recursive_directory_iterator("assets")) { if (!entry.is_regular_file()) { continue; } std::string ext = entry.path().extension().string(); std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return std::tolower(c); }); if (ext == ".material") { s_MaterialFiles.push_back( entry.path().generic_string()); } } std::sort(s_MaterialFiles.begin(), s_MaterialFiles.end()); } } return s_MaterialFiles; } // Create Override:内建材质快照 → assets/materials/_.material // → GUID 登记 → 同步加载 → 挂实体覆盖表。失败返回空串(不挂槽)。 // 数据段 = 内建材质只读快照(纹理路径 + 因子 + 透明语义),此后 // 该资产独立演化,内建材质不受影响 inline std::string CreateMaterialOverride( const std::string& modelPath, const Engine::MeshMaterialInfo& info, Engine::u32 meshIdx, Engine::ECS::MeshRendererComponent& mr, Engine::Assets::AssetManager& assets) { // 目标文件名:_.material;mesh 名非 // [A-Za-z0-9] 字符折叠 '_',空名回退下标;重名追加 _2/_3/... std::string stem = fs::path(modelPath).stem().string(); std::string leaf = info.name; if (leaf.empty()) { leaf = std::to_string(meshIdx); } for (char& c : leaf) { if (!std::isalnum(static_cast(c))) { c = '_'; } } const fs::path dir = "assets/materials"; std::error_code ec; fs::create_directories(dir, ec); if (ec) { ENGINE_WARN("MaterialUtil: cannot create {}: {}", dir.string(), ec.message()); return {}; } std::string base = stem + "_" + leaf; std::string rel = (dir / (base + ".material")).generic_string(); for (int n = 2; fs::exists(rel); ++n) { rel = (dir / (base + "_" + std::to_string(n) + ".material")) .generic_string(); } Engine::MaterialAssetData data; data.texPaths = info.texPaths; data.params = info.params; data.transparent = info.transparent; if (!Engine::MaterialAsset::Save(rel, data)) { ENGINE_WARN("MaterialUtil: failed to save material override '{}'", rel); return {}; } assets.GetOrCreateGUID(rel); if (!assets.LoadMaterialAsset(rel).Valid()) { ENGINE_WARN("MaterialUtil: override '{}' saved but failed to load", rel); return {}; } mr.materialOverrides[meshIdx] = rel; InvalidateMaterialFileList(); // 新文件进下拉列表 return rel; } } // namespace EditorMaterialUtil #endif //HELLOVULKAN_MATERIALASSETUTIL_H