MaterialComponentController.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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 <Material/MaterialComponentController.h>
  9. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  10. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AtomCore/Instance/InstanceDatabase.h>
  13. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
  14. namespace AZ
  15. {
  16. namespace Render
  17. {
  18. void MaterialComponentController::Reflect(ReflectContext* context)
  19. {
  20. MaterialComponentConfig::Reflect(context);
  21. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  22. {
  23. serializeContext->Class<MaterialComponentController>()
  24. ->Version(1)
  25. ->Field("Configuration", &MaterialComponentController::m_configuration)
  26. ;
  27. }
  28. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  29. {
  30. behaviorContext->EBus<MaterialComponentRequestBus>("MaterialComponentRequestBus")
  31. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  32. ->Attribute(AZ::Script::Attributes::Category, "render")
  33. ->Attribute(AZ::Script::Attributes::Module, "render")
  34. ->Event("GetOriginalMaterialAssignments", &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments)
  35. ->Event("FindMaterialAssignmentId", &MaterialComponentRequestBus::Events::FindMaterialAssignmentId)
  36. ->Event("SetMaterialOverrides", &MaterialComponentRequestBus::Events::SetMaterialOverrides)
  37. ->Event("GetMaterialOverrides", &MaterialComponentRequestBus::Events::GetMaterialOverrides)
  38. ->Event("ClearAllMaterialOverrides", &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides)
  39. ->Event("SetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride)
  40. ->Event("GetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::GetDefaultMaterialOverride)
  41. ->Event("ClearDefaultMaterialOverride", &MaterialComponentRequestBus::Events::ClearDefaultMaterialOverride)
  42. ->Event("SetMaterialOverride", &MaterialComponentRequestBus::Events::SetMaterialOverride)
  43. ->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride)
  44. ->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride)
  45. ->Event("SetPropertyOverride", &MaterialComponentRequestBus::Events::SetPropertyOverride)
  46. ->Event("GetPropertyOverride", &MaterialComponentRequestBus::Events::GetPropertyOverride)
  47. ->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride)
  48. ->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides)
  49. ->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides)
  50. ->Event("GetPropertyOverrides", &MaterialComponentRequestBus::Events::GetPropertyOverrides)
  51. ;
  52. }
  53. }
  54. void MaterialComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  55. {
  56. provided.push_back(AZ_CRC("MaterialProviderService", 0x64849a6b));
  57. }
  58. void MaterialComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  59. {
  60. incompatible.push_back(AZ_CRC("MaterialProviderService", 0x64849a6b));
  61. }
  62. void MaterialComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  63. {
  64. required.push_back(AZ_CRC("MaterialReceiverService", 0x0d1a6a74));
  65. }
  66. MaterialComponentController::MaterialComponentController(const MaterialComponentConfig& config)
  67. : m_configuration(config)
  68. {
  69. }
  70. void MaterialComponentController::Activate(EntityId entityId)
  71. {
  72. m_entityId = entityId;
  73. m_queuedMaterialUpdateNotification = false;
  74. MaterialComponentRequestBus::Handler::BusConnect(m_entityId);
  75. LoadMaterials();
  76. }
  77. void MaterialComponentController::Deactivate()
  78. {
  79. MaterialComponentRequestBus::Handler::BusDisconnect();
  80. MeshComponentNotificationBus::Handler::BusDisconnect();
  81. TickBus::Handler::BusDisconnect();
  82. ReleaseMaterials();
  83. m_queuedMaterialUpdateNotification = false;
  84. m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
  85. }
  86. void MaterialComponentController::SetConfiguration(const MaterialComponentConfig& config)
  87. {
  88. m_configuration = config;
  89. }
  90. const MaterialComponentConfig& MaterialComponentController::GetConfiguration() const
  91. {
  92. return m_configuration;
  93. }
  94. void MaterialComponentController::OnAssetReady(Data::Asset<Data::AssetData> asset)
  95. {
  96. InitializeMaterialInstance(asset);
  97. }
  98. void MaterialComponentController::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  99. {
  100. InitializeMaterialInstance(asset);
  101. }
  102. void MaterialComponentController::OnModelReady(const Data::Asset<RPI::ModelAsset>&, const Data::Instance<RPI::Model>&)
  103. {
  104. MeshComponentNotificationBus::Handler::BusDisconnect();
  105. // If there is a circumstance where the saved material assignments are empty, fill them in with the default material.
  106. // (This could happen as a result of LoadMaterials() clearing the asset reference to deal with an edge case)
  107. // Now that a model asset is ready, see if there are any empty assignments that need to be filled...
  108. RPI::ModelMaterialSlotMap modelMaterialSlots;
  109. MaterialReceiverRequestBus::EventResult(modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
  110. AZStd::vector<Data::Asset<RPI::MaterialAsset>> newMaterialAssets;
  111. newMaterialAssets.reserve(m_configuration.m_materials.size());
  112. // First we fill the empty slots but don't connect to AssetBus yet. If the same material asset appears multiple times,
  113. // AssetBus will call OnAssetReady only the *first* time we connect for that asset. The full list of m_configuration.m_materials
  114. // needs to be updated before that happens.
  115. for (auto& materialPair : m_configuration.m_materials)
  116. {
  117. auto& materialAsset = materialPair.second.m_materialAsset;
  118. if (!materialAsset.GetId().IsValid())
  119. {
  120. auto slotIter = modelMaterialSlots.find(materialPair.first.m_materialSlotStableId);
  121. if (slotIter != modelMaterialSlots.end())
  122. {
  123. materialAsset = slotIter->second.m_defaultMaterialAsset;
  124. newMaterialAssets.push_back(materialAsset);
  125. }
  126. else
  127. {
  128. AZ_Error("MaterialComponentController", false, "Could not find material slot %d", materialPair.first.m_materialSlotStableId);
  129. }
  130. }
  131. }
  132. // Now that the configuration is updated with all the default material assets, we can load and connect them.
  133. // If there are duplicates in this list, the redundant calls will be ignored.
  134. for (auto& materialAsset : newMaterialAssets)
  135. {
  136. if (!materialAsset.IsReady())
  137. {
  138. materialAsset.QueueLoad();
  139. }
  140. Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
  141. }
  142. }
  143. void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  144. {
  145. AZStd::unordered_set<MaterialAssignmentId> propertyOverrides;
  146. AZStd::swap(m_queuedPropertyOverrides, propertyOverrides);
  147. // Iterate through all MaterialAssignmentId's that have property overrides and attempt to apply them
  148. // if material instance is already compiling, delay application of property overrides until next frame
  149. for (const auto& materialAssignmentId : propertyOverrides)
  150. {
  151. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  152. if (materialIt == m_configuration.m_materials.end())
  153. {
  154. //Skip materials that do not exist in the map
  155. continue;
  156. }
  157. auto materialInstance = materialIt->second.m_materialInstance;
  158. if (!materialInstance)
  159. {
  160. //Skip materials with an invalid instances
  161. continue;
  162. }
  163. if (!materialInstance->CanCompile())
  164. {
  165. //If a material cannot currently be compiled then it must be queued again
  166. m_queuedPropertyOverrides.emplace(materialAssignmentId);
  167. continue;
  168. }
  169. const auto& propertyOverrides2 = materialIt->second.m_propertyOverrides;
  170. for (auto& propertyPair : propertyOverrides2)
  171. {
  172. const auto& materialPropertyIndex = materialInstance->FindPropertyIndex(propertyPair.first);
  173. if (!materialPropertyIndex.IsNull())
  174. {
  175. if (propertyPair.second.is<Data::AssetId>())
  176. {
  177. const auto& assetId = *AZStd::any_cast<Data::AssetId>(&propertyPair.second);
  178. Data::Asset<RPI::ImageAsset> imageAsset(assetId, azrtti_typeid<RPI::StreamingImageAsset>());
  179. materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue(imageAsset));
  180. }
  181. else
  182. {
  183. materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
  184. }
  185. }
  186. }
  187. materialInstance->Compile();
  188. }
  189. // Only disconnect from tick bus and send notification after all pending properties have been applied
  190. if (m_queuedPropertyOverrides.empty())
  191. {
  192. if (m_queuedMaterialUpdateNotification)
  193. {
  194. // Materials have been edited and instances have changed but the notification will only be sent once per tick
  195. m_queuedMaterialUpdateNotification = false;
  196. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
  197. }
  198. TickBus::Handler::BusDisconnect();
  199. }
  200. }
  201. void MaterialComponentController::LoadMaterials()
  202. {
  203. Data::AssetBus::MultiHandler::BusDisconnect();
  204. bool anyQueued = false;
  205. for (auto& materialPair : m_configuration.m_materials)
  206. {
  207. auto& materialAsset = materialPair.second.m_materialAsset;
  208. // This is a special case where a material was auto-generated from the model file, connected to a Material Component by the user,
  209. // and then later a setting was changed to NOT auto-generate the model materials anymore. We need to switch to the new default
  210. // material rather than trying to use the old default material which no longer exists. If that's the case, we reset the asset
  211. // and OnModelReady will fill in the appropriate default material asset later.
  212. {
  213. Data::AssetId modelAssetId;
  214. MeshComponentRequestBus::EventResult(modelAssetId, m_entityId, &MeshComponentRequestBus::Events::GetModelAssetId);
  215. bool materialWasGeneratedFromModel = (modelAssetId.m_guid == materialAsset.GetId().m_guid);
  216. Data::AssetInfo assetInfo;
  217. Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &Data::AssetCatalogRequestBus::Events::GetAssetInfoById, materialAsset.GetId());
  218. bool materialAssetExists = assetInfo.m_assetId.IsValid();
  219. if (materialWasGeneratedFromModel && !materialAssetExists)
  220. {
  221. AZ_Warning("MaterialComponentController", false, "The default material assignment for this slot has changed and will be replaced (was '%s').",
  222. materialAsset.ToString<AZStd::string>().c_str());
  223. materialAsset.Reset();
  224. }
  225. }
  226. if (materialAsset.GetId().IsValid())
  227. {
  228. if (!Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
  229. {
  230. anyQueued = true;
  231. materialAsset.QueueLoad();
  232. Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
  233. }
  234. }
  235. else
  236. {
  237. // Since a material asset wasn't found, we'll need to supply a default material. But the default materials
  238. // won't be known until after the mesh component has loaded the model data.
  239. MeshComponentNotificationBus::Handler::BusConnect(m_entityId);
  240. }
  241. }
  242. if (!anyQueued)
  243. {
  244. ReleaseMaterials();
  245. }
  246. }
  247. void MaterialComponentController::InitializeMaterialInstance(const Data::Asset<Data::AssetData>& asset)
  248. {
  249. bool allReady = true;
  250. for (auto& materialPair : m_configuration.m_materials)
  251. {
  252. auto& materialAsset = materialPair.second.m_materialAsset;
  253. if (materialAsset.GetId() == asset.GetId())
  254. {
  255. materialAsset = asset;
  256. }
  257. if (materialAsset.GetId().IsValid() && !materialAsset.IsReady())
  258. {
  259. allReady = false;
  260. }
  261. }
  262. if (allReady)
  263. {
  264. //Do not start updating materials and properties until all materials are loaded and ready
  265. //This prevents property changes from being queued and notifications from being sent with pending materials
  266. for (auto& materialPair : m_configuration.m_materials)
  267. {
  268. materialPair.second.RebuildInstance();
  269. QueuePropertyChanges(materialPair.first);
  270. }
  271. QueueMaterialUpdateNotification();
  272. }
  273. }
  274. void MaterialComponentController::ReleaseMaterials()
  275. {
  276. Data::AssetBus::MultiHandler::BusDisconnect();
  277. for (auto& materialPair : m_configuration.m_materials)
  278. {
  279. if (materialPair.second.m_materialAsset.GetId().IsValid())
  280. {
  281. materialPair.second.m_materialAsset.Release();
  282. materialPair.second.m_materialInstance = nullptr;
  283. }
  284. }
  285. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
  286. }
  287. MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const
  288. {
  289. MaterialAssignmentMap materialAssignmentMap;
  290. MaterialReceiverRequestBus::EventResult(
  291. materialAssignmentMap, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments);
  292. return materialAssignmentMap;
  293. }
  294. MaterialAssignmentId MaterialComponentController::FindMaterialAssignmentId(
  295. const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
  296. {
  297. MaterialAssignmentId materialAssignmentId;
  298. MaterialReceiverRequestBus::EventResult(
  299. materialAssignmentId, m_entityId, &MaterialReceiverRequestBus::Events::FindMaterialAssignmentId, lod, label);
  300. return materialAssignmentId;
  301. }
  302. void MaterialComponentController::SetMaterialOverrides(const MaterialAssignmentMap& materials)
  303. {
  304. // this function is called twice once material asset is changed, a temp variable is
  305. // needed to prevent material asset going out of scope during second call
  306. // before LoadMaterials() is called [LYN-2249]
  307. auto temp = m_configuration.m_materials;
  308. m_configuration.m_materials = materials;
  309. LoadMaterials();
  310. }
  311. const MaterialAssignmentMap& MaterialComponentController::GetMaterialOverrides() const
  312. {
  313. return m_configuration.m_materials;
  314. }
  315. void MaterialComponentController::ClearAllMaterialOverrides()
  316. {
  317. if (!m_configuration.m_materials.empty())
  318. {
  319. m_configuration.m_materials.clear();
  320. QueueMaterialUpdateNotification();
  321. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  322. }
  323. }
  324. void MaterialComponentController::SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId)
  325. {
  326. SetMaterialOverride(DefaultMaterialAssignmentId, materialAssetId);
  327. }
  328. const AZ::Data::AssetId MaterialComponentController::GetDefaultMaterialOverride() const
  329. {
  330. return GetMaterialOverride(DefaultMaterialAssignmentId);
  331. }
  332. void MaterialComponentController::ClearDefaultMaterialOverride()
  333. {
  334. ClearMaterialOverride(DefaultMaterialAssignmentId);
  335. }
  336. void MaterialComponentController::SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId)
  337. {
  338. m_configuration.m_materials[materialAssignmentId].m_materialAsset.Create(materialAssetId);
  339. LoadMaterials();
  340. }
  341. const AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const
  342. {
  343. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  344. if (materialIt == m_configuration.m_materials.end())
  345. {
  346. AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found.");
  347. return {};
  348. }
  349. return materialIt->second.m_materialAsset.GetId();
  350. }
  351. void MaterialComponentController::ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId)
  352. {
  353. if (m_configuration.m_materials.erase(materialAssignmentId) > 0)
  354. {
  355. QueueMaterialUpdateNotification();
  356. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  357. }
  358. }
  359. void MaterialComponentController::SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const Name& propertyName, const AZStd::any& propertyValue)
  360. {
  361. auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
  362. // When applying property overrides for the first time, new instance needs to be created in case the current instance is already used somewhere else to keep overrides local
  363. if (materialAssignment.m_propertyOverrides.empty())
  364. {
  365. materialAssignment.m_propertyOverrides[propertyName] = propertyValue;
  366. materialAssignment.RebuildInstance();
  367. QueueMaterialUpdateNotification();
  368. }
  369. else
  370. {
  371. materialAssignment.m_propertyOverrides[propertyName] = propertyValue;
  372. }
  373. QueuePropertyChanges(materialAssignmentId);
  374. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  375. }
  376. AZStd::any MaterialComponentController::GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const Name& propertyName) const
  377. {
  378. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  379. if (materialIt == m_configuration.m_materials.end())
  380. {
  381. AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found.");
  382. return {};
  383. }
  384. const auto propertyIt = materialIt->second.m_propertyOverrides.find(propertyName);
  385. if (propertyIt == materialIt->second.m_propertyOverrides.end())
  386. {
  387. AZ_Error("MaterialComponentController", false, "Property not found: %s.", propertyName.GetCStr());
  388. return {};
  389. }
  390. return propertyIt->second;
  391. }
  392. void MaterialComponentController::ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const Name& propertyName)
  393. {
  394. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  395. if (materialIt == m_configuration.m_materials.end())
  396. {
  397. AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found.");
  398. return;
  399. }
  400. auto propertyIt = materialIt->second.m_propertyOverrides.find(propertyName);
  401. if (propertyIt == materialIt->second.m_propertyOverrides.end())
  402. {
  403. AZ_Error("MaterialComponentController", false, "Property not found: %s.", propertyName.GetCStr());
  404. return;
  405. }
  406. materialIt->second.m_propertyOverrides.erase(propertyIt);
  407. if (materialIt->second.m_propertyOverrides.empty())
  408. {
  409. materialIt->second.RebuildInstance();
  410. QueueMaterialUpdateNotification();
  411. }
  412. QueuePropertyChanges(materialAssignmentId);
  413. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  414. }
  415. void MaterialComponentController::ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId)
  416. {
  417. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  418. if (materialIt == m_configuration.m_materials.end())
  419. {
  420. AZ_Error("MaterialComponentController", false, "MaterialAssignmentId not found.");
  421. return;
  422. }
  423. if (!materialIt->second.m_propertyOverrides.empty())
  424. {
  425. materialIt->second.m_propertyOverrides = {};
  426. materialIt->second.RebuildInstance();
  427. QueueMaterialUpdateNotification();
  428. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  429. }
  430. }
  431. void MaterialComponentController::ClearAllPropertyOverrides()
  432. {
  433. bool cleared = false;
  434. for (auto& materialPair : m_configuration.m_materials)
  435. {
  436. if (!materialPair.second.m_propertyOverrides.empty())
  437. {
  438. materialPair.second.m_propertyOverrides = {};
  439. materialPair.second.RebuildInstance();
  440. QueueMaterialUpdateNotification();
  441. cleared = true;
  442. }
  443. }
  444. if (cleared)
  445. {
  446. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited, m_configuration.m_materials);
  447. }
  448. }
  449. MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const
  450. {
  451. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  452. if (materialIt == m_configuration.m_materials.end())
  453. {
  454. AZ_Warning("MaterialComponentController", false, "MaterialAssignmentId not found.");
  455. return {};
  456. }
  457. return materialIt->second.m_propertyOverrides;
  458. }
  459. void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId)
  460. {
  461. m_queuedPropertyOverrides.emplace(materialAssignmentId);
  462. if (!TickBus::Handler::BusIsConnected())
  463. {
  464. TickBus::Handler::BusConnect();
  465. }
  466. }
  467. void MaterialComponentController::QueueMaterialUpdateNotification()
  468. {
  469. m_queuedMaterialUpdateNotification = true;
  470. if (!TickBus::Handler::BusIsConnected())
  471. {
  472. TickBus::Handler::BusConnect();
  473. }
  474. }
  475. } // namespace Render
  476. } // namespace AZ