阶段性合并:动画重定向、碎裂共享库、第三人称玩法、编辑器面板与 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
+152
View File
@@ -0,0 +1,152 @@
//
// LogPanel.cpp:运行时日志面板实现。
//
// 数据源 = Engine::Debug::LogRing(spdlog ring sink 落的整行)。
// 级别着色(trace 灰 / debug 青 / info 浅白 / warn 黄 / error 红 /
// fatal 亮红带背景条)+ 级别过滤 + 文本过滤 + Auto-scroll(滚动条
// 贴底才自动拉,上翻看历史不被拽回)。
//
#include "LogPanel.h"
#include <Engine/Core/LogRing.h>
#include <imgui.h>
#include <cctype>
#include <cstring>
#include <string>
void LogPanel::Draw(const EditorContext& ctx)
{
(void)ctx;
if (!ImGui::Begin("Log"))
{
ImGui::End();
return;
}
// ---- 工具栏:清空 / 自动滚动 / 文本过滤 / 级别过滤 ----
if (ImGui::Button("Clear"))
{
Engine::Debug::LogRing().Clear();
}
ImGui::SameLine();
ImGui::Checkbox("Auto-scroll", &m_AutoScroll);
ImGui::SameLine();
ImGui::SetNextItemWidth(160.0f);
ImGui::InputTextWithHint("##filter", "filter...", m_Filter, sizeof(m_Filter));
ImGui::SameLine();
ImGui::Checkbox("Trace", &m_ShowTrace);
ImGui::SameLine();
ImGui::Checkbox("Info", &m_ShowInfo);
ImGui::SameLine();
ImGui::Checkbox("Warn", &m_ShowWarn);
ImGui::SameLine();
ImGui::Checkbox("Error", &m_ShowError);
ImGui::Separator();
// ---- 版本感知:有新日志且滚动条贴底 → 拉底(上翻历史不拽回)----
const Engine::u64 ver = Engine::Debug::LogRing().Version();
bool scrollToBottom = false;
if (ver != m_LastVersion)
{
m_LastVersion = ver;
if (m_AutoScroll)
{
scrollToBottom = ImGui::GetScrollY() >=
ImGui::GetScrollMaxY() - 24.0f;
}
}
if (ImGui::BeginChild("loglist", {0.0f, 0.0f}, false,
ImGuiWindowFlags_HorizontalScrollbar))
{
const auto entries = Engine::Debug::LogRing().Snapshot();
for (const auto& e : entries)
{
using lv = spdlog::level::level_enum;
bool show = false;
switch (e.level)
{
case lv::trace:
case lv::debug: show = m_ShowTrace; break;
case lv::info: show = m_ShowInfo; break;
case lv::warn: show = m_ShowWarn; break;
case lv::err:
case lv::critical: show = m_ShowError; break;
default: show = m_ShowInfo; break;
}
if (!show) continue;
if (m_Filter[0] != '\0')
{
// 简单子串过滤(大小写不敏感)
std::string lower;
lower.reserve(e.msg.size());
for (char c : e.msg)
{
lower.push_back(static_cast<char>(
std::tolower(static_cast<unsigned char>(c))));
}
std::string needle;
needle.reserve(strlen(m_Filter));
for (const char* p = m_Filter; *p; ++p)
{
needle.push_back(static_cast<char>(
std::tolower(static_cast<unsigned char>(*p))));
}
if (lower.find(needle) == std::string::npos)
{
continue;
}
}
// ---- 渲染:级别徽标(恒彩色,固定列)+ 消息本体 ----
// 消息色只有 warn/error/fatal 染色(info 白是惯例),颜色
// 辨识主要靠徽标列——UE Output Log 的 Verbosity 列同位
ImVec4 tagCol, msgCol;
const char* tag = "INFO";
bool bg = false;
switch (e.level)
{
case lv::trace:
tag = "TRACE"; tagCol = {0.55f, 0.55f, 0.55f, 1.0f};
msgCol = {0.55f, 0.55f, 0.55f, 1.0f}; break;
case lv::debug:
tag = "DEBUG"; tagCol = {0.40f, 0.80f, 0.90f, 1.0f};
msgCol = {0.70f, 0.70f, 0.70f, 1.0f}; break;
case lv::info:
tag = "INFO"; tagCol = {0.45f, 0.72f, 0.95f, 1.0f};
msgCol = {0.90f, 0.90f, 0.90f, 1.0f}; break;
case lv::warn:
tag = "WARN"; tagCol = {1.00f, 0.78f, 0.20f, 1.0f};
msgCol = {1.00f, 0.78f, 0.20f, 1.0f}; break;
case lv::err:
tag = "ERROR"; tagCol = {1.00f, 0.35f, 0.35f, 1.0f};
msgCol = {1.00f, 0.35f, 0.35f, 1.0f}; break;
default:
tag = "FATAL"; tagCol = {1.00f, 0.15f, 0.15f, 1.0f};
msgCol = {1.00f, 0.15f, 0.15f, 1.0f};
bg = true; break;
}
if (bg)
{
// fatal:整行暗红背景条提级(先画背景再画文本)
const ImVec2 p0 = ImGui::GetCursorScreenPos();
const ImVec2 p1(p0.x + ImGui::GetContentRegionAvail().x,
p0.y + ImGui::GetTextLineHeight());
ImGui::GetWindowDrawList()->AddRectFilled(
p0, p1, IM_COL32(90, 10, 10, 160));
}
ImGui::TextColored(tagCol, "%s", tag);
ImGui::SameLine(40.0f); // 徽标固定列宽,消息对齐成两列
ImGui::TextColored(msgCol, "%s", e.msg.c_str());
}
if (scrollToBottom)
{
ImGui::SetScrollHereY(1.0f);
}
}
ImGui::EndChild();
ImGui::End();
}