physics: joint constraints (fixed/hinge/ball)
- JointComponent: type, target UUID, per-body local anchors, hinge axis + angle limits (data only, zero Jolt deps) - PhysicsWorld::SyncConstraints: create/remove/rebuild on snapshot change; body rebuild contagion via invalidated-bits list; target resolved lazily by UUID each sync (no two-pass load needed) - Jolt FixedConstraint/HingeConstraint/PointConstraint in LocalToBodyCOM space (anchors minus shape COM); fixed frames lock creation-time relative pose; hinge axis aligned across bodies - Inspector: Joint section with target entity name dropdown - Serialization: Joint map with UUID target, as<T> fallbacks
This commit is contained in:
@@ -116,6 +116,11 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
{
|
||||
reg.emplace<Engine::ECS::RigidBodyComponent>(selected);
|
||||
}
|
||||
if (!reg.all_of<Engine::ECS::JointComponent>(selected) &&
|
||||
ImGui::MenuItem("Joint"))
|
||||
{
|
||||
reg.emplace<Engine::ECS::JointComponent>(selected);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::Separator();
|
||||
@@ -334,6 +339,58 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Joint(约束:两端 body 就绪后由 PhysicsWorld 对账创建)----
|
||||
if (auto* jc = reg.try_get<Engine::ECS::JointComponent>(selected))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Joint", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
int type = static_cast<int>(jc->type);
|
||||
if (ImGui::Combo("type", &type, "Fixed\0Hinge\0Ball\0"))
|
||||
{
|
||||
jc->type = static_cast<Engine::ECS::JointComponent::Type>(type);
|
||||
}
|
||||
|
||||
// target 实体下拉:实体名列表,选中态按 UUID 匹配(变更写回)
|
||||
std::string items;
|
||||
std::vector<Engine::UUID> uuids;
|
||||
int current = -1;
|
||||
reg.view<Engine::ECS::IdComponent, Engine::ECS::NameComponent>().each(
|
||||
[&](entt::entity e, const Engine::ECS::IdComponent& id,
|
||||
const Engine::ECS::NameComponent& n)
|
||||
{
|
||||
if (e == selected)
|
||||
{
|
||||
return; // 自连无意义
|
||||
}
|
||||
if (id.uuid == jc->target)
|
||||
{
|
||||
current = static_cast<int>(uuids.size());
|
||||
}
|
||||
uuids.push_back(id.uuid);
|
||||
items.append(n.name);
|
||||
items.push_back('\0');
|
||||
});
|
||||
items.push_back('\0'); // ImGui Combo 双 \0 结尾
|
||||
if (ImGui::Combo("target", ¤t, items.c_str()) && current >= 0)
|
||||
{
|
||||
jc->target = uuids[static_cast<size_t>(current)];
|
||||
}
|
||||
|
||||
ImGui::DragFloat3("anchor A", &jc->anchorA.x, 0.05f);
|
||||
ImGui::DragFloat3("anchor B", &jc->anchorB.x, 0.05f);
|
||||
if (jc->type == Engine::ECS::JointComponent::Type::Hinge)
|
||||
{
|
||||
ImGui::DragFloat3("hinge axis", &jc->hingeAxis.x, 0.05f);
|
||||
ImGui::Checkbox("enable limit", &jc->enableLimit);
|
||||
if (jc->enableLimit)
|
||||
{
|
||||
ImGui::DragFloat("min deg", &jc->limitMin, 1.0f, -180.0f, 0.0f);
|
||||
ImGui::DragFloat("max deg", &jc->limitMax, 1.0f, 0.0f, 180.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Animation ----
|
||||
if (auto* anim = reg.try_get<Engine::ECS::AnimationComponent>(selected))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user