MeshComponentController.cpp 45 KB

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