资产系统侧线第一波:ResolveProcedural 提为公共静态(键格式唯一定义点);Inspector 程序化模型入口;修 Prefab hover 误导性 not loaded;Content Browser 模型拖拽 → Outliner 创建实体

This commit is contained in:
2026-10-08 23:36:08 +08:00
parent 2cf8d6e076
commit c43adc339d
6 changed files with 209 additions and 68 deletions
@@ -24,6 +24,7 @@
#define HELLOVULKAN_SCENESERIALIZER_H
#include <Engine/ECS/Scene.h> // Entity + glm vec3/quat(Prefab API 签名用)
#include <Engine/Core/AssetRef.h> // ModelRef(ResolveProcedural 返回值)
#include <string>
@@ -93,6 +94,13 @@ namespace Engine::Serialization {
const std::string& inPath,
const glm::vec3* overridePos = nullptr,
const glm::quat* overrideRot = nullptr);
// procedural:// 伪路径 → 参数反解重建(cube/plane/
// transparent-plane)。确定性键必然命中缓存;绝不走 assimp。
// 未知/格式坏伪路径返回无效句柄。场景加载与编辑器程序化
// 入口共用——键格式唯一定义点
static ModelRef ResolveProcedural(
Assets::AssetManager& assets, const std::string& path);
};
} // namespace Engine::Serialization
+66 -59
View File
@@ -179,71 +179,17 @@ namespace Engine::Serialization {
// procedural:// 伪路径 → 参数反解重建(确定性键必然命中缓存;
// 绝不对伪路径调 LoadModel——那会走 assimp 必炸)
ModelRef ResolveProcedural(Assets::AssetManager& assets,
const std::string& path)
{
constexpr std::string_view kPlane = "procedural://plane/";
constexpr std::string_view kTPlane =
"procedural://transparent-plane/";
constexpr std::string_view kCube = "procedural://cube/";
if (path.rfind(kCube, 0) == 0) {
// "{halfSize}/{r},{g},{b}"
const std::string rest = path.substr(kCube.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[3];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreateCube(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]));
}
catch (const std::exception&) { return {}; }
}
if (path.rfind(kTPlane, 0) == 0) {
// "{halfSize}/{r},{g},{b},{a}"
const std::string rest = path.substr(kTPlane.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[4];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreateTransparentPlane(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]), static_cast<u8>(c[3]));
}
catch (const std::exception&) { return {}; }
}
if (path.rfind(kPlane, 0) == 0) {
// "{halfSize}/{r},{g},{b}"
const std::string rest = path.substr(kPlane.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[3];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreatePlane(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]));
}
catch (const std::exception&) { return {}; }
}
return {}; // 未知伪路径
}
// 实现见 SceneSerializer::ResolveProcedural(公共静态,编辑器
// 程序化入口共用)
// 模型引用解析降级链:procedural 反解 → GUID 主键 → Path 兜底。
// 加载成功后 GetOrCreateGUID 回写注册表(注册表丢档自愈)
ModelRef ResolveModel(Assets::AssetManager& assets, u64 guid,
const std::string& path)
{
if (ModelRef ref = ResolveProcedural(assets, path); ref.Valid()) {
if (ModelRef ref =
SceneSerializer::ResolveProcedural(assets, path);
ref.Valid()) {
return ref;
}
@@ -1245,6 +1191,67 @@ namespace Engine::Serialization {
}
}
// procedural:// 伪路径 → 参数反解重建(键格式唯一定义点;
// 绝不对伪路径调 LoadModel——那会走 assimp 必炸)
ModelRef SceneSerializer::ResolveProcedural(
Assets::AssetManager& assets, const std::string& path)
{
constexpr std::string_view kPlane = "procedural://plane/";
constexpr std::string_view kTPlane =
"procedural://transparent-plane/";
constexpr std::string_view kCube = "procedural://cube/";
if (path.rfind(kCube, 0) == 0) {
// "{halfSize}/{r},{g},{b}"
const std::string rest = path.substr(kCube.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[3];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreateCube(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]));
}
catch (const std::exception&) { return {}; }
}
if (path.rfind(kTPlane, 0) == 0) {
// "{halfSize}/{r},{g},{b},{a}"
const std::string rest = path.substr(kTPlane.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[4];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreateTransparentPlane(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]), static_cast<u8>(c[3]));
}
catch (const std::exception&) { return {}; }
}
if (path.rfind(kPlane, 0) == 0) {
// "{halfSize}/{r},{g},{b}"
const std::string rest = path.substr(kPlane.size());
const auto slash = rest.find('/');
if (slash == std::string::npos) return {};
int c[3];
if (!ParseCsvInts(rest.substr(slash + 1), c)) return {};
try {
return assets.CreatePlane(
std::stof(rest.substr(0, slash)),
static_cast<u8>(c[0]), static_cast<u8>(c[1]),
static_cast<u8>(c[2]));
}
catch (const std::exception&) { return {}; }
}
return {}; // 未知伪路径
}
ECS::Entity SceneSerializer::InstantiatePrefab(
ECS::Scene& scene, Assets::AssetManager& assets,
const std::string& inPath,