Files
o3de/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp
T
Chris Santora 14d2e38b90 Refactored how model material slots work in preparation to support more flexible material conversion options for the scene asset pipeline. The material slot IDs are based on the MaterialUid that come from SceneAPI. Since these IDs are also used as the AssetId sub-ID for the converted material assets, the system was just checking the material asset sub-ID to determine the material slot ID. But in order to support certain FBX material conversion options, we needed to break this tie, so the slot ID is separate from the AssetId of the material in that slot. This will allow some other material to be used in the slot, instead of being forced to use one that was generated from the FBX.
Here we inttroduce a new struct ModelMaterialSlot which formalizes the concept of material slot, with an ID, display name, and default material assignment. The ID still comes from the MaterialUid like before. The display name is built-in, rather than being parsed out from the asset file name. And the default material assignment can be any material asset, it doesn't have to come from the FBX (or other scene file).

This commit is just the preliminary set of changes. Cursory testing shows that it works pretty well but more testing is needed (and likely some fixes) before merging.

Here is what's left to do...
Add serialization version converters to preserve prior prefab data.
See if we can get rid of GetLabelByAssetId function only rely on the display name inside ModelMaterialSlot.
I'm not sure if the condition for enabling the "Edit Material Instance..." context menu item is correct.
Test actors
Lots more testing in general

Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
2021-07-30 11:40:51 -07:00

