EditorMaterialComponent.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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_CE("Game"))
  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. InvalidatePropertyDisplay(AzToolsFramework::Refresh_AttributesAndValues);
  247. }
  248. void EditorMaterialComponent::OnMaterialsCreated(const MaterialAssignmentMap& materials)
  249. {
  250. // PSO-impacting property changes are allowed in the editor because the saved data can be analyzed to pre-compile the necessary PSOs.
  251. for (auto& materialAssignment : materials)
  252. {
  253. if (materialAssignment.second.m_materialInstance)
  254. {
  255. materialAssignment.second.m_materialInstance->SetPsoHandlingOverride(AZ::RPI::MaterialPropertyPsoHandling::Allowed);
  256. }
  257. }
  258. }
  259. void EditorMaterialComponent::OnEntityVisibilityChanged(bool visibility)
  260. {
  261. EditorRenderComponentAdapter::OnEntityVisibilityChanged(visibility);
  262. UpdateMaterialSlots();
  263. }
  264. AZ::u32 EditorMaterialComponent::OnConfigurationChanged()
  265. {
  266. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  267. }
  268. void EditorMaterialComponent::OnMaterialSlotLayoutChanged()
  269. {
  270. UpdateMaterialSlots();
  271. }
  272. void EditorMaterialComponent::UpdateMaterialSlots()
  273. {
  274. SetDirty();
  275. m_defaultMaterialSlot = {};
  276. m_materialSlots = {};
  277. m_materialSlotsByLod = {};
  278. // Get the known material assignment slots from the associated model or other source
  279. MaterialAssignmentMap originalMaterials;
  280. MaterialComponentRequestBus::EventResult(
  281. originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
  282. // Generate the table of editable materials using the source data to define number of groups, elements, and initial values
  283. for (const auto& materialPair : originalMaterials)
  284. {
  285. // Setup the material slot entry
  286. EditorMaterialComponentSlot slot(GetEntityId(), materialPair.first);
  287. if (slot.m_id.IsDefault())
  288. {
  289. m_defaultMaterialSlot = slot;
  290. continue;
  291. }
  292. if (slot.m_id.IsSlotIdOnly())
  293. {
  294. m_materialSlots.push_back(slot);
  295. continue;
  296. }
  297. if (slot.m_id.IsLodAndSlotId())
  298. {
  299. // Resize the containers to fit all elements
  300. m_materialSlotsByLod.resize(
  301. AZ::GetMax<size_t>(m_materialSlotsByLod.size(), aznumeric_cast<size_t>(slot.m_id.m_lodIndex + 1)));
  302. m_materialSlotsByLod[slot.m_id.m_lodIndex].push_back(slot);
  303. continue;
  304. }
  305. }
  306. // Sort all of the slots by label to ensure stable index values (originalMaterials is an unordered map)
  307. AZStd::sort(m_materialSlots.begin(), m_materialSlots.end(),
  308. [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
  309. for (auto& lodSlots : m_materialSlotsByLod)
  310. {
  311. AZStd::sort(lodSlots.begin(), lodSlots.end(),
  312. [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
  313. }
  314. MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited);
  315. InvalidatePropertyDisplay(AzToolsFramework::Refresh_EntireTree);
  316. }
  317. AZ::u32 EditorMaterialComponent::OpenMaterialExporterFromRPE()
  318. {
  319. return OpenMaterialExporter(EditorMaterialComponentUtil::GetEntitiesMatchingMaterialSlots(
  320. GetEntityId(), EditorMaterialComponentUtil::GetSelectedEntitiesFromActiveInspector()));
  321. }
  322. AZ::u32 EditorMaterialComponent::OpenMaterialExporter(const AzToolsFramework::EntityIdSet& entityIdsToEdit)
  323. {
  324. MaterialAssignmentMap originalMaterials;
  325. MaterialComponentRequestBus::EventResult(
  326. originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
  327. // Generate a unique set of all material asset IDs that will be used for source data generation
  328. AZStd::unordered_map<AZ::Data::AssetId, AZStd::string> assetIdToSlotNameMap;
  329. for (const auto& materialPair : originalMaterials)
  330. {
  331. const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId();
  332. if (originalAssetId.IsValid())
  333. {
  334. MaterialComponentRequestBus::EventResult(
  335. assetIdToSlotNameMap[originalAssetId], GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialLabel,
  336. materialPair.first);
  337. }
  338. }
  339. // Convert the unique set of asset IDs into export items that can be configured in the dialog
  340. // The order should not matter because the table in the dialog can sort itself for a specific row
  341. EditorMaterialComponentExporter::ExportItemsContainer exportItems;
  342. exportItems.reserve(assetIdToSlotNameMap.size());
  343. for (const auto& [assetId, slotName] : assetIdToSlotNameMap)
  344. {
  345. exportItems.emplace_back(assetId, slotName);
  346. }
  347. // Display the export dialog so that the user can configure how they want different materials to be exported
  348. if (EditorMaterialComponentExporter::OpenExportDialog(exportItems))
  349. {
  350. AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials.");
  351. // Create progress dialog to report the status of each material being generated.
  352. EditorMaterialComponentExporter::ProgressDialog progressDialog("Generating materials", "Generating material...", aznumeric_cast<int>(exportItems.size()));
  353. for (const EditorMaterialComponentExporter::ExportItem& exportItem : exportItems)
  354. {
  355. // Creating material source data from a product asset and resaving it as a new source material.
  356. if (!EditorMaterialComponentExporter::ExportMaterialSourceData(exportItem))
  357. {
  358. // This file was skipped because it was either marked to not be exported, not be overwritten, or another error occurred.
  359. progressDialog.CompleteItem();
  360. continue;
  361. }
  362. // After saving the source file, wait for it to be added to the catalog and processed by the AP so that a valid asset
  363. // can be assigned to the material component without spamming warning messages.
  364. const AZ::Data::AssetInfo assetInfo = progressDialog.ProcessItem(exportItem);
  365. if (!assetInfo.m_assetId.IsValid())
  366. {
  367. UpdateMaterialSlots();
  368. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  369. }
  370. // Valid asset info has been found for the file that was just saved so it can be assigned to the material component.
  371. for (const auto& materialPair : originalMaterials)
  372. {
  373. // We need to check if replaced material corresponds to this slot's default material.
  374. const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId();
  375. if (originalAssetId == exportItem.GetOriginalAssetId())
  376. {
  377. if (m_materialSlotsByLodEnabled || !materialPair.first.IsLodAndSlotId())
  378. {
  379. for (const AZ::EntityId& entityId : entityIdsToEdit)
  380. {
  381. AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
  382. &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entityId);
  383. MaterialComponentRequestBus::Event(
  384. entityId,
  385. &MaterialComponentRequestBus::Events::SetMaterialAssetId,
  386. materialPair.first,
  387. assetInfo.m_assetId);
  388. }
  389. }
  390. }
  391. }
  392. // Increment and update the progress dialog
  393. progressDialog.CompleteItem();
  394. }
  395. }
  396. UpdateMaterialSlots();
  397. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  398. }
  399. AZ::u32 EditorMaterialComponent::OnLodsToggled()
  400. {
  401. AzToolsFramework::ScopedUndoBatch undoBatch("Toggling LOD materials.");
  402. SetDirty();
  403. if (!m_materialSlotsByLodEnabled)
  404. {
  405. MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearMaterialsOnLodSlots);
  406. }
  407. UpdateMaterialSlots();
  408. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  409. }
  410. AZ::Crc32 EditorMaterialComponent::GetLodVisibility() const
  411. {
  412. return m_materialSlotsByLodEnabled ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
  413. }
  414. AZStd::string EditorMaterialComponent::GetLabelForLod(int lodIndex) const
  415. {
  416. return AZStd::string::format("LOD %d", lodIndex);
  417. }
  418. } // namespace Render
  419. } // namespace AZ