// // StatsPanel.cpp:遥测与全局设置实现(原 main.cpp Scene Debug 搬迁)。 // 面板按 View / Scene / Rendering / Physics / Performance 分组—— // 环境贴图/环境光已实体化(Skybox/SkyLight 组件,Details 配置), // 此处只留场景级 ClearColor。 // #include "StatsPanel.h" #include "Engine/ECS/Components.h" #include "Engine/Physics/PhysicsWorld.h" #include "Engine/Core/Time.h" #include #include using namespace Engine; // f32(Engine/Core/Types.h) void StatsPanel::Draw(const EditorContext& ctx) { ImGui::Begin("Stats"); auto& renderer = *ctx.renderer; auto& app = *ctx.app; // ---- View:看哪里(渲染目标)+ 相机位姿(跟随渲染源分派)---- if (ImGui::CollapsingHeader("View", ImGuiTreeNodeFlags_DefaultOpen)) { // 渲染源分派与 SceneRenderer::BuildFrame 同规则:PIE 内嵌播放 // 或 kDisplayDirect 全屏预览 = primary 相机,否则编辑器相机 const bool playing = ctx.playState && *ctx.playState; static const char* displayTarget[] = { "Fullscreen", "Viewport", }; int mode = renderer.GetDisplayMode(); if (ImGui::Combo("Display Target", &mode, displayTarget, IM_ARRAYSIZE(displayTarget))) { renderer.SetDisplayMode(mode); } if ((playing || renderer.GetDisplayMode() == kDisplayDirect)) { Engine::ECS::TransformComponent* camT = nullptr; if (ctx.scene->GetPrimaryCamera(&camT) != nullptr && camT != nullptr) { // world 位姿(相机实体可能挂父层级下) const glm::vec3& pos = camT->GetWorldPosition(); ImGui::Text("Game Camera Pos"); ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z); } else { ImGui::TextDisabled("(no primary camera)"); } } else if (ctx.editorCamera) { const glm::vec3& pos = ctx.editorCamera->GetTransform().GetPosition(); ImGui::Text("Editor Camera Pos"); ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z); } else { ImGui::TextDisabled("(no camera)"); } } // ---- Scene:模拟控制 + 场景级设置。PIE 暂停在主工具栏(UE 语义)---- if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::ColorEdit4("Clear Color", glm::value_ptr(ctx.scene->GetSettings().ClearColor)); } // ---- Rendering:渲染管线参数与开关 ---- if (ImGui::CollapsingHeader("Rendering", ImGuiTreeNodeFlags_DefaultOpen)) { f32 exposure = renderer.GetExposure(); if (ImGui::SliderFloat("Exposure", &exposure, 0.1f, 4.0f)) { renderer.SetExposure(exposure); } bool aces = renderer.GetUseACES(); if (ImGui::Checkbox("ACES", &aces)) { renderer.SetUseACES(aces); } // Bloom:开关即显隐——勾上才显示参数 bool bloom = renderer.GetBloomEnabled(); if (ImGui::Checkbox("Bloom", &bloom)) { renderer.SetBloomEnabled(bloom); } if (bloom) { f32 threshold = renderer.GetBloomThreshold(); if (ImGui::SliderFloat("Threshold", &threshold, 0.0f, 8.0f, "%.1f", ImGuiSliderFlags_Logarithmic)) { renderer.SetBloomThreshold(threshold); } f32 strength = renderer.GetBloomStrength(); if (ImGui::SliderFloat("Strength", &strength, 0.0f, 2.0f)) { renderer.SetBloomStrength(strength); } } // 无参数的纯开关:横排平铺 bool shadows = renderer.GetShadowsEnabled(); if (ImGui::Checkbox("Shadows", &shadows)) { renderer.SetShadowsEnabled(shadows); } ImGui::SameLine(); bool taa = renderer.GetTAAEnabled(); if (ImGui::Checkbox("TAA", &taa)) { renderer.SetTAAEnabled(taa); } ImGui::SameLine(); bool lod = renderer.GetLodEnabled(); if (ImGui::Checkbox("LOD", &lod)) { renderer.SetLodEnabled(lod); // 关 = 恒绘原网格 } // 调试视图:单看某个 pass 的中间输出(类 UE pass debug) const auto& viewNames = renderer.GetDebugViewNames(); int view = renderer.GetDebugView(); if (ImGui::Combo("Debug View", &view, viewNames.data(), static_cast(viewNames.size()))) { renderer.SetDebugView(view); } } // ---- Physics:调试可视化(collider/约束/重力/速度/加速度)---- if (ImGui::CollapsingHeader("Physics")) { auto& physics = app.GetPhysics(); u32 bits = physics.GetDebugDrawBits(); bool master = bits != 0; if (ImGui::Checkbox("Debug Draw##Phys", &master)) { bits = master ? kPhyDbgDefault : 0; } if (master) { ImGui::Indent(); auto toggle = [&bits](const char* label, u32 bit) { bool on = (bits & bit) != 0; if (ImGui::Checkbox(label, &on)) { bits = on ? (bits | bit) : (bits & ~bit); } }; toggle("Colliders", kPhyDbgColliders); toggle("Constraints", kPhyDbgConstraints); toggle("Gravity", kPhyDbgGravity); toggle("Velocity", kPhyDbgVelocity); toggle("Acceleration", kPhyDbgAccel); ImGui::Unindent(); } physics.SetDebugDrawBits(bits); // 碰撞事件计数(最近一次步进;掉落物触地时应 > 0) ImGui::Text("contacts (frame): %zu", physics.GetEvents().size()); } // ---- Performance:帧率控制 + GPU 遥测(默认折叠,不占面板高度)---- if (ImGui::CollapsingHeader("Performance")) { bool vsync = app.GetVSync(); if (ImGui::Checkbox("VSync", &vsync)) { app.SetVSync(vsync); if (!vsync) { const f32 target = app.GetTargetFps(); app.SetTargetFps(target); } } if (!vsync) { f32 target = app.GetTargetFps(); if (ImGui::SliderFloat("Target FPS", &target, 24.0f, 360.0f, "%.0f")) { app.SetTargetFps(target); } } ImGui::Text("fps: %.1f frame: %llu", 1.0f / Time::Current().UnscaledDt(), static_cast(app.GetFrameCount())); ImGui::Separator(); ImGui::Text("instances: %u main / %u shadow", renderer.GetMainInstanceCount(), renderer.GetShadowInstanceCount()); if (renderer.GetInstancesTruncated()) { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "TRUNCATED: instance buffer full!"); } const auto& names = renderer.GetPassNames(); const auto& times = renderer.GetPassGpuMs(); for (size_t i = 0; i < names.size() && i < times.size(); ++i) { ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]); } } ImGui::End(); }