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.
This commit is contained in:
@@ -263,7 +263,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
glm::vec3 pos = t->GetPosition();
|
glm::vec3 pos = t->GetPosition();
|
||||||
if (DragVec3Row("position", pos, 0.05f))
|
if (DragVec3Row("Position", pos, 0.05f))
|
||||||
{
|
{
|
||||||
t->SetPosition(pos);
|
t->SetPosition(pos);
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
glm::vec3 rot = s_RotMirror;
|
glm::vec3 rot = s_RotMirror;
|
||||||
// "%.1f" = UE Details 默认显示精度。mirror 值直显(无 wrap、
|
// "%.1f" = UE Details 默认显示精度。mirror 值直显(无 wrap、
|
||||||
// 无限累计);Ctrl+Click 手输全精度写入
|
// 无限累计);Ctrl+Click 手输全精度写入
|
||||||
if (DragVec3Row("rotation", rot, 0.5f, -3.4e38f, 3.4e38f,
|
if (DragVec3Row("Rotation", rot, 0.5f, -3.4e38f, 3.4e38f,
|
||||||
"%.1f", &editingRot))
|
"%.1f", &editingRot))
|
||||||
{
|
{
|
||||||
t->SetRotation(rot);
|
t->SetRotation(rot);
|
||||||
@@ -323,7 +323,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
}
|
}
|
||||||
s_RotEditingLast = editingRot;
|
s_RotEditingLast = editingRot;
|
||||||
glm::vec3 scl = t->GetScale();
|
glm::vec3 scl = t->GetScale();
|
||||||
if (DragVec3Row("scale", scl, 0.02f, 0.001f, 100.0f))
|
if (DragVec3Row("Scale", scl, 0.02f, 0.001f, 100.0f))
|
||||||
{
|
{
|
||||||
t->SetScale(scl);
|
t->SetScale(scl);
|
||||||
}
|
}
|
||||||
@@ -426,20 +426,20 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
ImGui::BeginDisabled(true);
|
ImGui::BeginDisabled(true);
|
||||||
}
|
}
|
||||||
int type = static_cast<int>(light->type);
|
int type = static_cast<int>(light->type);
|
||||||
if (ImGui::Combo("type", &type, "Directional\0Point\0"))
|
if (ImGui::Combo("Type", &type, "Directional\0Point\0"))
|
||||||
{
|
{
|
||||||
light->type = static_cast<Engine::ECS::LightComponent::Type>(type);
|
light->type = static_cast<Engine::ECS::LightComponent::Type>(type);
|
||||||
}
|
}
|
||||||
ImGui::ColorEdit3("color", &light->color.x);
|
ImGui::ColorEdit3("Color", &light->color.x);
|
||||||
ImGui::DragFloat("intensity", &light->color.a, 0.05f, 0.0f, 100.0f);
|
ImGui::DragFloat("Intensity", &light->color.a, 0.05f, 0.0f, 100.0f);
|
||||||
if (light->type == Engine::ECS::LightComponent::Type::Directional)
|
if (light->type == Engine::ECS::LightComponent::Type::Directional)
|
||||||
{
|
{
|
||||||
ImGui::SliderFloat3("direction", &light->direction.x, -1.0f, 1.0f);
|
ImGui::SliderFloat3("Direction", &light->direction.x, -1.0f, 1.0f);
|
||||||
ImGui::Checkbox("castShadow", &light->castShadow);
|
ImGui::Checkbox("Cast Shadows", &light->castShadow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("radius", &light->radius, 0.1f, 0.0f, 100.0f);
|
ImGui::DragFloat("Radius", &light->radius, 0.1f, 0.0f, 100.0f);
|
||||||
ImGui::TextDisabled("(position = entity Transform)");
|
ImGui::TextDisabled("(position = entity Transform)");
|
||||||
}
|
}
|
||||||
if (!light->active)
|
if (!light->active)
|
||||||
@@ -513,7 +513,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
{
|
{
|
||||||
ImGui::BeginDisabled(true);
|
ImGui::BeginDisabled(true);
|
||||||
}
|
}
|
||||||
ImGui::ColorEdit4("ambient", &sl->ambientColor.x);
|
ImGui::ColorEdit4("Ambient", &sl->ambientColor.x);
|
||||||
if (!sl->active)
|
if (!sl->active)
|
||||||
{
|
{
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
@@ -535,7 +535,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
{
|
{
|
||||||
ImGui::BeginDisabled(true);
|
ImGui::BeginDisabled(true);
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox("primary", &cam->primary) && cam->primary)
|
if (ImGui::Checkbox("Primary", &cam->primary) && cam->primary)
|
||||||
{
|
{
|
||||||
// primary 互斥:启用本相机时清掉其余实体的 primary
|
// primary 互斥:启用本相机时清掉其余实体的 primary
|
||||||
//(引擎取第一个 primary 作为游戏形态渲染视角)
|
//(引擎取第一个 primary 作为游戏形态渲染视角)
|
||||||
@@ -590,42 +590,46 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
"! conflicts with Character Controller "
|
"! conflicts with Character Controller "
|
||||||
"(disable or remove one)");
|
"(disable or remove one)");
|
||||||
}
|
}
|
||||||
|
// ---- Body:类型与碰撞体选择(变更 → PhysicsWorld 重建 body)----
|
||||||
|
ImGui::SeparatorText("Body");
|
||||||
int type = static_cast<int>(rb->type);
|
int type = static_cast<int>(rb->type);
|
||||||
if (ImGui::Combo("type", &type, "Static\0Dynamic\0Kinematic\0"))
|
if (ImGui::Combo("Type", &type, "Static\0Dynamic\0Kinematic\0"))
|
||||||
{
|
{
|
||||||
rb->type = static_cast<Engine::ECS::RigidBodyComponent::BodyType>(type);
|
rb->type = static_cast<Engine::ECS::RigidBodyComponent::BodyType>(type);
|
||||||
}
|
}
|
||||||
int collider = static_cast<int>(rb->collider);
|
int collider = static_cast<int>(rb->collider);
|
||||||
if (ImGui::Combo("collider", &collider,
|
if (ImGui::Combo("Collider", &collider,
|
||||||
"Box\0Sphere\0Mesh\0Capsule\0Cylinder\0Compound\0"))
|
"Box\0Sphere\0Mesh\0Capsule\0Cylinder\0Compound\0"))
|
||||||
{
|
{
|
||||||
rb->collider = static_cast<Engine::ECS::RigidBodyComponent::ColliderType>(collider);
|
rb->collider = static_cast<Engine::ECS::RigidBodyComponent::ColliderType>(collider);
|
||||||
}
|
}
|
||||||
|
// ---- Shape:碰撞体尺寸(随类型切换;× Transform 缩放烘焙)----
|
||||||
|
ImGui::SeparatorText("Shape");
|
||||||
if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Box)
|
if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Box)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat3("halfExtents", &rb->boxHalfExtents.x, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat3("Half Extents", &rb->boxHalfExtents.x, 0.05f, 0.01f, 500.0f);
|
||||||
}
|
}
|
||||||
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Sphere)
|
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Sphere)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("radius", &rb->sphereRadius, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat("Radius", &rb->sphereRadius, 0.05f, 0.01f, 500.0f);
|
||||||
}
|
}
|
||||||
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Capsule)
|
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Capsule)
|
||||||
{
|
{
|
||||||
// 半高 = 圆柱段半高(不含半球端盖),总高 = 2*(halfHeight + radius)
|
// 半高 = 圆柱段半高(不含半球端盖),总高 = 2*(halfHeight + radius)
|
||||||
ImGui::DragFloat("radius", &rb->capsuleRadius, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat("Radius", &rb->capsuleRadius, 0.05f, 0.01f, 500.0f);
|
||||||
ImGui::DragFloat("halfHeight", &rb->capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
|
ImGui::DragFloat("Half Height", &rb->capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
|
||||||
}
|
}
|
||||||
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Cylinder)
|
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Cylinder)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("radius", &rb->cylinderRadius, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat("Radius", &rb->cylinderRadius, 0.05f, 0.01f, 500.0f);
|
||||||
ImGui::DragFloat("halfHeight", &rb->cylinderHalfHeight, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat("Half Height", &rb->cylinderHalfHeight, 0.05f, 0.01f, 500.0f);
|
||||||
}
|
}
|
||||||
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Compound)
|
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Compound)
|
||||||
{
|
{
|
||||||
// 子形状列表:尺寸/位置变更 → PhysicsWorld 重建 body(既有模式)
|
// 子形状列表:尺寸/位置变更 → PhysicsWorld 重建 body(既有模式)
|
||||||
ImGui::Text("children: %d", static_cast<int>(rb->compound.size()));
|
ImGui::Text("Children: %d", static_cast<int>(rb->compound.size()));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("+ add"))
|
if (ImGui::Button("+ Add"))
|
||||||
{
|
{
|
||||||
rb->compound.emplace_back();
|
rb->compound.emplace_back();
|
||||||
}
|
}
|
||||||
@@ -636,24 +640,24 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
ImGui::PushID(i);
|
ImGui::PushID(i);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
int sh = static_cast<int>(c.shape);
|
int sh = static_cast<int>(c.shape);
|
||||||
if (ImGui::Combo("shape", &sh, "Box\0Sphere\0Capsule\0"))
|
if (ImGui::Combo("Shape", &sh, "Box\0Sphere\0Capsule\0"))
|
||||||
{
|
{
|
||||||
c.shape = static_cast<Engine::ECS::RigidBodyComponent::CompoundChild::Shape>(sh);
|
c.shape = static_cast<Engine::ECS::RigidBodyComponent::CompoundChild::Shape>(sh);
|
||||||
}
|
}
|
||||||
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Box)
|
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Box)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat3("halfExtents", &c.boxHalfExtents.x, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat3("Half Extents", &c.boxHalfExtents.x, 0.05f, 0.01f, 500.0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("radius", &c.radius, 0.05f, 0.01f, 500.0f);
|
ImGui::DragFloat("Radius", &c.radius, 0.05f, 0.01f, 500.0f);
|
||||||
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Capsule)
|
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Capsule)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("halfHeight", &c.capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
|
ImGui::DragFloat("Half Height", &c.capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::DragFloat3("offset", &c.position.x, 0.05f);
|
ImGui::DragFloat3("Offset", &c.position.x, 0.05f);
|
||||||
if (ImGui::Button("remove"))
|
if (ImGui::Button("Remove"))
|
||||||
{
|
{
|
||||||
remove = i;
|
remove = i;
|
||||||
}
|
}
|
||||||
@@ -670,20 +674,22 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
// Dynamic=凸包近似),无尺寸可调
|
// Dynamic=凸包近似),无尺寸可调
|
||||||
ImGui::TextDisabled("follows MeshRenderer geometry");
|
ImGui::TextDisabled("follows MeshRenderer geometry");
|
||||||
}
|
}
|
||||||
ImGui::DragFloat("mass", &rb->mass, 0.1f, 0.1f, 1000.0f, "%.1f");
|
// ---- Simulation:动态体物理参数 ----
|
||||||
ImGui::SliderFloat("gravityScale", &rb->gravityScale, -1.0f, 5.0f);
|
ImGui::SeparatorText("Simulation");
|
||||||
ImGui::SliderFloat("friction", &rb->friction, 0.0f, 2.0f);
|
ImGui::DragFloat("Mass", &rb->mass, 0.1f, 0.1f, 1000.0f, "%.1f");
|
||||||
ImGui::SliderFloat("restitution", &rb->restitution, 0.0f, 1.0f);
|
ImGui::SliderFloat("Gravity Scale", &rb->gravityScale, -1.0f, 5.0f);
|
||||||
ImGui::Checkbox("isTrigger", &rb->isTrigger);
|
ImGui::SliderFloat("Friction", &rb->friction, 0.0f, 2.0f);
|
||||||
|
ImGui::SliderFloat("Restitution", &rb->restitution, 0.0f, 1.0f);
|
||||||
|
ImGui::Checkbox("Is Trigger", &rb->isTrigger);
|
||||||
|
// ---- Debug:遥测与驱动调试(冲量瞬态不序列化)----
|
||||||
if (rb->bodyValid &&
|
if (rb->bodyValid &&
|
||||||
rb->type == Engine::ECS::RigidBodyComponent::BodyType::Dynamic)
|
rb->type == Engine::ECS::RigidBodyComponent::BodyType::Dynamic)
|
||||||
{
|
{
|
||||||
|
ImGui::SeparatorText("Debug");
|
||||||
ImGui::Text("vel: %.2f, %.2f, %.2f m/s",
|
ImGui::Text("vel: %.2f, %.2f, %.2f m/s",
|
||||||
rb->velocity.x, rb->velocity.y, rb->velocity.z);
|
rb->velocity.x, rb->velocity.y, rb->velocity.z);
|
||||||
|
|
||||||
// 驱动调试(冲量瞬态不序列化):直接推一把看响应
|
|
||||||
static glm::vec3 s_debugImpulse{0.0f, 5.0f, 0.0f};
|
static glm::vec3 s_debugImpulse{0.0f, 5.0f, 0.0f};
|
||||||
ImGui::DragFloat3("impulse", &s_debugImpulse.x, 0.1f);
|
ImGui::DragFloat3("Impulse", &s_debugImpulse.x, 0.1f);
|
||||||
if (ImGui::Button("Apply Impulse"))
|
if (ImGui::Button("Apply Impulse"))
|
||||||
{
|
{
|
||||||
ctx.app->GetPhysics().AddImpulse(
|
ctx.app->GetPhysics().AddImpulse(
|
||||||
@@ -718,8 +724,10 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
||||||
"! requires a RigidBody on this entity");
|
"! requires a RigidBody on this entity");
|
||||||
}
|
}
|
||||||
|
// ---- Constraint:类型与目标体 ----
|
||||||
|
ImGui::SeparatorText("Constraint");
|
||||||
int type = static_cast<int>(jc->type);
|
int type = static_cast<int>(jc->type);
|
||||||
if (ImGui::Combo("type", &type, "Fixed\0Hinge\0Ball\0"))
|
if (ImGui::Combo("Type", &type, "Fixed\0Hinge\0Ball\0"))
|
||||||
{
|
{
|
||||||
jc->type = static_cast<Engine::ECS::JointComponent::Type>(type);
|
jc->type = static_cast<Engine::ECS::JointComponent::Type>(type);
|
||||||
}
|
}
|
||||||
@@ -745,21 +753,25 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
items.push_back('\0');
|
items.push_back('\0');
|
||||||
});
|
});
|
||||||
items.push_back('\0'); // ImGui Combo 双 \0 结尾
|
items.push_back('\0'); // ImGui Combo 双 \0 结尾
|
||||||
if (ImGui::Combo("target", ¤t, items.c_str()) && current >= 0)
|
if (ImGui::Combo("Target", ¤t, items.c_str()) && current >= 0)
|
||||||
{
|
{
|
||||||
jc->target = uuids[static_cast<size_t>(current)];
|
jc->target = uuids[static_cast<size_t>(current)];
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::DragFloat3("anchor A", &jc->anchorA.x, 0.05f);
|
// ---- Anchors:各体局部系锚点 ----
|
||||||
ImGui::DragFloat3("anchor B", &jc->anchorB.x, 0.05f);
|
ImGui::SeparatorText("Anchors");
|
||||||
|
ImGui::DragFloat3("Anchor A", &jc->anchorA.x, 0.05f);
|
||||||
|
ImGui::DragFloat3("Anchor B", &jc->anchorB.x, 0.05f);
|
||||||
|
// ---- Hinge:转轴与角度限位(度)----
|
||||||
if (jc->type == Engine::ECS::JointComponent::Type::Hinge)
|
if (jc->type == Engine::ECS::JointComponent::Type::Hinge)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat3("hinge axis", &jc->hingeAxis.x, 0.05f);
|
ImGui::SeparatorText("Hinge");
|
||||||
ImGui::Checkbox("enable limit", &jc->enableLimit);
|
ImGui::DragFloat3("Hinge Axis", &jc->hingeAxis.x, 0.05f);
|
||||||
|
ImGui::Checkbox("Enable Limit", &jc->enableLimit);
|
||||||
if (jc->enableLimit)
|
if (jc->enableLimit)
|
||||||
{
|
{
|
||||||
ImGui::DragFloat("min deg", &jc->limitMin, 1.0f, -180.0f, 0.0f);
|
ImGui::DragFloat("Limit Min", &jc->limitMin, 1.0f, -180.0f, 0.0f);
|
||||||
ImGui::DragFloat("max deg", &jc->limitMax, 1.0f, 0.0f, 180.0f);
|
ImGui::DragFloat("Limit Max", &jc->limitMax, 1.0f, 0.0f, 180.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!jc->active)
|
if (!jc->active)
|
||||||
@@ -790,16 +802,20 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
"! conflicts with RigidBody "
|
"! conflicts with RigidBody "
|
||||||
"(disable or remove one)");
|
"(disable or remove one)");
|
||||||
}
|
}
|
||||||
// 实体原点 = 脚底(胶囊底端),总高 = 2*(halfHeight + radius)
|
// ---- Body:胶囊体(实体原点 = 脚底,总高 = 2*(halfHeight + radius))----
|
||||||
ImGui::DragFloat("radius", &cc->radius, 0.02f, 0.05f, 5.0f);
|
ImGui::SeparatorText("Body");
|
||||||
ImGui::DragFloat("halfHeight", &cc->capsuleHalfHeight, 0.02f, 0.0f, 5.0f);
|
ImGui::DragFloat("Radius", &cc->radius, 0.02f, 0.05f, 5.0f);
|
||||||
ImGui::DragFloat("mass", &cc->mass, 1.0f, 1.0f, 500.0f, "%.0f");
|
ImGui::DragFloat("Half Height", &cc->capsuleHalfHeight, 0.02f, 0.0f, 5.0f);
|
||||||
ImGui::DragFloat("max slope deg", &cc->maxSlopeAngle, 1.0f, 1.0f, 89.0f);
|
ImGui::DragFloat("Mass", &cc->mass, 1.0f, 1.0f, 500.0f, "%.0f");
|
||||||
ImGui::DragFloat("step height", &cc->stepHeight, 0.02f, 0.0f, 2.0f);
|
// ---- Movement:扫掠运动学参数 ----
|
||||||
|
ImGui::SeparatorText("Movement");
|
||||||
|
ImGui::DragFloat("Max Slope (deg)", &cc->maxSlopeAngle, 1.0f, 1.0f, 89.0f);
|
||||||
|
ImGui::DragFloat("Step Height", &cc->stepHeight, 0.02f, 0.0f, 2.0f);
|
||||||
|
|
||||||
// 遥测(只读):运行时状态在 PhysicsWorld 的 CharacterController
|
// ---- Debug:遥测(只读,运行时状态在 PhysicsWorld)----
|
||||||
if (auto& phy = ctx.app->GetPhysics(); phy.IsEnabled())
|
if (auto& phy = ctx.app->GetPhysics(); phy.IsEnabled())
|
||||||
{
|
{
|
||||||
|
ImGui::SeparatorText("Debug");
|
||||||
const uint32_t bits =
|
const uint32_t bits =
|
||||||
static_cast<uint32_t>(entt::to_integral(selected));
|
static_cast<uint32_t>(entt::to_integral(selected));
|
||||||
ImGui::Text("grounded: %s",
|
ImGui::Text("grounded: %s",
|
||||||
@@ -980,10 +996,16 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f),
|
||||||
"! requires a MeshRenderer component");
|
"! requires a MeshRenderer component");
|
||||||
}
|
}
|
||||||
else if (model != nullptr)
|
else if (model == nullptr)
|
||||||
{
|
{
|
||||||
// 动画资产槽:列全部模型容器文件(无法静态区分"模型
|
ImGui::TextDisabled("(no model assigned)");
|
||||||
// fbx"与"纯动画 fbx",一并列出,无 clip 的会警告跳过)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ---- Clips:动画资产槽(列全部模型容器文件——无法静态
|
||||||
|
// 区分"模型 fbx"与"纯动画 fbx",一并列出,无 clip 的会
|
||||||
|
// 警告跳过)----
|
||||||
|
ImGui::SeparatorText("Clips");
|
||||||
const auto& files = AssetModelFiles();
|
const auto& files = AssetModelFiles();
|
||||||
std::vector<const char*> opts;
|
std::vector<const char*> opts;
|
||||||
opts.push_back("<attach animation file...>");
|
opts.push_back("<attach animation file...>");
|
||||||
@@ -1016,9 +1038,15 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
.string()
|
.string()
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
if (!model->HasAnimations())
|
||||||
|
{
|
||||||
|
ImGui::TextDisabled("(no animation clips on model)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (model != nullptr && model->HasAnimations())
|
if (model != nullptr && model->HasAnimations())
|
||||||
{
|
{
|
||||||
|
// ---- Playback:播放控制(切换 clip 触发 crossfade)----
|
||||||
|
ImGui::SeparatorText("Playback");
|
||||||
const auto& clips = model->GetClips();
|
const auto& clips = model->GetClips();
|
||||||
std::vector<const char*> clipNames;
|
std::vector<const char*> clipNames;
|
||||||
clipNames.reserve(clips.size());
|
clipNames.reserve(clips.size());
|
||||||
@@ -1027,16 +1055,16 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
clipNames.push_back(c.name.c_str());
|
clipNames.push_back(c.name.c_str());
|
||||||
}
|
}
|
||||||
int cur = anim->clipIndex;
|
int cur = anim->clipIndex;
|
||||||
if (ImGui::Combo("clip", &cur, clipNames.data(),
|
if (ImGui::Combo("Clip", &cur, clipNames.data(),
|
||||||
static_cast<int>(clipNames.size())))
|
static_cast<int>(clipNames.size())))
|
||||||
{
|
{
|
||||||
anim->Play(cur); // 触发 crossfade(fade 秒)
|
anim->Play(cur); // 触发 crossfade(fade 秒)
|
||||||
}
|
}
|
||||||
ImGui::Checkbox("playing", &anim->playing);
|
ImGui::Checkbox("Playing", &anim->playing);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Checkbox("loop", &anim->loop);
|
ImGui::Checkbox("Loop", &anim->loop);
|
||||||
ImGui::SliderFloat("speed", &anim->speed, -2.0f, 2.0f);
|
ImGui::SliderFloat("Speed", &anim->speed, -2.0f, 2.0f);
|
||||||
ImGui::SliderFloat("fade", &anim->fadeDuration, 0.0f, 1.0f);
|
ImGui::SliderFloat("Fade", &anim->fadeDuration, 0.0f, 1.0f);
|
||||||
if (anim->clipIndex >= 0 &&
|
if (anim->clipIndex >= 0 &&
|
||||||
anim->clipIndex < static_cast<int>(clips.size()))
|
anim->clipIndex < static_cast<int>(clips.size()))
|
||||||
{
|
{
|
||||||
@@ -1046,7 +1074,7 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
// 动画数据表:每个 clip 的时长与有效轨道数(attach 进来的
|
// 动画数据表:每个 clip 的时长与有效轨道数(attach 进来的
|
||||||
// 外部动画在此核对骨骼匹配结果——轨道数明显偏少 = 骨骼
|
// 外部动画在此核对骨骼匹配结果——轨道数明显偏少 = 骨骼
|
||||||
// 命名不同源被跳过)
|
// 命名不同源被跳过)
|
||||||
if (ImGui::TreeNode("clip data"))
|
if (ImGui::TreeNode("Clip Data"))
|
||||||
{
|
{
|
||||||
ImGui::Columns(3, nullptr, false);
|
ImGui::Columns(3, nullptr, false);
|
||||||
ImGui::SetColumnWidth(0, 150.0f);
|
ImGui::SetColumnWidth(0, 150.0f);
|
||||||
@@ -1083,10 +1111,6 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
|||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui::TextDisabled("(no animation clips on model)");
|
|
||||||
}
|
|
||||||
if (!anim->active)
|
if (!anim->active)
|
||||||
{
|
{
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|||||||
+54
-60
@@ -1,5 +1,8 @@
|
|||||||
//
|
//
|
||||||
// StatsPanel.cpp:遥测与全局设置实现(原 main.cpp Scene Debug 搬迁)。
|
// StatsPanel.cpp:遥测与全局设置实现(原 main.cpp Scene Debug 搬迁)。
|
||||||
|
// 面板按 View / Scene / Rendering / Physics / Performance 分组——
|
||||||
|
// 环境贴图/环境光已实体化(Skybox/SkyLight 组件,Details 配置),
|
||||||
|
// 此处只留场景级 ClearColor。
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "StatsPanel.h"
|
#include "StatsPanel.h"
|
||||||
@@ -19,12 +22,23 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
auto& renderer = *ctx.renderer;
|
auto& renderer = *ctx.renderer;
|
||||||
auto& app = *ctx.app;
|
auto& app = *ctx.app;
|
||||||
|
|
||||||
// ---- Camera:视图相机位姿(只读遥测;跟随渲染源分派)----
|
// ---- View:看哪里(渲染目标)+ 相机位姿(跟随渲染源分派)----
|
||||||
|
if (ImGui::CollapsingHeader("View", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
{
|
{
|
||||||
if (ctx.renderer->GetDisplayMode() == kDisplayViewport && ctx.editorCamera)
|
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();
|
const glm::vec3& pos = ctx.editorCamera->GetTransform().GetPosition();
|
||||||
ImGui::Text("Editor camera pos");
|
ImGui::Text("Editor Camera Pos");
|
||||||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -34,7 +48,7 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
{
|
{
|
||||||
// world 位姿(相机实体可能挂父层级下)
|
// world 位姿(相机实体可能挂父层级下)
|
||||||
const glm::vec3& pos = camT->GetWorldPosition();
|
const glm::vec3& pos = camT->GetWorldPosition();
|
||||||
ImGui::Text("Game camera pos");
|
ImGui::Text("Game Camera Pos");
|
||||||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -42,35 +56,26 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
ImGui::TextDisabled("(no primary camera)");
|
ImGui::TextDisabled("(no primary camera)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
// ---- Scene:模拟控制 + 场景级设置 ----
|
||||||
static const char* displayTarget[] = {
|
if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
"Fullscreen",
|
|
||||||
"Viewport",
|
|
||||||
};
|
|
||||||
int mode = renderer.GetDisplayMode();
|
|
||||||
if (ImGui::Combo("##DisplayTarget", &mode, displayTarget, IM_ARRAYSIZE(displayTarget))) {
|
|
||||||
renderer.SetDisplayMode(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
// ---- Scene:暂停模拟(引擎 Time 门面:动画/物理统一冻结)----
|
|
||||||
{
|
{
|
||||||
|
// 暂停模拟(引擎 Time 门面:动画/物理统一冻结)
|
||||||
bool paused = Time::TimeScale() == 0.0f;
|
bool paused = Time::TimeScale() == 0.0f;
|
||||||
if (ImGui::Checkbox("Pause Simulation", &paused))
|
if (ImGui::Checkbox("Pause Simulation", &paused))
|
||||||
{
|
{
|
||||||
Time::SetTimeScale(paused ? 0.0f : 1.0f);
|
Time::SetTimeScale(paused ? 0.0f : 1.0f);
|
||||||
}
|
}
|
||||||
|
ImGui::ColorEdit4("Clear Color",
|
||||||
|
glm::value_ptr(ctx.scene->GetSettings().ClearColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Rendering:渲染管线参数与开关 ----
|
// ---- Rendering:渲染管线参数与开关 ----
|
||||||
if (ImGui::CollapsingHeader("Rendering", ImGuiTreeNodeFlags_DefaultOpen))
|
if (ImGui::CollapsingHeader("Rendering", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
{
|
{
|
||||||
f32 exposure = renderer.GetExposure();
|
f32 exposure = renderer.GetExposure();
|
||||||
if (ImGui::SliderFloat("exposure", &exposure, 0.1f, 4.0f))
|
if (ImGui::SliderFloat("Exposure", &exposure, 0.1f, 4.0f))
|
||||||
{
|
{
|
||||||
renderer.SetExposure(exposure);
|
renderer.SetExposure(exposure);
|
||||||
}
|
}
|
||||||
@@ -89,12 +94,12 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
if (bloom)
|
if (bloom)
|
||||||
{
|
{
|
||||||
f32 threshold = renderer.GetBloomThreshold();
|
f32 threshold = renderer.GetBloomThreshold();
|
||||||
if (ImGui::SliderFloat("threshold", &threshold, 0.0f, 2.0f))
|
if (ImGui::SliderFloat("Threshold", &threshold, 0.0f, 2.0f))
|
||||||
{
|
{
|
||||||
renderer.SetBloomThreshold(threshold);
|
renderer.SetBloomThreshold(threshold);
|
||||||
}
|
}
|
||||||
f32 strength = renderer.GetBloomStrength();
|
f32 strength = renderer.GetBloomStrength();
|
||||||
if (ImGui::SliderFloat("strength", &strength, 0.0f, 2.0f))
|
if (ImGui::SliderFloat("Strength", &strength, 0.0f, 2.0f))
|
||||||
{
|
{
|
||||||
renderer.SetBloomStrength(strength);
|
renderer.SetBloomStrength(strength);
|
||||||
}
|
}
|
||||||
@@ -118,14 +123,6 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
{
|
{
|
||||||
renderer.SetLodEnabled(lod); // 关 = 恒绘原网格
|
renderer.SetLodEnabled(lod); // 关 = 恒绘原网格
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Environment:清屏色 + 调试视图(决定"看什么")。
|
|
||||||
// 环境贴图/环境光已迁为实体组件(Skybox/SkyLight),在 Details 配 ----
|
|
||||||
if (ImGui::CollapsingHeader("Environment", ImGuiTreeNodeFlags_DefaultOpen))
|
|
||||||
{
|
|
||||||
ImGui::ColorEdit4("ClearColor",
|
|
||||||
glm::value_ptr(ctx.scene->GetSettings().ClearColor));
|
|
||||||
|
|
||||||
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
||||||
const auto& viewNames = renderer.GetDebugViewNames();
|
const auto& viewNames = renderer.GetDebugViewNames();
|
||||||
@@ -137,13 +134,13 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Physics Debug:物理调试可视化(collider/约束/重力/速度/加速度)----
|
// ---- Physics:调试可视化(collider/约束/重力/速度/加速度)----
|
||||||
if (ImGui::CollapsingHeader("Physics Debug"))
|
if (ImGui::CollapsingHeader("Physics"))
|
||||||
{
|
{
|
||||||
auto& physics = app.GetPhysics();
|
auto& physics = app.GetPhysics();
|
||||||
u32 bits = physics.GetDebugDrawBits();
|
u32 bits = physics.GetDebugDrawBits();
|
||||||
bool master = bits != 0;
|
bool master = bits != 0;
|
||||||
if (ImGui::Checkbox("Enable##PhysDebug", &master))
|
if (ImGui::Checkbox("Debug Draw##Phys", &master))
|
||||||
{
|
{
|
||||||
bits = master ? kPhyDbgDefault : 0;
|
bits = master ? kPhyDbgDefault : 0;
|
||||||
}
|
}
|
||||||
@@ -169,9 +166,32 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
ImGui::Text("contacts (frame): %zu", physics.GetEvents().size());
|
ImGui::Text("contacts (frame): %zu", physics.GetEvents().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- GPU Timings:只读遥测(默认折叠,不占面板高度)----
|
// ---- Performance:帧率控制 + GPU 遥测(默认折叠,不占面板高度)----
|
||||||
if (ImGui::CollapsingHeader("GPU Timings"))
|
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",
|
ImGui::Text("instances: %u main / %u shadow",
|
||||||
renderer.GetMainInstanceCount(),
|
renderer.GetMainInstanceCount(),
|
||||||
renderer.GetShadowInstanceCount());
|
renderer.GetShadowInstanceCount());
|
||||||
@@ -188,31 +208,5 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Frame:帧率控制与显示 ----
|
|
||||||
if (ImGui::CollapsingHeader("Frame", ImGuiTreeNodeFlags_DefaultOpen))
|
|
||||||
{
|
|
||||||
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::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user