3
0

EditorMaterialComponent.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  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 <Atom/RPI.Edit/Common/AssetUtils.h>
  9. #include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
  10. #include <Atom/RPI.Public/Image/StreamingImage.h>
  11. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  12. #include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
  13. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
  14. #include <AzCore/Asset/AssetManagerBus.h>
  15. #include <AzCore/RTTI/BehaviorContext.h>
  16. #include <AzCore/Serialization/Json/RegistrationContext.h>
  17. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  18. #include <AzToolsFramework/API/EditorWindowRequestBus.h>
  19. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  20. #include <Material/EditorMaterialComponent.h>
  21. #include <Material/EditorMaterialComponentExporter.h>
  22. #include <Material/EditorMaterialComponentSerializer.h>
  23. #include <Material/EditorMaterialComponentUtil.h>
  24. AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
  25. #include <QAction>
  26. #include <QApplication>
  27. #include <QCursor>
  28. #include <QMenu>
  29. #include <QProgressDialog>
  30. AZ_POP_DISABLE_WARNING
  31. namespace AZ
  32. {
  33. namespace Render
  34. {
  35. const char* EditorMaterialComponent::GenerateMaterialsButtonText = "Generate/Manage Source Materials...";
  36. const char* EditorMaterialComponent::GenerateMaterialsToolTipText = "Generate editable source material files from materials provided by the model.";
  37. // Update serialized data to the new format and data types
  38. bool EditorMaterialComponent::ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  39. {
  40. if (!BaseClass::ConvertToEditorRenderComponentAdapter<1>(context, classElement))
  41. {
  42. return false;
  43. }
  44. if (classElement.GetVersion() < 3)
  45. {
  46. AZ_Error("EditorMaterialComponent", false, "Material Component version < 3 is no longer supported");
  47. return false;
  48. }
  49. if (classElement.GetVersion() < 4)
  50. {
  51. classElement.AddElementWithData(context, "materialSlotsByLodEnabled", true);
  52. }
  53. return true;
  54. }
  55. void EditorMaterialComponent::Reflect(AZ::ReflectContext* context)
  56. {
  57. BaseClass::Reflect(context);
  58. EditorMaterialComponentSlot::Reflect(context);
  59. if (auto jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
  60. {
  61. jsonContext->Serializer<JsonEditorMaterialComponentSerializer>()->HandlesType<EditorMaterialComponent>();
  62. }
  63. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  64. {
  65. serializeContext->RegisterGenericType<EditorMaterialComponentSlotContainer>();
  66. serializeContext->RegisterGenericType<EditorMaterialComponentSlotsByLodContainer>();
  67. serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, Data::AssetId, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
  68. serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, MaterialPropertyOverrideMap, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
  69. serializeContext->Class<EditorMaterialComponent, BaseClass>()
  70. ->Version(5, &EditorMaterialComponent::ConvertVersion)
  71. ->Field("defaultMaterialSlot", &EditorMaterialComponent::m_defaultMaterialSlot)
  72. ->Field("materialSlots", &EditorMaterialComponent::m_materialSlots)
  73. ->Field("materialSlotsByLodEnabled", &EditorMaterialComponent::m_materialSlotsByLodEnabled)
  74. ->Field("materialSlotsByLod", &EditorMaterialComponent::m_materialSlotsByLod)
  75. ;
  76. if (auto editContext = serializeContext->GetEditContext())
  77. {
  78. editContext->Class<EditorMaterialComponent>(
  79. "Material", "The material component specifies the material to use for this entity")
  80. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  81. ->Attribute(AZ::Edit::Attributes::Category, "Graphics/Mesh")
  82. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
  83. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
  84. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
  85. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  86. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/material/")
  87. ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::MaterialAsset>::Uuid())
  88. ->UIElement(AZ::Edit::UIHandlers::Button, GenerateMaterialsButtonText, GenerateMaterialsToolTipText)
  89. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
  90. ->Attribute(AZ::Edit::Attributes::ButtonText, GenerateMaterialsButtonText)
  91. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OpenMaterialExporterFromRPE)
  92. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_defaultMaterialSlot, "Default Material", "Materials assigned to this slot will be applied to the entire model unless specific model or LOD materials are set.")
  93. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  94. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
  95. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_materialSlots, "Model Materials", "Materials assigned to these slots will be applied to every part of the model with same material slot name unless an overriding LOD material is specified.")
  96. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
  97. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  98. ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
  99. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_materialSlotsByLodEnabled, "Enable LOD Materials", "When this flag is enabled, materials can be specified per LOD.")
  100. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnLodsToggled)
  101. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_materialSlotsByLod, "LOD Materials", "Materials assigned to these slots will take precedence over all other materials settings.")
  102. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
  103. ->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &EditorMaterialComponent::GetLabelForLod)
  104. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetLodVisibility)
  105. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  106. ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
  107. ->ElementAttribute(AZ::Edit::Attributes::AutoExpand, false)
  108. ->ElementAttribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
  109. ;
  110. editContext->Class<MaterialComponentConfig>(
  111. "Material Component Config", "Material Component Config")
  112. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  113. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  114. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
  115. ->DataElement(AZ::Edit::UIHandlers::Default, &MaterialComponentConfig::m_materials, "Materials", "")
  116. ;
  117. }
  118. }
  119. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  120. {
  121. behaviorContext->ConstantProperty("EditorMaterialComponentTypeId", BehaviorConstant(Uuid(EditorMaterialComponentTypeId)))
  122. ->Attribute(AZ::Script::Attributes::Module, "render")
  123. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  124. ;
  125. }
  126. }
  127. EditorMaterialComponent::EditorMaterialComponent(const MaterialComponentConfig& config)
  128. : BaseClass(config)
  129. {
  130. }
  131. void EditorMaterialComponent::Activate()
  132. {
  133. BaseClass::Activate();
  134. MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId());
  135. UpdateMaterialSlots();
  136. }
  137. void EditorMaterialComponent::Deactivate()
  138. {
  139. MaterialComponentNotificationBus::Handler::BusDisconnect();
  140. BaseClass::Deactivate();
  141. }
  142. void EditorMaterialComponent::AddContextMenuActions(QMenu* menu)
  143. {
  144. const auto& entityIdsToEdit = EditorMaterialComponentUtil::GetSelectedEntitiesFromActiveInspector();
  145. QAction* action = nullptr;
  146. menu->addSeparator();
  147. action = menu->addAction(GenerateMaterialsButtonText, [this, entityIdsToEdit]() { OpenMaterialExporter(entityIdsToEdit); });
  148. action->setToolTip(GenerateMaterialsToolTipText);
  149. action->setEnabled(EditorMaterialComponentUtil::DoEntitiesHaveMatchingMaterialSlots(GetEntityId(), entityIdsToEdit));
  150. menu->addSeparator();
  151. action = menu->addAction("Clear Materials", [this, entityIdsToEdit]() {
  152. AzToolsFramework::ScopedUndoBatch undoBatch("Clear materials.");
  153. m_materialSlotsByLodEnabled = false;
  154. for (const AZ::EntityId& entityId : entityIdsToEdit)
  155. {
  156. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  157. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  158. MaterialComponentRequestBus::Event(entityId, &MaterialComponentRequestBus::Events::ClearMaterialMap);
  159. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  160. }
  161. UpdateMaterialSlots();
  162. });
  163. action->setToolTip("Clears all material and property overrides.");
  164. action = menu->addAction("Clear Materials On Model Slots", [this, entityIdsToEdit]() {
  165. AzToolsFramework::ScopedUndoBatch undoBatch("Clear materials on model slots.");
  166. for (const AZ::EntityId& entityId : entityIdsToEdit)
  167. {
  168. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  169. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  170. MaterialComponentRequestBus::Event(entityId, &MaterialComponentRequestBus::Events::ClearMaterialsOnModelSlots);
  171. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  172. }
  173. UpdateMaterialSlots();
  174. });
  175. action->setToolTip("Clears material and property overrides assigned to the Model Materials group.");
  176. action = menu->addAction("Clear Materials On LOD Slots", [this, entityIdsToEdit]() {
  177. AzToolsFramework::ScopedUndoBatch undoBatch("Clear materials on LOD slots.");
  178. m_materialSlotsByLodEnabled = false;
  179. for (const AZ::EntityId& entityId : entityIdsToEdit)
  180. {
  181. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  182. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  183. MaterialComponentRequestBus::Event(entityId, &MaterialComponentRequestBus::Events::ClearMaterialsOnLodSlots);
  184. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  185. }
  186. UpdateMaterialSlots();
  187. });
  188. action->setToolTip("Clears material and property overrides assigned to the LOD Materials group.");
  189. action = menu->addAction("Clear Materials On Invalid Slots", [this, entityIdsToEdit]() {
  190. AzToolsFramework::ScopedUndoBatch undoBatch("Clear materials on invalid slots.");
  191. for (const AZ::EntityId& entityId : entityIdsToEdit)
  192. {
  193. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  194. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  195. MaterialComponentRequestBus::Event(
  196. entityId, &MaterialComponentRequestBus::Events::ClearMaterialsOnInvalidSlots);
  197. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  198. }
  199. UpdateMaterialSlots();
  200. });
  201. action->setToolTip("Clears residual or hidden material and property overrides assigned to slots that do not match the current layout.");
  202. action = menu->addAction("Clear Materials With Missing Assets", [this, entityIdsToEdit]() {
  203. AzToolsFramework::ScopedUndoBatch undoBatch("Clear materials with missing assets.");
  204. for (const AZ::EntityId& entityId : entityIdsToEdit)
  205. {
  206. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  207. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  208. MaterialComponentRequestBus::Event(entityId, &MaterialComponentRequestBus::Events::ClearMaterialsWithMissingAssets);
  209. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  210. }
  211. UpdateMaterialSlots();
  212. });
  213. action->setToolTip("Clears material overrides referencing missing assets.");
  214. action = menu->addAction("Repair Materials With Missing Assets", [this, entityIdsToEdit]() {
  215. AzToolsFramework::ScopedUndoBatch undoBatch("Repair materials with missing assets.");
  216. for (const AZ::EntityId& entityId : entityIdsToEdit)
  217. {
  218. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  219. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  220. MaterialComponentRequestBus::Event(entityId, &MaterialComponentRequestBus::Events::RepairMaterialsWithMissingAssets);
  221. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  222. }
  223. UpdateMaterialSlots();
  224. });
  225. action->setToolTip("Removes references to any missing material assets.");
  226. action = menu->addAction("Repair Materials With Renamed Properties", [this, entityIdsToEdit]() {
  227. AzToolsFramework::ScopedUndoBatch undoBatch("Repair materials with renamed properties.");
  228. for (const AZ::EntityId& entityId : entityIdsToEdit)
  229. {
  230. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  231. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  232. uint32_t propertiesUpdated = 0;
  233. MaterialComponentRequestBus::EventResult(
  234. propertiesUpdated, entityId, &MaterialComponentRequestBus::Events::RepairMaterialsWithRenamedProperties);
  235. AZ_Printf("EditorMaterialComponent", "Updated %u property(s).", propertiesUpdated);
  236. MaterialComponentNotificationBus::Event(entityId, &MaterialComponentNotifications::OnMaterialsEdited);
  237. }
  238. UpdateMaterialSlots();
  239. });
  240. action->setToolTip("Update material property overrides referencing names that have changed since they were set on the component.");
  241. }
  242. void EditorMaterialComponent::SetPrimaryAsset(const AZ::Data::AssetId& assetId)
  243. {
  244. MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::SetMaterialAssetIdOnDefaultSlot, assetId);
  245. MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited);
  246. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  247. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
  248. }
  249. void EditorMaterialComponent::OnMaterialsCreated(const MaterialAssignmentMap& materials)
  250. {
  251. // PSO-impacting property changes are allowed in the editor because the saved data can be analyzed to pre-compile the necessary PSOs.
  252. for (auto& materialAssignment : materials)
  253. {
  254. if (materialAssignment.second.m_materialInstance)
  255. {
  256. materialAssignment.second.m_materialInstance->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed);
  257. }
  258. }
  259. }
  260. void EditorMaterialComponent::OnEntityVisibilityChanged(bool visibility)
  261. {
  262. EditorRenderComponentAdapter::OnEntityVisibilityChanged(visibility);
  263. UpdateMaterialSlots();
  264. }
  265. AZ::u32 EditorMaterialComponent::OnConfigurationChanged()
  266. {
  267. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  268. }
  269. void EditorMaterialComponent::OnMaterialSlotLayoutChanged()
  270. {
  271. UpdateMaterialSlots();
  272. }
  273. void EditorMaterialComponent::UpdateMaterialSlots()
  274. {
  275. SetDirty();
  276. m_defaultMaterialSlot = {};
  277. m_materialSlots = {};
  278. m_materialSlotsByLod = {};
  279. // Get the known material assignment slots from the associated model or other source
  280. MaterialAssignmentMap originalMaterials;
  281. MaterialComponentRequestBus::EventResult(
  282. originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
  283. // Generate the table of editable materials using the source data to define number of groups, elements, and initial values
  284. for (const auto& materialPair : originalMaterials)
  285. {
  286. // Setup the material slot entry
  287. EditorMaterialComponentSlot slot(GetEntityId(), materialPair.first);
  288. if (slot.m_id.IsDefault())
  289. {
  290. m_defaultMaterialSlot = slot;
  291. continue;
  292. }
  293. if (slot.m_id.IsSlotIdOnly())
  294. {
  295. m_materialSlots.push_back(slot);
  296. continue;
  297. }
  298. if (slot.m_id.IsLodAndSlotId())
  299. {
  300. // Resize the containers to fit all elements
  301. m_materialSlotsByLod.resize(
  302. AZ::GetMax<size_t>(m_materialSlotsByLod.size(), aznumeric_cast<size_t>(slot.m_id.m_lodIndex + 1)));
  303. m_materialSlotsByLod[slot.m_id.m_lodIndex].push_back(slot);
  304. continue;
  305. }
  306. }
  307. // Sort all of the slots by label to ensure stable index values (originalMaterials is an unordered map)
  308. AZStd::sort(m_materialSlots.begin(), m_materialSlots.end(),
  309. [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
  310. for (auto& lodSlots : m_materialSlotsByLod)
  311. {
  312. AZStd::sort(lodSlots.begin(), lodSlots.end(),
  313. [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
  314. }
  315. MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited);
  316. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  317. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
  318. }
  319. AZ::u32 EditorMaterialComponent::OpenMaterialExporterFromRPE()
  320. {
  321. return OpenMaterialExporter(EditorMaterialComponentUtil::GetEntitiesMatchingMaterialSlots(
  322. GetEntityId(), EditorMaterialComponentUtil::GetSelectedEntitiesFromActiveInspector()));
  323. }
  324. AZ::u32 EditorMaterialComponent::OpenMaterialExporter(const AzToolsFramework::EntityIdSet& entityIdsToEdit)
  325. {
  326. MaterialAssignmentMap originalMaterials;
  327. MaterialComponentRequestBus::EventResult(
  328. originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
  329. // Generate a unique set of all material asset IDs that will be used for source data generation
  330. AZStd::unordered_map<AZ::Data::AssetId, AZStd::string> assetIdToSlotNameMap;
  331. for (const auto& materialPair : originalMaterials)
  332. {
  333. const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId();
  334. if (originalAssetId.IsValid())
  335. {
  336. MaterialComponentRequestBus::EventResult(
  337. assetIdToSlotNameMap[originalAssetId], GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialLabel,
  338. materialPair.first);
  339. }
  340. }
  341. // Convert the unique set of asset IDs into export items that can be configured in the dialog
  342. // The order should not matter because the table in the dialog can sort itself for a specific row
  343. EditorMaterialComponentExporter::ExportItemsContainer exportItems;
  344. exportItems.reserve(assetIdToSlotNameMap.size());
  345. for (const auto& [assetId, slotName] : assetIdToSlotNameMap)
  346. {
  347. exportItems.emplace_back(assetId, slotName);
  348. }
  349. // Display the export dialog so that the user can configure how they want different materials to be exported
  350. if (EditorMaterialComponentExporter::OpenExportDialog(exportItems))
  351. {
  352. AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials.");
  353. // Create progress dialog to report the status of each material being generated.
  354. EditorMaterialComponentExporter::ProgressDialog progressDialog("Generating materials", "Generating material...", exportItems.size());
  355. for (const EditorMaterialComponentExporter::ExportItem& exportItem : exportItems)
  356. {
  357. // Creating material source data from a product asset and resaving it as a new source material.
  358. if (!EditorMaterialComponentExporter::ExportMaterialSourceData(exportItem))
  359. {
  360. // This file was skipped because it was either marked to not be exported, not be overwritten, or another error occurred.
  361. progressDialog.CompleteItem();
  362. continue;
  363. }
  364. // After saving the source file, wait for it to be added to the catalog and processed by the AP so that a valid asset
  365. // can be assigned to the material component without spamming warning messages.
  366. const AZ::Data::AssetInfo assetInfo = progressDialog.ProcessItem(exportItem);
  367. if (!assetInfo.m_assetId.IsValid())
  368. {
  369. UpdateMaterialSlots();
  370. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  371. }
  372. // Valid asset info has been found for the file that was just saved so it can be assigned to the material component.
  373. for (const auto& materialPair : originalMaterials)
  374. {
  375. // We need to check if replaced material corresponds to this slot's default material.
  376. const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId();
  377. if (originalAssetId == exportItem.GetOriginalAssetId())
  378. {
  379. if (m_materialSlotsByLodEnabled || !materialPair.first.IsLodAndSlotId())
  380. {
  381. for (const AZ::EntityId& entityId : entityIdsToEdit)
  382. {
  383. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  384. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  385. MaterialComponentRequestBus::Event(
  386. entityId,
  387. &MaterialComponentRequestBus::Events::SetMaterialAssetId,
  388. materialPair.first,
  389. assetInfo.m_assetId);
  390. }
  391. }
  392. }
  393. }
  394. // Increment and update the progress dialog
  395. progressDialog.CompleteItem();
  396. }
  397. }
  398. UpdateMaterialSlots();
  399. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  400. }
  401. AZ::u32 EditorMaterialComponent::OnLodsToggled()
  402. {
  403. AzToolsFramework::ScopedUndoBatch undoBatch("Toggling LOD materials.");
  404. SetDirty();
  405. if (!m_materialSlotsByLodEnabled)
  406. {
  407. MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearMaterialsOnLodSlots);
  408. }
  409. UpdateMaterialSlots();
  410. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  411. }
  412. AZ::Crc32 EditorMaterialComponent::GetLodVisibility() const
  413. {
  414. return m_materialSlotsByLodEnabled ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
  415. }
  416. AZStd::string EditorMaterialComponent::GetLabelForLod(int lodIndex) const
  417. {
  418. return AZStd::string::format("LOD %d", lodIndex);
  419. }
  420. } // namespace Render
  421. } // namespace AZ