EditorRigidBodyComponent.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Interface/Interface.h>
  9. #include <AzCore/Serialization/EditContext.h>
  10. #include <AzFramework/Physics/Utils.h>
  11. #include <AzFramework/Physics/NameConstants.h>
  12. #include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
  13. #include <Source/EditorRigidBodyComponent.h>
  14. #include <Source/EditorColliderComponent.h>
  15. #include <Source/EditorMeshColliderComponent.h>
  16. #include <Source/EditorShapeColliderComponent.h>
  17. #include <Editor/Source/Components/EditorSystemComponent.h>
  18. #include <Source/RigidBodyComponent.h>
  19. #include <Editor/InertiaPropertyHandler.h>
  20. #include <Editor/KinematicDescriptionDialog.h>
  21. #include <Source/NameConstants.h>
  22. #include <Source/Utils.h>
  23. #include <System/PhysXSystem.h>
  24. #include <PhysX/PhysXLocks.h>
  25. #include <AzFramework/Physics/PropertyTypes.h>
  26. #include <AzToolsFramework/UI/PropertyEditor/PropertyBoolComboBoxCtrl.hxx>
  27. #include <LyViewPaneNames.h>
  28. namespace PhysX
  29. {
  30. namespace Internal
  31. {
  32. AZStd::vector<AZStd::shared_ptr<Physics::Shape>> CreateCollisionShapes(AZ::Entity* entity)
  33. {
  34. AZStd::vector<AZStd::shared_ptr<Physics::Shape>> allShapes;
  35. const bool hasNonUniformScaleComponent = (AZ::NonUniformScaleRequestBus::FindFirstHandler(entity->GetId()) != nullptr);
  36. for (const EditorColliderComponent* collider : entity->FindComponents<EditorColliderComponent>())
  37. {
  38. const EditorProxyShapeConfig& shapeConfigurationProxy = collider->GetShapeConfiguration();
  39. const Physics::ShapeConfiguration& shapeConfiguration = shapeConfigurationProxy.GetCurrent();
  40. if (!hasNonUniformScaleComponent && !shapeConfigurationProxy.IsCylinderConfig())
  41. {
  42. const Physics::ColliderConfiguration colliderConfigurationScaled = collider->GetColliderConfigurationScaled();
  43. AZStd::shared_ptr<Physics::Shape> shape = AZ::Interface<Physics::System>::Get()->CreateShape(
  44. colliderConfigurationScaled, shapeConfiguration);
  45. AZ_Assert(shape, "CreateEditorWorldRigidBody: Shape must not be null!");
  46. if (shape)
  47. {
  48. allShapes.emplace_back(shape);
  49. }
  50. }
  51. else
  52. {
  53. const Physics::ColliderConfiguration colliderConfigurationUnscaled = collider->GetColliderConfiguration();
  54. auto convexConfig = Utils::CreateConvexFromPrimitive(colliderConfigurationUnscaled, shapeConfiguration,
  55. shapeConfigurationProxy.m_subdivisionLevel, shapeConfiguration.m_scale);
  56. auto colliderConfigurationNoOffset = colliderConfigurationUnscaled;
  57. colliderConfigurationNoOffset.m_rotation = AZ::Quaternion::CreateIdentity();
  58. colliderConfigurationNoOffset.m_position = AZ::Vector3::CreateZero();
  59. if (convexConfig.has_value())
  60. {
  61. AZStd::shared_ptr<Physics::Shape> shape = AZ::Interface<Physics::System>::Get()->CreateShape(
  62. colliderConfigurationNoOffset, convexConfig.value());
  63. allShapes.emplace_back(shape);
  64. }
  65. }
  66. }
  67. for (const EditorMeshColliderComponent* collider : entity->FindComponents<EditorMeshColliderComponent>())
  68. {
  69. const EditorProxyAssetShapeConfig& shapeConfigurationProxy = collider->GetShapeConfiguration();
  70. if (!shapeConfigurationProxy.m_physicsAsset.m_configuration.m_asset.IsReady())
  71. {
  72. continue;
  73. }
  74. const AZ::Vector3& assetScale = shapeConfigurationProxy.m_physicsAsset.m_configuration.m_assetScale;
  75. const Physics::ColliderConfiguration colliderConfigurationUnscaled = collider->GetColliderConfiguration();
  76. AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
  77. Utils::CreateShapesFromAsset(
  78. shapeConfigurationProxy.m_physicsAsset.m_configuration,
  79. colliderConfigurationUnscaled,
  80. hasNonUniformScaleComponent || !Physics::Utils::HasUniformScale(assetScale),
  81. shapeConfigurationProxy.m_subdivisionLevel,
  82. shapes);
  83. for (const auto& shape : shapes)
  84. {
  85. AZ_Assert(shape, "CreateEditorWorldRigidBody: Shape must not be null!");
  86. allShapes.emplace_back(shape);
  87. }
  88. }
  89. for (const EditorShapeColliderComponent* shapeCollider : entity->FindComponents<EditorShapeColliderComponent>())
  90. {
  91. const Physics::ColliderConfiguration colliderConfig = shapeCollider->GetColliderConfigurationScaled();
  92. const AZStd::vector<AZStd::shared_ptr<Physics::ShapeConfiguration>>& shapeConfigs =
  93. shapeCollider->GetShapeConfigurations();
  94. for (const auto& shapeConfig : shapeConfigs)
  95. {
  96. AZStd::shared_ptr<Physics::Shape> shape = AZ::Interface<Physics::System>::Get()->CreateShape(colliderConfig, *shapeConfig);
  97. AZ_Assert(shape, "CreateEditorWorldRigidBody: Shape must not be null!");
  98. allShapes.emplace_back(shape);
  99. }
  100. }
  101. return allShapes;
  102. }
  103. } // namespace Internal
  104. bool IsDefaultSceneCcdEnabled()
  105. {
  106. if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
  107. {
  108. return physicsSystem->GetDefaultSceneConfiguration().m_enableCcd;
  109. }
  110. return false;
  111. }
  112. static bool IsSceneCcdDisabled()
  113. {
  114. return !IsDefaultSceneCcdEnabled();
  115. }
  116. static void OpenPhysXConfigurationPane()
  117. {
  118. AzToolsFramework::EditorRequestBus::Broadcast(
  119. &AzToolsFramework::EditorRequests::OpenViewPane, LyViewPane::PhysXConfigurationEditor);
  120. }
  121. static AzToolsFramework::GenericEditResultOutcome<bool> OnEditButtonClicked(bool comboBoxValue)
  122. {
  123. QWidget* mainWindow = nullptr;
  124. AzToolsFramework::EditorRequestBus::BroadcastResult(mainWindow, &AzToolsFramework::EditorRequests::GetMainWindow);
  125. PhysX::Editor::KinematicDescriptionDialog kinematicDialog(comboBoxValue, mainWindow);
  126. int dialogResult = kinematicDialog.exec();
  127. if (dialogResult == QDialog::Accepted)
  128. {
  129. return AZ::Success(kinematicDialog.GetResult());
  130. }
  131. return AZ::Failure("No result from dialog");
  132. }
  133. void EditorRigidBodyConfiguration::Reflect(AZ::ReflectContext* context)
  134. {
  135. auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  136. if (serializeContext)
  137. {
  138. serializeContext->Class<EditorRigidBodyConfiguration, AzPhysics::RigidBodyConfiguration>()
  139. ->Version(1)
  140. ->Field("Debug Draw Center of Mass", &EditorRigidBodyConfiguration::m_centerOfMassDebugDraw)
  141. ;
  142. AZ::EditContext* editContext = serializeContext->GetEditContext();
  143. if (editContext)
  144. {
  145. editContext->Class<AzPhysics::RigidBodyConfiguration>(
  146. "PhysX Rigid Body Configuration", "")
  147. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  148. ->Attribute(AZ::Edit::Attributes::Category, "PhysX")
  149. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  150. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialLinearVelocity,
  151. "Initial linear velocity", "Linear velocity applied when the rigid body is activated.")
  152. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility)
  153. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSpeedUnit())
  154. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialAngularVelocity,
  155. "Initial angular velocity", "Angular velocity applied when the rigid body is activated (limited by maximum angular velocity).")
  156. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility)
  157. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit())
  158. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_linearDamping,
  159. "Linear damping", "The rate of decay over time for linear velocity even if no forces are acting on the rigid body.")
  160. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility)
  161. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  162. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_angularDamping,
  163. "Angular damping", "The rate of decay over time for angular velocity even if no forces are acting on the rigid body.")
  164. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility)
  165. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  166. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_sleepMinEnergy,
  167. "Sleep threshold", "The rigid body can go to sleep (settle) when kinetic energy per unit mass is persistently below this value.")
  168. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility)
  169. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  170. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSleepThresholdUnit())
  171. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_startAsleep,
  172. "Start asleep", "When active, the rigid body will be asleep when spawned, and wake when the body is disturbed.")
  173. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility)
  174. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_interpolateMotion,
  175. "Interpolate motion", "When active, simulation results are interpolated resulting in smoother motion.")
  176. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInterpolationVisibility)
  177. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_gravityEnabled,
  178. "Gravity enabled", "When active, global gravity affects this rigid body.")
  179. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetGravityVisibility)
  180. ->DataElement(
  181. AZ::Edit::UIHandlers::ComboBox,
  182. &AzPhysics::RigidBodyConfiguration::m_kinematic,
  183. "Type", "Determines how the movement/position of the rigid body is controlled.")
  184. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetKinematicVisibility)
  185. ->Attribute(AZ::Edit::Attributes::ReadOnly, &AzPhysics::RigidBodyConfiguration::m_ccdEnabled)
  186. ->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &AzPhysics::RigidBodyConfiguration::GetKinematicTooltip)
  187. ->Attribute(AZ_CRC_CE("EditButtonVisible"), true)
  188. ->Attribute(AZ_CRC_CE("SetTrueLabel"), "Kinematic")
  189. ->Attribute(AZ_CRC_CE("SetFalseLabel"), "Simulated")
  190. ->Attribute(
  191. AZ_CRC_CE("EditButtonCallback"), AzToolsFramework::GenericEditButtonCallback<bool>(&OnEditButtonClicked))
  192. ->Attribute(AZ_CRC_CE("EditButtonToolTip"), "Open Type dialog for a detailed description on the motion types")
  193. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
  194. // Linear axis locking properties
  195. ->ClassElement(AZ::Edit::ClassElements::Group, "Linear Axis Locking")
  196. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  197. ->DataElement(
  198. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearX, "Lock X",
  199. "When active, forces won't create translation on the X axis of the rigid body.")
  200. ->DataElement(
  201. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearY, "Lock Y",
  202. "When active, forces won't create translation on the Y axis of the rigid body.")
  203. ->DataElement(
  204. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearZ, "Lock Z",
  205. "When active, forces won't create translation on the Z axis of the rigid body.")
  206. // Angular axis locking properties
  207. ->ClassElement(AZ::Edit::ClassElements::Group, "Angular Axis Locking")
  208. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  209. ->DataElement(
  210. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularX, "Lock X",
  211. "When active, forces won't create rotation on the X axis of the rigid body.")
  212. ->DataElement(
  213. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularY, "Lock Y",
  214. "When active, forces won't create rotation on the Y axis of the rigid body.")
  215. ->DataElement(
  216. AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularZ, "Lock Z",
  217. "When active, forces won't create rotation on the Z axis of the rigid body.")
  218. ->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection")
  219. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  220. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCcdVisibility)
  221. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdEnabled,
  222. "CCD enabled", "When active, the rigid body has continuous collision detection (CCD). Use this to ensure accurate "
  223. "collision detection, particularly for fast moving rigid bodies. CCD must be activated in the global PhysX configuration.")
  224. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCcdVisibility)
  225. ->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &AzPhysics::RigidBodyConfiguration::GetCcdTooltip)
  226. ->Attribute(AZ::Edit::Attributes::ReadOnly, &AzPhysics::RigidBodyConfiguration::CcdReadOnly)
  227. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  228. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdMinAdvanceCoefficient,
  229. "Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness.")
  230. ->Attribute(AZ::Edit::Attributes::Min, 0.01f)
  231. ->Attribute(AZ::Edit::Attributes::Step, 0.01f)
  232. ->Attribute(AZ::Edit::Attributes::Max, 0.99f)
  233. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCcdEnabled)
  234. ->Attribute(AZ::Edit::Attributes::ReadOnly, &IsSceneCcdDisabled)
  235. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdFrictionEnabled,
  236. "CCD friction", "When active, friction is applied when continuous collision detection (CCD) collisions are resolved.")
  237. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCcdEnabled)
  238. ->Attribute(AZ::Edit::Attributes::ReadOnly, &IsSceneCcdDisabled)
  239. ->DataElement(
  240. AZ::Edit::UIHandlers::Button,
  241. &AzPhysics::RigidBodyConfiguration::m_configButton,
  242. "",
  243. "Click here to open the PhysX Configuration window. Enable global CCD to enable component CCD editing.")
  244. ->Attribute(AZ::Edit::Attributes::ButtonText, "Open PhysX Configuration to Enable CCD")
  245. ->Attribute(AZ::Edit::Attributes::Visibility, &IsSceneCcdDisabled)
  246. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenPhysXConfigurationPane)
  247. ->EndGroup()
  248. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_maxAngularVelocity,
  249. "Maximum angular velocity", "Clamp angular velocities to this maximum value. "
  250. "This prevents rigid bodies from rotating at unrealistic velocities after collisions.")
  251. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  252. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMaxVelocitiesVisibility)
  253. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit())
  254. // Mass properties
  255. ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeCenterOfMass,
  256. "Compute COM", "Compute the center of mass (COM) for this rigid body.")
  257. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
  258. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  259. ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_centerOfMassOffset,
  260. "COM offset", "Local space offset for the center of mass (COM).")
  261. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCoMVisibility)
  262. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetLengthUnit())
  263. ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeMass,
  264. "Compute Mass", "When active, the mass of the rigid body is computed based on the volume and density values of its colliders.")
  265. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
  266. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  267. ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_mass,
  268. "Mass", "The mass of the rigid body in kilograms. A value of 0 is treated as infinite. "
  269. "The trajectory of infinite mass bodies cannot be affected by any collisions or forces other than gravity.")
  270. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  271. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetMassUnit())
  272. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMassVisibility)
  273. ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeInertiaTensor,
  274. "Compute inertia", "When active, inertia is computed based on the mass and shape of the rigid body.")
  275. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
  276. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  277. ->DataElement(Editor::InertiaHandler, &AzPhysics::RigidBodyConfiguration::m_inertiaTensor,
  278. "Inertia diagonal", "Inertia diagonal elements that specify an inertia tensor; determines the "
  279. "torque required to rotate the rigid body on each axis.")
  280. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaVisibility)
  281. ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetInertiaUnit())
  282. ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_includeAllShapesInMassCalculation,
  283. "Include non-simulated shapes in Mass",
  284. "When active, non-simulated shapes are included in the center of mass, inertia, and mass calculations.")
  285. ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
  286. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  287. ;
  288. editContext->Class<EditorRigidBodyConfiguration>(
  289. "PhysX Rigid Body Configuration", "")
  290. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  291. ->Attribute(AZ::Edit::Attributes::Category, "PhysX")
  292. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  293. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorRigidBodyConfiguration::m_centerOfMassDebugDraw,
  294. "Debug draw COM", "Display the rigid body's center of mass (COM) in the viewport.")
  295. ;
  296. }
  297. }
  298. }
  299. void EditorRigidBodyComponent::Activate()
  300. {
  301. // During activation all the editor collider components will create their physics shapes.
  302. // Delaying the creation of the editor dynamic rigid body to OnEntityActivated so all the shapes are ready.
  303. AZ::EntityBus::Handler::BusConnect(GetEntityId());
  304. }
  305. void EditorRigidBodyComponent::OnEntityActivated(const AZ::EntityId& entityId)
  306. {
  307. AZ::EntityBus::Handler::BusDisconnect();
  308. AzToolsFramework::Components::EditorComponentBase::Activate();
  309. AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(entityId);
  310. AZ::TransformNotificationBus::Handler::BusConnect(entityId);
  311. Physics::ColliderComponentEventBus::Handler::BusConnect(entityId);
  312. AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
  313. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(entityId);
  314. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  315. {
  316. AzPhysics::SceneHandle editorSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName);
  317. sceneInterface->RegisterSceneSimulationStartHandler(editorSceneHandle, m_sceneStartSimHandler);
  318. }
  319. m_sceneConfigChangedHandler = AzPhysics::SystemEvents::OnDefaultSceneConfigurationChangedEvent::Handler(
  320. [this]([[maybe_unused]] const AzPhysics::SceneConfiguration* config)
  321. {
  322. this->InvalidatePropertyDisplay(AzToolsFramework::Refresh_EntireTree);
  323. });
  324. if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
  325. {
  326. physicsSystem->RegisterOnDefaultSceneConfigurationChangedEventHandler(m_sceneConfigChangedHandler);
  327. }
  328. m_nonUniformScaleChangedHandler = AZ::NonUniformScaleChangedEvent::Handler(
  329. [this](const AZ::Vector3& scale) {OnNonUniformScaleChanged(scale); });
  330. AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
  331. m_nonUniformScaleChangedHandler);
  332. if (auto* physXDebug = AZ::Interface<Debug::PhysXDebugInterface>::Get())
  333. {
  334. m_debugDisplayDataChangeHandler = Debug::DebugDisplayDataChangedEvent::Handler(
  335. [this](const Debug::DebugDisplayData& data)
  336. {
  337. this->UpdateDebugDrawSettings(data);
  338. });
  339. physXDebug->RegisterDebugDisplayDataChangedEvent(m_debugDisplayDataChangeHandler);
  340. UpdateDebugDrawSettings(physXDebug->GetDebugDisplayData());
  341. }
  342. CreateEditorWorldRigidBody();
  343. PhysX::EditorMeshColliderValidationRequestBus::Event(
  344. entityId, &PhysX::EditorMeshColliderValidationRequestBus::Events::ValidateRigidBodyMeshGeometryType);
  345. AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(entityId);
  346. }
  347. void EditorRigidBodyComponent::Deactivate()
  348. {
  349. AZ::EntityBus::Handler::BusDisconnect();
  350. m_debugDisplayDataChangeHandler.Disconnect();
  351. m_sceneConfigChangedHandler.Disconnect();
  352. AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect();
  353. m_nonUniformScaleChangedHandler.Disconnect();
  354. m_sceneStartSimHandler.Disconnect();
  355. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  356. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  357. Physics::ColliderComponentEventBus::Handler::BusDisconnect();
  358. AZ::TransformNotificationBus::Handler::BusDisconnect();
  359. AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
  360. AzToolsFramework::Components::EditorComponentBase::Deactivate();
  361. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  362. {
  363. sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorRigidBodyHandle);
  364. }
  365. }
  366. void EditorRigidBodyComponent::OnConfigurationChanged()
  367. {
  368. CreateEditorWorldRigidBody();
  369. // required in case the kinematic setting has changed
  370. PhysX::EditorMeshColliderValidationRequestBus::Event(
  371. GetEntityId(), &PhysX::EditorMeshColliderValidationRequestBus::Events::ValidateRigidBodyMeshGeometryType);
  372. }
  373. void EditorRigidBodyComponent::Reflect(AZ::ReflectContext* context)
  374. {
  375. EditorRigidBodyConfiguration::Reflect(context);
  376. auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  377. if (serializeContext)
  378. {
  379. serializeContext->Class<EditorRigidBodyComponent, AzToolsFramework::Components::EditorComponentBase>()
  380. ->Field("Configuration", &EditorRigidBodyComponent::m_config)
  381. ->Field("PhysXSpecificConfiguration", &EditorRigidBodyComponent::m_physxSpecificConfig)
  382. ->Version(1)
  383. ;
  384. AZ::EditContext* editContext = serializeContext->GetEditContext();
  385. if (editContext)
  386. {
  387. editContext->Class<EditorRigidBodyComponent>(
  388. "PhysX Dynamic Rigid Body", "The entity behaves as a movable rigid body in PhysX.")
  389. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  390. ->Attribute(AZ::Edit::Attributes::Category, "PhysX")
  391. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXRigidBody.svg")
  392. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXRigidBody.svg")
  393. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  394. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  395. ->Attribute(
  396. AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/rigid-body/")
  397. ->DataElement(
  398. AZ::Edit::UIHandlers::Default,
  399. &EditorRigidBodyComponent::m_config,
  400. "Configuration",
  401. "Configuration for rigid body physics.")
  402. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  403. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::OnConfigurationChanged)
  404. ->DataElement(
  405. AZ::Edit::UIHandlers::Default,
  406. &EditorRigidBodyComponent::m_physxSpecificConfig,
  407. "PhysX-Specific Configuration",
  408. "Settings which are specific to PhysX, rather than generic.")
  409. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  410. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRigidBodyComponent::OnConfigurationChanged)
  411. ;
  412. }
  413. }
  414. }
  415. EditorRigidBodyComponent::EditorRigidBodyComponent()
  416. {
  417. InitPhysicsTickHandler();
  418. }
  419. EditorRigidBodyComponent::EditorRigidBodyComponent(const EditorRigidBodyConfiguration& config)
  420. : m_config(config)
  421. {
  422. InitPhysicsTickHandler();
  423. }
  424. EditorRigidBodyComponent::EditorRigidBodyComponent(
  425. const EditorRigidBodyConfiguration& configuration, const RigidBodyConfiguration& physxSpecificConfiguration)
  426. : m_config(configuration)
  427. , m_physxSpecificConfig(physxSpecificConfiguration)
  428. {
  429. InitPhysicsTickHandler();
  430. }
  431. void EditorRigidBodyComponent::BuildGameEntity(AZ::Entity* gameEntity)
  432. {
  433. // for now use Invalid scene which will fall back on default scene when entity is activated.
  434. // update to correct scene once multi-scene is fully supported.
  435. gameEntity->CreateComponent<RigidBodyComponent>(m_config, m_physxSpecificConfig, AzPhysics::InvalidSceneHandle);
  436. }
  437. void EditorRigidBodyComponent::DisplayEntityViewport(
  438. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
  439. AzFramework::DebugDisplayRequests& debugDisplay)
  440. {
  441. if (m_config.m_centerOfMassDebugDraw)
  442. {
  443. if (const AzPhysics::RigidBody* body = GetRigidBody())
  444. {
  445. debugDisplay.DepthTestOff();
  446. debugDisplay.SetColor(m_centerOfMassDebugColor);
  447. debugDisplay.DrawBall(body->GetCenterOfMassWorld(), m_centerOfMassDebugSize);
  448. debugDisplay.DepthTestOn();
  449. }
  450. }
  451. }
  452. void EditorRigidBodyComponent::CreateEditorWorldRigidBody()
  453. {
  454. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  455. {
  456. m_editorSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName);
  457. if (m_editorSceneHandle == AzPhysics::InvalidSceneHandle)
  458. {
  459. AZ_Assert(false, "Attempting to create an edit time rigid body without an editor scene.");
  460. return;
  461. }
  462. }
  463. AZ::Transform colliderTransform = GetWorldTM();
  464. colliderTransform.ExtractUniformScale();
  465. AzPhysics::RigidBodyConfiguration configuration = m_config;
  466. configuration.m_orientation = colliderTransform.GetRotation();
  467. configuration.m_position = colliderTransform.GetTranslation();
  468. configuration.m_entityId = GetEntityId();
  469. configuration.m_debugName = GetEntity()->GetName();
  470. configuration.m_colliderAndShapeData = Internal::CreateCollisionShapes(GetEntity());
  471. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  472. {
  473. m_editorRigidBodyHandle = sceneInterface->AddSimulatedBody(m_editorSceneHandle, &configuration);
  474. if (auto* body = azdynamic_cast<AzPhysics::RigidBody*>(
  475. sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorRigidBodyHandle)
  476. ))
  477. {
  478. // AddSimulatedBody may update mass / CoM / Inertia tensor based on the config, so grab the updated values.
  479. m_config.m_mass = body->GetMass();
  480. m_config.m_centerOfMassOffset = body->GetCenterOfMassLocal();
  481. m_config.m_inertiaTensor = body->GetInertiaLocal();
  482. // Set simulation disabled for this actor so it doesn't actually interact when the editor world is updated.
  483. if (physx::PxActor* pxActor = static_cast<physx::PxActor*>(body->GetNativePointer()))
  484. {
  485. PHYSX_SCENE_WRITE_LOCK(pxActor->getScene());
  486. pxActor->setActorFlag(physx::PxActorFlag::eDISABLE_SIMULATION, true);
  487. }
  488. }
  489. }
  490. AZ_Error("EditorRigidBodyComponent",
  491. m_editorRigidBodyHandle != AzPhysics::InvalidSimulatedBodyHandle, "Failed to create editor rigid body");
  492. }
  493. void EditorRigidBodyComponent::OnColliderChanged()
  494. {
  495. //recreate the rigid body when collider changes
  496. SetShouldBeRecreated();
  497. }
  498. void EditorRigidBodyComponent::OnTransformChanged([[maybe_unused]]const AZ::Transform& local, [[maybe_unused]]const AZ::Transform& world)
  499. {
  500. SetShouldBeRecreated();
  501. }
  502. void EditorRigidBodyComponent::OnNonUniformScaleChanged([[maybe_unused]] const AZ::Vector3& scale)
  503. {
  504. SetShouldBeRecreated();
  505. }
  506. void EditorRigidBodyComponent::InitPhysicsTickHandler()
  507. {
  508. m_sceneStartSimHandler = AzPhysics::SceneEvents::OnSceneSimulationStartHandler([this](
  509. [[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
  510. [[maybe_unused]] float fixedDeltatime
  511. )
  512. {
  513. this->PrePhysicsTick();
  514. }, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components));
  515. }
  516. void EditorRigidBodyComponent::PrePhysicsTick()
  517. {
  518. if (m_shouldBeRecreated)
  519. {
  520. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  521. {
  522. sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorRigidBodyHandle);
  523. CreateEditorWorldRigidBody();
  524. }
  525. m_shouldBeRecreated = false;
  526. }
  527. }
  528. void EditorRigidBodyComponent::EnablePhysics()
  529. {
  530. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  531. {
  532. sceneInterface->EnableSimulationOfBody(m_editorSceneHandle, m_editorRigidBodyHandle);
  533. }
  534. }
  535. void EditorRigidBodyComponent::DisablePhysics()
  536. {
  537. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  538. {
  539. sceneInterface->DisableSimulationOfBody(m_editorSceneHandle, m_editorRigidBodyHandle);
  540. }
  541. }
  542. bool EditorRigidBodyComponent::IsPhysicsEnabled() const
  543. {
  544. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  545. {
  546. if (AzPhysics::SimulatedBody* body =
  547. sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorRigidBodyHandle))
  548. {
  549. return body->m_simulating;
  550. }
  551. }
  552. return false;
  553. }
  554. AZ::Aabb EditorRigidBodyComponent::GetAabb() const
  555. {
  556. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  557. {
  558. if (AzPhysics::SimulatedBody* body =
  559. sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorRigidBodyHandle))
  560. {
  561. return body->GetAabb();
  562. }
  563. }
  564. return AZ::Aabb::CreateNull();
  565. }
  566. AzPhysics::SimulatedBody* EditorRigidBodyComponent::GetSimulatedBody()
  567. {
  568. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  569. {
  570. return sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorRigidBodyHandle);
  571. }
  572. return nullptr;
  573. }
  574. AzPhysics::SimulatedBodyHandle EditorRigidBodyComponent::GetSimulatedBodyHandle() const
  575. {
  576. return m_editorRigidBodyHandle;
  577. }
  578. AzPhysics::SceneQueryHit EditorRigidBodyComponent::RayCast(const AzPhysics::RayCastRequest& request)
  579. {
  580. if (AzPhysics::SimulatedBody* body = GetSimulatedBody())
  581. {
  582. return body->RayCast(request);
  583. }
  584. return AzPhysics::SceneQueryHit();
  585. }
  586. void EditorRigidBodyComponent::UpdateDebugDrawSettings(const Debug::DebugDisplayData& data)
  587. {
  588. m_centerOfMassDebugColor = data.m_centerOfMassDebugColor;
  589. m_centerOfMassDebugSize = data.m_centerOfMassDebugSize;
  590. }
  591. const AzPhysics::RigidBody* EditorRigidBodyComponent::GetRigidBody() const
  592. {
  593. if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  594. {
  595. return azdynamic_cast<AzPhysics::RigidBody*>(
  596. sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorRigidBodyHandle));
  597. }
  598. return nullptr;
  599. }
  600. void EditorRigidBodyComponent::SetShouldBeRecreated()
  601. {
  602. if (!m_shouldBeRecreated)
  603. {
  604. m_shouldBeRecreated = true;
  605. }
  606. }
  607. AZ::Aabb EditorRigidBodyComponent::GetWorldBounds() const
  608. {
  609. return GetAabb();
  610. }
  611. AZ::Aabb EditorRigidBodyComponent::GetLocalBounds() const
  612. {
  613. AZ::Aabb worldBounds = GetWorldBounds();
  614. if (worldBounds.IsValid())
  615. {
  616. return worldBounds.GetTransformedAabb(GetWorldTM().GetInverse());
  617. }
  618. return AZ::Aabb::CreateNull();
  619. }
  620. bool EditorRigidBodyComponent::SupportsEditorRayIntersect()
  621. {
  622. return true;
  623. }
  624. AZ::Aabb EditorRigidBodyComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
  625. {
  626. return GetWorldBounds();
  627. }
  628. bool EditorRigidBodyComponent::EditorSelectionIntersectRayViewport(
  629. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
  630. {
  631. AzPhysics::RayCastRequest request;
  632. request.m_direction = dir;
  633. request.m_distance = distance;
  634. request.m_start = src;
  635. if (auto hit = RayCast(request))
  636. {
  637. distance = hit.m_distance;
  638. return true;
  639. }
  640. return false;
  641. }
  642. } // namespace PhysX