Material Component: Can set material property overrides on default materials
No longer need to assign material overrides to set/edit properties in UI or script Material component will load and use the default material for a slot if no override is assigned Signed-off-by: Guthrie Adams <guthadam@amazon.com>
This commit is contained in:
@@ -39,13 +39,21 @@ namespace AZ
|
||||
//! Otherwise an attempt will be made to find or create a shared instance.
|
||||
void RebuildInstance();
|
||||
|
||||
//! Release asset and instance references
|
||||
void Release();
|
||||
|
||||
//! Return true if contained assets have not been loaded
|
||||
bool RequiresLoading() const;
|
||||
|
||||
//! Returns a string composed of the asset path.
|
||||
AZStd::string ToString() const;
|
||||
|
||||
Data::Asset<RPI::MaterialAsset> m_materialAsset;
|
||||
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
|
||||
Data::Instance<RPI::Material> m_materialInstance;
|
||||
MaterialPropertyOverrideMap m_propertyOverrides;
|
||||
RPI::MaterialModelUvOverrideMap m_matModUvOverrides;
|
||||
bool m_materialInstancePreCreated = false;
|
||||
};
|
||||
|
||||
using MaterialAssignmentMap = AZStd::unordered_map<MaterialAssignmentId, MaterialAssignment>;
|
||||
|
||||
@@ -69,9 +69,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
MaterialAssignment::MaterialAssignment(const AZ::Data::AssetId& materialAssetId)
|
||||
: m_materialInstance()
|
||||
: m_materialAsset(materialAssetId, AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid())
|
||||
, m_materialInstance()
|
||||
{
|
||||
m_materialAsset.Create(materialAssetId);
|
||||
}
|
||||
|
||||
MaterialAssignment::MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset)
|
||||
@@ -88,14 +88,50 @@ namespace AZ
|
||||
|
||||
void MaterialAssignment::RebuildInstance()
|
||||
{
|
||||
if (m_materialAsset.IsReady())
|
||||
if (m_materialInstancePreCreated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
if (m_materialAsset.IsReady())
|
||||
{
|
||||
m_materialInstance =
|
||||
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
|
||||
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_defaultMaterialAsset.IsReady())
|
||||
{
|
||||
m_materialInstance =
|
||||
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
|
||||
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_defaultMaterialAsset) : RPI::Material::Create(m_defaultMaterialAsset);
|
||||
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialAssignment::Release()
|
||||
{
|
||||
if (!m_materialInstancePreCreated)
|
||||
{
|
||||
m_materialInstance = nullptr;
|
||||
}
|
||||
m_materialAsset.Release();
|
||||
m_defaultMaterialAsset.Release();
|
||||
}
|
||||
|
||||
bool MaterialAssignment::RequiresLoading() const
|
||||
{
|
||||
return
|
||||
!m_materialInstancePreCreated &&
|
||||
!m_materialAsset.IsReady() &&
|
||||
!m_materialAsset.IsLoading() &&
|
||||
!m_defaultMaterialAsset.IsReady() &&
|
||||
!m_defaultMaterialAsset.IsLoading();
|
||||
}
|
||||
|
||||
AZStd::string MaterialAssignment::ToString() const
|
||||
{
|
||||
AZStd::string assetPathString;
|
||||
|
||||
@@ -182,7 +182,9 @@ namespace MaterialEditor
|
||||
AZ_Error("MaterialViewportRenderer", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
|
||||
|
||||
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
|
||||
shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = m_shadowCatcherMaterial;
|
||||
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
|
||||
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
|
||||
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
|
||||
|
||||
AZ::Render::MaterialComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
|
||||
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, shadowCatcherMaterials);
|
||||
@@ -291,7 +293,9 @@ namespace MaterialEditor
|
||||
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
|
||||
|
||||
AZ::Render::MaterialAssignmentMap materials;
|
||||
materials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = materialInstance;
|
||||
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
|
||||
materialAssignment.m_materialInstance = materialInstance;
|
||||
materialAssignment.m_materialInstancePreCreated = true;
|
||||
|
||||
AZ::Render::MaterialComponentRequestBus::Event(m_modelEntity->GetId(),
|
||||
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
|
||||
|
||||
+3
@@ -151,7 +151,9 @@ namespace AZ
|
||||
//! Returns the list of all ModelMaterialSlot's for the model, across all LODs.
|
||||
virtual RPI::ModelMaterialSlotMap GetModelMaterialSlots() const = 0;
|
||||
|
||||
//! Returns the available, overridable material slots and the default assigned materials
|
||||
virtual MaterialAssignmentMap GetMaterialAssignments() const = 0;
|
||||
|
||||
virtual AZStd::unordered_set<AZ::Name> GetModelUvNames() const = 0;
|
||||
};
|
||||
using MaterialReceiverRequestBus = EBus<MaterialReceiverRequests>;
|
||||
@@ -161,6 +163,7 @@ namespace AZ
|
||||
: public ComponentBus
|
||||
{
|
||||
public:
|
||||
//! Notification that overridable material slots are available or have changed
|
||||
virtual void OnMaterialAssignmentsChanged() = 0;
|
||||
};
|
||||
using MaterialReceiverNotificationBus = EBus<MaterialReceiverNotifications>;
|
||||
|
||||
+14
-3
@@ -88,6 +88,12 @@ namespace AZ
|
||||
AZ::Data::AssetId materialAssetId = {};
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
|
||||
if (!materialAssetId.IsValid())
|
||||
{
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId,
|
||||
m_materialAssignmentId);
|
||||
}
|
||||
|
||||
if (!materialAssetId.IsValid())
|
||||
{
|
||||
@@ -728,11 +734,16 @@ namespace AZ
|
||||
|
||||
void MaterialPropertyInspector::UpdateUI()
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
AZ::Data::AssetId materialAssetId = {};
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
assetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
|
||||
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
|
||||
if (!materialAssetId.IsValid())
|
||||
{
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_materialAssignmentId);
|
||||
}
|
||||
|
||||
if (IsLoaded() && m_editData.m_materialAssetId == assetId)
|
||||
if (IsLoaded() && m_editData.m_materialAssetId == materialAssetId)
|
||||
{
|
||||
LoadOverridesFromEntity();
|
||||
}
|
||||
|
||||
+13
-7
@@ -117,10 +117,16 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const
|
||||
{
|
||||
return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId();
|
||||
}
|
||||
|
||||
AZ::Data::AssetId EditorMaterialComponentSlot::GetDefaultAssetId() const
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
MaterialComponentRequestBus::EventResult(assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id);
|
||||
MaterialComponentRequestBus::EventResult(
|
||||
assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id);
|
||||
return assetId;
|
||||
}
|
||||
|
||||
@@ -134,7 +140,7 @@ namespace AZ
|
||||
bool EditorMaterialComponentSlot::HasSourceData() const
|
||||
{
|
||||
// The slot only has valid source data if the source path is valid and the file has the correct extension
|
||||
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId());
|
||||
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId());
|
||||
return !sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension);
|
||||
}
|
||||
|
||||
@@ -219,7 +225,7 @@ namespace AZ
|
||||
|
||||
void EditorMaterialComponentSlot::OpenMaterialEditor() const
|
||||
{
|
||||
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId());
|
||||
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId());
|
||||
if (!sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension))
|
||||
{
|
||||
EditorMaterialSystemComponentRequestBus::Broadcast(
|
||||
@@ -235,7 +241,7 @@ namespace AZ
|
||||
|
||||
void EditorMaterialComponentSlot::OpenUvNameMapInspector()
|
||||
{
|
||||
if (m_materialAsset.GetId().IsValid())
|
||||
if (GetActiveAssetId().IsValid())
|
||||
{
|
||||
AZStd::unordered_set<AZ::Name> modelUvNames;
|
||||
MaterialReceiverRequestBus::EventResult(modelUvNames, m_entityId, &MaterialReceiverRequestBus::Events::GetModelUvNames);
|
||||
@@ -251,7 +257,7 @@ namespace AZ
|
||||
};
|
||||
|
||||
if (EditorMaterialComponentInspector::OpenInspectorDialog(
|
||||
m_materialAsset.GetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback))
|
||||
GetActiveAssetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback))
|
||||
{
|
||||
OnDataChanged();
|
||||
}
|
||||
@@ -273,10 +279,10 @@ namespace AZ
|
||||
action->setEnabled(HasSourceData());
|
||||
|
||||
action = menu.addAction("Edit Material Instance...", [this]() { OpenMaterialInspector(); });
|
||||
action->setEnabled(m_materialAsset.GetId().IsValid());
|
||||
action->setEnabled(GetActiveAssetId().IsValid());
|
||||
|
||||
action = menu.addAction("Edit Material Instance UV Map...", [this]() { OpenUvNameMapInspector(); });
|
||||
action->setEnabled(m_materialAsset.GetId().IsValid());
|
||||
action->setEnabled(GetActiveAssetId().IsValid());
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ namespace AZ
|
||||
static void Reflect(ReflectContext* context);
|
||||
static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
|
||||
|
||||
AZ::Data::AssetId GetActiveAssetId() const;
|
||||
AZ::Data::AssetId GetDefaultAssetId() const;
|
||||
AZStd::string GetLabel() const;
|
||||
bool HasSourceData() const;
|
||||
|
||||
+73
-30
@@ -110,12 +110,14 @@ namespace AZ
|
||||
m_queuedMaterialUpdateNotification = false;
|
||||
|
||||
MaterialComponentRequestBus::Handler::BusConnect(m_entityId);
|
||||
MaterialReceiverNotificationBus::Handler::BusConnect(m_entityId);
|
||||
LoadMaterials();
|
||||
}
|
||||
|
||||
void MaterialComponentController::Deactivate()
|
||||
{
|
||||
MaterialComponentRequestBus::Handler::BusDisconnect();
|
||||
MaterialReceiverNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
ReleaseMaterials();
|
||||
|
||||
@@ -214,13 +216,29 @@ namespace AZ
|
||||
bool anyQueued = false;
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
auto& materialAsset = materialPair.second.m_materialAsset;
|
||||
if (materialPair.second.m_materialInstancePreCreated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (materialAsset.GetId().IsValid() && !Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
|
||||
if (materialPair.second.m_materialAsset.GetId().IsValid() &&
|
||||
!Data::AssetBus::MultiHandler::BusIsConnectedId(materialPair.second.m_materialAsset.GetId()))
|
||||
{
|
||||
anyQueued = true;
|
||||
materialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
|
||||
materialPair.second.m_materialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialPair.second.m_materialAsset.GetId());
|
||||
continue;
|
||||
}
|
||||
|
||||
materialPair.second.m_defaultMaterialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(
|
||||
GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
|
||||
|
||||
if (materialPair.second.m_defaultMaterialAsset.GetId().IsValid() &&
|
||||
!Data::AssetBus::MultiHandler::BusIsConnectedId(materialPair.second.m_defaultMaterialAsset.GetId()))
|
||||
{
|
||||
anyQueued = true;
|
||||
materialPair.second.m_defaultMaterialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialPair.second.m_defaultMaterialAsset.GetId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +254,22 @@ namespace AZ
|
||||
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
auto& materialAsset = materialPair.second.m_materialAsset;
|
||||
if (materialAsset.GetId() == asset.GetId())
|
||||
if (materialPair.second.m_materialAsset.GetId() == asset.GetId())
|
||||
{
|
||||
materialAsset = asset;
|
||||
materialPair.second.m_materialAsset = asset;
|
||||
}
|
||||
|
||||
if (materialAsset.GetId().IsValid() && !materialAsset.IsReady())
|
||||
if (materialPair.second.m_materialAsset.GetId().IsValid() && !materialPair.second.m_materialAsset.IsReady())
|
||||
{
|
||||
allReady = false;
|
||||
}
|
||||
|
||||
if (materialPair.second.m_defaultMaterialAsset.GetId() == asset.GetId())
|
||||
{
|
||||
materialPair.second.m_defaultMaterialAsset = asset;
|
||||
}
|
||||
|
||||
if (materialPair.second.m_defaultMaterialAsset.GetId().IsValid() && !materialPair.second.m_defaultMaterialAsset.IsReady())
|
||||
{
|
||||
allReady = false;
|
||||
}
|
||||
@@ -268,11 +295,7 @@ namespace AZ
|
||||
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
if (materialPair.second.m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
materialPair.second.m_materialAsset.Release();
|
||||
materialPair.second.m_materialInstance = nullptr;
|
||||
}
|
||||
materialPair.second.Release();
|
||||
}
|
||||
|
||||
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
|
||||
@@ -459,18 +482,21 @@ namespace AZ
|
||||
void MaterialComponentController::SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value)
|
||||
{
|
||||
auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
|
||||
const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
|
||||
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
|
||||
|
||||
// When applying property overrides for the first time, new instance needs to be created in case the current instance is already used somewhere else to keep overrides local
|
||||
if (materialAssignment.m_propertyOverrides.empty())
|
||||
if (materialAssignment.RequiresLoading())
|
||||
{
|
||||
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
|
||||
materialAssignment.RebuildInstance();
|
||||
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment);
|
||||
QueueMaterialUpdateNotification();
|
||||
LoadMaterials();
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
|
||||
{
|
||||
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
|
||||
materialAssignment.RebuildInstance();
|
||||
MaterialComponentNotificationBus::Event(
|
||||
m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment);
|
||||
QueueMaterialUpdateNotification();
|
||||
}
|
||||
|
||||
QueuePropertyChanges(materialAssignmentId);
|
||||
@@ -703,6 +729,12 @@ namespace AZ
|
||||
const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
|
||||
materialAssignment.m_propertyOverrides = propertyOverrides;
|
||||
|
||||
if (materialAssignment.RequiresLoading())
|
||||
{
|
||||
LoadMaterials();
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
|
||||
{
|
||||
materialAssignment.RebuildInstance();
|
||||
@@ -712,14 +744,11 @@ namespace AZ
|
||||
QueuePropertyChanges(materialAssignmentId);
|
||||
}
|
||||
|
||||
MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const
|
||||
MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(
|
||||
const MaterialAssignmentId& materialAssignmentId) const
|
||||
{
|
||||
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
if (materialIt == m_configuration.m_materials.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return materialIt->second.m_propertyOverrides;
|
||||
return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_propertyOverrides : MaterialPropertyOverrideMap();
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetModelUvOverrides(
|
||||
@@ -729,6 +758,12 @@ namespace AZ
|
||||
const bool wasEmpty = materialAssignment.m_matModUvOverrides.empty();
|
||||
materialAssignment.m_matModUvOverrides = modelUvOverrides;
|
||||
|
||||
if (materialAssignment.RequiresLoading())
|
||||
{
|
||||
LoadMaterials();
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasEmpty != materialAssignment.m_matModUvOverrides.empty())
|
||||
{
|
||||
materialAssignment.RebuildInstance();
|
||||
@@ -742,11 +777,19 @@ namespace AZ
|
||||
const MaterialAssignmentId& materialAssignmentId) const
|
||||
{
|
||||
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
if (materialIt == m_configuration.m_materials.end())
|
||||
return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_matModUvOverrides : AZ::RPI::MaterialModelUvOverrideMap();
|
||||
}
|
||||
|
||||
void MaterialComponentController::OnMaterialAssignmentsChanged()
|
||||
{
|
||||
for (const auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
return {};
|
||||
if (materialPair.second.RequiresLoading())
|
||||
{
|
||||
LoadMaterials();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return materialIt->second.m_matModUvOverrides;
|
||||
}
|
||||
|
||||
void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId)
|
||||
|
||||
+4
@@ -22,6 +22,7 @@ namespace AZ
|
||||
//! to provide material overrides on a per-entity basis.
|
||||
class MaterialComponentController final
|
||||
: MaterialComponentRequestBus::Handler
|
||||
, MaterialReceiverNotificationBus::Handler
|
||||
, Data::AssetBus::MultiHandler
|
||||
, TickBus::Handler
|
||||
{
|
||||
@@ -100,6 +101,9 @@ namespace AZ
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) override;
|
||||
AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const override;
|
||||
|
||||
//! MaterialReceiverNotificationBus::Handler overrides...
|
||||
void OnMaterialAssignmentsChanged() override;
|
||||
|
||||
private:
|
||||
|
||||
AZ_DISABLE_COPY(MaterialComponentController);
|
||||
|
||||
Reference in New Issue
Block a user