MeshComponentController.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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 <Mesh/MeshComponentController.h>
  9. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
  10. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshHandleStateBus.h>
  11. #include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
  12. #include <Atom/RPI.Public/Model/Model.h>
  13. #include <Atom/RPI.Public/Scene.h>
  14. #include <AzCore/Asset/AssetManager.h>
  15. #include <AzCore/Asset/AssetManagerBus.h>
  16. #include <AzCore/Asset/AssetSerializer.h>
  17. #include <AzCore/Name/NameDictionary.h>
  18. #include <AzCore/Serialization/SerializeContext.h>
  19. #include <AzFramework/Entity/EntityContextBus.h>
  20. #include <AzFramework/Entity/EntityContext.h>
  21. #include <AzFramework/Scene/Scene.h>
  22. #include <AzFramework/Scene/SceneSystemInterface.h>
  23. #include <AzCore/RTTI/BehaviorContext.h>
  24. namespace AZ
  25. {
  26. namespace Render
  27. {
  28. static AZ::Name s_CLOTH_DATA_Name = AZ::Name::FromStringLiteral("CLOTH_DATA", AZ::Interface<AZ::NameDictionary>::Get());
  29. namespace Internal
  30. {
  31. struct MeshComponentNotificationBusHandler final
  32. : public MeshComponentNotificationBus::Handler
  33. , public AZ::BehaviorEBusHandler
  34. {
  35. AZ_EBUS_BEHAVIOR_BINDER(
  36. MeshComponentNotificationBusHandler, "{8B8F4977-817F-4C7C-9141-0E5FF899E1BC}", AZ::SystemAllocator, OnModelReady);
  37. void OnModelReady(
  38. [[maybe_unused]] const Data::Asset<RPI::ModelAsset>& modelAsset,
  39. [[maybe_unused]] const Data::Instance<RPI::Model>& model) override
  40. {
  41. Call(FN_OnModelReady);
  42. }
  43. };
  44. } // namespace Internal
  45. namespace MeshComponentControllerVersionUtility
  46. {
  47. bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  48. {
  49. if (classElement.GetVersion() < 2)
  50. {
  51. RPI::Cullable::LodOverride lodOverride = aznumeric_cast<RPI::Cullable::LodOverride>(classElement.FindElement(AZ_CRC_CE("LodOverride")));
  52. static constexpr uint8_t old_NoLodOverride = AZStd::numeric_limits <RPI::Cullable::LodOverride>::max();
  53. if (lodOverride == old_NoLodOverride)
  54. {
  55. classElement.AddElementWithData(context, "LodType", RPI::Cullable::LodType::SpecificLod);
  56. }
  57. }
  58. return true;
  59. }
  60. } // namespace MeshComponentControllerVersionUtility
  61. void MeshComponentConfig::Reflect(ReflectContext* context)
  62. {
  63. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  64. {
  65. serializeContext->Class<MeshComponentConfig>()
  66. ->Version(3, &MeshComponentControllerVersionUtility::VersionConverter)
  67. ->Field("ModelAsset", &MeshComponentConfig::m_modelAsset)
  68. ->Field("SortKey", &MeshComponentConfig::m_sortKey)
  69. ->Field("ExcludeFromReflectionCubeMaps", &MeshComponentConfig::m_excludeFromReflectionCubeMaps)
  70. ->Field("UseForwardPassIBLSpecular", &MeshComponentConfig::m_useForwardPassIblSpecular)
  71. ->Field("IsRayTracingEnabled", &MeshComponentConfig::m_isRayTracingEnabled)
  72. ->Field("IsAlwaysDynamic", &MeshComponentConfig::m_isAlwaysDynamic)
  73. ->Field("LodType", &MeshComponentConfig::m_lodType)
  74. ->Field("LodOverride", &MeshComponentConfig::m_lodOverride)
  75. ->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage)
  76. ->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate);
  77. }
  78. }
  79. bool MeshComponentConfig::IsAssetSet()
  80. {
  81. return m_modelAsset.GetId().IsValid();
  82. }
  83. bool MeshComponentConfig::LodTypeIsScreenCoverage()
  84. {
  85. return m_lodType == RPI::Cullable::LodType::ScreenCoverage;
  86. }
  87. bool MeshComponentConfig::LodTypeIsSpecificLOD()
  88. {
  89. return m_lodType == RPI::Cullable::LodType::SpecificLod;
  90. }
  91. bool MeshComponentConfig::ShowLodConfig()
  92. {
  93. return LodTypeIsScreenCoverage() || LodTypeIsSpecificLOD();
  94. }
  95. AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> MeshComponentConfig::GetLodOverrideValues()
  96. {
  97. AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> values;
  98. uint32_t lodCount = 0;
  99. if (IsAssetSet())
  100. {
  101. if (m_modelAsset.IsReady())
  102. {
  103. lodCount = static_cast<uint32_t>(m_modelAsset->GetLodCount());
  104. }
  105. else
  106. {
  107. // If the asset isn't loaded, it's still possible it exists in the instance database.
  108. Data::Instance<RPI::Model> model =
  109. Data::InstanceDatabase<RPI::Model>::Instance().Find(Data::InstanceId::CreateFromAsset(m_modelAsset));
  110. if (model)
  111. {
  112. lodCount = static_cast<uint32_t>(model->GetLodCount());
  113. }
  114. }
  115. }
  116. values.reserve(lodCount + 1);
  117. values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(0), "Default LOD 0 (Highest Detail)" });
  118. for (uint32_t i = 1; i < lodCount; ++i)
  119. {
  120. AZStd::string enumDescription = AZStd::string::format("LOD %i", i);
  121. values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(i), enumDescription.c_str() });
  122. }
  123. return values;
  124. }
  125. MeshComponentController::~MeshComponentController()
  126. {
  127. // Release memory, disconnect from buses in the right order and broadcast events so that other components are aware.
  128. Deactivate();
  129. }
  130. void MeshComponentController::Reflect(ReflectContext* context)
  131. {
  132. MeshComponentConfig::Reflect(context);
  133. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  134. {
  135. serializeContext->Class<MeshComponentController>()
  136. ->Version(0)
  137. ->Field("Configuration", &MeshComponentController::m_configuration);
  138. }
  139. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  140. {
  141. behaviorContext->ConstantProperty("DefaultLodOverride", BehaviorConstant(0))
  142. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  143. ->Attribute(AZ::Script::Attributes::Category, "render")
  144. ->Attribute(AZ::Script::Attributes::Module, "render");
  145. behaviorContext->ConstantProperty("DefaultLodType", BehaviorConstant(RPI::Cullable::LodType::Default))
  146. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  147. ->Attribute(AZ::Script::Attributes::Category, "render")
  148. ->Attribute(AZ::Script::Attributes::Module, "render");
  149. behaviorContext->EBus<MeshComponentRequestBus>("RenderMeshComponentRequestBus")
  150. ->Event("GetModelAssetId", &MeshComponentRequestBus::Events::GetModelAssetId)
  151. ->Event("SetModelAssetId", &MeshComponentRequestBus::Events::SetModelAssetId)
  152. ->Event("GetModelAssetPath", &MeshComponentRequestBus::Events::GetModelAssetPath)
  153. ->Event("SetModelAssetPath", &MeshComponentRequestBus::Events::SetModelAssetPath)
  154. ->Event("SetSortKey", &MeshComponentRequestBus::Events::SetSortKey)
  155. ->Event("GetSortKey", &MeshComponentRequestBus::Events::GetSortKey)
  156. ->Event("SetIsAlwaysDynamic", &MeshComponentRequestBus::Events::SetIsAlwaysDynamic)
  157. ->Event("GetIsAlwaysDynamic", &MeshComponentRequestBus::Events::GetIsAlwaysDynamic)
  158. ->Event("SetLodType", &MeshComponentRequestBus::Events::SetLodType)
  159. ->Event("GetLodType", &MeshComponentRequestBus::Events::GetLodType)
  160. ->Event("SetLodOverride", &MeshComponentRequestBus::Events::SetLodOverride)
  161. ->Event("GetLodOverride", &MeshComponentRequestBus::Events::GetLodOverride)
  162. ->Event("SetMinimumScreenCoverage", &MeshComponentRequestBus::Events::SetMinimumScreenCoverage)
  163. ->Event("GetMinimumScreenCoverage", &MeshComponentRequestBus::Events::GetMinimumScreenCoverage)
  164. ->Event("SetQualityDecayRate", &MeshComponentRequestBus::Events::SetQualityDecayRate)
  165. ->Event("GetQualityDecayRate", &MeshComponentRequestBus::Events::GetQualityDecayRate)
  166. ->Event("SetRayTracingEnabled", &MeshComponentRequestBus::Events::SetRayTracingEnabled)
  167. ->Event("GetExcludeFromReflectionCubeMaps", &MeshComponentRequestBus::Events::GetExcludeFromReflectionCubeMaps)
  168. ->Event("SetExcludeFromReflectionCubeMaps", &MeshComponentRequestBus::Events::SetExcludeFromReflectionCubeMaps)
  169. ->Event("GetRayTracingEnabled", &MeshComponentRequestBus::Events::GetRayTracingEnabled)
  170. ->Event("SetVisibility", &MeshComponentRequestBus::Events::SetVisibility)
  171. ->Event("GetVisibility", &MeshComponentRequestBus::Events::GetVisibility)
  172. ->VirtualProperty("ModelAssetId", "GetModelAssetId", "SetModelAssetId")
  173. ->VirtualProperty("ModelAssetPath", "GetModelAssetPath", "SetModelAssetPath")
  174. ->VirtualProperty("SortKey", "GetSortKey", "SetSortKey")
  175. ->VirtualProperty("IsAlwaysDynamic", "GetIsAlwaysDynamic", "SetIsAlwaysDynamic")
  176. ->VirtualProperty("LodType", "GetLodType", "SetLodType")
  177. ->VirtualProperty("LodOverride", "GetLodOverride", "SetLodOverride")
  178. ->VirtualProperty("MinimumScreenCoverage", "GetMinimumScreenCoverage", "SetMinimumScreenCoverage")
  179. ->VirtualProperty("QualityDecayRate", "GetQualityDecayRate", "SetQualityDecayRate")
  180. ->VirtualProperty("RayTracingEnabled", "GetRayTracingEnabled", "SetRayTracingEnabled")
  181. ->VirtualProperty("ExcludeFromReflectionCubeMaps", "GetExcludeFromReflectionCubeMaps", "SetExcludeFromReflectionCubeMaps")
  182. ->VirtualProperty("Visibility", "GetVisibility", "SetVisibility")
  183. ;
  184. behaviorContext->EBus<MeshComponentNotificationBus>("MeshComponentNotificationBus")
  185. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  186. ->Attribute(AZ::Script::Attributes::Category, "render")
  187. ->Attribute(AZ::Script::Attributes::Module, "render")
  188. ->Handler<Internal::MeshComponentNotificationBusHandler>();
  189. }
  190. }
  191. void MeshComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  192. {
  193. dependent.push_back(AZ_CRC_CE("TransformService"));
  194. dependent.push_back(AZ_CRC_CE("NonUniformScaleService"));
  195. }
  196. void MeshComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  197. {
  198. provided.push_back(AZ_CRC_CE("MaterialConsumerService"));
  199. provided.push_back(AZ_CRC_CE("MeshService"));
  200. }
  201. void MeshComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  202. {
  203. incompatible.push_back(AZ_CRC_CE("MaterialConsumerService"));
  204. incompatible.push_back(AZ_CRC_CE("MeshService"));
  205. }
  206. MeshComponentController::MeshComponentController(const MeshComponentConfig& config)
  207. : m_configuration(config)
  208. {
  209. }
  210. static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId)
  211. {
  212. AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
  213. AzFramework::EntityIdContextQueryBus::EventResult(
  214. contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
  215. return contextId;
  216. }
  217. void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair)
  218. {
  219. const AZ::EntityId entityId = entityComponentIdPair.GetEntityId();
  220. m_entityComponentIdPair = entityComponentIdPair;
  221. m_transformInterface = TransformBus::FindFirstHandler(entityId);
  222. AZ_Warning(
  223. "MeshComponentController", m_transformInterface,
  224. "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin.");
  225. m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(entityId);
  226. AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
  227. m_cachedNonUniformScale = AZ::Vector3::CreateOne();
  228. AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale);
  229. AZ::NonUniformScaleRequestBus::Event(
  230. entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler);
  231. const auto entityContextId = FindOwningContextId(entityId);
  232. MeshComponentRequestBus::Handler::BusConnect(entityId);
  233. MeshHandleStateRequestBus::Handler::BusConnect(entityId);
  234. AtomImGuiTools::AtomImGuiMeshCallbackBus::Handler::BusConnect(entityId);
  235. TransformNotificationBus::Handler::BusConnect(entityId);
  236. MaterialConsumerRequestBus::Handler::BusConnect(entityId);
  237. MaterialComponentNotificationBus::Handler::BusConnect(entityId);
  238. AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
  239. AzFramework::VisibleGeometryRequestBus::Handler::BusConnect(entityId);
  240. AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({ entityId, entityContextId });
  241. AzFramework::RenderGeometry::IntersectionNotificationBus::Bind(m_intersectionNotificationBus, entityContextId);
  242. // Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange
  243. RegisterModel();
  244. }
  245. void MeshComponentController::Deactivate()
  246. {
  247. // Buses must be disconnected after unregistering the model, otherwise they can't deliver the events during the process.
  248. UnregisterModel();
  249. AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusDisconnect();
  250. AzFramework::VisibleGeometryRequestBus::Handler::BusDisconnect();
  251. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  252. MaterialComponentNotificationBus::Handler::BusDisconnect();
  253. MaterialConsumerRequestBus::Handler::BusDisconnect();
  254. TransformNotificationBus::Handler::BusDisconnect();
  255. MeshComponentRequestBus::Handler::BusDisconnect();
  256. MeshHandleStateRequestBus::Handler::BusDisconnect();
  257. AtomImGuiTools::AtomImGuiMeshCallbackBus::Handler::BusDisconnect();
  258. m_nonUniformScaleChangedHandler.Disconnect();
  259. m_meshFeatureProcessor = nullptr;
  260. m_transformInterface = nullptr;
  261. m_entityComponentIdPair = AZ::EntityComponentIdPair(AZ::EntityId(), AZ::InvalidComponentId);
  262. m_configuration.m_modelAsset.Release();
  263. }
  264. void MeshComponentController::SetConfiguration(const MeshComponentConfig& config)
  265. {
  266. m_configuration = config;
  267. }
  268. const MeshComponentConfig& MeshComponentController::GetConfiguration() const
  269. {
  270. return m_configuration;
  271. }
  272. void MeshComponentController::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
  273. {
  274. if (m_meshFeatureProcessor)
  275. {
  276. m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale);
  277. }
  278. // ensure the render geometry is kept in sync with any changes to the entity the mesh is on
  279. AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
  280. m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
  281. m_entityComponentIdPair.GetEntityId());
  282. }
  283. void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale)
  284. {
  285. m_cachedNonUniformScale = nonUniformScale;
  286. if (m_meshFeatureProcessor)
  287. {
  288. m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale);
  289. }
  290. }
  291. MaterialAssignmentLabelMap MeshComponentController::GetMaterialLabels() const
  292. {
  293. return GetMaterialSlotLabelsFromModelAsset(GetModelAsset());
  294. }
  295. MaterialAssignmentId MeshComponentController::FindMaterialAssignmentId(
  296. const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
  297. {
  298. return GetMaterialSlotIdFromModelAsset(GetModelAsset(), lod, label);
  299. }
  300. MaterialAssignmentMap MeshComponentController::GetDefaultMaterialMap() const
  301. {
  302. return GetDefaultMaterialMapFromModelAsset(GetModelAsset());
  303. }
  304. AZStd::unordered_set<AZ::Name> MeshComponentController::GetModelUvNames() const
  305. {
  306. const Data::Instance<RPI::Model> model = GetModel();
  307. return model ? model->GetUvNames() : AZStd::unordered_set<AZ::Name>();
  308. }
  309. void MeshComponentController::OnMaterialsUpdated(const MaterialAssignmentMap& materials)
  310. {
  311. if (m_meshFeatureProcessor)
  312. {
  313. m_meshFeatureProcessor->SetCustomMaterials(m_meshHandle, ConvertToCustomMaterialMap(materials));
  314. }
  315. }
  316. void MeshComponentController::OnMaterialPropertiesUpdated([[maybe_unused]] const MaterialAssignmentMap& materials)
  317. {
  318. if (m_meshFeatureProcessor)
  319. {
  320. m_meshFeatureProcessor->SetRayTracingDirty(m_meshHandle);
  321. }
  322. }
  323. bool MeshComponentController::RequiresCloning(const Data::Asset<RPI::ModelAsset>& modelAsset)
  324. {
  325. // Is the model asset containing a cloth buffer? If yes, we need to clone the model asset for instancing.
  326. const AZStd::span<const AZ::Data::Asset<AZ::RPI::ModelLodAsset>> lodAssets = modelAsset->GetLodAssets();
  327. for (const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodAsset : lodAssets)
  328. {
  329. const AZStd::span<const AZ::RPI::ModelLodAsset::Mesh> meshes = lodAsset->GetMeshes();
  330. for (const AZ::RPI::ModelLodAsset::Mesh& mesh : meshes)
  331. {
  332. if (mesh.GetSemanticBufferAssetView(s_CLOTH_DATA_Name) != nullptr)
  333. {
  334. return true;
  335. }
  336. }
  337. }
  338. return false;
  339. }
  340. void MeshComponentController::HandleModelChange(Data::Instance<RPI::Model> model)
  341. {
  342. Data::Asset<RPI::ModelAsset> modelAsset = m_meshFeatureProcessor->GetModelAsset(m_meshHandle);
  343. if (model && modelAsset)
  344. {
  345. const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
  346. m_configuration.m_modelAsset = modelAsset;
  347. MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model);
  348. MaterialConsumerNotificationBus::Event(entityId, &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  349. AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(entityId);
  350. AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
  351. m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
  352. m_entityComponentIdPair.GetEntityId());
  353. MeshHandleStateNotificationBus::Event(entityId, &MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
  354. }
  355. }
  356. void MeshComponentController::HandleObjectSrgCreate(const Data::Instance<RPI::ShaderResourceGroup>& objectSrg)
  357. {
  358. MeshComponentNotificationBus::Event(m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnObjectSrgCreated, objectSrg);
  359. }
  360. void MeshComponentController::RegisterModel()
  361. {
  362. if (m_meshFeatureProcessor && m_configuration.m_modelAsset.GetId().IsValid())
  363. {
  364. const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
  365. MaterialAssignmentMap materials;
  366. MaterialComponentRequestBus::EventResult(materials, entityId, &MaterialComponentRequests::GetMaterialMap);
  367. m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
  368. MeshHandleDescriptor meshDescriptor;
  369. meshDescriptor.m_modelAsset = m_configuration.m_modelAsset;
  370. meshDescriptor.m_useForwardPassIblSpecular = m_configuration.m_useForwardPassIblSpecular;
  371. meshDescriptor.m_requiresCloneCallback = RequiresCloning;
  372. meshDescriptor.m_isRayTracingEnabled = m_configuration.m_isRayTracingEnabled;
  373. meshDescriptor.m_excludeFromReflectionCubeMaps = m_configuration.m_excludeFromReflectionCubeMaps;
  374. meshDescriptor.m_isAlwaysDynamic = m_configuration.m_isAlwaysDynamic;
  375. m_meshHandle = m_meshFeatureProcessor->AcquireMesh(meshDescriptor, ConvertToCustomMaterialMap(materials));
  376. m_meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_changeEventHandler);
  377. m_meshFeatureProcessor->ConnectObjectSrgCreatedEventHandler(m_meshHandle, m_objectSrgCreatedHandler);
  378. const AZ::Transform& transform =
  379. m_transformInterface ? m_transformInterface->GetWorldTM() : AZ::Transform::CreateIdentity();
  380. m_meshFeatureProcessor->SetTransform(m_meshHandle, transform, m_cachedNonUniformScale);
  381. m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey);
  382. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, GetMeshLodConfiguration());
  383. m_meshFeatureProcessor->SetVisible(m_meshHandle, m_isVisible);
  384. m_meshFeatureProcessor->SetRayTracingEnabled(m_meshHandle, meshDescriptor.m_isRayTracingEnabled);
  385. // [GFX TODO] This should happen automatically. m_changeEventHandler should be passed to AcquireMesh
  386. // If the model instance or asset already exists, announce a model change to let others know it's loaded.
  387. HandleModelChange(m_meshFeatureProcessor->GetModel(m_meshHandle));
  388. }
  389. else
  390. {
  391. // If there is no model asset to be loaded then we need to invalidate the material slot configuration
  392. MaterialConsumerNotificationBus::Event(
  393. m_entityComponentIdPair.GetEntityId(), &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  394. }
  395. }
  396. void MeshComponentController::UnregisterModel()
  397. {
  398. if (m_meshFeatureProcessor && m_meshHandle.IsValid())
  399. {
  400. MeshComponentNotificationBus::Event(
  401. m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnModelPreDestroy);
  402. m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
  403. MeshHandleStateNotificationBus::Event(
  404. m_entityComponentIdPair.GetEntityId(), &MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
  405. // Model has been released which invalidates the material slot configuration
  406. MaterialConsumerNotificationBus::Event(
  407. m_entityComponentIdPair.GetEntityId(), &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  408. }
  409. }
  410. void MeshComponentController::RefreshModelRegistration()
  411. {
  412. // [GFX TODO][ATOM-13364] Without the Suspend / Resume calls below, a model refresh will trigger an asset unload and reload
  413. // that breaks Material Thumbnail Previews in the Editor. The asset unload/reload itself is undesirable, but the flow should
  414. // get investigated further to determine what state management and notifications need to be modified, since the previews ought
  415. // to still work even if a full asset reload were to occur here.
  416. // The unregister / register combination can cause the asset reference to get released, which could trigger a full reload
  417. // of the asset. Tell the Asset Manager not to release any asset references until after the registration is complete.
  418. // This will ensure that if we're reusing the same model, it remains loaded.
  419. Data::AssetManager::Instance().SuspendAssetRelease();
  420. UnregisterModel();
  421. RegisterModel();
  422. Data::AssetManager::Instance().ResumeAssetRelease();
  423. }
  424. void MeshComponentController::SetModelAssetId(Data::AssetId modelAssetId)
  425. {
  426. SetModelAsset(Data::Asset<RPI::ModelAsset>(modelAssetId, azrtti_typeid<RPI::ModelAsset>()));
  427. }
  428. void MeshComponentController::SetModelAsset(Data::Asset<RPI::ModelAsset> modelAsset)
  429. {
  430. if (m_configuration.m_modelAsset != modelAsset)
  431. {
  432. m_configuration.m_modelAsset = modelAsset;
  433. m_configuration.m_modelAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
  434. RefreshModelRegistration();
  435. }
  436. }
  437. Data::Asset<const RPI::ModelAsset> MeshComponentController::GetModelAsset() const
  438. {
  439. return m_configuration.m_modelAsset;
  440. }
  441. Data::AssetId MeshComponentController::GetModelAssetId() const
  442. {
  443. return m_configuration.m_modelAsset.GetId();
  444. }
  445. void MeshComponentController::SetModelAssetPath(const AZStd::string& modelAssetPath)
  446. {
  447. AZ::Data::AssetId assetId;
  448. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, modelAssetPath.c_str(), AZ::RPI::ModelAsset::RTTI_Type(), false);
  449. SetModelAssetId(assetId);
  450. }
  451. AZStd::string MeshComponentController::GetModelAssetPath() const
  452. {
  453. AZStd::string assetPathString;
  454. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_modelAsset.GetId());
  455. return assetPathString;
  456. }
  457. Data::Instance<RPI::Model> MeshComponentController::GetModel() const
  458. {
  459. return m_meshFeatureProcessor ? m_meshFeatureProcessor->GetModel(m_meshHandle) : Data::Instance<RPI::Model>();
  460. }
  461. const RPI::MeshDrawPacketLods* MeshComponentController::GetDrawPackets() const
  462. {
  463. return m_meshFeatureProcessor ? &m_meshFeatureProcessor->GetDrawPackets(m_meshHandle) : nullptr;
  464. }
  465. void MeshComponentController::SetSortKey(RHI::DrawItemSortKey sortKey)
  466. {
  467. m_configuration.m_sortKey = sortKey; // Save for serialization
  468. m_meshFeatureProcessor->SetSortKey(m_meshHandle, sortKey);
  469. }
  470. RHI::DrawItemSortKey MeshComponentController::GetSortKey() const
  471. {
  472. return m_meshFeatureProcessor->GetSortKey(m_meshHandle);
  473. }
  474. void MeshComponentController::SetIsAlwaysDynamic(bool isAlwaysDynamic)
  475. {
  476. m_configuration.m_isAlwaysDynamic = isAlwaysDynamic; // Save for serialization
  477. m_meshFeatureProcessor->SetIsAlwaysDynamic(m_meshHandle, isAlwaysDynamic);
  478. }
  479. bool MeshComponentController::GetIsAlwaysDynamic() const
  480. {
  481. return m_meshFeatureProcessor->GetIsAlwaysDynamic(m_meshHandle);
  482. }
  483. RPI::Cullable::LodConfiguration MeshComponentController::GetMeshLodConfiguration() const
  484. {
  485. return {
  486. m_configuration.m_lodType,
  487. m_configuration.m_lodOverride,
  488. m_configuration.m_minimumScreenCoverage,
  489. m_configuration.m_qualityDecayRate
  490. };
  491. }
  492. // -----------------------
  493. void MeshComponentController::SetLodType(RPI::Cullable::LodType lodType)
  494. {
  495. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  496. lodConfig.m_lodType = lodType;
  497. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  498. }
  499. RPI::Cullable::LodType MeshComponentController::GetLodType() const
  500. {
  501. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  502. return lodConfig.m_lodType;
  503. }
  504. void MeshComponentController::SetLodOverride(RPI::Cullable::LodOverride lodOverride)
  505. {
  506. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  507. lodConfig.m_lodOverride = lodOverride;
  508. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  509. }
  510. RPI::Cullable::LodOverride MeshComponentController::GetLodOverride() const
  511. {
  512. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  513. return lodConfig.m_lodOverride;
  514. }
  515. void MeshComponentController::SetMinimumScreenCoverage(float minimumScreenCoverage)
  516. {
  517. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  518. lodConfig.m_minimumScreenCoverage = minimumScreenCoverage;
  519. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  520. }
  521. float MeshComponentController::GetMinimumScreenCoverage() const
  522. {
  523. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  524. return lodConfig.m_minimumScreenCoverage;
  525. }
  526. void MeshComponentController::SetQualityDecayRate(float qualityDecayRate)
  527. {
  528. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  529. lodConfig.m_qualityDecayRate = qualityDecayRate;
  530. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  531. }
  532. float MeshComponentController::GetQualityDecayRate() const
  533. {
  534. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  535. return lodConfig.m_qualityDecayRate;
  536. }
  537. void MeshComponentController::SetVisibility(bool visible)
  538. {
  539. if (m_isVisible != visible)
  540. {
  541. if (m_meshFeatureProcessor)
  542. {
  543. m_meshFeatureProcessor->SetVisible(m_meshHandle, visible);
  544. }
  545. m_isVisible = visible;
  546. }
  547. }
  548. bool MeshComponentController::GetVisibility() const
  549. {
  550. return m_isVisible;
  551. }
  552. void MeshComponentController::SetRayTracingEnabled(bool enabled)
  553. {
  554. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  555. {
  556. m_meshFeatureProcessor->SetRayTracingEnabled(m_meshHandle, enabled);
  557. m_configuration.m_isRayTracingEnabled = enabled;
  558. }
  559. }
  560. bool MeshComponentController::GetRayTracingEnabled() const
  561. {
  562. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  563. {
  564. return m_meshFeatureProcessor->GetRayTracingEnabled(m_meshHandle);
  565. }
  566. return false;
  567. }
  568. void MeshComponentController::SetExcludeFromReflectionCubeMaps(bool excludeFromReflectionCubeMaps)
  569. {
  570. m_configuration.m_excludeFromReflectionCubeMaps = excludeFromReflectionCubeMaps;
  571. if (m_meshFeatureProcessor)
  572. {
  573. m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, excludeFromReflectionCubeMaps);
  574. }
  575. }
  576. bool MeshComponentController::GetExcludeFromReflectionCubeMaps() const
  577. {
  578. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  579. {
  580. return m_meshFeatureProcessor->GetExcludeFromReflectionCubeMaps(m_meshHandle);
  581. }
  582. return false;
  583. }
  584. Aabb MeshComponentController::GetWorldBounds()
  585. {
  586. if (const AZ::Aabb localBounds = GetLocalBounds(); localBounds.IsValid())
  587. {
  588. return localBounds.GetTransformedAabb(m_transformInterface->GetWorldTM());
  589. }
  590. return AZ::Aabb::CreateNull();
  591. }
  592. Aabb MeshComponentController::GetLocalBounds()
  593. {
  594. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  595. {
  596. if (Aabb aabb = m_meshFeatureProcessor->GetLocalAabb(m_meshHandle); aabb.IsValid())
  597. {
  598. aabb.MultiplyByScale(m_cachedNonUniformScale);
  599. return aabb;
  600. }
  601. }
  602. return Aabb::CreateNull();
  603. }
  604. void MeshComponentController::GetVisibleGeometry(
  605. [[maybe_unused]] const AZ::Aabb& bounds, AzFramework::VisibleGeometryContainer& geometryContainer) const
  606. {
  607. // Attempt to copy the triangle list geometry data out of the model asset into the visible geometry structure
  608. const auto& modelAsset = GetModelAsset();
  609. if (!modelAsset.IsReady() || modelAsset->GetLodAssets().empty())
  610. {
  611. AZ_Warning("MeshComponentController", false, "Unable to get geometry because mesh asset is not ready or empty.");
  612. return;
  613. }
  614. // This will only extract data from the first LOD. It might be necessary to make the LOD selectable.
  615. const auto& lodAsset = modelAsset->GetLodAssets().front();
  616. if (!lodAsset.IsReady())
  617. {
  618. AZ_Warning("MeshComponentController", false, "Unable to get geometry because selected LOD asset is not ready.");
  619. return;
  620. }
  621. const AZ::Name positionName{ "POSITION" };
  622. for (const auto& mesh : lodAsset->GetMeshes())
  623. {
  624. // Get the index buffer data, confirming that the asset is valid and indices are 32 bit integers. Other formats are
  625. // currently not supported.
  626. const AZ::RPI::BufferAssetView& indexBufferView = mesh.GetIndexBufferAssetView();
  627. const AZ::RHI::BufferViewDescriptor& indexBufferViewDesc = indexBufferView.GetBufferViewDescriptor();
  628. const AZ::Data::Asset<AZ::RPI::BufferAsset> indexBufferAsset = indexBufferView.GetBufferAsset();
  629. if (!indexBufferAsset.IsReady() || indexBufferViewDesc.m_elementSize != sizeof(uint32_t))
  630. {
  631. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because index buffer asset is not ready or is an incompatible format.");
  632. continue;
  633. }
  634. // Get the position buffer data, if it exists with the expected name.
  635. const AZ::RPI::BufferAssetView* positionBufferView = mesh.GetSemanticBufferAssetView(positionName);
  636. if (!positionBufferView)
  637. {
  638. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because position buffer data was not found.");
  639. continue;
  640. }
  641. // Confirm that the position buffer is valid and contains 3 32 bit floats for each position. Other formats are currently not
  642. // supported.
  643. constexpr uint32_t ElementsPerVertex = 3;
  644. const AZ::RHI::BufferViewDescriptor& positionBufferViewDesc = positionBufferView->GetBufferViewDescriptor();
  645. const AZ::Data::Asset<AZ::RPI::BufferAsset> positionBufferAsset = positionBufferView->GetBufferAsset();
  646. if (!positionBufferAsset.IsReady() || positionBufferViewDesc.m_elementSize != sizeof(float) * ElementsPerVertex)
  647. {
  648. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because position buffer asset is not ready or is an incompatible format.");
  649. continue;
  650. }
  651. const AZStd::span<const uint8_t> indexRawBuffer = indexBufferAsset->GetBuffer();
  652. const uint32_t* indexPtr = reinterpret_cast<const uint32_t*>(
  653. indexRawBuffer.data() + (indexBufferViewDesc.m_elementOffset * indexBufferViewDesc.m_elementSize));
  654. const AZStd::span<const uint8_t> positionRawBuffer = positionBufferAsset->GetBuffer();
  655. const float* positionPtr = reinterpret_cast<const float*>(
  656. positionRawBuffer.data() + (positionBufferViewDesc.m_elementOffset * positionBufferViewDesc.m_elementSize));
  657. // Copy the index and position data into the visible geometry structure.
  658. AzFramework::VisibleGeometry visibleGeometry;
  659. visibleGeometry.m_transform = AZ::Matrix4x4::CreateFromTransform(m_transformInterface->GetWorldTM());
  660. // Reserve space for indices and copy data, assuming stride between elements is 0.
  661. visibleGeometry.m_indices.resize_no_construct(indexBufferViewDesc.m_elementCount);
  662. AZ_Assert(
  663. (sizeof(visibleGeometry.m_indices[0]) * visibleGeometry.m_indices.size()) >=
  664. (indexBufferViewDesc.m_elementSize * indexBufferViewDesc.m_elementCount),
  665. "Index buffer size exceeds memory allocated for visible geometry indices.");
  666. memcpy(&visibleGeometry.m_indices[0], indexPtr, indexBufferViewDesc.m_elementCount * indexBufferViewDesc.m_elementSize);
  667. // Reserve space for vertices and copy data, assuming stride between elements is 0.
  668. visibleGeometry.m_vertices.resize_no_construct(positionBufferViewDesc.m_elementCount * ElementsPerVertex);
  669. AZ_Assert(
  670. (sizeof(visibleGeometry.m_vertices[0]) * visibleGeometry.m_vertices.size()) >=
  671. (positionBufferViewDesc.m_elementSize * positionBufferViewDesc.m_elementCount),
  672. "Position buffer size exceeds memory allocated for visible geometry vertices.");
  673. memcpy(&visibleGeometry.m_vertices[0], positionPtr, positionBufferViewDesc.m_elementCount * positionBufferViewDesc.m_elementSize);
  674. geometryContainer.emplace_back(AZStd::move(visibleGeometry));
  675. }
  676. }
  677. AzFramework::RenderGeometry::RayResult MeshComponentController::RenderGeometryIntersect(
  678. const AzFramework::RenderGeometry::RayRequest& ray)
  679. {
  680. AzFramework::RenderGeometry::RayResult result;
  681. if (const Data::Instance<RPI::Model> model = GetModel())
  682. {
  683. float t;
  684. AZ::Vector3 normal;
  685. if (model->RayIntersection(
  686. m_transformInterface->GetWorldTM(), m_cachedNonUniformScale, ray.m_startWorldPosition,
  687. ray.m_endWorldPosition - ray.m_startWorldPosition, t, normal))
  688. {
  689. // fill in ray result structure after successful intersection
  690. const auto intersectionLine = (ray.m_endWorldPosition - ray.m_startWorldPosition);
  691. result.m_uv = AZ::Vector2::CreateZero();
  692. result.m_worldPosition = ray.m_startWorldPosition + intersectionLine * t;
  693. result.m_worldNormal = normal;
  694. result.m_distance = intersectionLine.GetLength() * t;
  695. result.m_entityAndComponent = m_entityComponentIdPair;
  696. }
  697. }
  698. return result;
  699. }
  700. const MeshFeatureProcessorInterface::MeshHandle* MeshComponentController::GetMeshHandle() const
  701. {
  702. return &m_meshHandle;
  703. }
  704. } // namespace Render
  705. } // namespace AZ