EditorHeightfieldColliderComponent.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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 <Source/EditorHeightfieldColliderComponent.h>
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Math/Color.h>
  12. #include <AzCore/Math/MathStringConversions.h>
  13. #include <AzCore/std/smart_ptr/make_shared.h>
  14. #include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
  15. #include <AzFramework/Physics/Configuration/StaticRigidBodyConfiguration.h>
  16. #include <AzFramework/Physics/Shape.h>
  17. #include <AzFramework/Physics/SimulatedBodies/StaticRigidBody.h>
  18. #include <AzFramework/Viewport/CameraState.h>
  19. #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
  20. #include <Editor/ColliderComponentMode.h>
  21. #include <Source/HeightfieldColliderComponent.h>
  22. #include <Source/Shape.h>
  23. #include <Source/Utils.h>
  24. #include <System/PhysXSystem.h>
  25. #include <utility>
  26. #include <Pipeline/HeightFieldAssetHandler.h>
  27. namespace PhysX
  28. {
  29. AZ_CVAR(float, physx_heightfieldDebugDrawDistance, 50.0f, nullptr,
  30. AZ::ConsoleFunctorFlags::Null, "Distance for PhysX Heightfields debug visualization.");
  31. AZ_CVAR(bool, physx_heightfieldDebugDrawBoundingBox, false,
  32. nullptr, AZ::ConsoleFunctorFlags::Null, "Draw the bounding box used for heightfield debug visualization.");
  33. void EditorHeightfieldColliderComponent::Reflect(AZ::ReflectContext* context)
  34. {
  35. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  36. {
  37. serializeContext->Class<EditorHeightfieldColliderComponent, EditorComponentBase>()
  38. ->Version(1)
  39. ->Field("ColliderConfiguration", &EditorHeightfieldColliderComponent::m_colliderConfig)
  40. ->Field("DebugDrawSettings", &EditorHeightfieldColliderComponent::m_colliderDebugDraw)
  41. ->Field("UseBakedHeightfield", &EditorHeightfieldColliderComponent::m_useBakedHeightfield)
  42. ->Field("BakedHeightfieldRelativePath", &EditorHeightfieldColliderComponent::m_bakedHeightfieldRelativePath)
  43. ->Field("BakedHeightfieldAsset", &EditorHeightfieldColliderComponent::m_bakedHeightfieldAsset)
  44. ;
  45. if (auto editContext = serializeContext->GetEditContext())
  46. {
  47. editContext->Class<EditorHeightfieldColliderComponent>(
  48. "PhysX Heightfield Collider", "Creates geometry in the PhysX simulation based on an attached heightfield component")
  49. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  50. ->Attribute(AZ::Edit::Attributes::Category, "PhysX")
  51. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/PhysXHeightfieldCollider.svg")
  52. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/PhysXHeightfieldCollider.svg")
  53. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  54. ->Attribute(
  55. AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/heightfield-collider/")
  56. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  57. ->DataElement(
  58. AZ::Edit::UIHandlers::Default, &EditorHeightfieldColliderComponent::m_colliderConfig, "Collider configuration",
  59. "Configuration of the collider")
  60. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  61. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHeightfieldColliderComponent::OnConfigurationChanged)
  62. ->DataElement(
  63. AZ::Edit::UIHandlers::Default,
  64. &EditorHeightfieldColliderComponent::m_colliderDebugDraw,
  65. "Debug draw settings",
  66. "Debug draw settings")
  67. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  68. ->DataElement(
  69. AZ::Edit::UIHandlers::Default, &EditorHeightfieldColliderComponent::m_useBakedHeightfield, "Use Baked Heightfield",
  70. "Selects between a dynamically generated heightfield or a prebaked one. "
  71. "A prebaked one will remain unchanged at game time even if the heightfield provider changes its data. "
  72. "A dynamic one will change with heightfield provider changes.")
  73. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHeightfieldColliderComponent::OnToggleBakedHeightfield)
  74. ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorHeightfieldColliderComponent::IsHeightfieldInvalid)
  75. ->DataElement(
  76. AZ::Edit::UIHandlers::MultiLineEdit, &EditorHeightfieldColliderComponent::m_bakedHeightfieldRelativePath,
  77. "Baked Heightfield Relative Path", "Path to the baked heightfield asset")
  78. ->Attribute(AZ::Edit::Attributes::ReadOnly, true)
  79. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorHeightfieldColliderComponent::GetBakedHeightfieldVisibilitySetting)
  80. ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Heightfield", "Bake Heightfield")
  81. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
  82. ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Heightfield")
  83. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorHeightfieldColliderComponent::RequestHeightfieldBaking)
  84. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorHeightfieldColliderComponent::GetBakedHeightfieldVisibilitySetting)
  85. ;
  86. }
  87. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  88. {
  89. behaviorContext->EBus<EditorHeightfieldColliderRequestBus>("EditorHeightfieldColliderRequestBus")
  90. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  91. ->Attribute(AZ::Script::Attributes::Module, "physics")
  92. ->Event("RequestHeightfieldBaking", &EditorHeightfieldColliderInterface::RequestHeightfieldBaking);
  93. }
  94. }
  95. }
  96. AZ::u32 EditorHeightfieldColliderComponent::GetBakedHeightfieldVisibilitySetting() const
  97. {
  98. // Controls that are specific to baked heightfields call this to determine their visibility
  99. // they are visible when the mode is set to baked, otherwise hidden
  100. return m_useBakedHeightfield ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
  101. }
  102. void EditorHeightfieldColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  103. {
  104. provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService"));
  105. provided.push_back(AZ_CRC_CE("PhysicsColliderService"));
  106. provided.push_back(AZ_CRC_CE("PhysicsHeightfieldColliderService"));
  107. }
  108. void EditorHeightfieldColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  109. {
  110. required.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService"));
  111. }
  112. void EditorHeightfieldColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  113. {
  114. incompatible.push_back(AZ_CRC_CE("PhysicsColliderService"));
  115. // Incompatible with other rigid bodies because it handles its own rigid body
  116. // internally and it would conflict if another rigid body is added to the entity.
  117. incompatible.push_back(AZ_CRC_CE("PhysicsRigidBodyService"));
  118. }
  119. EditorHeightfieldColliderComponent::EditorHeightfieldColliderComponent()
  120. : m_physXConfigChangedHandler(
  121. []([[maybe_unused]] const AzPhysics::SystemConfiguration* config)
  122. {
  123. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(
  124. &AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh,
  125. AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
  126. })
  127. , m_heightfieldAssetBakingJob(this)
  128. {
  129. // By default, disable heightfield collider debug drawing. This doesn't need to be viewed in the common case.
  130. m_colliderDebugDraw.SetDisplayFlag(false);
  131. // Heightfields don't support the following:
  132. // - Offset: There shouldn't be a need to offset the data, since the heightfield provider is giving a physics representation
  133. // - IsTrigger: PhysX heightfields don't support acting as triggers
  134. // - MaterialSelection: The heightfield provider provides per-vertex material selection
  135. m_colliderConfig->SetPropertyVisibility(Physics::ColliderConfiguration::Offset, false);
  136. m_colliderConfig->SetPropertyVisibility(Physics::ColliderConfiguration::IsTrigger, false);
  137. m_colliderConfig->SetPropertyVisibility(Physics::ColliderConfiguration::MaterialSelection, false);
  138. }
  139. EditorHeightfieldColliderComponent ::~EditorHeightfieldColliderComponent()
  140. {
  141. }
  142. // AZ::Component
  143. void EditorHeightfieldColliderComponent::Activate()
  144. {
  145. EditorHeightfieldColliderRequestBus::Handler::BusConnect(GetEntityId());
  146. m_bakingCompletion.Reset(true /*isClearDependent*/);
  147. m_heightfieldAssetBakingJob.Reset(true);
  148. AzPhysics::SceneHandle sceneHandle = AzPhysics::InvalidSceneHandle;
  149. if (auto sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
  150. {
  151. sceneHandle = sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName);
  152. }
  153. m_heightfieldCollider = AZStd::make_unique<HeightfieldCollider>(
  154. GetEntityId(), GetEntity()->GetName(), sceneHandle, m_colliderConfig,
  155. m_shapeConfig, HeightfieldCollider::DataSource::GenerateNewHeightfield);
  156. AzToolsFramework::Components::EditorComponentBase::Activate();
  157. const AZ::EntityId entityId = GetEntityId();
  158. AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusConnect(entityId);
  159. // Debug drawing
  160. m_colliderDebugDraw.Connect(entityId);
  161. m_colliderDebugDraw.SetDisplayCallback(this);
  162. }
  163. void EditorHeightfieldColliderComponent::Deactivate()
  164. {
  165. m_colliderDebugDraw.Disconnect();
  166. AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect();
  167. AzToolsFramework::Components::EditorComponentBase::Deactivate();
  168. EditorHeightfieldColliderRequestBus::Handler::BusDisconnect();
  169. if (m_useBakedHeightfield)
  170. {
  171. // Wait for any in progress heightfield asset baking job to complete
  172. FinishHeightfieldBakingJob();
  173. }
  174. m_heightfieldCollider.reset();
  175. }
  176. void EditorHeightfieldColliderComponent::BlockOnPendingJobs()
  177. {
  178. if (m_heightfieldCollider)
  179. {
  180. m_heightfieldCollider->BlockOnPendingJobs();
  181. }
  182. }
  183. void EditorHeightfieldColliderComponent::BuildGameEntity(AZ::Entity* gameEntity)
  184. {
  185. auto* heightfieldColliderComponent = gameEntity->CreateComponent<HeightfieldColliderComponent>();
  186. heightfieldColliderComponent->SetColliderConfiguration(*m_colliderConfig);
  187. heightfieldColliderComponent->SetBakedHeightfieldAsset(m_bakedHeightfieldAsset);
  188. }
  189. AZ::u32 EditorHeightfieldColliderComponent::OnConfigurationChanged()
  190. {
  191. m_heightfieldCollider->RefreshHeightfield(Physics::HeightfieldProviderNotifications::HeightfieldChangeMask::Settings);
  192. return AZ::Edit::PropertyRefreshLevels::None;
  193. }
  194. bool EditorHeightfieldColliderComponent::SaveHeightfieldAssetToDisk()
  195. {
  196. auto assetType = AZ::AzTypeInfo<PhysX::Pipeline::HeightFieldAsset>::Uuid();
  197. auto assetHandler = const_cast<AZ::Data::AssetHandler*>(AZ::Data::AssetManager::Instance().GetHandler(assetType));
  198. if (assetHandler)
  199. {
  200. char projectPath[AZ_MAX_PATH_LEN];
  201. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
  202. AZStd::string heightfieldFullPath;
  203. AzFramework::StringFunc::Path::ConstructFull(projectPath, m_bakedHeightfieldRelativePath.c_str(), heightfieldFullPath, true);
  204. AZ::IO::FileIOStream fileStream(heightfieldFullPath.c_str(), AZ::IO::OpenMode::ModeWrite);
  205. if (fileStream.IsOpen())
  206. {
  207. if (!assetHandler->SaveAssetData(m_bakedHeightfieldAsset, &fileStream))
  208. {
  209. AZ_Error("PhysX", false, "Unable to save heightfield asset %s", heightfieldFullPath.c_str());
  210. return false;
  211. }
  212. }
  213. else
  214. {
  215. AZ_Error("PhysX", false, "Unable to open heightfield asset file %s", heightfieldFullPath.c_str());
  216. return false;
  217. }
  218. }
  219. return true;
  220. }
  221. void EditorHeightfieldColliderComponent::StartHeightfieldBakingJob()
  222. {
  223. FinishHeightfieldBakingJob();
  224. m_bakingCompletion.Reset(true /*isClearDependent*/);
  225. m_heightfieldAssetBakingJob.Reset(true);
  226. m_heightfieldAssetBakingJob.SetDependent(&m_bakingCompletion);
  227. m_heightfieldAssetBakingJob.Start();
  228. }
  229. bool EditorHeightfieldColliderComponent::IsHeightfieldInvalid() const
  230. {
  231. return m_shapeConfig->GetCachedNativeHeightfield() == nullptr;
  232. }
  233. void EditorHeightfieldColliderComponent::FinishHeightfieldBakingJob()
  234. {
  235. m_bakingCompletion.StartAndWaitForCompletion();
  236. }
  237. bool EditorHeightfieldColliderComponent::CheckHeightfieldPathExists()
  238. {
  239. if (!m_bakedHeightfieldRelativePath.empty())
  240. {
  241. char projectPath[AZ_MAX_PATH_LEN];
  242. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
  243. // retrieve the source heightfield path from the configuration
  244. // we need to make sure to use the same source heightfield for each baking
  245. // test to see if the heightfield file is actually there, if it was removed we need to
  246. // generate a new filename, otherwise it will cause an error in the asset system
  247. AZStd::string fullPath;
  248. AzFramework::StringFunc::Path::Join(projectPath,
  249. m_bakedHeightfieldRelativePath.c_str(), fullPath,
  250. /*bCaseInsensitive*/true, /*bNormalize*/true);
  251. if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str()))
  252. {
  253. // clear it to force the generation of a new filename
  254. m_bakedHeightfieldRelativePath.clear();
  255. }
  256. }
  257. return !m_bakedHeightfieldRelativePath.empty();
  258. }
  259. void EditorHeightfieldColliderComponent::GenerateHeightfieldAsset()
  260. {
  261. AZStd::string entityName = GetEntity()->GetName();
  262. // the file name is a combination of the entity name and a UUID
  263. AZ::Uuid uuid = AZ::Uuid::CreateRandom();
  264. AZStd::string uuidString;
  265. uuid.ToString(uuidString);
  266. m_bakedHeightfieldRelativePath = "Heightfields/" + entityName + "_" + uuidString;
  267. // replace any invalid filename characters
  268. auto invalidCharacters = [](char letter)
  269. {
  270. return
  271. letter == ':' || letter == '"' || letter == '\'' ||
  272. letter == '{' || letter == '}' ||
  273. letter == '<' || letter == '>';
  274. };
  275. AZStd::replace_if(m_bakedHeightfieldRelativePath.begin(), m_bakedHeightfieldRelativePath.end(), invalidCharacters, '_');
  276. m_bakedHeightfieldRelativePath += AZ_FILESYSTEM_EXTENSION_SEPARATOR;
  277. m_bakedHeightfieldRelativePath += Pipeline::HeightFieldAssetHandler::s_assetFileExtension;
  278. {
  279. AZ::Data::AssetId generatedAssetId;
  280. AZ::Data::AssetCatalogRequestBus::BroadcastResult(generatedAssetId,
  281. &AZ::Data::AssetCatalogRequests::GenerateAssetIdTEMP, m_bakedHeightfieldRelativePath.c_str());
  282. AZ::Data::Asset<Pipeline::HeightFieldAsset> asset = AZ::Data::AssetManager::Instance().FindAsset(generatedAssetId, AZ::Data::AssetLoadBehavior::Default);
  283. if (!asset.GetId().IsValid())
  284. {
  285. asset = AZ::Data::AssetManager::Instance().CreateAsset<PhysX::Pipeline::HeightFieldAsset>(generatedAssetId);
  286. }
  287. m_bakedHeightfieldAsset = asset;
  288. physx::PxHeightField* pxHeightfield = static_cast<physx::PxHeightField*>(m_shapeConfig->GetCachedNativeHeightfield());
  289. // Since PxHeightfield will have shared ownership in both HeightfieldAsset and HeightfieldShapeConfiguration,
  290. // we need to increment the reference counter here. Both of these places call release() in destructors,
  291. // so we need to avoid double deletion this way.
  292. pxHeightfield->acquireReference();
  293. m_bakedHeightfieldAsset.Get()->SetHeightField(pxHeightfield);
  294. m_bakedHeightfieldAsset.Get()->SetMinHeight(m_shapeConfig->GetMinHeightBounds());
  295. m_bakedHeightfieldAsset.Get()->SetMaxHeight(m_shapeConfig->GetMaxHeightBounds());
  296. }
  297. }
  298. bool EditorHeightfieldColliderComponent::CheckoutHeightfieldAsset() const
  299. {
  300. char projectPath[AZ_MAX_PATH_LEN];
  301. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
  302. AZStd::string heightfieldFullPath;
  303. AzFramework::StringFunc::Path::ConstructFull(projectPath, m_bakedHeightfieldRelativePath.c_str(), heightfieldFullPath, true);
  304. // make sure the folder is created
  305. AZStd::string heightfieldCaptureFolderPath;
  306. AzFramework::StringFunc::Path::GetFolderPath(heightfieldFullPath.data(), heightfieldCaptureFolderPath);
  307. AZ::IO::SystemFile::CreateDir(heightfieldCaptureFolderPath.c_str());
  308. // check out the file in source control
  309. bool checkedOutSuccessfully = false;
  310. AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
  311. checkedOutSuccessfully,
  312. &AzToolsFramework::ToolsApplicationRequestBus::Events::RequestEditForFileBlocking,
  313. heightfieldFullPath.c_str(),
  314. "Checking out for edit...",
  315. AzToolsFramework::ToolsApplicationRequestBus::Events::RequestEditProgressCallback());
  316. if (!checkedOutSuccessfully)
  317. {
  318. AZ_Error("PhysX", false, "Source control checkout failed for file [%s]", heightfieldFullPath.c_str());
  319. return false;
  320. }
  321. return true;
  322. }
  323. void EditorHeightfieldColliderComponent::RequestHeightfieldBaking()
  324. {
  325. if (IsHeightfieldInvalid())
  326. {
  327. AZ_Error("PhysX", false,
  328. "Unable to start heightfield baking for entity [%s]. Invalid heightfield.",
  329. GetEntity()->GetName().c_str());
  330. return;
  331. }
  332. if (!CheckHeightfieldPathExists())
  333. {
  334. GenerateHeightfieldAsset();
  335. }
  336. if (CheckoutHeightfieldAsset())
  337. {
  338. StartHeightfieldBakingJob();
  339. }
  340. }
  341. AZ::u32 EditorHeightfieldColliderComponent::OnToggleBakedHeightfield()
  342. {
  343. if (m_useBakedHeightfield)
  344. {
  345. RequestHeightfieldBaking();
  346. }
  347. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  348. }
  349. // AzToolsFramework::EntitySelectionEvents
  350. void EditorHeightfieldColliderComponent::OnSelected()
  351. {
  352. if (auto* physXSystem = GetPhysXSystem())
  353. {
  354. if (!m_physXConfigChangedHandler.IsConnected())
  355. {
  356. physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler);
  357. }
  358. }
  359. }
  360. // AzToolsFramework::EntitySelectionEvents
  361. void EditorHeightfieldColliderComponent::OnDeselected()
  362. {
  363. m_physXConfigChangedHandler.Disconnect();
  364. }
  365. // DisplayCallback
  366. void EditorHeightfieldColliderComponent::Display(const AzFramework::ViewportInfo& viewportInfo,
  367. AzFramework::DebugDisplayRequests& debugDisplay) const
  368. {
  369. const AzPhysics::SimulatedBody* simulatedBody = m_heightfieldCollider->GetSimulatedBody();
  370. if (!simulatedBody)
  371. {
  372. return;
  373. }
  374. const AzPhysics::StaticRigidBody* staticRigidBody = azrtti_cast<const AzPhysics::StaticRigidBody*>(simulatedBody);
  375. if (!staticRigidBody)
  376. {
  377. return;
  378. }
  379. // Calculate the center of a box in front of the camera - this will be the area to draw
  380. const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId);
  381. const AZ::Vector3 boundsAabbCenter = cameraState.m_position + cameraState.m_forward * physx_heightfieldDebugDrawDistance * 0.5f;
  382. const AZ::Vector3 bodyPosition = staticRigidBody->GetPosition();
  383. const AZ::Vector3 aabbCenterLocalBody = boundsAabbCenter - bodyPosition;
  384. const AZ::u32 shapeCount = staticRigidBody->GetShapeCount();
  385. for (AZ::u32 shapeIndex = 0; shapeIndex < shapeCount; ++shapeIndex)
  386. {
  387. const AZStd::shared_ptr<const Physics::Shape> shape = staticRigidBody->GetShape(shapeIndex);
  388. m_colliderDebugDraw.DrawHeightfield(debugDisplay, aabbCenterLocalBody,
  389. physx_heightfieldDebugDrawDistance, shape);
  390. }
  391. if (physx_heightfieldDebugDrawBoundingBox)
  392. {
  393. const AZ::Aabb boundsAabb = AZ::Aabb::CreateCenterRadius(aabbCenterLocalBody, physx_heightfieldDebugDrawDistance);
  394. if (boundsAabb.IsValid())
  395. {
  396. debugDisplay.DrawWireBox(boundsAabb.GetMin(), boundsAabb.GetMax());
  397. }
  398. }
  399. }
  400. void EditorHeightfieldColliderComponent::HeightfieldBakingJob::Process()
  401. {
  402. if (m_owner)
  403. {
  404. m_owner->SaveHeightfieldAssetToDisk();
  405. }
  406. }
  407. EditorHeightfieldColliderComponent::HeightfieldBakingJob::HeightfieldBakingJob(EditorHeightfieldColliderComponent* owner) : AZ::Job(false, nullptr), m_owner(owner)
  408. {
  409. }
  410. } // namespace PhysX