Merge branch 'development' into cmake/SPEC-7484

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/ToolBox.cpp
This commit is contained in:
Esteban Papp
2021-08-02 18:45:25 -07:00
450 changed files with 7261 additions and 5529 deletions
@@ -21,6 +21,8 @@ namespace AZ
public:
//! Get all material assignments that can be overridden
virtual MaterialAssignmentMap GetOriginalMaterialAssignments() const = 0;
//! Get material assignment id matching lod and label substring
virtual MaterialAssignmentId FindMaterialAssignmentId(const MaterialAssignmentLodIndex lod, const AZStd::string& label) const = 0;
//! Set material overrides
virtual void SetMaterialOverrides(const MaterialAssignmentMap& materials) = 0;
//! Get material overrides
@@ -69,6 +71,13 @@ namespace AZ
: public ComponentBus
{
public:
//! Get material assignment id matching lod and label substring
virtual MaterialAssignmentId FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const = 0;
//! Returns the list of all ModelMaterialSlot's for the model, across all LODs.
virtual RPI::ModelMaterialSlotMap GetModelMaterialSlots() const = 0;
virtual MaterialAssignmentMap GetMaterialAssignments() const = 0;
virtual AZStd::unordered_set<AZ::Name> GetModelUvNames() const = 0;
};
@@ -17,6 +17,7 @@
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QMenu>
@@ -44,64 +45,15 @@ namespace AZ
if (classElement.GetVersion() < 3)
{
// The default material was changed from an asset to an EditorMaterialComponentSlot and old data must be converted
constexpr AZ::u32 defaultMaterialAssetDataCrc = AZ_CRC("defaultMaterialAsset", 0x736fc071);
Data::Asset<RPI::MaterialAsset> oldDefaultMaterialData;
if (!classElement.GetChildData(defaultMaterialAssetDataCrc, oldDefaultMaterialData))
{
AZ_Error("AZ::Render::EditorMaterialComponent::ConvertVersion", false, "Failed to get defaultMaterialAsset element");
return false;
}
if (!classElement.RemoveElementByName(defaultMaterialAssetDataCrc))
{
AZ_Error("AZ::Render::EditorMaterialComponent::ConvertVersion", false, "Failed to remove defaultMaterialAsset element");
return false;
}
EditorMaterialComponentSlot newDefaultMaterialData;
newDefaultMaterialData.m_id = DefaultMaterialAssignmentId;
newDefaultMaterialData.m_materialAsset = oldDefaultMaterialData;
classElement.AddElementWithData(context, "defaultMaterialSlot", newDefaultMaterialData);
// Slots now support and display the default material asset when empty
// The old placeholder assignments are irrelevant and must be cleared
constexpr AZ::u32 materialSlotsByLodDataCrc = AZ_CRC("materialSlotsByLod", 0xb1498db6);
EditorMaterialComponentSlotsByLodContainer lodSlotData;
if (!classElement.GetChildData(materialSlotsByLodDataCrc, lodSlotData))
{
AZ_Error("AZ::Render::EditorMaterialComponent::ConvertVersion", false, "Failed to get materialSlotsByLod element");
return false;
}
if (!classElement.RemoveElementByName(materialSlotsByLodDataCrc))
{
AZ_Error("AZ::Render::EditorMaterialComponent::ConvertVersion", false, "Failed to remove materialSlotsByLod element");
return false;
}
// Find and clear all slots that are assigned to the slot's default value
for (auto& lodSlots : lodSlotData)
{
for (auto& slot : lodSlots)
{
if (slot.m_materialAsset.GetId() == slot.m_id.m_materialAssetId)
{
slot.m_materialAsset = {};
}
}
}
classElement.AddElementWithData(context, "materialSlotsByLod", lodSlotData);
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;
}
@@ -238,7 +190,7 @@ namespace AZ
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot->m_id.IsAssetOnly())
if (materialSlot->m_id.IsSlotIdOnly())
{
materialSlot->Clear();
}
@@ -251,7 +203,7 @@ namespace AZ
for (auto& materialSlotPair : GetMaterialSlots())
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot->m_id.IsLodAndAsset())
if (materialSlot->m_id.IsLodAndSlotId())
{
materialSlot->Clear();
}
@@ -318,7 +270,7 @@ namespace AZ
// Build the controller configuration from the editor configuration
MaterialComponentConfig config = m_controller.GetConfiguration();
config.m_materials.clear();
for (const auto& materialSlotPair : GetMaterialSlots())
{
const EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
@@ -341,7 +293,7 @@ namespace AZ
else if (!materialSlot->m_propertyOverrides.empty() || !materialSlot->m_matModUvOverrides.empty())
{
MaterialAssignment& materialAssignment = config.m_materials[materialSlot->m_id];
materialAssignment.m_materialAsset.Create(materialSlot->m_id.m_materialAssetId);
materialAssignment.m_materialAsset = materialSlot->m_defaultMaterialAsset;
materialAssignment.m_propertyOverrides = materialSlot->m_propertyOverrides;
materialAssignment.m_matModUvOverrides = materialSlot->m_matModUvOverrides;
}
@@ -362,6 +314,9 @@ namespace AZ
// 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)
@@ -385,9 +340,33 @@ namespace AZ
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;
@@ -400,13 +379,13 @@ namespace AZ
continue;
}
if (slot.m_id.IsAssetOnly())
if (slot.m_id.IsSlotIdOnly())
{
m_materialSlots.push_back(slot);
continue;
}
if (slot.m_id.IsLodAndAsset())
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)));
@@ -452,27 +431,26 @@ namespace AZ
{
AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials.");
SetDirty();
// 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;
AZStd::unordered_map<AZ::Data::AssetId, AZStd::string /*slot name*/> assetIdMap;
auto materialSlots = GetMaterialSlots();
for (auto& materialSlotPair : materialSlots)
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot->m_id.m_materialAssetId.IsValid())
Data::AssetId defaultMaterialAssetId = materialSlotPair.second->m_defaultMaterialAsset.GetId();
if (defaultMaterialAssetId.IsValid())
{
assetIds.insert(materialSlot->m_id.m_materialAssetId);
assetIdMap[defaultMaterialAssetId] = materialSlotPair.second->GetLabel();
}
}
// 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)
for (auto assetIdInfo : assetIdMap)
{
EditorMaterialComponentExporter::ExportItem exportItem;
exportItem.m_assetId = assetId;
EditorMaterialComponentExporter::ExportItem exportItem{assetIdInfo.first, assetIdInfo.second};
exportItems.push_back(exportItem);
}
@@ -486,15 +464,20 @@ namespace AZ
continue;
}
const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.m_exportPath, 0);
const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0);
if (assetIdOutcome)
{
for (auto& materialSlotPair : materialSlots)
{
EditorMaterialComponentSlot* materialSlot = materialSlotPair.second;
if (materialSlot && materialSlot->m_id.m_materialAssetId == exportItem.m_assetId)
EditorMaterialComponentSlot* editorMaterialSlot = materialSlotPair.second;
if (editorMaterialSlot)
{
materialSlot->m_materialAsset.Create(assetIdOutcome.GetValue());
// We need to check whether replaced material corresponds to this slot's default material.
if (editorMaterialSlot->m_defaultMaterialAsset.GetId() == exportItem.GetOriginalAssetId())
{
editorMaterialSlot->m_materialAsset.Create(assetIdOutcome.GetValue());
}
}
}
}
@@ -582,3 +565,4 @@ namespace AZ
}
} // namespace Render
} // namespace AZ
@@ -37,47 +37,7 @@ namespace AZ
{
namespace EditorMaterialComponentExporter
{
AZStd::string GetLabelByAssetId(const AZ::Data::AssetId& assetId)
{
AZStd::string label;
if (assetId.IsValid())
{
// Material assets that are exported through the scene pipeline have their filenames generated by adding
// the DCC material name as a prefix and a unique number to the end of the source file name.
// Rather than storing the DCC material name inside of the material asset we can reproduce it by removing
// the prefix and suffix from the product file name.
// We need the material product path as the initial string that will be stripped down
const AZStd::string& productPath = AZ::RPI::AssetUtils::GetProductPathByAssetId(assetId);
if (!productPath.empty() && AzFramework::StringFunc::Path::GetFileName(productPath.c_str(), label))
{
// If there is a source file, typically an FBX or other model file, we must get its filename to remove the prefix from the label
AZStd::string prefix;
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(assetId);
if (!sourcePath.empty() && AZ::StringFunc::Path::GetFileName(sourcePath.c_str(), prefix))
{
if (!prefix.empty() && prefix.size() < label.size())
{
if (AZ::StringFunc::StartsWith(label, prefix, false))
{
// All of the product filename's tokens are separated by underscores so we must also remove the first underscore after the prefix
label = label.substr(prefix.size() + 1);
}
}
}
// We can remove the numeric suffix by stripping the label of everything after the last underscore
const auto iter = label.find_last_of("_");
if (iter != AZStd::string::npos)
{
label = label.substr(0, iter);
}
}
}
return label;
}
AZStd::string GetExportPathByAssetId(const AZ::Data::AssetId& assetId)
AZStd::string GetExportPathByAssetId(const AZ::Data::AssetId& assetId, const AZStd::string& materialSlotName)
{
AZStd::string exportPath;
if (assetId.IsValid())
@@ -85,7 +45,7 @@ namespace AZ
exportPath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(assetId);
AZ::StringFunc::Path::StripExtension(exportPath);
exportPath += "_";
exportPath += GetLabelByAssetId(assetId);
exportPath += materialSlotName;
exportPath += ".";
exportPath += AZ::RPI::MaterialSourceData::Extension;
AZ::StringFunc::Path::Normalize(exportPath);
@@ -132,12 +92,12 @@ namespace AZ
int row = 0;
for (ExportItem& exportItem : exportItems)
{
QFileInfo fileInfo(GetExportPathByAssetId(exportItem.m_assetId).c_str());
QFileInfo fileInfo(GetExportPathByAssetId(exportItem.GetOriginalAssetId(), exportItem.GetMaterialSlotName()).c_str());
// Configuring initial settings based on whether or not the target file already exists
exportItem.m_exportPath = fileInfo.absoluteFilePath().toUtf8().constData();
exportItem.m_exists = fileInfo.exists();
exportItem.m_overwrite = false;
exportItem.SetExportPath(fileInfo.absoluteFilePath().toUtf8().constData());
exportItem.SetExists(fileInfo.exists());
exportItem.SetOverwrite(false);
// Populate the table with data for every column
tableWidget->setItem(row, MaterialSlotColumn, new QTableWidgetItem());
@@ -146,23 +106,23 @@ namespace AZ
// Create a check box for toggling the enabled state of this item
QCheckBox* materialSlotCheckBox = new QCheckBox(tableWidget);
materialSlotCheckBox->setChecked(exportItem.m_enabled);
materialSlotCheckBox->setText(GetLabelByAssetId(exportItem.m_assetId).c_str());
materialSlotCheckBox->setChecked(exportItem.GetEnabled());
materialSlotCheckBox->setText(exportItem.GetMaterialSlotName().c_str());
tableWidget->setCellWidget(row, MaterialSlotColumn, materialSlotCheckBox);
// Create a file picker widget for selecting the save path for the exported material
AzQtComponents::BrowseEdit* materialFileWidget = new AzQtComponents::BrowseEdit(tableWidget);
materialFileWidget->setLineEditReadOnly(true);
materialFileWidget->setClearButtonEnabled(false);
materialFileWidget->setEnabled(exportItem.m_enabled);
materialFileWidget->setEnabled(exportItem.GetEnabled());
materialFileWidget->setText(fileInfo.fileName());
tableWidget->setCellWidget(row, MaterialFileColumn, materialFileWidget);
// Create a check box for toggling the overwrite state of this item
QWidget* overwriteCheckBoxContainer = new QWidget(tableWidget);
QCheckBox* overwriteCheckBox = new QCheckBox(overwriteCheckBoxContainer);
overwriteCheckBox->setChecked(exportItem.m_overwrite);
overwriteCheckBox->setEnabled(exportItem.m_enabled && exportItem.m_exists);
overwriteCheckBox->setChecked(exportItem.GetOverwrite());
overwriteCheckBox->setEnabled(exportItem.GetEnabled() && exportItem.GetExists());
overwriteCheckBoxContainer->setLayout(new QHBoxLayout(overwriteCheckBoxContainer));
overwriteCheckBoxContainer->layout()->addWidget(overwriteCheckBox);
@@ -173,21 +133,21 @@ namespace AZ
// Whenever the selection is updated, automatically apply the change to the export item
QObject::connect(materialSlotCheckBox, &QCheckBox::stateChanged, materialSlotCheckBox, [&exportItem, materialFileWidget, materialSlotCheckBox, overwriteCheckBox]([[maybe_unused]] int state) {
exportItem.m_enabled = materialSlotCheckBox->isChecked();
materialFileWidget->setEnabled(exportItem.m_enabled);
overwriteCheckBox->setEnabled(exportItem.m_enabled && exportItem.m_exists);
exportItem.SetEnabled(materialSlotCheckBox->isChecked());
materialFileWidget->setEnabled(exportItem.GetEnabled());
overwriteCheckBox->setEnabled(exportItem.GetEnabled() && exportItem.GetExists());
});
// Whenever the overwrite check box is updated, automatically apply the change to the export item
QObject::connect(overwriteCheckBox, &QCheckBox::stateChanged, overwriteCheckBox, [&exportItem, overwriteCheckBox]([[maybe_unused]] int state) {
exportItem.m_overwrite = overwriteCheckBox->isChecked();
exportItem.SetOverwrite(overwriteCheckBox->isChecked());
});
// Whenever the browse button is clicked, open a save file dialog in the same location as the current export file setting
QObject::connect(materialFileWidget, &AzQtComponents::BrowseEdit::attachedButtonTriggered, materialFileWidget, [&dialog, &exportItem, materialFileWidget, overwriteCheckBox]() {
QFileInfo fileInfo = QFileDialog::getSaveFileName(&dialog,
QString("Select Material Filename"),
exportItem.m_exportPath.c_str(),
exportItem.GetExportPath().c_str(),
QString("Material (*.material)"),
nullptr,
QFileDialog::DontConfirmOverwrite);
@@ -195,14 +155,14 @@ namespace AZ
// Only update the export data if a valid path and filename was selected
if (!fileInfo.absoluteFilePath().isEmpty())
{
exportItem.m_exportPath = fileInfo.absoluteFilePath().toUtf8().constData();
exportItem.m_exists = fileInfo.exists();
exportItem.m_overwrite = fileInfo.exists();
exportItem.SetExportPath(fileInfo.absoluteFilePath().toUtf8().constData());
exportItem.SetExists(fileInfo.exists());
exportItem.SetOverwrite(fileInfo.exists());
// Update the controls to display the new state
materialFileWidget->setText(fileInfo.fileName());
overwriteCheckBox->setChecked(exportItem.m_overwrite);
overwriteCheckBox->setEnabled(exportItem.m_enabled && exportItem.m_exists);
overwriteCheckBox->setChecked(exportItem.GetOverwrite());
overwriteCheckBox->setEnabled(exportItem.GetEnabled() && exportItem.GetExists());
}
});
@@ -245,24 +205,24 @@ namespace AZ
bool ExportMaterialSourceData(const ExportItem& exportItem)
{
if (!exportItem.m_enabled || exportItem.m_exportPath.empty())
if (!exportItem.GetEnabled() || exportItem.GetExportPath().empty())
{
return false;
}
if (exportItem.m_exists && !exportItem.m_overwrite)
if (exportItem.GetExists() && !exportItem.GetOverwrite())
{
return true;
}
EditorMaterialComponentUtil::MaterialEditData editData;
if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(exportItem.m_assetId, editData))
if (!EditorMaterialComponentUtil::LoadMaterialEditDataFromAssetId(exportItem.GetOriginalAssetId(), editData))
{
AZ_Warning("AZ::Render::EditorMaterialComponentExporter", false, "Failed to load material data.");
return false;
}
if (!EditorMaterialComponentUtil::SaveSourceMaterialFromEditData(exportItem.m_exportPath, editData))
if (!EditorMaterialComponentUtil::SaveSourceMaterialFromEditData(exportItem.GetExportPath(), editData))
{
AZ_Warning("AZ::Render::EditorMaterialComponentExporter", false, "Failed to save material data.");
return false;
@@ -19,27 +19,48 @@ namespace AZ
{
namespace EditorMaterialComponentExporter
{
// Attemts to generate a display label for a material slot by parsing its file name
AZStd::string GetLabelByAssetId(const AZ::Data::AssetId& assetId);
//! Generates a destination file path for exporting material source data
AZStd::string GetExportPathByAssetId(const AZ::Data::AssetId& assetId, const AZStd::string& materialSlotName);
// Generates a destination file path for exporting material source data
AZStd::string GetExportPathByAssetId(const AZ::Data::AssetId& assetId);
struct ExportItem
class ExportItem
{
public:
//! @param originalAssetId AssetId of the original built-in material, which will be exported.
//! @param materialSlotName The name of the material slot will be used as part of the exported file name.
ExportItem(AZ::Data::AssetId originalAssetId, const AZStd::string& materialSlotName)
: m_originalAssetId(originalAssetId)
, m_materialSlotName(materialSlotName)
{}
void SetEnabled(bool enabled) { m_enabled = enabled; }
void SetExists(bool exists) { m_exists = exists; }
void SetOverwrite(bool overwrite) { m_overwrite = overwrite; }
void SetExportPath(const AZStd::string& exportPath) { m_exportPath = exportPath; }
bool GetEnabled() const { return m_enabled; }
bool GetExists() const { return m_exists; }
bool GetOverwrite() const { return m_overwrite; }
const AZStd::string& GetExportPath() const { return m_exportPath; }
AZ::Data::AssetId GetOriginalAssetId() const { return m_originalAssetId; }
const AZStd::string& GetMaterialSlotName() const { return m_materialSlotName; }
private:
bool m_enabled = true;
bool m_exists = false;
bool m_overwrite = false;
AZ::Data::AssetId m_assetId;
AZStd::string m_exportPath;
AZ::Data::AssetId m_originalAssetId; //!< AssetId of the original built-in material, which will be exported.
AZStd::string m_materialSlotName;
};
using ExportItemsContainer = AZStd::vector<ExportItem>;
// Generates and opens a dialog for configuring material data export paths and actions
//! Generates and opens a dialog for configuring material data export paths and actions.
//! Note this will not modify the m_originalAssetId field in each ExportItem.
bool OpenExportDialog(ExportItemsContainer& exportItems);
// Attemts to construct and save material source data from a product asset
//! Attemts to construct and save material source data from a product asset
bool ExportMaterialSourceData(const ExportItem& exportItem);
} // namespace EditorMaterialComponentExporter
} // namespace Render
@@ -20,7 +20,7 @@
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QMenu>
#include <QAction>
#include <QAction>
#include <QCursor>
AZ_POP_DISABLE_WARNING
@@ -48,7 +48,7 @@ namespace AZ
return false;
}
const MaterialAssignmentId newId(oldId.first, oldId.second);
const MaterialAssignmentId newId(oldId.first, oldId.second.m_subId);
classElement.AddElementWithData(context, "id", newId);
}
@@ -80,9 +80,10 @@ namespace AZ
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorMaterialComponentSlot>()
->Version(5, &EditorMaterialComponentSlot::ConvertVersion)
->Version(6, &EditorMaterialComponentSlot::ConvertVersion)
->Field("id", &EditorMaterialComponentSlot::m_id)
->Field("materialAsset", &EditorMaterialComponentSlot::m_materialAsset)
->Field("defaultMaterialAsset", &EditorMaterialComponentSlot::m_defaultMaterialAsset)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
@@ -121,21 +122,12 @@ namespace AZ
AZ::Data::AssetId EditorMaterialComponentSlot::GetDefaultAssetId() const
{
return m_id.m_materialAssetId;
return m_defaultMaterialAsset.GetId();
}
AZStd::string EditorMaterialComponentSlot::GetLabel() const
{
// Generate the label for the material slot based on the assignment ID
// If this is the default material assignment ID then it represents the default slot which is not contained in any other group
if (m_id == DefaultMaterialAssignmentId)
{
return "Default Material";
}
// Otherwise the label can be generated by parsing the source file name associated with the asset ID
const AZStd::string& label = EditorMaterialComponentExporter::GetLabelByAssetId(m_id.m_materialAssetId);
return !label.empty() ? label : "<unknown>";
return m_label;
}
bool EditorMaterialComponentSlot::HasSourceData() const
@@ -183,27 +175,21 @@ namespace AZ
OnMaterialChanged();
}
void EditorMaterialComponentSlot::SetDefaultAsset()
void EditorMaterialComponentSlot::ResetToDefaultAsset()
{
m_materialAsset = {};
m_materialAsset = m_defaultMaterialAsset;
m_propertyOverrides = {};
m_matModUvOverrides = {};
if (m_id.m_materialAssetId.IsValid())
{
// If no material is assigned to this slot, assign the default material from the slot id to edit its properties
m_materialAsset.Create(m_id.m_materialAssetId);
}
OnMaterialChanged();
}
void EditorMaterialComponentSlot::OpenMaterialExporter()
{
// Because we are generating a source material from this specific slot there is only one entry
// But we still need to allow the user to reconfigure it using the dialogue
// But we still need to allow the user to reconfigure it using the dialog
EditorMaterialComponentExporter::ExportItemsContainer exportItems;
{
EditorMaterialComponentExporter::ExportItem exportItem;
exportItem.m_assetId = m_id.m_materialAssetId;
EditorMaterialComponentExporter::ExportItem exportItem{m_defaultMaterialAsset.GetId(), m_label};
exportItems.push_back(exportItem);
}
@@ -218,7 +204,7 @@ namespace AZ
}
// Generate a new asset ID utilizing the export file path so that we can update this material slot to reference the new asset
const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.m_exportPath, 0);
const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0);
if (assetIdOutcome)
{
m_materialAsset.Create(assetIdOutcome.GetValue());
@@ -258,7 +244,7 @@ namespace AZ
// Treated as a special property. It will be updated together with properties.
OnPropertyChanged();
};
if (m_materialAsset.GetId().IsValid())
{
if (EditorMaterialComponentInspector::OpenInspectorDialog(m_materialAsset.GetId(), m_matModUvOverrides, m_modelUvNames, applyMatModUvOverrideChangedCallback))
@@ -275,7 +261,7 @@ namespace AZ
QAction* action = nullptr;
action = menu.addAction("Generate/Manage Source Material...", [this]() { OpenMaterialExporter(); });
action->setEnabled(m_id.m_materialAssetId.IsValid());
action->setEnabled(m_defaultMaterialAsset.GetId().IsValid());
menu.addSeparator();
@@ -34,7 +34,7 @@ namespace AZ
AZStd::string GetLabel() const;
bool HasSourceData() const;
void OpenMaterialEditor() const;
void SetDefaultAsset();
void ResetToDefaultAsset();
void Clear();
void ClearOverrides();
void OpenMaterialExporter();
@@ -42,7 +42,9 @@ namespace AZ
void OpenUvNameMapInspector();
MaterialAssignmentId m_id;
AZStd::string m_label;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
MaterialPropertyOverrideMap m_propertyOverrides;
AZStd::function<void()> m_materialChangedCallback;
AZStd::function<void()> m_propertyChangedCallback;
@@ -44,7 +44,7 @@ namespace AZ
for (const auto& oldPair : oldMaterials)
{
const DeprecatedMaterialAssignmentId& oldId = oldPair.first;
const MaterialAssignmentId newId(oldId.first, oldId.second);
const MaterialAssignmentId newId(oldId.first, oldId.second.m_subId);
newMaterials[newId] = oldPair.second;
}
@@ -33,6 +33,7 @@ namespace AZ
->Attribute(AZ::Script::Attributes::Category, "render")
->Attribute(AZ::Script::Attributes::Module, "render")
->Event("GetOriginalMaterialAssignments", &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments)
->Event("FindMaterialAssignmentId", &MaterialComponentRequestBus::Events::FindMaterialAssignmentId)
->Event("SetMaterialOverrides", &MaterialComponentRequestBus::Events::SetMaterialOverrides)
->Event("GetMaterialOverrides", &MaterialComponentRequestBus::Events::GetMaterialOverrides)
->Event("ClearAllMaterialOverrides", &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides)
@@ -249,10 +250,20 @@ namespace AZ
MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const
{
MaterialAssignmentMap materialAssignmentMap;
MaterialReceiverRequestBus::EventResult(materialAssignmentMap, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments);
MaterialReceiverRequestBus::EventResult(
materialAssignmentMap, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments);
return materialAssignmentMap;
}
MaterialAssignmentId MaterialComponentController::FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
{
MaterialAssignmentId materialAssignmentId;
MaterialReceiverRequestBus::EventResult(
materialAssignmentId, m_entityId, &MaterialReceiverRequestBus::Events::FindMaterialAssignmentId, lod, label);
return materialAssignmentId;
}
void MaterialComponentController::SetMaterialOverrides(const MaterialAssignmentMap& materials)
{
// this function is called twice once material asset is changed, a temp variable is
@@ -46,6 +46,7 @@ namespace AZ
//! MaterialComponentRequestBus overrides...
MaterialAssignmentMap GetOriginalMaterialAssignments() const override;
MaterialAssignmentId FindMaterialAssignmentId(const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
void SetMaterialOverrides(const MaterialAssignmentMap& materials) override;
const MaterialAssignmentMap& GetMaterialOverrides() const override;
void ClearAllMaterialOverrides() override;
@@ -251,6 +251,25 @@ namespace AZ
m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale);
}
}
RPI::ModelMaterialSlotMap MeshComponentController::GetModelMaterialSlots() const
{
Data::Asset<const RPI::ModelAsset> modelAsset = GetModelAsset();
if (modelAsset.IsReady())
{
return modelAsset->GetMaterialSlots();
}
else
{
return {};
}
}
MaterialAssignmentId MeshComponentController::FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
{
return FindMaterialAssignmentIdInModel(GetModel(), lod, label);
}
MaterialAssignmentMap MeshComponentController::GetMaterialAssignments() const
{
@@ -111,6 +111,9 @@ namespace AZ
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
// MaterialReceiverRequestBus::Handler overrides ...
virtual MaterialAssignmentId FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
RPI::ModelMaterialSlotMap GetModelMaterialSlots() const override;
MaterialAssignmentMap GetMaterialAssignments() const override;
AZStd::unordered_set<AZ::Name> GetModelUvNames() const override;
@@ -96,12 +96,11 @@ namespace AZ
skinnedSubMesh.m_vertexCount = aznumeric_cast<uint32_t>(subMeshVertexCount);
lodVertexCount += aznumeric_cast<uint32_t>(subMeshVertexCount);
// The default material id used by a sub-mesh is the guid of the source scene file plus the subId which is a unique material ID from the scene API
AZ::u32 subId = modelMesh.GetMaterialAsset().GetId().m_subId;
AZ::Data::AssetId materialId{ actorAssetId.m_guid, subId };
skinnedSubMesh.m_materialSlot = actor->GetMeshAsset()->FindMaterialSlot(modelMesh.GetMaterialSlotId());
// Queue the material asset - the ModelLod seems to handle delayed material loads
skinnedSubMesh.m_material = Data::AssetManager::Instance().GetAsset(materialId, azrtti_typeid<RPI::MaterialAsset>(), skinnedSubMesh.m_material.GetAutoLoadBehavior());
skinnedSubMesh.m_materialSlot.m_defaultMaterialAsset.QueueLoad();
subMeshes.push_back(skinnedSubMesh);
}
else
@@ -307,6 +307,30 @@ namespace AZ
m_meshFeatureProcessor = nullptr;
m_skinnedMeshFeatureProcessor = nullptr;
}
RPI::ModelMaterialSlotMap AtomActorInstance::GetModelMaterialSlots() const
{
Data::Asset<const RPI::ModelAsset> modelAsset = GetModelAsset();
if (modelAsset.IsReady())
{
return modelAsset->GetMaterialSlots();
}
else
{
return {};
}
}
MaterialAssignmentId AtomActorInstance::FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
{
if (m_skinnedMeshInstance && m_skinnedMeshInstance->m_model)
{
return FindMaterialAssignmentIdInModel(m_skinnedMeshInstance->m_model, lod, label);
}
return MaterialAssignmentId();
}
MaterialAssignmentMap AtomActorInstance::GetMaterialAssignments() const
{
@@ -490,16 +514,18 @@ namespace AZ
const AZStd::vector< SkinnedSubMeshProperties>& subMeshProperties = inputLod.GetSubMeshProperties();
for (const SkinnedSubMeshProperties& submesh : subMeshProperties)
{
AZ_Error("AtomActorInstance", submesh.m_material, "Actor does not have a valid default material in lod %d", lodIndex);
if (submesh.m_material)
Data::Asset<RPI::MaterialAsset> materialAsset = submesh.m_materialSlot.m_defaultMaterialAsset;
AZ_Error("AtomActorInstance", materialAsset, "Actor does not have a valid default material in lod %d", lodIndex);
if (materialAsset)
{
if (!submesh.m_material->IsReady())
if (!materialAsset->IsReady())
{
// Start listening for the material's OnAssetReady event.
// AtomActorInstance::Create is called on the main thread, so there should be no need to synchronize with the OnAssetReady event handler
// since those events will also come from the main thread
m_waitForMaterialLoadIds.insert(submesh.m_material->GetId());
Data::AssetBus::MultiHandler::BusConnect(submesh.m_material->GetId());
m_waitForMaterialLoadIds.insert(materialAsset->GetId());
Data::AssetBus::MultiHandler::BusConnect(materialAsset->GetId());
}
}
}
@@ -120,6 +120,9 @@ namespace AZ
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MaterialReceiverRequestBus::Handler overrides...
virtual MaterialAssignmentId FindMaterialAssignmentId(
const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
RPI::ModelMaterialSlotMap GetModelMaterialSlots() const override;
MaterialAssignmentMap GetMaterialAssignments() const override;
AZStd::unordered_set<AZ::Name> GetModelUvNames() const override;