- 资产浏览器可视化与配色:面板重构(网格/列表/缩略图)、AssetKind 分类、 EditorColors 统一配色、AssetItemView 抽出条目绘制;HierarchyPanel/ MaterialEditorPanel 同步调整 - 材质缩略图改走 GPU 回读烘焙:MaterialPreviewPass 新增 bake pass (SDR atlas → host staging copyImageToBuffer)、Renderer 提供 Request/PollMaterialPreviewBake(无 waitIdle,依赖 per-slot fence), Editor 每帧排空后注入 ThumbnailCache——脱离预览 tile 池,消除"滚出视野 即失效/池耗尽"两问题 - 缩略图失效:BakedIdentity(serial, editVersion) 与当前材质身份比对, 换贴图槽(重载新 serial)/调参数(editVersion++)不符即 Invalidate 重烘, 修"修改材质后资产浏览器缩略图不更新" - 材质加载闸门:每帧至多 1 个 + 按实测耗时冷却,替代固定 2/帧 - 纹理上传批式化:VulkanAssetUploader 新增路径纹理缓存(跨材质去重, 同一贴图只解码/上传一次)+ 同批新纹理合并为单 cmd/单 submit/单 fence; Texture 抽出 RecordPixelsUpload/CreateDeferred/ReleasePendingStaging, 析构兜底释放 staging;MaterialAsset 纹理所有权移交 uploader 缓存—— 修大材质目录首次打开卡顿
171 lines
5.8 KiB
C++
171 lines
5.8 KiB
C++
//
|
||
// 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 <Engine/Assets/AssetManager.h>
|
||
#include <Engine/Core/Log.h>
|
||
#include <Engine/ECS/Components.h>
|
||
#include <Engine/Renderer/MaterialAsset.h>
|
||
#include <Engine/Renderer/Model.h>
|
||
|
||
#include <algorithm>
|
||
#include <cctype>
|
||
#include <filesystem>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace EditorMaterialUtil {
|
||
|
||
namespace fs = std::filesystem;
|
||
|
||
// .material 文件枚举(会话级缓存 + 脏标)。inline 变量 = 所有 TU
|
||
// 共享同一份(Create Override 新建文件后置脏重扫)
|
||
inline std::vector<std::string> s_MaterialFiles;
|
||
inline bool s_MaterialFilesDirty = true;
|
||
|
||
inline void InvalidateMaterialFileList() { s_MaterialFilesDirty = true; }
|
||
|
||
// 递归收集 assets/ 下纹理文件(材质编辑器纹理槽选择器;纹理文件
|
||
// 不被编辑器创建,无需脏标)。会话级缓存
|
||
inline const std::vector<std::string>& AssetTextureFiles()
|
||
{
|
||
static std::vector<std::string> 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<std::string>& 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/<model>_<mesh>.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)
|
||
{
|
||
// 目标文件名:<modelStem>_<meshName|idx>.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<unsigned char>(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
|