583 lines
29 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Material/EditorMaterialComponent.h>
#include <Material/EditorMaterialComponentExporter.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QMenu>
#include <QAction>
#include <QCursor>
AZ_POP_DISABLE_WARNING
namespace AZ
{
namespace Render
{
const char* EditorMaterialComponent::GenerateMaterialsButtonText = "Generate/Manage Source Materials...";
const char* EditorMaterialComponent::GenerateMaterialsToolTipText = "Generate editable source material files from materials provided by the model.";
const char* EditorMaterialComponent::ResetMaterialsButtonText = "Reset Materials";
const char* EditorMaterialComponent::ResetMaterialsToolTipText = "Clear all settings, materials, and properties then rebuild material slots from the associated model.";
// Update serialized data to the new format and data types
bool EditorMaterialComponent::ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (!BaseClass::ConvertToEditorRenderComponentAdapter<1>(context, classElement))
{
return false;
}
if (classElement.GetVersion() < 3)
{
AZ_Error("EditorMaterialComponent", false, "Material Component version < 3 is no longer supported");
return false;
}
if (classElement.GetVersion() < 4)
{
classElement.AddElementWithData(context, "materialSlotsByLodEnabled", true);
}
return true;
}
void EditorMaterialComponent::Reflect(AZ::ReflectContext* context)
{
BaseClass::Reflect(context);
EditorMaterialComponentSlot::Reflect(context);
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->RegisterGenericType<EditorMaterialComponentSlotContainer>();
serializeContext->RegisterGenericType<EditorMaterialComponentSlotsByLodContainer>();
serializeContext->Class<EditorMaterialComponent, BaseClass>()
->Version(5, &EditorMaterialComponent::ConvertVersion)
->Field("message", &EditorMaterialComponent::m_message)
->Field("defaultMaterialSlot", &EditorMaterialComponent::m_defaultMaterialSlot)
->Field("materialSlots", &EditorMaterialComponent::m_materialSlots)
->Field("materialSlotsByLodEnabled", &EditorMaterialComponent::m_materialSlotsByLodEnabled)
->Field("materialSlotsByLod", &EditorMaterialComponent::m_materialSlotsByLod)
;
serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, Data::AssetId, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, MaterialPropertyOverrideMap, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorMaterialComponent>(
"Material", "The material component specifies the material to use for this entity")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Atom")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::MaterialAsset>::Uuid())
->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorMaterialComponent::m_message, "Message", "")
->Attribute(AZ_CRC("PlaceholderText", 0xa23ec278), "Component cannot be edited with multiple entities selected")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetMessageVisibility)
->Attribute(AZ::Edit::Attributes::ReadOnly, true)
->UIElement(AZ::Edit::UIHandlers::Button, GenerateMaterialsButtonText, GenerateMaterialsToolTipText)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->Attribute(AZ::Edit::Attributes::ButtonText, GenerateMaterialsButtonText)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OpenMaterialExporter)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetEditorVisibility)
->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 material overrides are set.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetDefaultMaterialVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
->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.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetEditorVisibility)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_materialSlotsByLodEnabled, "Enable LOD Materials", "When this flag is enabled, materials can be specified per LOD.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnLodsToggled)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetEditorVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorMaterialComponent::m_materialSlotsByLod, "LOD Materials", "Materials assigned to these slots will take precedence over all other materials settings.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorMaterialComponent::OnConfigurationChanged)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &EditorMaterialComponent::GetLabelForLod)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorMaterialComponent::GetLodVisibility)
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->ElementAttribute(AZ::Edit::Attributes::AutoExpand, false)
->ElementAttribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
;
editContext->Class<MaterialComponentConfig>(
"Material Component Config", "Material Component Config")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialComponentConfig::m_materials, "Materials", "")
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("EditorMaterialComponentTypeId", BehaviorConstant(Uuid(EditorMaterialComponentTypeId)))
->Attribute(AZ::Script::Attributes::Module, "render")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
;
}
}
EditorMaterialComponent::EditorMaterialComponent(const MaterialComponentConfig& config)
: BaseClass(config)
{
}
void EditorMaterialComponent::Activate()
{
BaseClass::Activate();
MaterialReceiverNotificationBus::Handler::BusConnect(GetEntityId());
MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId());
UpdateMaterialSlots();
}
void EditorMaterialComponent::Deactivate()
{
MaterialReceiverNotificationBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
BaseClass::Deactivate();
}
void EditorMaterialComponent::AddContextMenuActions(QMenu* menu)
{
// Don't add menu options if more than one entity is selected
if (!IsEditingAllowed())
{
return;
}
QAction* action = nullptr;
menu->addSeparator();
action = menu->addAction(GenerateMaterialsButtonText, [this]() { OpenMaterialExporter(); });
action->setToolTip(GenerateMaterialsToolTipText);
menu->addSeparator();
action = menu->addAction(ResetMaterialsButtonText, [this]() { ResetMaterialSlots(); });
action->setToolTip(ResetMaterialsToolTipText);
menu->addSeparator();
action = menu->addAction("Clear Model Materials", [this]() {
AzToolsFramework::ScopedUndoBatch undoBatch("Clearing model materials.");
SetDirty();
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot->m_id.IsSlotIdOnly())
{
materialSlot->Clear();
}
}
});
action = menu->addAction("Clear LOD Materials", [this]() {
AzToolsFramework::ScopedUndoBatch undoBatch("Clearing LOD materials.");
SetDirty();
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot->m_id.IsLodAndSlotId())
{
materialSlot->Clear();
}
}
});
action->setEnabled(m_materialSlotsByLodEnabled);
}
void EditorMaterialComponent::SetPrimaryAsset(const AZ::Data::AssetId& assetId)
{
m_controller.SetDefaultMaterialOverride(assetId);
}
AZ::u32 EditorMaterialComponent::OnConfigurationChanged()
{
// Whenever the user makes changes to the editor component data the controller configuration must be rebuilt
m_configurationChangeInProgress = true;
UpdateController();
m_configurationChangeInProgress = false;
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
}
void EditorMaterialComponent::OnMaterialAssignmentsChanged()
{
// [GFX TODO][ATOM-4604] remove flag after mesh component material handling is fixed to not recreate/reload mesh for material changes
if (!m_configurationChangeInProgress)
{
UpdateMaterialSlots();
}
}
void EditorMaterialComponent::OnMaterialsEdited(const MaterialAssignmentMap& materials)
{
AzToolsFramework::ScopedUndoBatch undoBatch("Materials edited.");
SetDirty();
// The layout of the materials slots is already set.
// We just need to read the values from any edited overrides into the editor component
// and refresh.
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot& slot = *materialSlotPair.second;
const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(materials, slot.m_id);
slot.m_materialAsset = materialFromController.m_materialAsset;
slot.m_propertyOverrides = materialFromController.m_propertyOverrides;
slot.m_matModUvOverrides = materialFromController.m_matModUvOverrides;
}
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_AttributesAndValues);
}
void EditorMaterialComponent::UpdateConfiguration(const MaterialComponentConfig& config)
{
m_controller.SetMaterialOverrides(config.m_materials);
}
void EditorMaterialComponent::UpdateController()
{
SetDirty();
// Build the controller configuration from the editor configuration
MaterialComponentConfig config = m_controller.GetConfiguration();
config.m_materials.clear();
RPI::ModelMaterialSlotMap modelMaterialSlots;
MaterialReceiverRequestBus::EventResult(modelMaterialSlots, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
for (const auto& materialSlotPair : GetMaterialSlots())
{
const EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
// Do not apply materials for lods if they are disabled
if (materialSlot->m_id.m_lodIndex != MaterialAssignmentId::NonLodIndex && !m_materialSlotsByLodEnabled)
{
continue;
}
// Only material slots with a valid asset IDs or property overrides will be copied
// to minimize the amount of data stored in the controller and game component
if (materialSlot->m_materialAsset.GetId().IsValid())
{
MaterialAssignment& materialAssignment = config.m_materials[materialSlot->m_id];
materialAssignment.m_materialAsset = materialSlot->m_materialAsset;
materialAssignment.m_propertyOverrides = materialSlot->m_propertyOverrides;
materialAssignment.m_matModUvOverrides = materialSlot->m_matModUvOverrides;
}
else if (!materialSlot->m_propertyOverrides.empty() || !materialSlot->m_matModUvOverrides.empty())
{
auto materialSlotIter = modelMaterialSlots.find(materialSlot->m_id.m_materialSlotStableId);
if (materialSlotIter != modelMaterialSlots.end())
{
MaterialAssignment& materialAssignment = config.m_materials[materialSlot->m_id];
materialAssignment.m_materialAsset = materialSlotIter->second.m_defaultMaterialAsset;
materialAssignment.m_propertyOverrides = materialSlot->m_propertyOverrides;
materialAssignment.m_matModUvOverrides = materialSlot->m_matModUvOverrides;
}
}
}
UpdateConfiguration(config);
}
void EditorMaterialComponent::UpdateMaterialSlots()
{
SetDirty();
m_defaultMaterialSlot = {};
m_materialSlots = {};
m_materialSlotsByLod = {};
const MaterialComponentConfig& config = m_controller.GetConfiguration();
// Get the known material assignment slots from the associated model or other source
MaterialAssignmentMap materialsFromSource;
MaterialReceiverRequestBus::EventResult(materialsFromSource, GetEntityId(), &MaterialReceiverRequestBus::Events::GetMaterialAssignments);
RPI::ModelMaterialSlotMap modelMaterialSlots;
MaterialReceiverRequestBus::EventResult(modelMaterialSlots, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
// Generate the table of editable materials using the source data to define number of groups, elements, and initial values
for (const auto& materialPair : materialsFromSource)
{
// Setup the material slot entry
EditorMaterialComponentSlot slot;
slot.m_id = materialPair.first;
slot.m_materialChangedCallback = [this]() {
// This callback is triggered whenever an individual material slot changes outside of normal inspector interactions
// So we must manually handle undo, update configuration, and refresh the inspector to display the new values
AzToolsFramework::ScopedUndoBatch undoBatch("Material slot changed.");
SetDirty();
OnConfigurationChanged();
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_AttributesAndValues);
};
slot.m_propertyChangedCallback = [this]() {
OnConfigurationChanged();
};
const char* UnknownSlotName = "<unknown>";
// If this is the default material assignment ID then it represents the default slot which is not contained in any other group
if (slot.m_id == DefaultMaterialAssignmentId)
{
slot.m_label = "Default Material";
}
else
{
auto slotIter = modelMaterialSlots.find(slot.m_id.m_materialSlotStableId);
if (slotIter != modelMaterialSlots.end())
{
const Name& displayName = slotIter->second.m_displayName;
slot.m_label = !displayName.IsEmpty() ? displayName.GetStringView() : UnknownSlotName;
slot.m_defaultMaterialAsset = slotIter->second.m_defaultMaterialAsset;
}
else
{
slot.m_label = UnknownSlotName;
}
}
// if material is present in controller configuration, assign its data
const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(config.m_materials, slot.m_id);
slot.m_materialAsset = materialFromController.m_materialAsset;
slot.m_propertyOverrides = materialFromController.m_propertyOverrides;
slot.m_matModUvOverrides = materialFromController.m_matModUvOverrides;
// Attempt to get the UV names from model meshes.
MaterialReceiverRequestBus::EventResult(slot.m_modelUvNames, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelUvNames);
if (slot.m_id.IsDefault())
{
m_defaultMaterialSlot = slot;
continue;
}
if (slot.m_id.IsSlotIdOnly())
{
m_materialSlots.push_back(slot);
continue;
}
if (slot.m_id.IsLodAndSlotId())
{
// Resize the containers to fit all elements
m_materialSlotsByLod.resize(AZ::GetMax<size_t>(m_materialSlotsByLod.size(), aznumeric_cast<size_t>(slot.m_id.m_lodIndex + 1)));
m_materialSlotsByLod[slot.m_id.m_lodIndex].push_back(slot);
continue;
}
}
// Sort all of the slots by label to ensure stable index values (materialsFromSource is an unordered map)
AZStd::sort(m_materialSlots.begin(), m_materialSlots.end(),
[](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
for (auto& lodSlots : m_materialSlotsByLod)
{
AZStd::sort(lodSlots.begin(), lodSlots.end(),
[](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); });
}
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_EntireTree);
}
AZ::u32 EditorMaterialComponent::ResetMaterialSlots()
{
AzToolsFramework::ScopedUndoBatch undoBatch("Resetting materials.");
SetDirty();
UpdateConfiguration(MaterialComponentConfig());
UpdateMaterialSlots();
m_materialSlotsByLodEnabled = false;
// Forcing refresh in case triggered from context menu action
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_EntireTree);
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZ::u32 EditorMaterialComponent::OpenMaterialExporter()
{
AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials.");
SetDirty();
RPI::ModelMaterialSlotMap modelMaterialSlots;
MaterialReceiverRequestBus::EventResult(modelMaterialSlots, GetEntityId(), &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
// First generating a unique set of all material asset IDs that will be used for source data generation
AZStd::unordered_set<AZ::Data::AssetId> assetIds;
for (auto& materialSlot : modelMaterialSlots)
{
Data::AssetId defaultMaterialAssetId = materialSlot.second.m_defaultMaterialAsset.GetId();
if (defaultMaterialAssetId.IsValid())
{
assetIds.insert(defaultMaterialAssetId);
}
}
// Convert the unique set of asset IDs into export items that can be configured in the dialog
// The order should not matter because the table in the dialog can sort itself for a specific row
EditorMaterialComponentExporter::ExportItemsContainer exportItems;
for (const AZ::Data::AssetId& assetId : assetIds)
{
EditorMaterialComponentExporter::ExportItem exportItem;
exportItem.m_originalAssetId = assetId;
exportItems.push_back(exportItem);
}
// Display the export dialog so that the user can configure how they want different materials to be exported
if (EditorMaterialComponentExporter::OpenExportDialog(exportItems))
{
for (const EditorMaterialComponentExporter::ExportItem& exportItem : exportItems)
{
if (!EditorMaterialComponentExporter::ExportMaterialSourceData(exportItem))
{
continue;
}
const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.m_exportPath, 0);
if (assetIdOutcome)
{
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot* editorMaterialSlot = materialSlotPair.second;
if (editorMaterialSlot)
{
// Only update the slot of it was originally empty, having no override material.
// We need to check whether replaced material corresponds to this slot's default material.
if (!editorMaterialSlot->m_materialAsset.GetId().IsValid())
{
auto materialSlot = modelMaterialSlots.find(editorMaterialSlot->m_id.m_materialSlotStableId);
if (materialSlot != modelMaterialSlots.end() &&
materialSlot->second.m_defaultMaterialAsset.GetId() == exportItem.m_originalAssetId)
{
editorMaterialSlot->m_materialAsset.Create(assetIdOutcome.GetValue());
}
}
}
}
}
}
}
// Forcing refresh in case triggered from context menu action
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_AttributesAndValues);
return OnConfigurationChanged();
}
AZ::u32 EditorMaterialComponent::OnLodsToggled()
{
OnConfigurationChanged();
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZ::Crc32 EditorMaterialComponent::GetLodVisibility() const
{
return IsEditingAllowed() && m_materialSlotsByLodEnabled ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 EditorMaterialComponent::GetDefaultMaterialVisibility() const
{
return IsEditingAllowed() ? AZ::Edit::PropertyVisibility::ShowChildrenOnly : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 EditorMaterialComponent::GetEditorVisibility() const
{
return IsEditingAllowed() ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 EditorMaterialComponent::GetMessageVisibility() const
{
return IsEditingAllowed() ? AZ::Edit::PropertyVisibility::Hide : AZ::Edit::PropertyVisibility::Show;
}
bool EditorMaterialComponent::IsEditingAllowed() const
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::GetSelectedEntities);
return selectedEntities.size() == 1;
}
AZStd::string EditorMaterialComponent::GetLabelForLod(int lodIndex) const
{
return AZStd::string::format("LOD %d", lodIndex);
}
template<typename ComponentType, typename ContainerType>
void EditorMaterialComponent::BuildMaterialSlotMap(ComponentType& component, ContainerType& materialSlots)
{
materialSlots[DefaultMaterialAssignmentId] = &component.m_defaultMaterialSlot;
for (auto& slot : component.m_materialSlots)
{
materialSlots[slot.m_id] = &slot;
}
for (auto& slotsForLod : component.m_materialSlotsByLod)
{
for (auto& slot : slotsForLod)
{
materialSlots[slot.m_id] = &slot;
}
}
}
AZStd::unordered_map<MaterialAssignmentId, EditorMaterialComponentSlot*> EditorMaterialComponent::GetMaterialSlots()
{
AZStd::unordered_map<MaterialAssignmentId, EditorMaterialComponentSlot*> materialSlots;
BuildMaterialSlotMap(*this, materialSlots);
return AZStd::move(materialSlots);
}
AZStd::unordered_map<MaterialAssignmentId, const EditorMaterialComponentSlot*> EditorMaterialComponent::GetMaterialSlots() const
{
AZStd::unordered_map<MaterialAssignmentId, const EditorMaterialComponentSlot*> materialSlots;
BuildMaterialSlotMap(*this, materialSlots);
return AZStd::move(materialSlots);
}
} // namespace Render
} // namespace AZ