physics: capsule/cylinder/compound collider shapes

- ColliderType + Capsule/Cylinder/Compound; CompoundChild list (Box/
  Sphere/Capsule, local pos/quat, non-recursive)
- CreateBody: CapsuleShape/CylinderShape (ctor param orders differ) +
  StaticCompoundShape; empty/failed compound falls back to Box
- BodyRecord caches radius/halfHeight/compound; SyncScene rebuilds on
  change (per-collider param extraction)
- Inspector: combo entries + shape params + compound child list editor
- Serialization: new fields with as<T> fallback, compound seq round-trip
This commit is contained in:
2026-10-03 16:17:44 +08:00
parent 6c81bfd70d
commit a744b332eb
5 changed files with 245 additions and 10 deletions
+57 -1
View File
@@ -236,7 +236,8 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
rb->type = static_cast<Engine::ECS::RigidBodyComponent::BodyType>(type);
}
int collider = static_cast<int>(rb->collider);
if (ImGui::Combo("collider", &collider, "Box\0Sphere\0Mesh\0"))
if (ImGui::Combo("collider", &collider,
"Box\0Sphere\0Mesh\0Capsule\0Cylinder\0Compound\0"))
{
rb->collider = static_cast<Engine::ECS::RigidBodyComponent::ColliderType>(collider);
}
@@ -248,6 +249,61 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
{
ImGui::DragFloat("radius", &rb->sphereRadius, 0.05f, 0.01f, 500.0f);
}
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Capsule)
{
// 半高 = 圆柱段半高(不含半球端盖),总高 = 2*(halfHeight + radius)
ImGui::DragFloat("radius", &rb->capsuleRadius, 0.05f, 0.01f, 500.0f);
ImGui::DragFloat("halfHeight", &rb->capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
}
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Cylinder)
{
ImGui::DragFloat("radius", &rb->cylinderRadius, 0.05f, 0.01f, 500.0f);
ImGui::DragFloat("halfHeight", &rb->cylinderHalfHeight, 0.05f, 0.01f, 500.0f);
}
else if (rb->collider == Engine::ECS::RigidBodyComponent::ColliderType::Compound)
{
// 子形状列表:尺寸/位置变更 → PhysicsWorld 重建 body(既有模式)
ImGui::Text("children: %d", static_cast<int>(rb->compound.size()));
ImGui::SameLine();
if (ImGui::Button("+ add"))
{
rb->compound.emplace_back();
}
int remove = -1;
for (int i = 0; i < static_cast<int>(rb->compound.size()); ++i)
{
auto& c = rb->compound[i];
ImGui::PushID(i);
ImGui::Separator();
int sh = static_cast<int>(c.shape);
if (ImGui::Combo("shape", &sh, "Box\0Sphere\0Capsule\0"))
{
c.shape = static_cast<Engine::ECS::RigidBodyComponent::CompoundChild::Shape>(sh);
}
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Box)
{
ImGui::DragFloat3("halfExtents", &c.boxHalfExtents.x, 0.05f, 0.01f, 500.0f);
}
else
{
ImGui::DragFloat("radius", &c.radius, 0.05f, 0.01f, 500.0f);
if (c.shape == Engine::ECS::RigidBodyComponent::CompoundChild::Shape::Capsule)
{
ImGui::DragFloat("halfHeight", &c.capsuleHalfHeight, 0.05f, 0.0f, 500.0f);
}
}
ImGui::DragFloat3("offset", &c.position.x, 0.05f);
if (ImGui::Button("remove"))
{
remove = i;
}
ImGui::PopID();
}
if (remove >= 0)
{
rb->compound.erase(rb->compound.begin() + remove);
}
}
else
{
// Mesh:跟随 MeshRenderer 视觉网格(Static=精确网格,