/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include namespace AZ { namespace RPI { const char* MaterialAsset::DisplayName = "MaterialAsset"; const char* MaterialAsset::Group = "Material"; const char* MaterialAsset::Extension = "azmaterial"; void MaterialAsset::Reflect(ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(9) ->Field("materialTypeAsset", &MaterialAsset::m_materialTypeAsset) ->Field("propertyValues", &MaterialAsset::m_propertyValues) ; } } MaterialAsset::MaterialAsset() { } MaterialAsset::~MaterialAsset() { MaterialReloadNotificationBus::Handler::BusDisconnect(); Data::AssetBus::Handler::BusDisconnect(); AssetInitBus::Handler::BusDisconnect(); } const Data::Asset& MaterialAsset::GetMaterialTypeAsset() const { return m_materialTypeAsset; } const ShaderCollection& MaterialAsset::GetShaderCollection() const { return m_materialTypeAsset->GetShaderCollection(); } const MaterialFunctorList& MaterialAsset::GetMaterialFunctors() const { return m_materialTypeAsset->GetMaterialFunctors(); } const AZ::Data::Asset& MaterialAsset::GetMaterialSrgAsset() const { return m_materialTypeAsset->GetMaterialSrgAsset(); } const AZ::Data::Asset& MaterialAsset::GetObjectSrgAsset() const { return m_materialTypeAsset->GetObjectSrgAsset(); } const MaterialPropertiesLayout* MaterialAsset::GetMaterialPropertiesLayout() const { return m_materialTypeAsset->GetMaterialPropertiesLayout(); } AZStd::array_view MaterialAsset::GetPropertyValues() const { return m_propertyValues; } void MaterialAsset::SetReady() { m_status = AssetStatus::Ready; // If this was created dynamically using MaterialAssetCreator (which is what calls SetReady()), // we need to connect to the AssetBus for reloads. PostLoadInit(); } bool MaterialAsset::PostLoadInit() { if (!m_materialTypeAsset.Get()) { AssetInitBus::Handler::BusDisconnect(); // Any MaterialAsset with invalid MaterialTypeAsset is not a successfully-loaded asset. return false; } else { Data::AssetBus::Handler::BusConnect(m_materialTypeAsset.GetId()); MaterialReloadNotificationBus::Handler::BusConnect(m_materialTypeAsset.GetId()); AssetInitBus::Handler::BusDisconnect(); return true; } } void MaterialAsset::OnMaterialTypeAssetReinitialized(const Data::Asset& materialTypeAsset) { // When reloads occur, it's possible for old Asset objects to hang around and report reinitialization, // so we can reduce unnecessary reinitialization in that case. if (materialTypeAsset.Get() == m_materialTypeAsset.Get()) { ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnMaterialTypeAssetReinitialized %s", this, materialTypeAsset.GetHint().c_str()); // MaterialAsset doesn't need to reinitialize any of its own data when MaterialTypeAsset reinitializes, // because all it depends on is the MaterialTypeAsset reference, rather than the data inside it. // Ultimately it's the Material that cares about these changes, so we just forward any signal we get. MaterialReloadNotificationBus::Event(GetId(), &MaterialReloadNotifications::OnMaterialAssetReinitialized, Data::Asset{this, AZ::Data::AssetLoadBehavior::PreLoad}); } } void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset asset) { Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; if (newMaterialTypeAsset) { // The order of asset reloads is non-deterministic. If the MaterialAsset reloads before the // MaterialTypeAsset, this will make sure the MaterialAsset gets update with latest one. // This also covers the case where just the MaterialTypeAsset is reloaded and not the MaterialAsset. m_materialTypeAsset = newMaterialTypeAsset; // Notify interested parties that this MaterialAsset is changed and may require other data to reinitialize as well MaterialReloadNotificationBus::Event(GetId(), &MaterialReloadNotifications::OnMaterialAssetReinitialized, Data::Asset{this, AZ::Data::AssetLoadBehavior::PreLoad}); } } void MaterialAsset::OnAssetReloaded(Data::Asset asset) { ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); ReinitializeMaterialTypeAsset(asset); } void MaterialAsset::OnAssetReady(Data::Asset asset) { // Regarding why we listen to both OnAssetReloaded and OnAssetReady, see explanation in ShaderAsset::OnAssetReady. ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReady %s", this, asset.GetHint().c_str()); ReinitializeMaterialTypeAsset(asset); } Data::AssetHandler::LoadResult MaterialAssetHandler::LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) { if (Base::LoadAssetData(asset, stream, assetLoadFilterCB) == Data::AssetHandler::LoadResult::LoadComplete) { asset.GetAs()->AssetInitBus::Handler::BusConnect(); return Data::AssetHandler::LoadResult::LoadComplete; } return Data::AssetHandler::LoadResult::Error; } } // namespace RPI } // namespace AZ