diff --git a/Content/models/constraint_test.wiscene b/Content/models/constraint_test.wiscene index e7eefa5dc..024f4f318 100644 Binary files a/Content/models/constraint_test.wiscene and b/Content/models/constraint_test.wiscene differ diff --git a/Editor/ConstraintWindow.cpp b/Editor/ConstraintWindow.cpp index 348ea93ca..9ab7d63dc 100644 --- a/Editor/ConstraintWindow.cpp +++ b/Editor/ConstraintWindow.cpp @@ -30,6 +30,23 @@ void ConstraintWindow::Create(EditorComponent* _editor) infoLabel.SetText("Constraints can be added to bind one or two rigid bodies. If only one body is specified, then it will be bound to the constraint's location. If two bodies are specified, they will be bound together with the constraint acting as the pivot between them at the time of binding."); AddWidget(&infoLabel); + physicsDebugCheckBox.Create(ICON_EYE " Physics visualizer: "); + physicsDebugCheckBox.SetTooltip("Visualize the physics world"); + physicsDebugCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::physics::SetDebugDrawEnabled(args.bValue); + editor->generalWnd.physicsDebugCheckBox.SetCheck(args.bValue); + editor->componentsWnd.rigidWnd.physicsDebugCheckBox.SetCheck(args.bValue); + }); + physicsDebugCheckBox.SetCheck(wi::physics::IsDebugDrawEnabled()); + AddWidget(&physicsDebugCheckBox); + + constraintDebugSlider.Create(0, 10, 1, 100, "Debug size: "); + constraintDebugSlider.SetValue(wi::physics::GetConstraintDebugSize()); + constraintDebugSlider.OnSlide([](wi::gui::EventArgs args) { + wi::physics::SetConstraintDebugSize(args.fValue); + }); + AddWidget(&constraintDebugSlider); + rebindButton.Create("Rebind Constraint"); rebindButton.OnClick([this](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); @@ -51,6 +68,7 @@ void ConstraintWindow::Create(EditorComponent* _editor) typeComboBox.AddItem("Hinge", (uint64_t)PhysicsConstraintComponent::Type::Hinge); typeComboBox.AddItem("Cone", (uint64_t)PhysicsConstraintComponent::Type::Cone); typeComboBox.AddItem("Six DOF", (uint64_t)PhysicsConstraintComponent::Type::SixDOF); + typeComboBox.AddItem("Swing Twist", (uint64_t)PhysicsConstraintComponent::Type::SwingTwist); typeComboBox.OnSelect([&](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); for (auto& x : editor->translator.selected) @@ -121,6 +139,9 @@ void ConstraintWindow::Create(EditorComponent* _editor) case PhysicsConstraintComponent::Type::Cone: physicscomponent->cone_constraint.half_cone_angle = wi::math::DegreesToRadians(args.fValue); break; + case PhysicsConstraintComponent::Type::SwingTwist: + physicscomponent->swing_twist.min_twist_angle = wi::math::DegreesToRadians(args.fValue); + break; default: break; } @@ -146,6 +167,9 @@ void ConstraintWindow::Create(EditorComponent* _editor) case PhysicsConstraintComponent::Type::Hinge: physicscomponent->hinge_constraint.max_angle = wi::math::DegreesToRadians(args.fValue); break; + case PhysicsConstraintComponent::Type::SwingTwist: + physicscomponent->swing_twist.max_twist_angle = wi::math::DegreesToRadians(args.fValue); + break; default: break; } @@ -157,6 +181,38 @@ void ConstraintWindow::Create(EditorComponent* _editor) + normalConeSlider.Create(0, 90, 1, 90, "Normal Angle: "); + normalConeSlider.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->swing_twist.normal_half_cone_angle = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&normalConeSlider); + + planeConeSlider.Create(0, 90, 1, 90, "Plane Angle: "); + planeConeSlider.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->swing_twist.plane_half_cone_angle = wi::math::DegreesToRadians(args.fValue); + physicscomponent->SetRefreshParametersNeeded(true); + } + } + }); + AddWidget(&planeConeSlider); + + + fixedXButton.Create("Fix X"); fixedXButton.OnClick([=](wi::gui::EventArgs args) { @@ -479,6 +535,16 @@ void ConstraintWindow::SetEntity(Entity entity) minSlider.SetRange(0, 90); minSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->cone_constraint.half_cone_angle)); break; + case PhysicsConstraintComponent::Type::SwingTwist: + minSlider.SetText("Min twist angle: "); + maxSlider.SetText("Max twist angle: "); + minSlider.SetRange(-180, 0); + maxSlider.SetRange(0, 180); + minSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->swing_twist.min_twist_angle)); + maxSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->swing_twist.max_twist_angle)); + normalConeSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->swing_twist.normal_half_cone_angle)); + planeConeSlider.SetValue(wi::math::RadiansToDegrees(physicsComponent->swing_twist.plane_half_cone_angle)); + break; default: break; } @@ -569,6 +635,7 @@ void ConstraintWindow::ResizeLayout() const PhysicsConstraintComponent* physicsComponent = scene.constraints.GetComponent(entity); if (physicsComponent != nullptr) { + switch (physicsComponent->type) { case PhysicsConstraintComponent::Type::Distance: @@ -576,6 +643,34 @@ void ConstraintWindow::ResizeLayout() minSlider.SetVisible(true); maxSlider.SetVisible(true); + normalConeSlider.SetVisible(false); + planeConeSlider.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::SwingTwist: + minSlider.SetVisible(true); + maxSlider.SetVisible(true); + normalConeSlider.SetVisible(true); + planeConeSlider.SetVisible(true); + fixedXButton.SetVisible(false); fixedYButton.SetVisible(false); fixedZButton.SetVisible(false); @@ -599,6 +694,9 @@ void ConstraintWindow::ResizeLayout() minSlider.SetVisible(true); maxSlider.SetVisible(false); + normalConeSlider.SetVisible(false); + planeConeSlider.SetVisible(false); + fixedXButton.SetVisible(false); fixedYButton.SetVisible(false); fixedZButton.SetVisible(false); @@ -622,6 +720,9 @@ void ConstraintWindow::ResizeLayout() minSlider.SetVisible(false); maxSlider.SetVisible(false); + normalConeSlider.SetVisible(false); + planeConeSlider.SetVisible(false); + fixedXButton.SetVisible(true); fixedYButton.SetVisible(true); fixedZButton.SetVisible(true); @@ -661,30 +762,20 @@ void ConstraintWindow::ResizeLayout() 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; } + margin_right = 40; + + if (normalConeSlider.IsVisible()) + { + add(normalConeSlider); + } + if (planeConeSlider.IsVisible()) + { + add(planeConeSlider); + } + if (minSlider.IsVisible()) { add(minSlider); @@ -693,5 +784,8 @@ void ConstraintWindow::ResizeLayout() { add(maxSlider); } + + add_right(physicsDebugCheckBox); + add(constraintDebugSlider); } } diff --git a/Editor/ConstraintWindow.h b/Editor/ConstraintWindow.h index 04ba0a643..f94e556af 100644 --- a/Editor/ConstraintWindow.h +++ b/Editor/ConstraintWindow.h @@ -11,6 +11,8 @@ public: void SetEntity(wi::ecs::Entity entity); wi::gui::Label infoLabel; + wi::gui::CheckBox physicsDebugCheckBox; + wi::gui::Slider constraintDebugSlider; wi::gui::Button rebindButton; wi::gui::ComboBox typeComboBox; wi::gui::ComboBox bodyAComboBox; @@ -18,6 +20,9 @@ public: wi::gui::Slider minSlider; wi::gui::Slider maxSlider; + wi::gui::Slider normalConeSlider; + wi::gui::Slider planeConeSlider; + wi::gui::Button fixedXButton; wi::gui::Button fixedYButton; wi::gui::Button fixedZButton; diff --git a/Editor/GeneralWindow.cpp b/Editor/GeneralWindow.cpp index 138fcbd8c..78ae88d11 100644 --- a/Editor/GeneralWindow.cpp +++ b/Editor/GeneralWindow.cpp @@ -33,6 +33,7 @@ void GeneralWindow::Create(EditorComponent* _editor) physicsDebugCheckBox.OnClick([=](wi::gui::EventArgs args) { wi::physics::SetDebugDrawEnabled(args.bValue); editor->componentsWnd.rigidWnd.physicsDebugCheckBox.SetCheck(args.bValue); + editor->componentsWnd.constraintWnd.physicsDebugCheckBox.SetCheck(args.bValue); }); physicsDebugCheckBox.SetCheck(wi::physics::IsDebugDrawEnabled()); AddWidget(&physicsDebugCheckBox); diff --git a/Editor/RigidBodyWindow.cpp b/Editor/RigidBodyWindow.cpp index 970dceb7c..794202678 100644 --- a/Editor/RigidBodyWindow.cpp +++ b/Editor/RigidBodyWindow.cpp @@ -355,6 +355,7 @@ void RigidBodyWindow::Create(EditorComponent* _editor) physicsDebugCheckBox.OnClick([=](wi::gui::EventArgs args) { wi::physics::SetDebugDrawEnabled(args.bValue); editor->generalWnd.physicsDebugCheckBox.SetCheck(args.bValue); + editor->componentsWnd.constraintWnd.physicsDebugCheckBox.SetCheck(args.bValue); }); physicsDebugCheckBox.SetCheck(wi::physics::IsDebugDrawEnabled()); AddWidget(&physicsDebugCheckBox); diff --git a/WickedEngine/wiPhysics.h b/WickedEngine/wiPhysics.h index 1e1e39ed8..8f78258c6 100644 --- a/WickedEngine/wiPhysics.h +++ b/WickedEngine/wiPhysics.h @@ -30,6 +30,10 @@ namespace wi::physics void SetDebugDrawEnabled(bool value); bool IsDebugDrawEnabled(); + // Adjust constraint debugging sizes + void SetConstraintDebugSize(float value); + float GetConstraintDebugSize(); + // Set the accuracy of the simulation // This value corresponds to maximum simulation step count // Higher values will be slower but more accurate diff --git a/WickedEngine/wiPhysics_Jolt.cpp b/WickedEngine/wiPhysics_Jolt.cpp index 07cd692aa..f04d46c88 100644 --- a/WickedEngine/wiPhysics_Jolt.cpp +++ b/WickedEngine/wiPhysics_Jolt.cpp @@ -81,6 +81,7 @@ namespace wi::physics bool ENABLED = true; bool SIMULATION_ENABLED = true; bool DEBUGDRAW_ENABLED = false; + float CONSTRAINT_DEBUGSIZE = 1; int ACCURACY = 4; int softbodyIterationCount = 6; float TIMESTEP = 1.0f / 60.0f; @@ -1056,6 +1057,19 @@ namespace wi::physics settings.mLimitMax[SixDOFConstraintSettings::EAxis::RotationZ] = physicscomponent.six_dof.maxRotationAxes.z; physicsobject.constraint = settings.Create(*body1, *body2); } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::SwingTwist) + { + SwingTwistConstraintSettings settings; + settings.mSpace = EConstraintSpace::WorldSpace; + settings.mPosition1 = settings.mPosition2 = cast(transform.GetPosition()); + settings.mTwistAxis1 = settings.mTwistAxis2 = cast(transform.GetRight()).Normalized(); + settings.mPlaneAxis1 = settings.mPlaneAxis2 = cast(transform.GetUp()).Normalized(); + settings.mNormalHalfConeAngle = physicscomponent.swing_twist.normal_half_cone_angle; + settings.mPlaneHalfConeAngle = physicscomponent.swing_twist.plane_half_cone_angle; + settings.mTwistMinAngle = physicscomponent.swing_twist.min_twist_angle; + settings.mTwistMaxAngle = physicscomponent.swing_twist.max_twist_angle; + physicsobject.constraint = settings.Create(*body1, *body2); + } else { wilog("Constraint creation failed: constraint type is not valid!"); @@ -1812,6 +1826,9 @@ namespace wi::physics bool IsDebugDrawEnabled() { return DEBUGDRAW_ENABLED; } void SetDebugDrawEnabled(bool value) { DEBUGDRAW_ENABLED = value; } + void SetConstraintDebugSize(float value) { CONSTRAINT_DEBUGSIZE = value; } + float GetConstraintDebugSize() { return CONSTRAINT_DEBUGSIZE; } + int GetAccuracy() { return ACCURACY; } void SetAccuracy(int value) { ACCURACY = value; } @@ -1989,6 +2006,14 @@ namespace wi::physics ptr->SetTranslationLimits(cast(physicscomponent.six_dof.minTranslationAxes), cast(physicscomponent.six_dof.maxTranslationAxes)); ptr->SetRotationLimits(cast(physicscomponent.six_dof.minRotationAxes), cast(physicscomponent.six_dof.maxRotationAxes)); } + else if (physicscomponent.type == PhysicsConstraintComponent::Type::SwingTwist) + { + SwingTwistConstraint* ptr = ((SwingTwistConstraint*)constraint.constraint.GetPtr()); + ptr->SetNormalHalfConeAngle(physicscomponent.swing_twist.normal_half_cone_angle); + ptr->SetPlaneHalfConeAngle(physicscomponent.swing_twist.plane_half_cone_angle); + ptr->SetTwistMinAngle(physicscomponent.swing_twist.min_twist_angle); + ptr->SetTwistMaxAngle(physicscomponent.swing_twist.max_twist_angle); + } } }); @@ -2345,6 +2370,7 @@ namespace wi::physics if (physicscomponent.physicsobject == nullptr) return; Constraint& constraint = GetConstraint(physicscomponent); + constraint.constraint->SetDrawConstraintSize(CONSTRAINT_DEBUGSIZE); if (constraint.body1_self.IsInvalid() && constraint.body2_self.IsInvalid()) return; diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 0762f3629..437d9b129 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", 1); // version = 1 + wi::ecs::ComponentManager& constraints = componentLibrary.Register("wi::scene::Scene::constraints", 2); // version = 2 // Non-serialized attributes: float dt = 0; diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 90d56c3cf..58ca3076f 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -543,6 +543,7 @@ namespace wi::scene 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 + SwingTwist, // cone + rotational limits } type = Type::Fixed; wi::ecs::Entity bodyA = wi::ecs::INVALID_ENTITY; @@ -587,6 +588,14 @@ namespace wi::scene void SetFreeRotationZ() { minRotationAxes.z = -XM_PI; maxRotationAxes.z = XM_PI; } } six_dof; + struct SwingTwistConstraintSettings + { + float normal_half_cone_angle = 0; // radians + float plane_half_cone_angle = 0; // radians + float min_twist_angle = 0; // radians [-PI, PI] + float max_twist_angle = 0; // radians [-PI, PI] + } swing_twist; + // 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. diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index cc9283ba2..9e62f2dfb 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -929,6 +929,13 @@ namespace wi::scene archive >> six_dof.minRotationAxes; archive >> six_dof.maxRotationAxes; } + if (seri.GetVersion() >= 2) + { + archive >> swing_twist.normal_half_cone_angle; + archive >> swing_twist.plane_half_cone_angle; + archive >> swing_twist.min_twist_angle; + archive >> swing_twist.max_twist_angle; + } } else { @@ -948,6 +955,13 @@ namespace wi::scene archive << six_dof.minRotationAxes; archive << six_dof.maxRotationAxes; } + if (seri.GetVersion() >= 2) + { + archive << swing_twist.normal_half_cone_angle; + archive << swing_twist.plane_half_cone_angle; + archive << swing_twist.min_twist_angle; + archive << swing_twist.max_twist_angle; + } } } void SoftBodyPhysicsComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 1421ac3bb..8f9bcba33 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 732; + const int revision = 733; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);