阶段性合并:动画重定向、碎裂共享库、第三人称玩法、编辑器面板与 vendored ufbx

- 动画:HumanoidRig 三层架构(standard 标准槽 / dialects 方言映射 / mapper 配对),
  指/趾统一走世界增量两遍式烘焙;AnimationAttach 拆为纯算法 + facade;修正 HumanoidBasis
  行走基朝向与跨骨架去重、根运动通道
- 物理:CharacterController 角色互碰撞 / shapeOffset / 接触事件;PhysicsWorld 接触合成与
  字符冲量判定
- 渲染与材质:Model 多材质按材质拆子 mesh 与贴图路径解析、DDS 直传、GLB 内嵌纹理落盘、
  不透明通道 alpha 固定 1.0、glTF FlipUVs 对齐;SceneSerializer 支持 meshIndex 与材质覆盖
- 脚本:FractureLib 抽为共享库(HostView 桥接)、WallFracture 参数直绑、新增
  ThirdPersonCamera / Follower / WASDMove 玩法;移除 GameDirector / PlayerController / Target
- 编辑器:新增 GamepadPanel、LogPanel(LogRing),改进 Inspector / 材质 / 统计 / 视口面板
- 依赖:vendored ufbx(FBX 5.x/6.x/7.x 全版本解析,替代 assimp 对 FBX 6.1 的拒读)
This commit is contained in:
2026-10-11 22:22:19 +08:00
parent 334819bf97
commit beba491bcb
57 changed files with 43200 additions and 1378 deletions
+39 -10
View File
@@ -198,6 +198,12 @@ namespace
for (u32 i = 0; i < meshes.size(); ++i)
{
// 「mesh」下拉的过滤:选了具体子 mesh 就只列该槽位(与渲染/
// 物理的子引用语义一致);All meshes(-1)= 全列
if (mr.meshIndex >= 0 && static_cast<int>(i) != mr.meshIndex)
{
continue;
}
ImGui::PushID(static_cast<int>(i));
const std::string meshName =
@@ -689,13 +695,15 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
ImGui::RadioButton("Plane", &shape, 1);
ImGui::SameLine();
ImGui::RadioButton("Trans.Plane", &shape, 2);
ImGui::SetNextItemWidth(-1.0f);
// 固定宽度而非 -1:弹窗是 AlwaysAutoResize,-1(撑满可用
// 宽度)会拿上一帧宽度当基准 → 逐帧膨胀直到撑满屏幕
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("half size", &half, 0.01f, 10.0f,
"%.2f");
ImGui::ColorEdit3("color", col);
if (shape == 2)
{
ImGui::SetNextItemWidth(-1.0f);
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("alpha", &alpha, 0.0f, 1.0f,
"%.2f");
}
@@ -1271,6 +1279,8 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
ImGui::DragFloat("Radius", &cc->radius, 0.02f, 0.05f, 5.0f);
ImGui::DragFloat("Half Height", &cc->capsuleHalfHeight, 0.02f, 0.0f, 5.0f);
ImGui::DragFloat("Mass", &cc->mass, 1.0f, 1.0f, 500.0f, "%.0f");
// 形状附加偏移:叠加在脚底约定之上(模型 pivot 对齐用)
ImGui::DragFloat3("Shape Offset", &cc->shapeOffset.x, 0.02f, -5.0f, 5.0f);
// ---- Movement:扫掠运动学参数 ----
ImGui::SeparatorText("Movement");
ImGui::DragFloat("Max Slope (deg)", &cc->maxSlopeAngle, 1.0f, 1.0f, 89.0f);
@@ -1901,11 +1911,31 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
// 警告跳过)----
ImGui::SeparatorText("Clips");
const auto& files = AssetModelFiles();
std::vector<const char*> opts;
opts.push_back("<attach animation file...>");
// 显示真名(AssetDisplayName:文件 stem,导入资产库的
// 哈希 "asset" 读同目录 pathname 取原始名);重名者补
// 父目录区分——不再把哈希路径直接摆给用户(= 显示成 id)
std::vector<std::string> labels;
labels.reserve(files.size());
for (const auto& f : files)
{
opts.push_back(f.c_str());
std::string label = Engine::Model::AssetDisplayName(f);
if (std::find(labels.begin(), labels.end(), label) !=
labels.end())
{
label += " (" +
std::filesystem::path(f)
.parent_path()
.filename()
.string() +
")";
}
labels.push_back(std::move(label));
}
std::vector<const char*> opts;
opts.push_back("<attach animation file...>");
for (const auto& l : labels)
{
opts.push_back(l.c_str());
}
int sel = 0;
ImGui::SetNextItemWidth(-1.0f);
@@ -1925,7 +1955,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
}
// attach 遥测(骨骼匹配结果,面板下方显示)
s_LastAttachFile =
std::filesystem::path(path).filename().string();
Engine::Model::AssetDisplayName(path);
s_LastAttachMatched =
res.channelsMatched + res.channelsRetargeted;
s_LastAttachRetargeted = res.channelsRetargeted;
@@ -1938,10 +1968,9 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
for (size_t i = 0; i < anim->attachedAnimPaths.size(); ++i)
{
const auto& p = anim->attachedAnimPaths[i];
ImGui::BulletText("%s", std::filesystem::path(p)
.filename()
.string()
.c_str());
ImGui::BulletText("%s",
Engine::Model::AssetDisplayName(p)
.c_str());
ImGui::SameLine();
ImGui::PushID(static_cast<int>(i));
const bool doDetach = ImGui::SmallButton("x");