Added a new registry setting that disables automatic conversion of materials from model files like FBX.
By default, processing of model files (like FBX) automatically convert the included materials to Atom materials, using StandardPBR. This adds a job dependency on StandardPBR.materialtype, which propagates to any related azsl files as well. Thus any change to azsl code will cause all model files in the project to rebuild.
Some game teams have no interest in using the auto-converted materials; they always use a Material Component to apply material overrides for every mesh. This new setting allows teams to disable material auto-conversion for the entire project, thus removing the job dependency on StandardPBR.materialtype. Instead, every mesh will be assigned the same default material. Any change to azsl code will cause that one default material to rebuild, but this will not trigger any models to rebuild.
Details:
- Added /O3DE/SceneAPI/MaterialConverter registry settings for configuring the scene material converter. It includes an enable flag, and a default material to use when conversion is disabled.
- Added SceneBuilderDependencyRequests::AddFingerprintInfo which allows ScenePI components to modify the scene builder analysis fingerprint. We use this to reprocess scene files when the material converter settings change.
- Updated SceneAPI's material asset builder to skip the StandardPBR dependency when material conversion is disabled.
- Added some code to MaterialComponentController to handle an edge case that may when disabling material conversion on an existing project, and assigned materials disappear.
Testing:
- Changing the registery setting does trigger a rebuild of the fbx files.
- When material conversion is disabled, changing an azsl file does not cause fbx files to rebuild, but the shader still reloads as expected.
- Made a test level using multiple models with multiple meshes, made various adjustments to the material slots for each mesh, and tried switcihng the material conversion registry setting from true to false. (Details below)
- TODO: Will merge this change to a customer's fork and test on their existing content.
Details about my test level:
- Made a new test level AtomTest project
- Added two entities, both using multi-mat_mesh-groups_1m_cubes.fbx
- Added a material component to both entities
- Entity 1 material assignments
- Blue_Zaxis: left as-is
- Green_Yaxis: exported the material
- Red_Xaxis: exported the material, and changed the material instance color to pink
- StingrayPBS1: exported the material, scaled the UVs in the exported material source, and changed the material instance color to green.
- With_Texture: selected an existing brick material, changed the material instance color to red.
- Entity 2 material assignments
- Default Material: set to an existing brick material
- Blue_Zaxis: manually assigned built-in material that was converted from fbx
- Green_Yaxis: manually assigned built-in material that was converted from fbx, and changed the material instance color to orange
Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
This commit is contained in:
+88
-5
@@ -8,8 +8,10 @@
|
||||
|
||||
#include <Material/MaterialComponentController.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AtomCore/Instance/InstanceDatabase.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -85,6 +87,7 @@ namespace AZ
|
||||
void MaterialComponentController::Deactivate()
|
||||
{
|
||||
MaterialComponentRequestBus::Handler::BusDisconnect();
|
||||
MeshComponentNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
ReleaseMaterials();
|
||||
|
||||
@@ -111,6 +114,55 @@ namespace AZ
|
||||
{
|
||||
InitializeMaterialInstance(asset);
|
||||
}
|
||||
|
||||
void MaterialComponentController::OnModelReady(const Data::Asset<RPI::ModelAsset>&, const Data::Instance<RPI::Model>&)
|
||||
{
|
||||
MeshComponentNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
// If there is a circumstance where the saved material assignments are empty, fill them in with the default material.
|
||||
// (This could happen as a result of LoadMaterials() clearing the asset reference to deal with an edge case)
|
||||
|
||||
// Now that a model asset is ready, see if there are any empty assignments that need to be filled...
|
||||
RPI::ModelMaterialSlotMap modelMaterialSlots;
|
||||
MaterialReceiverRequestBus::EventResult(modelMaterialSlots, m_entityId, &MaterialReceiverRequestBus::Events::GetModelMaterialSlots);
|
||||
|
||||
AZStd::vector<Data::Asset<RPI::MaterialAsset>> newMaterialAssets;
|
||||
newMaterialAssets.reserve(m_configuration.m_materials.size());
|
||||
|
||||
// First we fill the empty slots but don't connect to AssetBus yet. If the same material asset appears multiple times,
|
||||
// AssetBus will call OnAssetReady only the *first* time we connect for that asset. The full list of m_configuration.m_materials
|
||||
// needs to be updated before that happens.
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
auto& materialAsset = materialPair.second.m_materialAsset;
|
||||
|
||||
if (!materialAsset.GetId().IsValid())
|
||||
{
|
||||
auto slotIter = modelMaterialSlots.find(materialPair.first.m_materialSlotStableId);
|
||||
if (slotIter != modelMaterialSlots.end())
|
||||
{
|
||||
materialAsset = slotIter->second.m_defaultMaterialAsset;
|
||||
newMaterialAssets.push_back(materialAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("MaterialComponentController", false, "Could not find material slot %d", materialPair.first.m_materialSlotStableId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the configuration is updated with all the default material assets, we can load and connect them.
|
||||
// If there are duplicates in this list, the redundant calls will be ignored.
|
||||
for (auto& materialAsset : newMaterialAssets)
|
||||
{
|
||||
if (!materialAsset.IsReady())
|
||||
{
|
||||
materialAsset.QueueLoad();
|
||||
}
|
||||
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
@@ -186,11 +238,42 @@ namespace AZ
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
auto& materialAsset = materialPair.second.m_materialAsset;
|
||||
if (materialAsset.GetId().IsValid() && !Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
|
||||
|
||||
// This is a special case where a material was auto-generated from the model file, connected to a Material Component by the user,
|
||||
// and then later a setting was changed to NOT auto-generate the model materials anymore. We need to switch to the new default
|
||||
// material rather than trying to use the old default material which no longer exists. If that's the case, we reset the asset
|
||||
// and OnModelReady will fill in the appropriate default material asset later.
|
||||
{
|
||||
anyQueued = true;
|
||||
materialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
|
||||
Data::AssetId modelAssetId;
|
||||
MeshComponentRequestBus::EventResult(modelAssetId, m_entityId, &MeshComponentRequestBus::Events::GetModelAssetId);
|
||||
bool materialWasGeneratedFromModel = (modelAssetId.m_guid == materialAsset.GetId().m_guid);
|
||||
|
||||
Data::AssetInfo assetInfo;
|
||||
Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &Data::AssetCatalogRequestBus::Events::GetAssetInfoById, materialAsset.GetId());
|
||||
bool materialAssetExists = assetInfo.m_assetId.IsValid();
|
||||
|
||||
if (materialWasGeneratedFromModel && !materialAssetExists)
|
||||
{
|
||||
AZ_Warning("MaterialComponentController", false, "The default material assignment for this slot has changed and will be replaced (was '%s').",
|
||||
materialAsset.ToString<AZStd::string>().c_str());
|
||||
materialAsset.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (materialAsset.GetId().IsValid())
|
||||
{
|
||||
if (!Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
|
||||
{
|
||||
anyQueued = true;
|
||||
materialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Since a material asset wasn't found, we'll need to supply a default material. But the default materials
|
||||
// won't be known until after the mesh component has loaded the model data.
|
||||
MeshComponentNotificationBus::Handler::BusConnect(m_entityId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +282,7 @@ namespace AZ
|
||||
ReleaseMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MaterialComponentController::InitializeMaterialInstance(const Data::Asset<Data::AssetData>& asset)
|
||||
{
|
||||
bool allReady = true;
|
||||
|
||||
Reference in New Issue
Block a user