From 8bff4ee8490f2316e8d583bee10ce52d0aba0b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Sun, 6 Apr 2025 12:45:41 +0200 Subject: [PATCH] sixdof constraint --- Content/models/constraint_test.wiscene | Bin 52887 -> 43345 bytes Editor/ConstraintWindow.cpp | 403 ++++++++++++++++++++++++- Editor/ConstraintWindow.h | 19 ++ WickedEngine/CommonInclude.h | 1 + WickedEngine/wiGUI.cpp | 31 +- WickedEngine/wiPhysics_Jolt.cpp | 56 ++++ WickedEngine/wiScene.h | 2 +- WickedEngine/wiScene_Components.h | 44 ++- WickedEngine/wiScene_Serializers.cpp | 14 + 9 files changed, 552 insertions(+), 18 deletions(-) diff --git a/Content/models/constraint_test.wiscene b/Content/models/constraint_test.wiscene index e8c05c61a50e20949a824d25f1f179bfd06d060e..e7eefa5dc0585730b664d3c80347fdb135b80102 100644 GIT binary patch delta 24285 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ⅈ0IuTH2*J;wDsAYbG+o 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 delta 36679 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 {comD?WL0-i;@ZtE7F}#9UfeMccu}o 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 v^4F!5k5={dr-V{`pGetCurrentScene(); for (auto& x : editor->translator.selected) @@ -123,7 +124,7 @@ void ConstraintWindow::Create(EditorComponent* _editor) default: break; } - physicscomponent->physicsobject = nullptr; + physicscomponent->SetRefreshParametersNeeded(true); } } }); @@ -148,13 +149,295 @@ void ConstraintWindow::Create(EditorComponent* _editor) default: break; } - physicscomponent->physicsobject = nullptr; + physicscomponent->SetRefreshParametersNeeded(true); } } }); AddWidget(&maxSlider); + + + fixedXButton.Create("Fix X"); + fixedXButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedX(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedXButton); + + fixedYButton.Create("Fix Y"); + fixedYButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedY(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedYButton); + + fixedZButton.Create("Fix Z"); + fixedZButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedZ(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedZButton); + + fixedXRotationButton.Create("Fix Rot X"); + fixedXRotationButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedRotationX(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedXRotationButton); + + fixedYRotationButton.Create("Fix Rot Y"); + fixedYRotationButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedRotationY(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedYRotationButton); + + fixedZRotationButton.Create("Fix Rot Z"); + fixedZRotationButton.OnClick([=](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.SetFixedRotationZ(); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + SetEntity(entity); + }); + AddWidget(&fixedZRotationButton); + + + minTranslationXSlider.Create(-10, 0, 1, 100000, "Min Translation X: "); + minTranslationXSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minTranslationAxes.x = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minTranslationXSlider); + + minTranslationYSlider.Create(-10, 0, 1, 100000, "Min Translation Y: "); + minTranslationYSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minTranslationAxes.y = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minTranslationYSlider); + + minTranslationZSlider.Create(-10, 0, 1, 100000, "Min Translation Z: "); + minTranslationZSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minTranslationAxes.z = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minTranslationZSlider); + + maxTranslationXSlider.Create(0, 10, 1, 100000, "Max Translation X: "); + maxTranslationXSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxTranslationAxes.x = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxTranslationXSlider); + + maxTranslationYSlider.Create(0, 10, 1, 100000, "Max Translation Y: "); + maxTranslationYSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxTranslationAxes.y = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxTranslationYSlider); + + maxTranslationZSlider.Create(0, 10, 1, 100000, "Max Translation Z: "); + maxTranslationZSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxTranslationAxes.z = args.fValue; + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxTranslationZSlider); + + + + minRotationXSlider.Create(-180, 0, 1, 100000, "Min Rotation X: "); + minRotationXSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minRotationAxes.x = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minRotationXSlider); + + minRotationYSlider.Create(-180, 0, 1, 100000, "Min Rotation Y: "); + minRotationYSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minRotationAxes.y = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minRotationYSlider); + + minRotationZSlider.Create(-180, 0, 1, 100000, "Min Rotation Z: "); + minRotationZSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.minRotationAxes.z = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&minRotationZSlider); + + maxRotationXSlider.Create(0, 180, 1, 100000, "Max Rotation X: "); + maxRotationXSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxRotationAxes.x = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxRotationXSlider); + + maxRotationYSlider.Create(0, 180, 1, 100000, "Max Rotation Y: "); + maxRotationYSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxRotationAxes.y = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxRotationYSlider); + + maxRotationZSlider.Create(0, 180, 1, 100000, "Max Rotation Z: "); + maxRotationZSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + PhysicsConstraintComponent* physicscomponent = scene.constraints.GetComponent(x.entity); + if (physicscomponent != nullptr) + { + physicscomponent->six_dof.maxRotationAxes.z = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&maxRotationZSlider); + + SetMinimized(true); SetVisible(false); @@ -169,8 +452,6 @@ void ConstraintWindow::SetEntity(Entity entity) if (physicsComponent != nullptr) { - if (this->entity == entity) - return; this->entity = entity; typeComboBox.SetSelectedByUserdataWithoutCallback((uint64_t)physicsComponent->type); @@ -218,6 +499,19 @@ void ConstraintWindow::SetEntity(Entity entity) } bodyAComboBox.SetSelectedByUserdataWithoutCallback(physicsComponent->bodyA); bodyBComboBox.SetSelectedByUserdataWithoutCallback(physicsComponent->bodyB); + + minTranslationXSlider.SetValue(physicsComponent->six_dof.minTranslationAxes.x); + minTranslationYSlider.SetValue(physicsComponent->six_dof.minTranslationAxes.y); + minTranslationZSlider.SetValue(physicsComponent->six_dof.minTranslationAxes.z); + maxTranslationXSlider.SetValue(physicsComponent->six_dof.maxTranslationAxes.x); + maxTranslationYSlider.SetValue(physicsComponent->six_dof.maxTranslationAxes.y); + maxTranslationZSlider.SetValue(physicsComponent->six_dof.maxTranslationAxes.z); + minRotationXSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.minRotationAxes.x)); + minRotationYSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.minRotationAxes.y)); + minRotationZSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.minRotationAxes.z)); + maxRotationXSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.maxRotationAxes.x)); + maxRotationYSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.maxRotationAxes.y)); + maxRotationZSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->six_dof.maxRotationAxes.z)); } else { @@ -236,7 +530,7 @@ void ConstraintWindow::ResizeLayout() float jump = 20; const float margin_left = 145; - const float margin_right = 40; + float margin_right = 40; auto add = [&](wi::gui::Widget& widget) { if (!widget.IsVisible()) @@ -281,14 +575,113 @@ void ConstraintWindow::ResizeLayout() case PhysicsConstraintComponent::Type::Hinge: minSlider.SetVisible(true); maxSlider.SetVisible(true); + + fixedXButton.SetVisible(false); + fixedYButton.SetVisible(false); + fixedZButton.SetVisible(false); + fixedXRotationButton.SetVisible(false); + fixedYRotationButton.SetVisible(false); + fixedZRotationButton.SetVisible(false); + minTranslationXSlider.SetVisible(false); + minTranslationYSlider.SetVisible(false); + minTranslationZSlider.SetVisible(false); + maxTranslationXSlider.SetVisible(false); + maxTranslationYSlider.SetVisible(false); + maxTranslationZSlider.SetVisible(false); + minRotationXSlider.SetVisible(false); + minRotationYSlider.SetVisible(false); + minRotationZSlider.SetVisible(false); + maxRotationXSlider.SetVisible(false); + maxRotationYSlider.SetVisible(false); + maxRotationZSlider.SetVisible(false); break; case PhysicsConstraintComponent::Type::Cone: minSlider.SetVisible(true); maxSlider.SetVisible(false); + + fixedXButton.SetVisible(false); + fixedYButton.SetVisible(false); + fixedZButton.SetVisible(false); + fixedXRotationButton.SetVisible(false); + fixedYRotationButton.SetVisible(false); + fixedZRotationButton.SetVisible(false); + minTranslationXSlider.SetVisible(false); + minTranslationYSlider.SetVisible(false); + minTranslationZSlider.SetVisible(false); + maxTranslationXSlider.SetVisible(false); + maxTranslationYSlider.SetVisible(false); + maxTranslationZSlider.SetVisible(false); + minRotationXSlider.SetVisible(false); + minRotationYSlider.SetVisible(false); + minRotationZSlider.SetVisible(false); + maxRotationXSlider.SetVisible(false); + maxRotationYSlider.SetVisible(false); + maxRotationZSlider.SetVisible(false); + break; + case PhysicsConstraintComponent::Type::SixDOF: + minSlider.SetVisible(false); + maxSlider.SetVisible(false); + + fixedXButton.SetVisible(true); + fixedYButton.SetVisible(true); + fixedZButton.SetVisible(true); + fixedXRotationButton.SetVisible(true); + fixedYRotationButton.SetVisible(true); + fixedZRotationButton.SetVisible(true); + minTranslationXSlider.SetVisible(true); + minTranslationYSlider.SetVisible(true); + minTranslationZSlider.SetVisible(true); + maxTranslationXSlider.SetVisible(true); + maxTranslationYSlider.SetVisible(true); + maxTranslationZSlider.SetVisible(true); + minRotationXSlider.SetVisible(true); + minRotationYSlider.SetVisible(true); + minRotationZSlider.SetVisible(true); + maxRotationXSlider.SetVisible(true); + maxRotationYSlider.SetVisible(true); + maxRotationZSlider.SetVisible(true); + add_fullwidth(fixedXButton); + add_fullwidth(fixedYButton); + add_fullwidth(fixedZButton); + add_fullwidth(fixedXRotationButton); + add_fullwidth(fixedYRotationButton); + add_fullwidth(fixedZRotationButton); + margin_right = 80; + add(minTranslationXSlider); + add(minTranslationYSlider); + add(minTranslationZSlider); + add(maxTranslationXSlider); + add(maxTranslationYSlider); + add(maxTranslationZSlider); + add(minRotationXSlider); + add(minRotationYSlider); + add(minRotationZSlider); + add(maxRotationXSlider); + add(maxRotationYSlider); + add(maxRotationZSlider); break; default: minSlider.SetVisible(false); maxSlider.SetVisible(false); + + fixedXButton.SetVisible(false); + fixedYButton.SetVisible(false); + fixedZButton.SetVisible(false); + fixedXRotationButton.SetVisible(false); + fixedYRotationButton.SetVisible(false); + fixedZRotationButton.SetVisible(false); + minTranslationXSlider.SetVisible(false); + minTranslationYSlider.SetVisible(false); + minTranslationZSlider.SetVisible(false); + maxTranslationXSlider.SetVisible(false); + maxTranslationYSlider.SetVisible(false); + maxTranslationZSlider.SetVisible(false); + minRotationXSlider.SetVisible(false); + minRotationYSlider.SetVisible(false); + minRotationZSlider.SetVisible(false); + maxRotationXSlider.SetVisible(false); + maxRotationYSlider.SetVisible(false); + maxRotationZSlider.SetVisible(false); break; } diff --git a/Editor/ConstraintWindow.h b/Editor/ConstraintWindow.h index 969e916bc..04ba0a643 100644 --- a/Editor/ConstraintWindow.h +++ b/Editor/ConstraintWindow.h @@ -18,6 +18,25 @@ public: wi::gui::Slider minSlider; wi::gui::Slider maxSlider; + wi::gui::Button fixedXButton; + wi::gui::Button fixedYButton; + wi::gui::Button fixedZButton; + wi::gui::Button fixedXRotationButton; + wi::gui::Button fixedYRotationButton; + wi::gui::Button fixedZRotationButton; + wi::gui::Slider minTranslationXSlider; + wi::gui::Slider minTranslationYSlider; + wi::gui::Slider minTranslationZSlider; + wi::gui::Slider maxTranslationXSlider; + wi::gui::Slider maxTranslationYSlider; + wi::gui::Slider maxTranslationZSlider; + wi::gui::Slider minRotationXSlider; + wi::gui::Slider minRotationYSlider; + wi::gui::Slider minRotationZSlider; + wi::gui::Slider maxRotationXSlider; + wi::gui::Slider maxRotationYSlider; + wi::gui::Slider maxRotationZSlider; + void ResizeLayout() override; }; diff --git a/WickedEngine/CommonInclude.h b/WickedEngine/CommonInclude.h index 490b463c6..5d062b7d8 100644 --- a/WickedEngine/CommonInclude.h +++ b/WickedEngine/CommonInclude.h @@ -4,6 +4,7 @@ // This is a helper include file pasted into all engine headers, try to keep it minimal! // Do not include engine features in this file! +#include #include #include diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index 9f78c10bb..8eb74e967 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -1480,9 +1480,20 @@ namespace wi::gui } void TextInputField::SetValue(float newValue) { - std::stringstream ss(""); - ss << newValue; - font.SetText(ss.str()); + if (newValue == FLT_MAX) + { + font.SetText(L"FLT_MAX"); + } + else if (newValue == -FLT_MAX) + { + font.SetText(L"-FLT_MAX"); + } + else + { + std::stringstream ss(""); + ss << newValue; + font.SetText(ss.str()); + } } const std::string TextInputField::GetValue() { @@ -1901,7 +1912,7 @@ namespace wi::gui valueInputField.Create(name + "_endInputField"); valueInputField.SetLocalizationEnabled(LocalizationEnabled::None); valueInputField.SetShadowRadius(0); - valueInputField.SetTooltip("Enter number to modify value even outside slider limits. Enter \"reset\" to reset slider to initial state."); + valueInputField.SetTooltip("Enter number to modify value even outside slider limits. Other inputs:\n - reset : reset slider to initial state.\n - FLT_MAX : float max value\n - -FLT_MAX : negative float max value."); valueInputField.SetValue(end); valueInputField.OnInputAccepted([this, start, end, defaultValue](EventArgs args) { if (args.sValue.compare("reset") == 0) @@ -1912,6 +1923,18 @@ namespace wi::gui args.fValue = this->value; args.iValue = (int)this->value; } + else if (args.sValue.compare("FLT_MAX") == 0) + { + this->value = FLT_MAX; + args.fValue = this->value; + args.iValue = (int)this->value; + } + else if (args.sValue.compare("-FLT_MAX") == 0) + { + this->value = -FLT_MAX; + args.fValue = this->value; + args.iValue = (int)this->value; + } else { this->value = args.fValue; diff --git a/WickedEngine/wiPhysics_Jolt.cpp b/WickedEngine/wiPhysics_Jolt.cpp index c4e2ed561..f000f3858 100644 --- a/WickedEngine/wiPhysics_Jolt.cpp +++ b/WickedEngine/wiPhysics_Jolt.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1034,6 +1035,27 @@ namespace wi::physics settings.mHalfConeAngle = physicscomponent.cone_constraint.half_cone_angle; physicsobject.constraint = settings.Create(*body1, *body2); } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::SixDOF) + { + SixDOFConstraintSettings settings; + settings.mSpace = EConstraintSpace::WorldSpace; + settings.mPosition1 = settings.mPosition2 = cast(transform.GetPosition()); + settings.mAxisX1 = settings.mAxisX2 = cast(transform.GetRight()).Normalized(); + settings.mAxisY1 = settings.mAxisY2 = cast(transform.GetUp()).Normalized(); + settings.mLimitMin[SixDOFConstraintSettings::EAxis::TranslationX] = physicscomponent.six_dof.minTranslationAxes.x; + settings.mLimitMin[SixDOFConstraintSettings::EAxis::TranslationY] = physicscomponent.six_dof.minTranslationAxes.y; + settings.mLimitMin[SixDOFConstraintSettings::EAxis::TranslationZ] = physicscomponent.six_dof.minTranslationAxes.z; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::TranslationX] = physicscomponent.six_dof.maxTranslationAxes.x; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::TranslationY] = physicscomponent.six_dof.maxTranslationAxes.y; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::TranslationZ] = physicscomponent.six_dof.maxTranslationAxes.z; + settings.mLimitMin[SixDOFConstraintSettings::EAxis::RotationX] = physicscomponent.six_dof.minRotationAxes.x; + settings.mLimitMin[SixDOFConstraintSettings::EAxis::RotationY] = physicscomponent.six_dof.minRotationAxes.y; + settings.mLimitMin[SixDOFConstraintSettings::EAxis::RotationZ] = physicscomponent.six_dof.minRotationAxes.z; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::RotationX] = physicscomponent.six_dof.maxRotationAxes.x; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::RotationY] = physicscomponent.six_dof.maxRotationAxes.y; + settings.mLimitMax[SixDOFConstraintSettings::EAxis::RotationZ] = physicscomponent.six_dof.maxRotationAxes.z; + physicsobject.constraint = settings.Create(*body1, *body2); + } else { wilog("Constraint creation failed: constraint type is not valid!"); @@ -1047,6 +1069,7 @@ namespace wi::physics } physics_scene.physics_system.AddConstraint(physicsobject.constraint); + physicscomponent.SetRefreshParametersNeeded(false); } struct Ragdoll @@ -1934,6 +1957,39 @@ namespace wi::physics return; AddConstraint(scene, entity, physicscomponent, *transform); } + + if (physicscomponent.physicsobject != nullptr && physicscomponent.IsRefreshParametersNeeded()) + { + physicscomponent.SetRefreshParametersNeeded(false); + Constraint& constraint = GetConstraint(physicscomponent); + if (physicscomponent.type == PhysicsConstraintComponent::Type::Fixed) + { + } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::Point) + { + } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::Distance) + { + DistanceConstraint* ptr = ((DistanceConstraint*)constraint.constraint.GetPtr()); + ptr->SetDistance(physicscomponent.distance_constraint.min_distance, physicscomponent.distance_constraint.max_distance); + } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::Hinge) + { + HingeConstraint* ptr = ((HingeConstraint*)constraint.constraint.GetPtr()); + ptr->SetLimits(physicscomponent.hinge_constraint.min_angle, physicscomponent.hinge_constraint.max_angle); + } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::Cone) + { + ConeConstraint* ptr = ((ConeConstraint*)constraint.constraint.GetPtr()); + ptr->SetHalfConeAngle(physicscomponent.cone_constraint.half_cone_angle); + } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::SixDOF) + { + SixDOFConstraint* ptr = ((SixDOFConstraint*)constraint.constraint.GetPtr()); + ptr->SetTranslationLimits(cast(physicscomponent.six_dof.minTranslationAxes), cast(physicscomponent.six_dof.maxTranslationAxes)); + ptr->SetRotationLimits(cast(physicscomponent.six_dof.minRotationAxes), cast(physicscomponent.six_dof.maxRotationAxes)); + } + } }); wi::jobsystem::Wait(ctx); // wait for all creations diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 60bbdb9e3..0762f3629 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -63,7 +63,7 @@ namespace wi::scene wi::ecs::ComponentManager& voxel_grids = componentLibrary.Register("wi::scene::Scene::voxel_grids"); wi::ecs::ComponentManager& metadatas = componentLibrary.Register("wi::scene::Scene::metadatas"); wi::ecs::ComponentManager& characters = componentLibrary.Register("wi::scene::Scene::characters"); - wi::ecs::ComponentManager& constraints = componentLibrary.Register("wi::scene::Scene::constraints"); + wi::ecs::ComponentManager& constraints = componentLibrary.Register("wi::scene::Scene::constraints", 1); // version = 1 // Non-serialized attributes: float dt = 0; diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index f55516fc6..90d56c3cf 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -531,16 +531,18 @@ namespace wi::scene enum FLAGS { EMPTY = 0, + REFRESH_PARAMETERS_REQUEST = 1 << 0, }; uint32_t _flags = EMPTY; enum class Type { - Fixed, - Point, - Distance, - Hinge, - Cone, + Fixed, // fixed in place completely + Point, // fixed to a point but can rotate around it + Distance, // point constraint within specified distance + Hinge, // rotation around a point on the UP axis of the contraint transform + Cone, // constrain to a cone shape specified by the cone angle + SixDOF, // manual specification of axes movement and rotation limits } type = Type::Fixed; wi::ecs::Entity bodyA = wi::ecs::INVALID_ENTITY; @@ -554,18 +556,44 @@ namespace wi::scene struct HingeConstraintSettings { - float min_angle = -XM_PI; - float max_angle = XM_PI; + float min_angle = -XM_PI; // radians + float max_angle = XM_PI; // radians } hinge_constraint; // note: hinge axis is UP, normal axis is RIGHT directions of the TransformComponent on this entity struct ConeConstraintSettings { - float half_cone_angle = 0; + float half_cone_angle = 0; // radians } cone_constraint; // note: cone axis is RIGHT of the TransformComponent on this entity + struct SixDOFConstraintSettings + { + XMFLOAT3 minTranslationAxes = XMFLOAT3(-FLT_MAX, -FLT_MAX, -FLT_MAX); + XMFLOAT3 maxTranslationAxes = XMFLOAT3(FLT_MAX, FLT_MAX, FLT_MAX); + XMFLOAT3 minRotationAxes = XMFLOAT3(-XM_PI, -XM_PI, -XM_PI); + XMFLOAT3 maxRotationAxes = XMFLOAT3(XM_PI, XM_PI, XM_PI); + + void SetFixedX() { minTranslationAxes.x = FLT_MAX; maxTranslationAxes.x = -FLT_MAX; } + void SetFreeX() { minTranslationAxes.x = -FLT_MAX; maxTranslationAxes.x = FLT_MAX; } + void SetFixedY() { minTranslationAxes.y = FLT_MAX; maxTranslationAxes.y = -FLT_MAX; } + void SetFreeY() { minTranslationAxes.y = -FLT_MAX; maxTranslationAxes.y = FLT_MAX; } + void SetFixedZ() { minTranslationAxes.z = FLT_MAX; maxTranslationAxes.z = -FLT_MAX; } + void SetFreeZ() { minTranslationAxes.z = -FLT_MAX; maxTranslationAxes.z = FLT_MAX; } + + void SetFixedRotationX() { minRotationAxes.x = XM_PI; maxRotationAxes.x = -XM_PI; } + void SetFreeRotationX() { minRotationAxes.x = -XM_PI; maxRotationAxes.x = XM_PI; } + void SetFixedRotationY() { minRotationAxes.y = XM_PI; maxRotationAxes.y = -XM_PI; } + void SetFreeRotationY() { minRotationAxes.y = -XM_PI; maxRotationAxes.y = XM_PI; } + void SetFixedRotationZ() { minRotationAxes.z = XM_PI; maxRotationAxes.z = -XM_PI; } + void SetFreeRotationZ() { minRotationAxes.z = -XM_PI; maxRotationAxes.z = XM_PI; } + } six_dof; + // Non-serialized attributes: std::shared_ptr physicsobject = nullptr; // You can set to null to recreate the physics object the next time phsyics system will be running. + // Request refreshing of constraint settings without recreating the constraint + constexpr void SetRefreshParametersNeeded(bool value = true) { if (value) { _flags |= REFRESH_PARAMETERS_REQUEST; } else { _flags &= ~REFRESH_PARAMETERS_REQUEST; } } + constexpr bool IsRefreshParametersNeeded() const { return _flags & REFRESH_PARAMETERS_REQUEST; } + void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 65c8c9fc6..cc9283ba2 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -922,6 +922,13 @@ namespace wi::scene archive >> hinge_constraint.min_angle; archive >> hinge_constraint.max_angle; archive >> cone_constraint.half_cone_angle; + if (seri.GetVersion() >= 1) + { + archive >> six_dof.minTranslationAxes; + archive >> six_dof.maxTranslationAxes; + archive >> six_dof.minRotationAxes; + archive >> six_dof.maxRotationAxes; + } } else { @@ -934,6 +941,13 @@ namespace wi::scene archive << hinge_constraint.min_angle; archive << hinge_constraint.max_angle; archive << cone_constraint.half_cone_angle; + if (seri.GetVersion() >= 1) + { + archive << six_dof.minTranslationAxes; + archive << six_dof.maxTranslationAxes; + archive << six_dof.minRotationAxes; + archive << six_dof.maxRotationAxes; + } } } void SoftBodyPhysicsComponent::Serialize(wi::Archive& archive, EntitySerializer& seri)