Files
HelloVulkan/App/editor/StatsPanel.cpp
T
atdunbg 5ca5ac2a1e Editor: group Details parameters and reorganize Stats panel
Details: multi-parameter components (RigidBody/Joint/CharacterController/
Animation) now use SeparatorText category headers in UE Details style;
all parameter labels normalized to Pascal Case.

Stats: rearranged into View / Scene / Rendering / Physics / Performance
collapsing groups. Clear Color moved to Scene, Debug View to Rendering,
GPU Timings + Frame merged into Performance; bare top-level widgets and
the now-obsolete Environment group removed.
2026-10-05 01:20:00 +08:00

213 lines
7.3 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.
//
// 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 <imgui.h>
#include <glm/gtc/type_ptr.hpp>
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))
{
static const char* displayTarget[] = {
"Fullscreen",
"Viewport",
};
int mode = renderer.GetDisplayMode();
if (ImGui::Combo("Display Target", &mode, displayTarget,
IM_ARRAYSIZE(displayTarget)))
{
renderer.SetDisplayMode(mode);
}
if (renderer.GetDisplayMode() == kDisplayViewport && 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
{
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)");
}
}
}
// ---- Scene:模拟控制 + 场景级设置 ----
if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen))
{
// 暂停模拟(引擎 Time 门面:动画/物理统一冻结)
bool paused = Time::TimeScale() == 0.0f;
if (ImGui::Checkbox("Pause Simulation", &paused))
{
Time::SetTimeScale(paused ? 0.0f : 1.0f);
}
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, 2.0f))
{
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<int>(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<unsigned long long>(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();
}