Files
HelloVulkan/App/editor/MaterialAssetUtil.h
T
atdunbg cc837bc605 Add animation controller system, material assets and skeleton visualization
- .animctrl state machine asset (params/transitions/blend trees, 1D+2D
  directional blending) with Unity Animator-style editor panel: state
  list, transitions, params, directional pad preview driving live
  CrossFade on the selected entity
- Split animation evaluation out of SceneRenderer into
  AnimationSystem/AnimationEvaluator; skin palettes with rest-pose
  fallback and blend-weight epsilon filtering
- MaterialAsset (.material) with reload-safe preview tile pool
  (DropMaterial clears dirty refs before instance destruction)
- EditorGizmoPass skeleton wires + viewport joint overlay; camera
  projection rework (baked Vulkan Y-flip, ortho support)
- Editor/game build split via HELLOVULKAN_EDITOR option
2026-10-07 01:50:04 +08:00

174 lines
6.1 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.
//
// 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 <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;
}
std::string ext = entry.path().extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c)
{ return std::tolower(c); });
if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" ||
ext == ".tga" || ext == ".bmp" || ext == ".hdr")
{
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