MaterialComponentController.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  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/Asset/AssetSerializer.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AtomCore/Instance/InstanceDatabase.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("GetDefaultMaterialAssetId", &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId)
  37. ->Event("GetMaterialSlotLabel", &MaterialComponentRequestBus::Events::GetMaterialSlotLabel)
  38. ->Event("SetMaterialOverrides", &MaterialComponentRequestBus::Events::SetMaterialOverrides)
  39. ->Event("GetMaterialOverrides", &MaterialComponentRequestBus::Events::GetMaterialOverrides)
  40. ->Event("ClearAllMaterialOverrides", &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides)
  41. ->Event("SetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride)
  42. ->Event("GetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::GetDefaultMaterialOverride)
  43. ->Event("ClearDefaultMaterialOverride", &MaterialComponentRequestBus::Events::ClearDefaultMaterialOverride)
  44. ->Event("ClearModelMaterialOverrides", &MaterialComponentRequestBus::Events::ClearModelMaterialOverrides)
  45. ->Event("ClearLodMaterialOverrides", &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides)
  46. ->Event("ClearIncompatibleMaterialOverrides", &MaterialComponentRequestBus::Events::ClearIncompatibleMaterialOverrides)
  47. ->Event("ClearInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::ClearInvalidMaterialOverrides)
  48. ->Event("RepairInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::RepairInvalidMaterialOverrides)
  49. ->Event("ApplyAutomaticPropertyUpdates", &MaterialComponentRequestBus::Events::ApplyAutomaticPropertyUpdates)
  50. ->Event("SetMaterialOverride", &MaterialComponentRequestBus::Events::SetMaterialOverride)
  51. ->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride)
  52. ->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride)
  53. ->Event("SetPropertyOverride", &MaterialComponentRequestBus::Events::SetPropertyOverride)
  54. ->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<bool>)
  55. ->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<int32_t>)
  56. ->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
  57. ->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<float>)
  58. ->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector2>)
  59. ->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector3>)
  60. ->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector4>)
  61. ->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Color>)
  62. ->Event("SetPropertyOverrideImage", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Data::AssetId>)
  63. ->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZStd::string>)
  64. ->Event("SetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
  65. ->Event("GetPropertyOverride", &MaterialComponentRequestBus::Events::GetPropertyOverride)
  66. ->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<bool>)
  67. ->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<int32_t>)
  68. ->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
  69. ->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<float>)
  70. ->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector2>)
  71. ->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector3>)
  72. ->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector4>)
  73. ->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Color>)
  74. ->Event("GetPropertyOverrideImage", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Data::AssetId>)
  75. ->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZStd::string>)
  76. ->Event("GetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
  77. ->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride)
  78. ->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides)
  79. ->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides)
  80. ->Event("SetPropertyOverrides", &MaterialComponentRequestBus::Events::SetPropertyOverrides)
  81. ->Event("GetPropertyOverrides", &MaterialComponentRequestBus::Events::GetPropertyOverrides)
  82. ;
  83. }
  84. }
  85. void MaterialComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  86. {
  87. provided.push_back(AZ_CRC("MaterialProviderService", 0x64849a6b));
  88. }
  89. void MaterialComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  90. {
  91. incompatible.push_back(AZ_CRC("MaterialProviderService", 0x64849a6b));
  92. }
  93. void MaterialComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  94. {
  95. required.push_back(AZ_CRC("MaterialReceiverService", 0x0d1a6a74));
  96. }
  97. MaterialComponentController::MaterialComponentController(const MaterialComponentConfig& config)
  98. : m_configuration(config)
  99. {
  100. ConvertAssetsForSerialization();
  101. }
  102. void MaterialComponentController::Activate(EntityId entityId)
  103. {
  104. m_entityId = entityId;
  105. m_queuedMaterialUpdateNotification = false;
  106. MaterialComponentRequestBus::Handler::BusConnect(m_entityId);
  107. MaterialReceiverNotificationBus::Handler::BusConnect(m_entityId);
  108. LoadMaterials();
  109. }
  110. void MaterialComponentController::Deactivate()
  111. {
  112. MaterialComponentRequestBus::Handler::BusDisconnect();
  113. MaterialReceiverNotificationBus::Handler::BusDisconnect();
  114. TickBus::Handler::BusDisconnect();
  115. ReleaseMaterials();
  116. // Sending notification to wipe any previously assigned material overrides
  117. MaterialComponentNotificationBus::Event(
  118. m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, MaterialAssignmentMap());
  119. m_queuedMaterialUpdateNotification = false;
  120. m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
  121. }
  122. void MaterialComponentController::SetConfiguration(const MaterialComponentConfig& config)
  123. {
  124. m_configuration = config;
  125. ConvertAssetsForSerialization();
  126. }
  127. const MaterialComponentConfig& MaterialComponentController::GetConfiguration() const
  128. {
  129. return m_configuration;
  130. }
  131. void MaterialComponentController::OnAssetReady(Data::Asset<Data::AssetData> asset)
  132. {
  133. InitializeMaterialInstance(asset);
  134. }
  135. void MaterialComponentController::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  136. {
  137. InitializeMaterialInstance(asset);
  138. }
  139. void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  140. {
  141. AZStd::unordered_set<MaterialAssignmentId> materialsWithDirtyProperties;
  142. AZStd::swap(m_materialsWithDirtyProperties, materialsWithDirtyProperties);
  143. // Iterate through all MaterialAssignmentId's that have property overrides and attempt to apply them
  144. // if material instance is already compiling, delay application of property overrides until next frame
  145. for (const auto& materialAssignmentId : materialsWithDirtyProperties)
  146. {
  147. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  148. if (materialIt != m_configuration.m_materials.end())
  149. {
  150. if (!materialIt->second.ApplyProperties())
  151. {
  152. // If a material cannot currently be compiled then it must be queued again
  153. m_materialsWithDirtyProperties.emplace(materialAssignmentId);
  154. }
  155. }
  156. }
  157. // Only disconnect from tick bus and send notification after all pending properties have been applied
  158. if (m_materialsWithDirtyProperties.empty())
  159. {
  160. if (m_queuedMaterialUpdateNotification)
  161. {
  162. // Materials have been edited and instances have changed but the notification will only be sent once per tick
  163. m_queuedMaterialUpdateNotification = false;
  164. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
  165. }
  166. TickBus::Handler::BusDisconnect();
  167. }
  168. }
  169. void MaterialComponentController::LoadMaterials()
  170. {
  171. Data::AssetBus::MultiHandler::BusDisconnect();
  172. bool anyQueued = false;
  173. auto queueAsset = [&anyQueued, this](AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset) -> bool
  174. {
  175. if (materialAsset.GetId().IsValid() && !this->Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
  176. {
  177. anyQueued = true;
  178. materialAsset.QueueLoad();
  179. this->Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
  180. return true;
  181. }
  182. return false;
  183. };
  184. for (auto& materialPair : m_configuration.m_materials)
  185. {
  186. if (materialPair.second.m_materialInstancePreCreated)
  187. {
  188. continue;
  189. }
  190. materialPair.second.m_defaultMaterialAsset = {};
  191. if (!queueAsset(materialPair.second.m_materialAsset))
  192. {
  193. // Only assign and load the default material if there was no material override and there are propoerties to apply
  194. if (!materialPair.second.m_propertyOverrides.empty() || !materialPair.second.m_matModUvOverrides.empty())
  195. {
  196. materialPair.second.m_defaultMaterialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(
  197. GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
  198. queueAsset(materialPair.second.m_defaultMaterialAsset);
  199. }
  200. }
  201. }
  202. if (!anyQueued)
  203. {
  204. ReleaseMaterials();
  205. // If no other materials were loaded, the notification must still be sent in case there are externally managed material
  206. // instances in the configuration
  207. MaterialComponentNotificationBus::Event(
  208. m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
  209. }
  210. }
  211. void MaterialComponentController::InitializeMaterialInstance(const Data::Asset<Data::AssetData>& asset)
  212. {
  213. bool allReady = true;
  214. auto updateAsset = [&](AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset)
  215. {
  216. if (materialAsset.GetId() == asset.GetId())
  217. {
  218. materialAsset = asset;
  219. }
  220. if (materialAsset.GetId().IsValid() && !materialAsset.IsReady())
  221. {
  222. allReady = false;
  223. }
  224. };
  225. for (auto& materialPair : m_configuration.m_materials)
  226. {
  227. updateAsset(materialPair.second.m_materialAsset);
  228. updateAsset(materialPair.second.m_defaultMaterialAsset);
  229. }
  230. if (allReady)
  231. {
  232. //Do not start updating materials and properties until all materials are loaded and ready
  233. //This prevents property changes from being queued and notifications from being sent with pending materials
  234. for (auto& materialPair : m_configuration.m_materials)
  235. {
  236. materialPair.second.RebuildInstance();
  237. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialPair.second);
  238. QueuePropertyChanges(materialPair.first);
  239. }
  240. QueueMaterialUpdateNotification();
  241. }
  242. }
  243. void MaterialComponentController::ReleaseMaterials()
  244. {
  245. Data::AssetBus::MultiHandler::BusDisconnect();
  246. for (auto& materialPair : m_configuration.m_materials)
  247. {
  248. materialPair.second.Release();
  249. }
  250. }
  251. MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const
  252. {
  253. MaterialAssignmentMap originalMaterials;
  254. MaterialReceiverRequestBus::EventResult(
  255. originalMaterials, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments);
  256. return originalMaterials;
  257. }
  258. MaterialAssignmentId MaterialComponentController::FindMaterialAssignmentId(
  259. const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
  260. {
  261. MaterialAssignmentId materialAssignmentId;
  262. MaterialReceiverRequestBus::EventResult(
  263. materialAssignmentId, m_entityId, &MaterialReceiverRequestBus::Events::FindMaterialAssignmentId, lod, label);
  264. return materialAssignmentId;
  265. }
  266. AZ::Data::AssetId MaterialComponentController::GetDefaultMaterialAssetId(const MaterialAssignmentId& materialAssignmentId) const
  267. {
  268. RPI::ModelMaterialSlotMap modelMaterialSlots;
  269. MaterialReceiverRequestBus::EventResult(
  270. modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
  271. auto slotIter = modelMaterialSlots.find(materialAssignmentId.m_materialSlotStableId);
  272. return slotIter != modelMaterialSlots.end() ? slotIter->second.m_defaultMaterialAsset.GetId() : AZ::Data::AssetId();
  273. }
  274. AZStd::string MaterialComponentController::GetMaterialSlotLabel(const MaterialAssignmentId& materialAssignmentId) const
  275. {
  276. if (materialAssignmentId == DefaultMaterialAssignmentId)
  277. {
  278. return "Default Material";
  279. }
  280. RPI::ModelMaterialSlotMap modelMaterialSlots;
  281. MaterialReceiverRequestBus::EventResult(
  282. modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
  283. auto slotIter = modelMaterialSlots.find(materialAssignmentId.m_materialSlotStableId);
  284. if (slotIter != modelMaterialSlots.end())
  285. {
  286. const Name& displayName = slotIter->second.m_displayName;
  287. if (!displayName.IsEmpty())
  288. {
  289. return displayName.GetStringView();
  290. }
  291. }
  292. return "<unknown>";
  293. }
  294. void MaterialComponentController::SetMaterialOverrides(const MaterialAssignmentMap& materials)
  295. {
  296. // this function is called twice once material asset is changed, a temp variable is
  297. // needed to prevent material asset going out of scope during second call
  298. // before LoadMaterials() is called [LYN-2249]
  299. auto temp = m_configuration.m_materials;
  300. m_configuration.m_materials = materials;
  301. ConvertAssetsForSerialization();
  302. LoadMaterials();
  303. }
  304. const MaterialAssignmentMap& MaterialComponentController::GetMaterialOverrides() const
  305. {
  306. return m_configuration.m_materials;
  307. }
  308. void MaterialComponentController::ClearAllMaterialOverrides()
  309. {
  310. if (!m_configuration.m_materials.empty())
  311. {
  312. m_configuration.m_materials.clear();
  313. QueueMaterialUpdateNotification();
  314. }
  315. }
  316. void MaterialComponentController::ClearModelMaterialOverrides()
  317. {
  318. AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) {
  319. return materialPair.first.IsSlotIdOnly();
  320. });
  321. QueueMaterialUpdateNotification();
  322. }
  323. void MaterialComponentController::ClearLodMaterialOverrides()
  324. {
  325. AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) {
  326. return materialPair.first.IsLodAndSlotId();
  327. });
  328. QueueMaterialUpdateNotification();
  329. }
  330. void MaterialComponentController::ClearIncompatibleMaterialOverrides()
  331. {
  332. const MaterialAssignmentMap& originalMaterials = GetOriginalMaterialAssignments();
  333. AZStd::erase_if(m_configuration.m_materials, [&originalMaterials](const auto& materialPair) {
  334. return originalMaterials.find(materialPair.first) == originalMaterials.end();
  335. });
  336. QueueMaterialUpdateNotification();
  337. }
  338. void MaterialComponentController::ClearInvalidMaterialOverrides()
  339. {
  340. AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) {
  341. if (materialPair.second.m_materialAsset.GetId().IsValid())
  342. {
  343. AZ::Data::AssetInfo assetInfo;
  344. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  345. assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById,
  346. materialPair.second.m_materialAsset.GetId());
  347. return !assetInfo.m_assetId.IsValid();
  348. }
  349. return false;
  350. });
  351. QueueMaterialUpdateNotification();
  352. }
  353. void MaterialComponentController::RepairInvalidMaterialOverrides()
  354. {
  355. for (auto& materialPair : m_configuration.m_materials)
  356. {
  357. if (materialPair.second.m_materialAsset.GetId().IsValid())
  358. {
  359. AZ::Data::AssetInfo assetInfo;
  360. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  361. assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById,
  362. materialPair.second.m_materialAsset.GetId());
  363. if (!assetInfo.m_assetId.IsValid())
  364. {
  365. materialPair.second.m_materialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(
  366. GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
  367. }
  368. }
  369. }
  370. LoadMaterials();
  371. }
  372. uint32_t MaterialComponentController::ApplyAutomaticPropertyUpdates()
  373. {
  374. uint32_t propertiesUpdated = 0;
  375. for (auto& materialAssignmentPair : m_configuration.m_materials)
  376. {
  377. MaterialAssignment& materialAssignment = materialAssignmentPair.second;
  378. AZStd::vector<AZStd::pair<Name, Name>> renamedProperties;
  379. for (const auto& propertyPair : materialAssignment.m_propertyOverrides)
  380. {
  381. Name propertyId = propertyPair.first;
  382. if (materialAssignment.m_materialInstance->GetAsset()->GetMaterialTypeAsset()->ApplyPropertyRenames(propertyId))
  383. {
  384. renamedProperties.emplace_back(propertyPair.first, propertyId);
  385. ++propertiesUpdated;
  386. }
  387. }
  388. for (const auto& [oldName, newName] : renamedProperties)
  389. {
  390. materialAssignment.m_propertyOverrides[newName] = materialAssignment.m_propertyOverrides[oldName];
  391. materialAssignment.m_propertyOverrides.erase(oldName);
  392. }
  393. }
  394. return propertiesUpdated;
  395. }
  396. void MaterialComponentController::SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId)
  397. {
  398. SetMaterialOverride(DefaultMaterialAssignmentId, materialAssetId);
  399. }
  400. const AZ::Data::AssetId MaterialComponentController::GetDefaultMaterialOverride() const
  401. {
  402. return GetMaterialOverride(DefaultMaterialAssignmentId);
  403. }
  404. void MaterialComponentController::ClearDefaultMaterialOverride()
  405. {
  406. ClearMaterialOverride(DefaultMaterialAssignmentId);
  407. }
  408. void MaterialComponentController::SetMaterialOverride(
  409. const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId)
  410. {
  411. m_configuration.m_materials[materialAssignmentId].m_materialAsset =
  412. AZ::Data::Asset<AZ::RPI::MaterialAsset>(materialAssetId, AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
  413. LoadMaterials();
  414. }
  415. AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const
  416. {
  417. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  418. return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_materialAsset.GetId() : AZ::Data::AssetId();
  419. }
  420. void MaterialComponentController::ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId)
  421. {
  422. if (m_configuration.m_materials.erase(materialAssignmentId) > 0)
  423. {
  424. QueueMaterialUpdateNotification();
  425. }
  426. }
  427. void MaterialComponentController::SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value)
  428. {
  429. auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
  430. const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
  431. materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
  432. ConvertAssetsForSerialization();
  433. if (materialAssignment.RequiresLoading())
  434. {
  435. LoadMaterials();
  436. return;
  437. }
  438. if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
  439. {
  440. materialAssignment.RebuildInstance();
  441. MaterialComponentNotificationBus::Event(
  442. m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment);
  443. QueueMaterialUpdateNotification();
  444. }
  445. QueuePropertyChanges(materialAssignmentId);
  446. }
  447. AZStd::any MaterialComponentController::GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
  448. {
  449. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  450. if (materialIt == m_configuration.m_materials.end())
  451. {
  452. return {};
  453. }
  454. const auto propertyIt = materialIt->second.m_propertyOverrides.find(AZ::Name(propertyName));
  455. if (propertyIt == materialIt->second.m_propertyOverrides.end())
  456. {
  457. return {};
  458. }
  459. return propertyIt->second;
  460. }
  461. void MaterialComponentController::ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName)
  462. {
  463. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  464. if (materialIt == m_configuration.m_materials.end())
  465. {
  466. return;
  467. }
  468. auto propertyIt = materialIt->second.m_propertyOverrides.find(AZ::Name(propertyName));
  469. if (propertyIt == materialIt->second.m_propertyOverrides.end())
  470. {
  471. return;
  472. }
  473. materialIt->second.m_propertyOverrides.erase(propertyIt);
  474. if (materialIt->second.m_propertyOverrides.empty())
  475. {
  476. materialIt->second.RebuildInstance();
  477. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialIt->second);
  478. QueueMaterialUpdateNotification();
  479. }
  480. QueuePropertyChanges(materialAssignmentId);
  481. }
  482. void MaterialComponentController::ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId)
  483. {
  484. auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  485. if (materialIt == m_configuration.m_materials.end())
  486. {
  487. return;
  488. }
  489. if (!materialIt->second.m_propertyOverrides.empty())
  490. {
  491. materialIt->second.m_propertyOverrides = {};
  492. materialIt->second.RebuildInstance();
  493. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialIt->second);
  494. QueueMaterialUpdateNotification();
  495. }
  496. }
  497. void MaterialComponentController::ClearAllPropertyOverrides()
  498. {
  499. for (auto& materialPair : m_configuration.m_materials)
  500. {
  501. if (!materialPair.second.m_propertyOverrides.empty())
  502. {
  503. materialPair.second.m_propertyOverrides = {};
  504. materialPair.second.RebuildInstance();
  505. MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialPair.second);
  506. QueueMaterialUpdateNotification();
  507. }
  508. }
  509. }
  510. void MaterialComponentController::SetPropertyOverrides(
  511. const MaterialAssignmentId& materialAssignmentId, const MaterialPropertyOverrideMap& propertyOverrides)
  512. {
  513. auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
  514. const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
  515. materialAssignment.m_propertyOverrides = propertyOverrides;
  516. ConvertAssetsForSerialization();
  517. if (materialAssignment.RequiresLoading())
  518. {
  519. LoadMaterials();
  520. return;
  521. }
  522. if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
  523. {
  524. materialAssignment.RebuildInstance();
  525. QueueMaterialUpdateNotification();
  526. }
  527. QueuePropertyChanges(materialAssignmentId);
  528. }
  529. MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(
  530. const MaterialAssignmentId& materialAssignmentId) const
  531. {
  532. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  533. return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_propertyOverrides : MaterialPropertyOverrideMap();
  534. }
  535. void MaterialComponentController::SetModelUvOverrides(
  536. const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides)
  537. {
  538. auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
  539. const bool wasEmpty = materialAssignment.m_matModUvOverrides.empty();
  540. materialAssignment.m_matModUvOverrides = modelUvOverrides;
  541. if (materialAssignment.RequiresLoading())
  542. {
  543. LoadMaterials();
  544. return;
  545. }
  546. if (wasEmpty != materialAssignment.m_matModUvOverrides.empty())
  547. {
  548. materialAssignment.RebuildInstance();
  549. QueueMaterialUpdateNotification();
  550. }
  551. QueuePropertyChanges(materialAssignmentId);
  552. }
  553. AZ::RPI::MaterialModelUvOverrideMap MaterialComponentController::GetModelUvOverrides(
  554. const MaterialAssignmentId& materialAssignmentId) const
  555. {
  556. const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
  557. return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_matModUvOverrides : AZ::RPI::MaterialModelUvOverrideMap();
  558. }
  559. void MaterialComponentController::OnMaterialAssignmentsChanged()
  560. {
  561. for (const auto& materialPair : m_configuration.m_materials)
  562. {
  563. if (materialPair.second.RequiresLoading())
  564. {
  565. LoadMaterials();
  566. return;
  567. }
  568. }
  569. }
  570. void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId)
  571. {
  572. m_materialsWithDirtyProperties.emplace(materialAssignmentId);
  573. if (!TickBus::Handler::BusIsConnected())
  574. {
  575. TickBus::Handler::BusConnect();
  576. }
  577. }
  578. void MaterialComponentController::QueueMaterialUpdateNotification()
  579. {
  580. m_queuedMaterialUpdateNotification = true;
  581. if (!TickBus::Handler::BusIsConnected())
  582. {
  583. TickBus::Handler::BusConnect();
  584. }
  585. }
  586. void MaterialComponentController::ConvertAssetsForSerialization()
  587. {
  588. for (auto& materialAssignmentPair : m_configuration.m_materials)
  589. {
  590. MaterialAssignment& materialAssignment = materialAssignmentPair.second;
  591. for (auto& propertyPair : materialAssignment.m_propertyOverrides)
  592. {
  593. auto& value = propertyPair.second;
  594. if (value.is<AZ::Data::Asset<AZ::Data::AssetData>>())
  595. {
  596. value = AZStd::any_cast<AZ::Data::Asset<AZ::Data::AssetData>>(value).GetId();
  597. }
  598. else if (value.is<AZ::Data::Asset<AZ::RPI::StreamingImageAsset>>())
  599. {
  600. value = AZStd::any_cast<AZ::Data::Asset<AZ::RPI::StreamingImageAsset>>(value).GetId();
  601. }
  602. else if (value.is<AZ::Data::Asset<AZ::RPI::ImageAsset>>())
  603. {
  604. value = AZStd::any_cast<AZ::Data::Asset<AZ::RPI::ImageAsset>>(value).GetId();
  605. }
  606. else if (value.is<AZ::Data::Instance<AZ::RPI::Image>>())
  607. {
  608. value = AZStd::any_cast<AZ::Data::Instance<AZ::RPI::Image>>(value)->GetAssetId();
  609. }
  610. }
  611. }
  612. }
  613. } // namespace Render
  614. } // namespace AZ