EditorMeshColliderComponent.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  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 <AzFramework/Physics/Configuration/StaticRigidBodyConfiguration.h>
  9. #include <AzFramework/Physics/Utils.h>
  10. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  11. #include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
  12. #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
  13. #include <Editor/ColliderComponentMode.h>
  14. #include <System/PhysXSystem.h>
  15. #include <Source/EditorRigidBodyComponent.h>
  16. #include <Source/EditorStaticRigidBodyComponent.h>
  17. #include <Source/MeshColliderComponent.h>
  18. #include <Source/Utils.h>
  19. #include <Source/EditorMeshColliderComponent.h>
  20. namespace PhysX
  21. {
  22. void EditorProxyPhysicsAsset::Reflect(AZ::ReflectContext* context)
  23. {
  24. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  25. {
  26. serializeContext->Class<EditorProxyPhysicsAsset>()
  27. ->Version(1)
  28. ->Field("Asset", &EditorProxyPhysicsAsset::m_pxAsset)
  29. ->Field("Configuration", &EditorProxyPhysicsAsset::m_configuration);
  30. if (auto* editContext = serializeContext->GetEditContext())
  31. {
  32. editContext->Class<EditorProxyPhysicsAsset>("EditorProxyPhysicsAsset", "PhysX Asset.")
  33. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  34. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  35. ->DataElement(
  36. AZ::Edit::UIHandlers::Default,
  37. &EditorProxyPhysicsAsset::m_pxAsset,
  38. "PhysX Mesh",
  39. "Specifies the PhysX mesh collider asset for this PhysX collider component.")
  40. ->Attribute(AZ_CRC_CE("EditButton"), "")
  41. ->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings")
  42. ->Attribute(AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"), true)
  43. ->DataElement(
  44. AZ::Edit::UIHandlers::Default,
  45. &EditorProxyPhysicsAsset::m_configuration,
  46. "Configuration",
  47. "PhysX mesh asset collider configuration.")
  48. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
  49. }
  50. }
  51. }
  52. void EditorProxyAssetShapeConfig::Reflect(AZ::ReflectContext* context)
  53. {
  54. EditorProxyPhysicsAsset::Reflect(context);
  55. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  56. {
  57. serializeContext->Class<EditorProxyAssetShapeConfig>()
  58. ->Version(1)
  59. ->Field("PhysicsAsset", &EditorProxyAssetShapeConfig::m_physicsAsset)
  60. ->Field("HasNonUniformScale", &EditorProxyAssetShapeConfig::m_hasNonUniformScale)
  61. ->Field("SubdivisionLevel", &EditorProxyAssetShapeConfig::m_subdivisionLevel);
  62. if (auto* editContext = serializeContext->GetEditContext())
  63. {
  64. editContext->Class<EditorProxyAssetShapeConfig>("EditorProxyAssetShapeConfig", "PhysX asset collider.")
  65. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  66. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  67. ->DataElement(
  68. AZ::Edit::UIHandlers::Default,
  69. &EditorProxyAssetShapeConfig::m_physicsAsset,
  70. "Asset",
  71. "Configuration of asset shape.")
  72. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyAssetShapeConfig::OnConfigurationChanged)
  73. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorProxyAssetShapeConfig::PhysXMeshAssetShapeTypeName)
  74. ->DataElement(
  75. AZ::Edit::UIHandlers::Default,
  76. &EditorProxyAssetShapeConfig::m_subdivisionLevel,
  77. "Subdivision level",
  78. "The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.")
  79. ->Attribute(AZ::Edit::Attributes::Min, Utils::MinCapsuleSubdivisionLevel)
  80. ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxCapsuleSubdivisionLevel)
  81. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyAssetShapeConfig::ShowingSubdivisionLevel)
  82. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyAssetShapeConfig::OnConfigurationChanged);
  83. }
  84. }
  85. }
  86. EditorProxyAssetShapeConfig::EditorProxyAssetShapeConfig(
  87. const Physics::PhysicsAssetShapeConfiguration& assetShapeConfiguration)
  88. {
  89. m_physicsAsset.m_pxAsset = assetShapeConfiguration.m_asset;
  90. m_physicsAsset.m_configuration = assetShapeConfiguration;
  91. }
  92. AZStd::vector<EditorProxyAssetShapeConfig::ShapeType> EditorProxyAssetShapeConfig::GetShapeTypesInsideAsset() const
  93. {
  94. if (!m_physicsAsset.m_pxAsset.IsReady())
  95. {
  96. return {};
  97. }
  98. Physics::ColliderConfiguration defaultColliderConfiguration;
  99. Physics::PhysicsAssetShapeConfiguration physicsAssetConfiguration = m_physicsAsset.m_configuration;
  100. physicsAssetConfiguration.m_asset = m_physicsAsset.m_pxAsset;
  101. physicsAssetConfiguration.m_assetScale = AZ::Vector3::CreateOne(); // Remove the scale so it doesn't affect the query for the asset mesh type
  102. const bool hasNonUniformScale = false;
  103. AzPhysics::ShapeColliderPairList shapeConfigList;
  104. Utils::GetColliderShapeConfigsFromAsset(
  105. physicsAssetConfiguration, defaultColliderConfiguration, hasNonUniformScale, m_subdivisionLevel, shapeConfigList);
  106. AZStd::vector<ShapeType> shapeTypes;
  107. shapeTypes.reserve(shapeConfigList.size());
  108. for (const auto& shapeConfig : shapeConfigList)
  109. {
  110. const Physics::ShapeConfiguration* shapeConfiguration = shapeConfig.second.get();
  111. AZ_Assert(shapeConfiguration, "GetShapeTypesInsideAsset: Invalid shape-collider configuration pair");
  112. switch (shapeConfiguration->GetShapeType())
  113. {
  114. case Physics::ShapeType::CookedMesh:
  115. {
  116. const Physics::CookedMeshShapeConfiguration* cookedMeshShapeConfiguration =
  117. static_cast<const Physics::CookedMeshShapeConfiguration*>(shapeConfiguration);
  118. switch (cookedMeshShapeConfiguration->GetMeshType())
  119. {
  120. case Physics::CookedMeshShapeConfiguration::MeshType::Convex:
  121. shapeTypes.push_back(ShapeType::Convex);
  122. break;
  123. case Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh:
  124. shapeTypes.push_back(ShapeType::TriangleMesh);
  125. break;
  126. default:
  127. shapeTypes.push_back(ShapeType::Invalid);
  128. break;
  129. }
  130. }
  131. break;
  132. case Physics::ShapeType::Sphere:
  133. case Physics::ShapeType::Box:
  134. case Physics::ShapeType::Capsule:
  135. shapeTypes.push_back(ShapeType::Primitive);
  136. break;
  137. default:
  138. shapeTypes.push_back(ShapeType::Invalid);
  139. break;
  140. }
  141. }
  142. return shapeTypes;
  143. }
  144. AZStd::string EditorProxyAssetShapeConfig::PhysXMeshAssetShapeTypeName() const
  145. {
  146. const AZStd::string assetName = "Asset";
  147. const AZStd::vector<ShapeType> shapeTypes = GetShapeTypesInsideAsset();
  148. if (shapeTypes.empty())
  149. {
  150. return assetName;
  151. }
  152. // Using the first shape type as representative for shapes inside the asset.
  153. switch (shapeTypes[0])
  154. {
  155. case ShapeType::Primitive:
  156. return assetName + " (Primitive)";
  157. case ShapeType::Convex:
  158. return assetName + " (Convex)";
  159. case ShapeType::TriangleMesh:
  160. return assetName + " (Triangle Mesh)";
  161. default:
  162. return assetName;
  163. }
  164. }
  165. bool EditorProxyAssetShapeConfig::ShowingSubdivisionLevel() const
  166. {
  167. const AZStd::vector<ShapeType> shapeTypes = GetShapeTypesInsideAsset();
  168. return m_hasNonUniformScale &&
  169. AZStd::any_of(
  170. shapeTypes.begin(),
  171. shapeTypes.end(),
  172. [](const ShapeType& shapeType)
  173. {
  174. return shapeType == ShapeType::Primitive;
  175. });
  176. }
  177. AZ::u32 EditorProxyAssetShapeConfig::OnConfigurationChanged()
  178. {
  179. return AZ::Edit::PropertyRefreshLevels::ValuesOnly;
  180. }
  181. void EditorMeshColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  182. {
  183. provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService"));
  184. provided.push_back(AZ_CRC_CE("PhysicsColliderService"));
  185. provided.push_back(AZ_CRC_CE("PhysicsTriggerService"));
  186. }
  187. void EditorMeshColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  188. {
  189. required.push_back(AZ_CRC_CE("TransformService"));
  190. required.push_back(AZ_CRC_CE("PhysicsRigidBodyService"));
  191. }
  192. void EditorMeshColliderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  193. {
  194. dependent.push_back(AZ_CRC_CE("NonUniformScaleService"));
  195. }
  196. void EditorMeshColliderComponent::Reflect(AZ::ReflectContext* context)
  197. {
  198. EditorProxyAssetShapeConfig::Reflect(context);
  199. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  200. {
  201. serializeContext->Class<EditorMeshColliderComponent, EditorComponentBase>()
  202. ->Version(1 + (1<<PX_PHYSICS_VERSION_MAJOR)) // Use PhysX version to trigger prefabs recompilation when switching between PhysX 4 and 5.
  203. ->Field("ColliderConfiguration", &EditorMeshColliderComponent::m_configuration)
  204. ->Field("ShapeConfiguration", &EditorMeshColliderComponent::m_proxyShapeConfiguration)
  205. ->Field("DebugDrawSettings", &EditorMeshColliderComponent::m_colliderDebugDraw)
  206. ->Field("ComponentMode", &EditorMeshColliderComponent::m_componentModeDelegate)
  207. ->Field("HasNonUniformScale", &EditorMeshColliderComponent::m_hasNonUniformScale)
  208. ;
  209. if (auto editContext = serializeContext->GetEditContext())
  210. {
  211. editContext
  212. ->Class<EditorMeshColliderComponent>(
  213. "PhysX Mesh Collider", "Creates geometry in the PhysX simulation using geometry from an asset.")
  214. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  215. ->Attribute(AZ::Edit::Attributes::Category, "PhysX")
  216. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXMeshCollider.svg")
  217. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/PhysXMeshCollider.svg")
  218. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  219. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://www.o3de.org/docs/user-guide/components/reference/physx/mesh-collider/")
  220. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  221. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorMeshColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider.")
  222. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  223. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMeshColliderComponent::OnConfigurationChanged)
  224. ->DataElement(
  225. AZ::Edit::UIHandlers::Default,
  226. &EditorMeshColliderComponent::m_proxyShapeConfiguration,
  227. "Shape Configuration",
  228. "Configuration of physics asset shape.")
  229. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  230. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMeshColliderComponent::OnConfigurationChanged)
  231. ->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorMeshColliderComponent::ValidateRigidBodyMeshGeometryType)
  232. ->DataElement(
  233. AZ::Edit::UIHandlers::Default,
  234. &EditorMeshColliderComponent::m_componentModeDelegate,
  235. "Component Mode",
  236. "Collider Component Mode.")
  237. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  238. ->DataElement(
  239. AZ::Edit::UIHandlers::Default,
  240. &EditorMeshColliderComponent::m_colliderDebugDraw,
  241. "Debug draw settings", "Debug draw settings.")
  242. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  243. ;
  244. }
  245. }
  246. }
  247. AZ::ComponentDescriptor* EditorMeshColliderComponent::CreateDescriptor()
  248. {
  249. return aznew EditorMeshColliderComponentDescriptor();
  250. }
  251. EditorMeshColliderComponent::EditorMeshColliderComponent(
  252. const Physics::ColliderConfiguration& colliderConfiguration,
  253. const EditorProxyAssetShapeConfig& proxyAssetShapeConfig,
  254. bool debugDrawDisplayFlagEnabled)
  255. : m_configuration(colliderConfiguration)
  256. , m_proxyShapeConfiguration(proxyAssetShapeConfig)
  257. {
  258. m_colliderDebugDraw.SetDisplayFlag(debugDrawDisplayFlagEnabled);
  259. }
  260. EditorMeshColliderComponent::EditorMeshColliderComponent(
  261. const Physics::ColliderConfiguration& colliderConfiguration,
  262. const Physics::PhysicsAssetShapeConfiguration& assetShapeConfig)
  263. : m_configuration(colliderConfiguration)
  264. , m_proxyShapeConfiguration(assetShapeConfig)
  265. {
  266. }
  267. const EditorProxyAssetShapeConfig& EditorMeshColliderComponent::GetShapeConfiguration() const
  268. {
  269. return m_proxyShapeConfiguration;
  270. }
  271. const Physics::ColliderConfiguration& EditorMeshColliderComponent::GetColliderConfiguration() const
  272. {
  273. return m_configuration;
  274. }
  275. Physics::ColliderConfiguration EditorMeshColliderComponent::GetColliderConfigurationScaled() const
  276. {
  277. // Scale the collider offset
  278. Physics::ColliderConfiguration colliderConfiguration = m_configuration;
  279. colliderConfiguration.m_position *= Utils::GetTransformScale(GetEntityId()) * m_cachedNonUniformScale;
  280. return colliderConfiguration;
  281. }
  282. Physics::ColliderConfiguration EditorMeshColliderComponent::GetColliderConfigurationNoOffset() const
  283. {
  284. Physics::ColliderConfiguration colliderConfiguration = m_configuration;
  285. colliderConfiguration.m_position = AZ::Vector3::CreateZero();
  286. colliderConfiguration.m_rotation = AZ::Quaternion::CreateIdentity();
  287. return colliderConfiguration;
  288. }
  289. bool EditorMeshColliderComponent::IsDebugDrawDisplayFlagEnabled() const
  290. {
  291. return m_colliderDebugDraw.IsDisplayFlagEnabled();
  292. }
  293. void EditorMeshColliderComponent::Activate()
  294. {
  295. m_sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  296. if (m_sceneInterface)
  297. {
  298. m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName);
  299. }
  300. m_physXConfigChangedHandler = AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler(
  301. []([[maybe_unused]] const AzPhysics::SystemConfiguration* config)
  302. {
  303. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh,
  304. AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
  305. });
  306. const auto entityId = GetEntityId();
  307. const auto componentId = GetId();
  308. AzToolsFramework::Components::EditorComponentBase::Activate();
  309. AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusConnect(entityId);
  310. PhysX::MeshColliderComponentRequestsBus::Handler::BusConnect(entityId);
  311. AZ::TransformNotificationBus::Handler::BusConnect(entityId);
  312. ColliderShapeRequestBus::Handler::BusConnect(entityId);
  313. AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(entityId);
  314. EditorColliderComponentRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(entityId, componentId));
  315. EditorMeshColliderComponentRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(entityId, componentId));
  316. EditorMeshColliderValidationRequestBus::Handler::BusConnect(entityId);
  317. AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
  318. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(entityId);
  319. m_nonUniformScaleChangedHandler = AZ::NonUniformScaleChangedEvent::Handler(
  320. [this](const AZ::Vector3& scale) {OnNonUniformScaleChanged(scale); });
  321. AZ::NonUniformScaleRequestBus::Event(
  322. entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
  323. m_nonUniformScaleChangedHandler);
  324. AZ::TransformBus::EventResult(m_cachedWorldTransform, entityId, &AZ::TransformInterface::GetWorldTM);
  325. m_cachedNonUniformScale = AZ::Vector3::CreateOne();
  326. AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale);
  327. // Debug drawing
  328. m_colliderDebugDraw.Connect(entityId);
  329. m_colliderDebugDraw.SetDisplayCallback(this);
  330. // ComponentMode
  331. m_componentModeDelegate.ConnectWithSingleComponentMode<
  332. EditorMeshColliderComponent, ColliderComponentMode>(
  333. AZ::EntityComponentIdPair(entityId, componentId), nullptr);
  334. if (ShouldUpdateCollisionMeshFromRender())
  335. {
  336. SetCollisionMeshFromRender();
  337. }
  338. UpdateMeshAsset();
  339. UpdateCollider();
  340. }
  341. void EditorMeshColliderComponent::Deactivate()
  342. {
  343. AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect();
  344. m_colliderDebugDraw.Disconnect();
  345. AZ::Data::AssetBus::Handler::BusDisconnect();
  346. m_nonUniformScaleChangedHandler.Disconnect();
  347. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  348. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  349. EditorMeshColliderValidationRequestBus::Handler::BusDisconnect();
  350. EditorMeshColliderComponentRequestBus::Handler::BusDisconnect();
  351. EditorColliderComponentRequestBus::Handler::BusDisconnect();
  352. AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect();
  353. ColliderShapeRequestBus::Handler::BusDisconnect();
  354. AZ::TransformNotificationBus::Handler::BusDisconnect();
  355. PhysX::MeshColliderComponentRequestsBus::Handler::BusDisconnect();
  356. AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect();
  357. AzToolsFramework::Components::EditorComponentBase::Deactivate();
  358. m_componentModeDelegate.Disconnect();
  359. // When Deactivate is triggered from an application shutdown, it's possible that the
  360. // scene interface has already been deleted, so check for its existence here again
  361. m_sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  362. if (m_sceneInterface)
  363. {
  364. m_sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorBodyHandle);
  365. }
  366. }
  367. AZ::u32 EditorMeshColliderComponent::OnConfigurationChanged()
  368. {
  369. UpdateMeshAsset();
  370. // ensure we refresh the ComponentMode (and Manipulators) when the configuration
  371. // changes to keep the ComponentMode in sync with the shape (otherwise the manipulators
  372. // will move out of alignment with the shape)
  373. AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
  374. &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::Refresh,
  375. AZ::EntityComponentIdPair(GetEntityId(), GetId()));
  376. UpdateCollider();
  377. ValidateRigidBodyMeshGeometryType();
  378. return AZ::Edit::PropertyRefreshLevels::None;
  379. }
  380. void EditorMeshColliderComponent::OnSelected()
  381. {
  382. if (auto* physXSystem = GetPhysXSystem())
  383. {
  384. physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler);
  385. }
  386. }
  387. void EditorMeshColliderComponent::OnDeselected()
  388. {
  389. m_physXConfigChangedHandler.Disconnect();
  390. }
  391. void EditorMeshColliderComponent::BuildGameEntity(AZ::Entity* gameEntity)
  392. {
  393. auto sharedColliderConfig = AZStd::make_shared<Physics::ColliderConfiguration>(m_configuration);
  394. m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_subdivisionLevel = m_proxyShapeConfiguration.m_subdivisionLevel;
  395. auto* meshColliderComponent = gameEntity->CreateComponent<MeshColliderComponent>();
  396. meshColliderComponent->SetShapeConfigurationList({ AZStd::make_pair(
  397. sharedColliderConfig,
  398. AZStd::make_shared<Physics::PhysicsAssetShapeConfiguration>(m_proxyShapeConfiguration.m_physicsAsset.m_configuration)) });
  399. AZ_Warning(
  400. "PhysX",
  401. m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.GetId().IsValid(),
  402. "EditorMeshColliderComponent::BuildGameEntity. No asset assigned to Collider Component. Entity: %s",
  403. GetEntity()->GetName().c_str());
  404. }
  405. AZ::Transform EditorMeshColliderComponent::GetColliderLocalTransform() const
  406. {
  407. return AZ::Transform::CreateFromQuaternionAndTranslation(
  408. m_configuration.m_rotation, m_configuration.m_position);
  409. }
  410. void EditorMeshColliderComponent::UpdateMeshAsset()
  411. {
  412. if (m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.GetId().IsValid())
  413. {
  414. AZ::Data::AssetBus::Handler::BusDisconnect(); // Disconnect in case there was a previous asset being used.
  415. AZ::Data::AssetBus::Handler::BusConnect(m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.GetId());
  416. m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.QueueLoad();
  417. m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_asset = m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset;
  418. m_colliderDebugDraw.ClearCachedGeometry();
  419. }
  420. UpdateMaterialSlotsFromMeshAsset();
  421. }
  422. void EditorMeshColliderComponent::UpdateCollider()
  423. {
  424. UpdateShapeConfiguration();
  425. CreateStaticEditorCollider();
  426. Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged);
  427. }
  428. void EditorMeshColliderComponent::CreateStaticEditorCollider()
  429. {
  430. m_cachedAabbDirty = true;
  431. if (!GetEntity()->FindComponent<EditorStaticRigidBodyComponent>())
  432. {
  433. m_colliderDebugDraw.ClearCachedGeometry();
  434. return;
  435. }
  436. if (m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.GetStatus() != AZ::Data::AssetData::AssetStatus::Ready)
  437. {
  438. // Mesh asset has not been loaded, wait for OnAssetReady to be invoked.
  439. // We specifically check Ready state here rather than ReadyPreNotify to ensure OnAssetReady has been invoked
  440. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  441. {
  442. m_sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorBodyHandle);
  443. }
  444. return;
  445. }
  446. AZ::Transform colliderTransform = GetWorldTM();
  447. colliderTransform.ExtractUniformScale();
  448. AzPhysics::StaticRigidBodyConfiguration configuration;
  449. configuration.m_orientation = colliderTransform.GetRotation();
  450. configuration.m_position = colliderTransform.GetTranslation();
  451. configuration.m_entityId = GetEntityId();
  452. configuration.m_debugName = GetEntity()->GetName();
  453. AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
  454. Utils::CreateShapesFromAsset(
  455. m_proxyShapeConfiguration.m_physicsAsset.m_configuration,
  456. m_configuration, m_hasNonUniformScale, m_proxyShapeConfiguration.m_subdivisionLevel, shapes);
  457. configuration.m_colliderAndShapeData = shapes;
  458. if (m_sceneInterface)
  459. {
  460. //remove the previous body if any
  461. if (m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  462. {
  463. m_sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorBodyHandle);
  464. }
  465. m_editorBodyHandle = m_sceneInterface->AddSimulatedBody(m_editorSceneHandle, &configuration);
  466. }
  467. m_colliderDebugDraw.ClearCachedGeometry();
  468. AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId());
  469. }
  470. AZ::Data::Asset<Pipeline::MeshAsset> EditorMeshColliderComponent::GetMeshAsset() const
  471. {
  472. return m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset;
  473. }
  474. void EditorMeshColliderComponent::SetMeshAsset(const AZ::Data::AssetId& id)
  475. {
  476. if (id.IsValid())
  477. {
  478. m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.Create(id);
  479. UpdateMeshAsset();
  480. m_colliderDebugDraw.ClearCachedGeometry();
  481. }
  482. }
  483. void EditorMeshColliderComponent::UpdateMaterialSlotsFromMeshAsset()
  484. {
  485. Utils::SetMaterialsFromPhysicsAssetShape(m_proxyShapeConfiguration.m_physicsAsset.m_configuration, m_configuration.m_materialSlots);
  486. m_configuration.m_materialSlots.SetSlotsReadOnly(m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_useMaterialsFromAsset);
  487. InvalidatePropertyDisplay(AzToolsFramework::Refresh_EntireTree);
  488. // By refreshing the entire tree the component's properties reflected on edit context
  489. // will get updated correctly and show the right material slots list.
  490. // Unfortunately, the level prefab did its check against the dirty entity before
  491. // this and it will save old data to file (the previous material slots list).
  492. // To workaround this issue we mark the entity as dirty again so the prefab
  493. // will save the most current data.
  494. // There is a side effect to this fix though, the undo stack needs to be amended and there is
  495. // no good way to do that at the moment. This means a user will have to hit Ctrl+Z twice
  496. // to revert its last change, which is not good, but not as bad as losing data.
  497. AzToolsFramework::ScopedUndoBatch undoBatch("PhysX editor mesh collider component material slots updated");
  498. undoBatch.MarkEntityDirty(GetEntityId());
  499. ValidateAssetMaterials();
  500. }
  501. void EditorMeshColliderComponent::ValidateAssetMaterials()
  502. {
  503. const AZ::Data::Asset<Pipeline::MeshAsset>& physicsAsset = m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset;
  504. if (!physicsAsset.IsReady())
  505. {
  506. return;
  507. }
  508. // Here we check the material indices assigned to every shape and validate that every index is used at least once.
  509. // It's not an error if the validation fails here but something we want to let the designers know about.
  510. [[maybe_unused]] size_t materialsNum = physicsAsset->m_assetData.m_materialSlots.GetSlotsCount();
  511. const AZStd::vector<AZ::u16>& indexPerShape = physicsAsset->m_assetData.m_materialIndexPerShape;
  512. AZStd::unordered_set<AZ::u16> usedIndices;
  513. for (AZ::u16 index : indexPerShape)
  514. {
  515. if (index == Pipeline::MeshAssetData::TriangleMeshMaterialIndex)
  516. {
  517. // Triangle mesh indices are cooked into binary data, pass the validation in this case.
  518. return;
  519. }
  520. usedIndices.insert(index);
  521. }
  522. AZ_Warning("PhysX", usedIndices.size() == materialsNum,
  523. "EditorMeshColliderComponent::ValidateMaterialSurfaces. Entity: %s. Number of materials used by the shape (%d) does not match the "
  524. "total number of materials in the asset (%d). Please check that there are no convex meshes with per-face materials. Asset: %s",
  525. GetEntity()->GetName().c_str(), usedIndices.size(), materialsNum, physicsAsset.GetHint().c_str())
  526. }
  527. void EditorMeshColliderComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  528. {
  529. if (asset == m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset)
  530. {
  531. m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset = asset;
  532. m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_asset = asset;
  533. UpdateMaterialSlotsFromMeshAsset();
  534. UpdateCollider();
  535. ValidateRigidBodyMeshGeometryType();
  536. }
  537. else
  538. {
  539. m_componentWarnings.clear();
  540. InvalidatePropertyDisplay(AzToolsFramework::Refresh_EntireTree);
  541. }
  542. }
  543. void EditorMeshColliderComponent::ValidateRigidBodyMeshGeometryType()
  544. {
  545. const PhysX::EditorRigidBodyComponent* entityRigidbody = m_entity->FindComponent<PhysX::EditorRigidBodyComponent>();
  546. if (entityRigidbody && m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.IsReady())
  547. {
  548. AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
  549. Utils::CreateShapesFromAsset(
  550. m_proxyShapeConfiguration.m_physicsAsset.m_configuration,
  551. m_configuration,
  552. m_hasNonUniformScale,
  553. m_proxyShapeConfiguration.m_subdivisionLevel, shapes);
  554. if (shapes.empty())
  555. {
  556. m_componentWarnings.clear();
  557. InvalidatePropertyDisplay(AzToolsFramework::Refresh_EntireTree);
  558. return;
  559. }
  560. //We check if the shapes are triangle meshes, if any mesh is a triangle mesh we activate the warning.
  561. bool shapeIsTriangleMesh = false;
  562. for (const auto& shape : shapes)
  563. {
  564. auto current_shape = AZStd::rtti_pointer_cast<PhysX::Shape>(shape);
  565. if (current_shape &&
  566. current_shape->GetPxShape()->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH &&
  567. entityRigidbody->GetRigidBody() &&
  568. entityRigidbody->GetRigidBody()->IsKinematic() == false)
  569. {
  570. shapeIsTriangleMesh = true;
  571. break;
  572. }
  573. }
  574. if (shapeIsTriangleMesh)
  575. {
  576. m_componentWarnings.clear();
  577. AZStd::string assetPath = m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_asset.GetHint().c_str();
  578. const size_t lastSlash = assetPath.rfind('/');
  579. if (lastSlash != AZStd::string::npos)
  580. {
  581. assetPath = assetPath.substr(lastSlash + 1);
  582. }
  583. m_componentWarnings.push_back(AZStd::string::format(
  584. "The physics asset \"%s\" was exported using triangle mesh geometry, which is not compatible with non-kinematic "
  585. "dynamic rigid bodies. To make the collider compatible, you can export the asset using primitive or convex mesh "
  586. "geometry, use mesh decomposition when exporting the asset, or set the rigid body to kinematic. Learn more about "
  587. "<a href=\"https://www.o3de.org/docs/user-guide/components/reference/physx/mesh-collider/\">colliders</a>.",
  588. assetPath.c_str()));
  589. // make sure the entity inspector scrolls so the warning is visible by marking this component as having
  590. // new content
  591. AzToolsFramework::EntityPropertyEditorRequestBus::Broadcast(
  592. &AzToolsFramework::EntityPropertyEditorRequests::SetNewComponentId, GetId());
  593. }
  594. else
  595. {
  596. m_componentWarnings.clear();
  597. }
  598. }
  599. else
  600. {
  601. m_componentWarnings.clear();
  602. }
  603. InvalidatePropertyDisplay(
  604. m_componentWarnings.empty() ? AzToolsFramework::Refresh_EntireTree : AzToolsFramework::Refresh_EntireTree_NewContent);
  605. }
  606. void EditorMeshColliderComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  607. {
  608. OnAssetReady(asset);
  609. }
  610. void EditorMeshColliderComponent::BuildDebugDrawMesh() const
  611. {
  612. const AZ::Data::Asset<Pipeline::MeshAsset>& physicsAsset = m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset;
  613. const Physics::PhysicsAssetShapeConfiguration& physicsAssetConfiguration = m_proxyShapeConfiguration.m_physicsAsset.m_configuration;
  614. if (!physicsAsset.IsReady())
  615. {
  616. // Skip processing if the asset isn't ready
  617. return;
  618. }
  619. AzPhysics::ShapeColliderPairList shapeConfigList;
  620. Utils::GetColliderShapeConfigsFromAsset(physicsAssetConfiguration, m_configuration, m_hasNonUniformScale,
  621. m_proxyShapeConfiguration.m_subdivisionLevel, shapeConfigList);
  622. for (size_t shapeIndex = 0; shapeIndex < shapeConfigList.size(); shapeIndex++)
  623. {
  624. const Physics::ShapeConfiguration* shapeConfiguration = shapeConfigList[shapeIndex].second.get();
  625. AZ_Assert(shapeConfiguration, "BuildDebugDrawMesh: Invalid shape configuration");
  626. if (shapeConfiguration)
  627. {
  628. m_colliderDebugDraw.BuildMeshes(*shapeConfiguration, static_cast<AZ::u32>(shapeIndex));
  629. }
  630. }
  631. }
  632. void EditorMeshColliderComponent::DisplayMeshCollider(AzFramework::DebugDisplayRequests& debugDisplay) const
  633. {
  634. if (!m_colliderDebugDraw.HasCachedGeometry() || !m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_asset.IsReady())
  635. {
  636. return;
  637. }
  638. const Physics::PhysicsAssetShapeConfiguration& physicsAssetConfiguration = m_proxyShapeConfiguration.m_physicsAsset.m_configuration;
  639. AzPhysics::ShapeColliderPairList shapeConfigList;
  640. Utils::GetColliderShapeConfigsFromAsset(physicsAssetConfiguration, m_configuration, m_hasNonUniformScale,
  641. m_proxyShapeConfiguration.m_subdivisionLevel, shapeConfigList);
  642. const AZ::Vector3& assetScale = physicsAssetConfiguration.m_assetScale;
  643. for (size_t shapeIndex = 0; shapeIndex < shapeConfigList.size(); shapeIndex++)
  644. {
  645. const Physics::ColliderConfiguration* colliderConfiguration = shapeConfigList[shapeIndex].first.get();
  646. const Physics::ShapeConfiguration* shapeConfiguration = shapeConfigList[shapeIndex].second.get();
  647. AZ_Assert(shapeConfiguration && colliderConfiguration, "DisplayMeshCollider: Invalid shape-collider configuration pair");
  648. switch (shapeConfiguration->GetShapeType())
  649. {
  650. case Physics::ShapeType::CookedMesh:
  651. {
  652. const Physics::CookedMeshShapeConfiguration* cookedMeshShapeConfiguration =
  653. static_cast<const Physics::CookedMeshShapeConfiguration*>(shapeConfiguration);
  654. const AZ::Vector3 overallScale = Utils::GetTransformScale(GetEntityId()) * m_cachedNonUniformScale * assetScale;
  655. Physics::ColliderConfiguration nonUniformScaledColliderConfiguration = *colliderConfiguration;
  656. nonUniformScaledColliderConfiguration.m_position *= m_cachedNonUniformScale;
  657. m_colliderDebugDraw.DrawMesh(debugDisplay, nonUniformScaledColliderConfiguration, *cookedMeshShapeConfiguration,
  658. overallScale, static_cast<AZ::u32>(shapeIndex));
  659. break;
  660. }
  661. case Physics::ShapeType::Sphere:
  662. {
  663. const Physics::SphereShapeConfiguration* sphereShapeConfiguration =
  664. static_cast<const Physics::SphereShapeConfiguration*>(shapeConfiguration);
  665. m_colliderDebugDraw.DrawSphere(debugDisplay, *colliderConfiguration, *sphereShapeConfiguration, assetScale);
  666. break;
  667. }
  668. case Physics::ShapeType::Box:
  669. {
  670. const Physics::BoxShapeConfiguration* boxShapeConfiguration =
  671. static_cast<const Physics::BoxShapeConfiguration*>(shapeConfiguration);
  672. m_colliderDebugDraw.DrawBox(debugDisplay, *colliderConfiguration, *boxShapeConfiguration, assetScale);
  673. break;
  674. }
  675. case Physics::ShapeType::Capsule:
  676. {
  677. const Physics::CapsuleShapeConfiguration* capsuleShapeConfiguration =
  678. static_cast<const Physics::CapsuleShapeConfiguration*>(shapeConfiguration);
  679. m_colliderDebugDraw.DrawCapsule(debugDisplay, *colliderConfiguration, *capsuleShapeConfiguration, assetScale);
  680. break;
  681. }
  682. default:
  683. {
  684. AZ_Error("EditorMeshColliderComponent", false, "DisplayMeshCollider: Unsupported ShapeType %d. Entity %s, ID: %llu",
  685. static_cast<AZ::u32>(shapeConfiguration->GetShapeType()), GetEntity()->GetName().c_str(), GetEntityId());
  686. break;
  687. }
  688. }
  689. }
  690. }
  691. void EditorMeshColliderComponent::Display(
  692. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
  693. AzFramework::DebugDisplayRequests& debugDisplay) const
  694. {
  695. if (!m_colliderDebugDraw.HasCachedGeometry())
  696. {
  697. BuildDebugDrawMesh();
  698. }
  699. if (m_colliderDebugDraw.HasCachedGeometry())
  700. {
  701. DisplayMeshCollider(debugDisplay);
  702. }
  703. }
  704. void EditorMeshColliderComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
  705. {
  706. if (world.IsClose(m_cachedWorldTransform))
  707. {
  708. return;
  709. }
  710. m_cachedWorldTransform = world;
  711. UpdateCollider();
  712. }
  713. void EditorMeshColliderComponent::OnNonUniformScaleChanged(const AZ::Vector3& nonUniformScale)
  714. {
  715. m_cachedNonUniformScale = nonUniformScale;
  716. UpdateCollider();
  717. }
  718. // PhysX::ColliderShapeBus
  719. AZ::Aabb EditorMeshColliderComponent::GetColliderShapeAabb()
  720. {
  721. if (m_cachedAabbDirty)
  722. {
  723. m_cachedAabb = PhysX::Utils::GetColliderAabb(GetWorldTM()
  724. , m_hasNonUniformScale
  725. , m_proxyShapeConfiguration.m_subdivisionLevel
  726. , m_proxyShapeConfiguration.m_physicsAsset.m_configuration
  727. , m_configuration);
  728. m_cachedAabbDirty = false;
  729. }
  730. return m_cachedAabb;
  731. }
  732. void EditorMeshColliderComponent::UpdateShapeConfigurationScale()
  733. {
  734. const AZ::Vector3& assetScale = m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_assetScale;
  735. m_hasNonUniformScale =
  736. !Physics::Utils::HasUniformScale(assetScale) || (AZ::NonUniformScaleRequestBus::FindFirstHandler(GetEntityId()) != nullptr);
  737. m_proxyShapeConfiguration.m_hasNonUniformScale = m_hasNonUniformScale;
  738. m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_scale = GetWorldTM().ExtractUniformScale() * m_cachedNonUniformScale;
  739. }
  740. void EditorMeshColliderComponent::EnablePhysics()
  741. {
  742. if (!IsPhysicsEnabled())
  743. {
  744. UpdateCollider();
  745. }
  746. }
  747. void EditorMeshColliderComponent::DisablePhysics()
  748. {
  749. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  750. {
  751. m_sceneInterface->RemoveSimulatedBody(m_editorSceneHandle, m_editorBodyHandle);
  752. }
  753. }
  754. bool EditorMeshColliderComponent::IsPhysicsEnabled() const
  755. {
  756. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  757. {
  758. if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorBodyHandle))
  759. {
  760. return body->m_simulating;
  761. }
  762. }
  763. return false;
  764. }
  765. AZ::Aabb EditorMeshColliderComponent::GetAabb() const
  766. {
  767. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  768. {
  769. if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorBodyHandle))
  770. {
  771. return body->GetAabb();
  772. }
  773. }
  774. return AZ::Aabb::CreateNull();
  775. }
  776. AzPhysics::SimulatedBody* EditorMeshColliderComponent::GetSimulatedBody()
  777. {
  778. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  779. {
  780. if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorBodyHandle))
  781. {
  782. return body;
  783. }
  784. }
  785. return nullptr;
  786. }
  787. AzPhysics::SimulatedBodyHandle EditorMeshColliderComponent::GetSimulatedBodyHandle() const
  788. {
  789. return m_editorBodyHandle;
  790. }
  791. AzPhysics::SceneQueryHit EditorMeshColliderComponent::RayCast(const AzPhysics::RayCastRequest& request)
  792. {
  793. if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle)
  794. {
  795. if (auto* body = m_sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorBodyHandle))
  796. {
  797. return body->RayCast(request);
  798. }
  799. }
  800. return AzPhysics::SceneQueryHit();
  801. }
  802. bool EditorMeshColliderComponent::IsTrigger()
  803. {
  804. return m_configuration.m_isTrigger;
  805. }
  806. void EditorMeshColliderComponent::SetColliderOffset(const AZ::Vector3& offset)
  807. {
  808. m_configuration.m_position = offset;
  809. UpdateCollider();
  810. }
  811. AZ::Vector3 EditorMeshColliderComponent::GetColliderOffset() const
  812. {
  813. return m_configuration.m_position;
  814. }
  815. void EditorMeshColliderComponent::SetColliderRotation(const AZ::Quaternion& rotation)
  816. {
  817. m_configuration.m_rotation = rotation;
  818. UpdateCollider();
  819. }
  820. AZ::Quaternion EditorMeshColliderComponent::GetColliderRotation() const
  821. {
  822. return m_configuration.m_rotation;
  823. }
  824. AZ::Transform EditorMeshColliderComponent::GetColliderWorldTransform() const
  825. {
  826. return GetWorldTM() * GetColliderLocalTransform();
  827. }
  828. bool EditorMeshColliderComponent::ShouldUpdateCollisionMeshFromRender() const
  829. {
  830. const bool collisionMeshNotSet = !m_proxyShapeConfiguration.m_physicsAsset.m_pxAsset.GetId().IsValid();
  831. return collisionMeshNotSet;
  832. }
  833. AZ::Data::AssetId EditorMeshColliderComponent::FindMatchingPhysicsAsset(
  834. const AZ::Data::Asset<AZ::Data::AssetData>& renderMeshAsset,
  835. const AZStd::vector<AZ::Data::AssetId>& physicsAssets)
  836. {
  837. AZ::Data::AssetId foundAssetId;
  838. // Extract the file name from the path to the asset
  839. AZStd::string renderMeshFileName;
  840. AzFramework::StringFunc::Path::Split(renderMeshAsset.GetHint().c_str(),
  841. nullptr, nullptr, &renderMeshFileName);
  842. // Find the collision mesh asset matching the render mesh
  843. for (const AZ::Data::AssetId& assetId : physicsAssets)
  844. {
  845. AZStd::string assetPath;
  846. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPath,
  847. &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId);
  848. AZStd::string physicsAssetFileName;
  849. AzFramework::StringFunc::Path::Split(assetPath.c_str(), nullptr, nullptr, &physicsAssetFileName);
  850. if (physicsAssetFileName == renderMeshFileName)
  851. {
  852. foundAssetId = assetId;
  853. break;
  854. }
  855. }
  856. return foundAssetId;
  857. };
  858. AZ::Data::Asset<AZ::Data::AssetData> EditorMeshColliderComponent::GetRenderMeshAsset() const
  859. {
  860. // Try Atom MeshComponent
  861. AZ::Data::Asset<AZ::RPI::ModelAsset> atomMeshAsset;
  862. AZ::Render::MeshComponentRequestBus::EventResult(atomMeshAsset, GetEntityId(),
  863. &AZ::Render::MeshComponentRequestBus::Events::GetModelAsset);
  864. return atomMeshAsset;
  865. }
  866. void EditorMeshColliderComponent::SetCollisionMeshFromRender()
  867. {
  868. AZ::Data::Asset<AZ::Data::AssetData> renderMeshAsset = GetRenderMeshAsset();
  869. if (!renderMeshAsset.GetId().IsValid())
  870. {
  871. // No render mesh component assigned
  872. return;
  873. }
  874. bool productsQueryResult = false;
  875. AZStd::vector<AZ::Data::AssetInfo> productsInfo;
  876. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(productsQueryResult,
  877. &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID,
  878. renderMeshAsset.GetId().m_guid, productsInfo);
  879. if (productsQueryResult)
  880. {
  881. AZStd::vector<AZ::Data::AssetId> physicsAssets;
  882. physicsAssets.reserve(productsInfo.size());
  883. for (const AZ::Data::AssetInfo& info : productsInfo)
  884. {
  885. if (info.m_assetType == AZ::AzTypeInfo<Pipeline::MeshAsset>::Uuid())
  886. {
  887. physicsAssets.push_back(info.m_assetId);
  888. }
  889. }
  890. // If there's only one physics asset, we set it regardless of the name
  891. if (physicsAssets.size() == 1)
  892. {
  893. SetMeshAsset(physicsAssets[0]);
  894. }
  895. // For multiple assets we pick the one matching the name of the render mesh asset
  896. else if (physicsAssets.size() > 1)
  897. {
  898. AZ::Data::AssetId matchingPhysicsAsset = FindMatchingPhysicsAsset(renderMeshAsset, physicsAssets);
  899. if (matchingPhysicsAsset.IsValid())
  900. {
  901. SetMeshAsset(matchingPhysicsAsset);
  902. }
  903. else
  904. {
  905. AZ_Warning("EditorMeshColliderComponent", false,
  906. "SetCollisionMeshFromRender on entity %s: Unable to find a matching physics asset "
  907. "for the render mesh asset GUID: %s, hint: %s",
  908. GetEntity()->GetName().c_str(),
  909. renderMeshAsset.GetId().m_guid.ToString<AZStd::string>().c_str(),
  910. renderMeshAsset.GetHint().c_str());
  911. }
  912. }
  913. // This is not necessarily an incorrect case but it's worth reporting
  914. // in case if we forgot to configure the source asset to produce the collision mesh
  915. else if (physicsAssets.empty())
  916. {
  917. AZ_TracePrintf("EditorMeshColliderComponent",
  918. "SetCollisionMeshFromRender on entity %s: The source asset for %s did not produce any physics assets",
  919. GetEntity()->GetName().c_str(),
  920. renderMeshAsset.GetHint().c_str());
  921. }
  922. }
  923. else
  924. {
  925. AZ_Warning("EditorMeshColliderComponent", false,
  926. "SetCollisionMeshFromRender on entity %s: Unable to get the assets produced by the render mesh asset GUID: %s, hint: %s",
  927. GetEntity()->GetName().c_str(),
  928. renderMeshAsset.GetId().m_guid.ToString<AZStd::string>().c_str(),
  929. renderMeshAsset.GetHint().c_str());
  930. }
  931. }
  932. void EditorMeshColliderComponent::OnModelReady(
  933. [[maybe_unused]] const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset,
  934. [[maybe_unused]] const AZ::Data::Instance<AZ::RPI::Model>& model)
  935. {
  936. if (ShouldUpdateCollisionMeshFromRender())
  937. {
  938. SetCollisionMeshFromRender();
  939. }
  940. }
  941. void EditorMeshColliderComponent::SetAssetScale(const AZ::Vector3& scale)
  942. {
  943. m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_assetScale = scale;
  944. UpdateCollider();
  945. }
  946. AZ::Vector3 EditorMeshColliderComponent::GetAssetScale() const
  947. {
  948. return m_proxyShapeConfiguration.m_physicsAsset.m_configuration.m_assetScale;
  949. }
  950. void EditorMeshColliderComponent::UpdateShapeConfiguration()
  951. {
  952. UpdateShapeConfigurationScale();
  953. }
  954. AZ::Aabb EditorMeshColliderComponent::GetWorldBounds() const
  955. {
  956. return GetAabb();
  957. }
  958. AZ::Aabb EditorMeshColliderComponent::GetLocalBounds() const
  959. {
  960. AZ::Aabb worldBounds = GetWorldBounds();
  961. if (worldBounds.IsValid())
  962. {
  963. return worldBounds.GetTransformedAabb(m_cachedWorldTransform.GetInverse());
  964. }
  965. return AZ::Aabb::CreateNull();
  966. }
  967. bool EditorMeshColliderComponent::SupportsEditorRayIntersect()
  968. {
  969. return true;
  970. }
  971. AZ::Aabb EditorMeshColliderComponent::GetEditorSelectionBoundsViewport(
  972. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
  973. {
  974. return GetWorldBounds();
  975. }
  976. bool EditorMeshColliderComponent::EditorSelectionIntersectRayViewport(
  977. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
  978. {
  979. AzPhysics::RayCastRequest request;
  980. request.m_direction = dir;
  981. request.m_distance = distance;
  982. request.m_start = src;
  983. if (auto hit = RayCast(request))
  984. {
  985. distance = hit.m_distance;
  986. return true;
  987. }
  988. return false;
  989. }
  990. void EditorMeshColliderComponentDescriptor::Reflect(AZ::ReflectContext* reflection) const
  991. {
  992. EditorMeshColliderComponent::Reflect(reflection);
  993. }
  994. void EditorMeshColliderComponentDescriptor::GetProvidedServices(
  995. AZ::ComponentDescriptor::DependencyArrayType& provided, [[maybe_unused]] const AZ::Component* instance) const
  996. {
  997. EditorMeshColliderComponent::GetProvidedServices(provided);
  998. }
  999. void EditorMeshColliderComponentDescriptor::GetDependentServices(
  1000. [[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent, [[maybe_unused]] const AZ::Component* instance) const
  1001. {
  1002. EditorMeshColliderComponent::GetDependentServices(dependent);
  1003. }
  1004. void EditorMeshColliderComponentDescriptor::GetRequiredServices(
  1005. AZ::ComponentDescriptor::DependencyArrayType& required, [[maybe_unused]] const AZ::Component* instance) const
  1006. {
  1007. EditorMeshColliderComponent::GetRequiredServices(required);
  1008. }
  1009. void EditorMeshColliderComponentDescriptor::GetWarnings(
  1010. AZ::ComponentDescriptor::StringWarningArray& warnings, const AZ::Component* instance) const
  1011. {
  1012. const PhysX::EditorMeshColliderComponent* editorMeshColliderComponent =
  1013. azrtti_cast<const PhysX::EditorMeshColliderComponent*>(instance);
  1014. if (editorMeshColliderComponent != nullptr)
  1015. {
  1016. warnings = editorMeshColliderComponent->GetComponentWarnings();
  1017. }
  1018. }
  1019. } // namespace PhysX