From 64f42ba1cba87fb6c47e2662350e3ee09539b805 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Tue, 4 May 2021 01:24:14 -0700 Subject: [PATCH] Removed the asset callback from the EditorReflectionProbeComponent, since the component may have been destroyed and recreated between the bake and the asset load. Replaced with an OnTick handler that polls the ReflectionProbeFeatureProcessor to determine when the asset is ready. --- .../ReflectionProbeFeatureProcessor.h | 13 +- ...ReflectionProbeFeatureProcessorInterface.h | 9 +- .../ReflectionProbe/ReflectionProbe.cpp | 3 +- .../Source/ReflectionProbe/ReflectionProbe.h | 5 +- .../ReflectionProbeFeatureProcessor.cpp | 72 +++--- .../EditorReflectionProbeComponent.cpp | 225 +++++++++--------- .../EditorReflectionProbeComponent.h | 6 + .../ReflectionProbeComponentController.cpp | 31 ++- .../ReflectionProbeComponentController.h | 2 +- 9 files changed, 210 insertions(+), 156 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h index a89875aaa6..ff84f77a3e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h @@ -36,10 +36,11 @@ namespace AZ void RemoveProbe(ReflectionProbeHandle& probe) override; void SetProbeOuterExtents(const ReflectionProbeHandle& probe, const AZ::Vector3& outerExtents) override; void SetProbeInnerExtents(const ReflectionProbeHandle& probe, const AZ::Vector3& innerExtents) override; - void SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance& cubeMapImage) override; + void SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance& cubeMapImage, const AZStd::string& relativePath) override; void SetProbeTransform(const ReflectionProbeHandle& probe, const AZ::Transform& transform) override; - void BakeProbe(const ReflectionProbeHandle& probe, BuildCubeMapCallback callback) override; - void NotifyCubeMapAssetReady(const AZStd::string relativePath, NotifyCubeMapAssetReadyCallback callback) override; + void BakeProbe(const ReflectionProbeHandle& probe, BuildCubeMapCallback callback, const AZStd::string& relativePath) override; + bool CheckCubeMapAssetNotification(const AZStd::string& relativePath, Data::Asset& outCubeMapAsset, CubeMapAssetNotificationType& outNotificationType) override; + bool IsCubeMapReferenced(const AZStd::string& relativePath) override; bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const override { return (probe.get() != nullptr); } void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) override; @@ -72,10 +73,9 @@ namespace AZ // AssetBus::MultiHandler overrides... void OnAssetReady(Data::Asset asset) override; void OnAssetError(Data::Asset asset) override; - void OnAssetReloaded(Data::Asset asset) override; // notifies and removes the notification entry - void HandleAssetNotification(Data::Asset asset, CubeMapAssetNotificationType notificationTyp); + void HandleAssetNotification(Data::Asset asset, CubeMapAssetNotificationType notificationType); // list of reflection probes const size_t InitialProbeAllocationSize = 64; @@ -86,9 +86,8 @@ namespace AZ { AZStd::string m_relativePath; AZ::Data::AssetId m_assetId; - bool m_existingAsset = false; - NotifyCubeMapAssetReadyCallback m_callback; Data::Asset m_asset; + CubeMapAssetNotificationType m_notificationType = CubeMapAssetNotificationType::None; }; typedef AZStd::vector NotifyCubeMapAssetVector; NotifyCubeMapAssetVector m_notifyCubeMapAssets; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h index 8b277e97c8..03c44d0442 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h @@ -30,11 +30,11 @@ namespace AZ enum CubeMapAssetNotificationType { + None, Ready, Error, Reloaded }; - using NotifyCubeMapAssetReadyCallback = AZStd::function& cubeMapAsset, CubeMapAssetNotificationType notificationType)>; // ReflectionProbeFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom class ReflectionProbeFeatureProcessorInterface @@ -47,10 +47,11 @@ namespace AZ virtual void RemoveProbe(ReflectionProbeHandle& handle) = 0; virtual void SetProbeOuterExtents(const ReflectionProbeHandle& handle, const AZ::Vector3& outerExtents) = 0; virtual void SetProbeInnerExtents(const ReflectionProbeHandle& handle, const AZ::Vector3& innerExtents) = 0; - virtual void SetProbeCubeMap(const ReflectionProbeHandle& handle, Data::Instance& cubeMapImage) = 0; + virtual void SetProbeCubeMap(const ReflectionProbeHandle& handle, Data::Instance& cubeMapImage, const AZStd::string& relativePath) = 0; virtual void SetProbeTransform(const ReflectionProbeHandle& handle, const AZ::Transform& transform) = 0; - virtual void BakeProbe(const ReflectionProbeHandle& handle, BuildCubeMapCallback callback) = 0; - virtual void NotifyCubeMapAssetReady(const AZStd::string relativePath, NotifyCubeMapAssetReadyCallback callback) = 0; + virtual void BakeProbe(const ReflectionProbeHandle& handle, BuildCubeMapCallback callback, const AZStd::string& relativePath) = 0; + virtual bool CheckCubeMapAssetNotification(const AZStd::string& relativePath, Data::Asset& outCubeMapAsset, CubeMapAssetNotificationType& outNotificationType) = 0; + virtual bool IsCubeMapReferenced(const AZStd::string& relativePath) = 0; virtual bool IsValidProbeHandle(const ReflectionProbeHandle& probe) const = 0; virtual void ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) = 0; }; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index cfce718146..e0b79e23aa 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -234,9 +234,10 @@ namespace AZ m_updateSrg = true; } - void ReflectionProbe::SetCubeMapImage(const Data::Instance& cubeMapImage) + void ReflectionProbe::SetCubeMapImage(const Data::Instance& cubeMapImage, const AZStd::string& relativePath) { m_cubeMapImage = cubeMapImage; + m_cubeMapRelativePath = relativePath; m_updateSrg = true; } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index 42b3ff5c5a..ab0e7a81d3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -88,7 +88,9 @@ namespace AZ const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; } const Data::Instance& GetCubeMapImage() const { return m_cubeMapImage; } - void SetCubeMapImage(const Data::Instance& cubeMapImage); + void SetCubeMapImage(const Data::Instance& cubeMapImage, const AZStd::string& relativePath); + + const AZStd::string& GetCubeMapRelativePath() const { return m_cubeMapRelativePath; } bool GetUseParallaxCorrection() const { return m_useParallaxCorrection; } void SetUseParallaxCorrection(bool useParallaxCorrection) { m_useParallaxCorrection = useParallaxCorrection; } @@ -135,6 +137,7 @@ namespace AZ // cubemap Data::Instance m_cubeMapImage; + AZStd::string m_cubeMapRelativePath; bool m_useParallaxCorrection = false; // probe visualization diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index a0d3e6ac51..740069fc6b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -178,9 +178,9 @@ namespace AZ if (assetId.IsValid()) { - Data::AssetBus::MultiHandler::BusConnect(assetId); notificationEntry.m_assetId = assetId; notificationEntry.m_asset.Create(assetId, true); + Data::AssetBus::MultiHandler::BusConnect(assetId); } } @@ -257,10 +257,10 @@ namespace AZ m_probeSortRequired = true; } - void ReflectionProbeFeatureProcessor::SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance& cubeMapImage) + void ReflectionProbeFeatureProcessor::SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance& cubeMapImage, const AZStd::string& relativePath) { AZ_Assert(probe.get(), "SetProbeCubeMap called with an invalid handle"); - probe->SetCubeMapImage(cubeMapImage); + probe->SetCubeMapImage(cubeMapImage, relativePath); } void ReflectionProbeFeatureProcessor::SetProbeTransform(const ReflectionProbeHandle& probe, const AZ::Transform& transform) @@ -270,14 +270,11 @@ namespace AZ m_probeSortRequired = true; } - void ReflectionProbeFeatureProcessor::BakeProbe(const ReflectionProbeHandle& probe, BuildCubeMapCallback callback) + void ReflectionProbeFeatureProcessor::BakeProbe(const ReflectionProbeHandle& probe, BuildCubeMapCallback callback, const AZStd::string& relativePath) { AZ_Assert(probe.get(), "BakeProbe called with an invalid handle"); probe->BuildCubeMap(callback); - } - void ReflectionProbeFeatureProcessor::NotifyCubeMapAssetReady(const AZStd::string relativePath, NotifyCubeMapAssetReadyCallback callback) - { // check to see if this is an existing asset AZ::Data::AssetId assetId; AZ::Data::AssetCatalogRequestBus::BroadcastResult( @@ -287,15 +284,46 @@ namespace AZ azrtti_typeid(), false); - bool existingAsset = assetId.IsValid(); - m_notifyCubeMapAssets.push_back({ relativePath, assetId, existingAsset, callback }); - - if (existingAsset) + // we only track notifications for new cubemap assets, existing assets are automatically reloaded by the RPI + if (!assetId.IsValid()) { - Data::AssetBus::MultiHandler::BusConnect(assetId); + m_notifyCubeMapAssets.push_back({ relativePath, assetId }); } } + bool ReflectionProbeFeatureProcessor::CheckCubeMapAssetNotification(const AZStd::string& relativePath, Data::Asset& outCubeMapAsset, CubeMapAssetNotificationType& outNotificationType) + { + for (NotifyCubeMapAssetVector::iterator itNotification = m_notifyCubeMapAssets.begin(); itNotification != m_notifyCubeMapAssets.end(); ++itNotification) + { + if (itNotification->m_relativePath == relativePath) + { + outNotificationType = itNotification->m_notificationType; + if (outNotificationType != CubeMapAssetNotificationType::None) + { + outCubeMapAsset = itNotification->m_asset; + m_notifyCubeMapAssets.erase(itNotification); + } + + return true; + } + } + + return false; + } + + bool ReflectionProbeFeatureProcessor::IsCubeMapReferenced(const AZStd::string& relativePath) + { + for (auto& reflectionProbe : m_reflectionProbes) + { + if (reflectionProbe->GetCubeMapRelativePath() == relativePath) + { + return true; + } + } + + return false; + } + void ReflectionProbeFeatureProcessor::ShowProbeVisualization(const ReflectionProbeHandle& probe, bool showVisualization) { AZ_Assert(probe.get(), "ShowProbeVisualization called with an invalid handle"); @@ -509,17 +537,9 @@ namespace AZ { if (itNotification->m_assetId == asset.GetId()) { - if (itNotification->m_existingAsset && notificationType == CubeMapAssetNotificationType::Ready) - { - // existing cubemap assets only notify on reload or error - break; - } - - // notify - itNotification->m_callback(Data::static_pointer_cast(asset), notificationType); - - // remove the entry from the list - m_notifyCubeMapAssets.erase(itNotification); + // store the cubemap asset + itNotification->m_asset = Data::static_pointer_cast(asset); + itNotification->m_notificationType = notificationType; // stop notifications on this asset Data::AssetBus::MultiHandler::BusDisconnect(itNotification->m_assetId); @@ -540,11 +560,5 @@ namespace AZ HandleAssetNotification(asset, CubeMapAssetNotificationType::Error); } - - void ReflectionProbeFeatureProcessor::OnAssetReloaded(Data::Asset asset) - { - HandleAssetNotification(asset, CubeMapAssetNotificationType::Reloaded); - } - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 9d34553eba..735eab5368 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -133,23 +133,14 @@ namespace AZ AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); EditorReflectionProbeBus::Handler::BusConnect(GetEntityId()); + AZ::TickBus::Handler::BusConnect(); ReflectionProbeComponentConfig& configuration = m_controller.m_configuration; - // special handling is required if this component is being cloned in the editor: - // if the entityId in the configuration does not match this component's entityId it is being cloned - AZ::u64 entityId = (AZ::u64)GetEntityId(); - if (configuration.m_entityId != EntityId::InvalidEntityId - && configuration.m_entityId != entityId) - { - // clear the cubeMapRelativePath to prevent the newly cloned reflection probe - // from using the same cubemap path as the original reflection probe - configuration.m_bakedCubeMapRelativePath = ""; - } - // update UI cubemap path display m_bakedCubeMapRelativePath = configuration.m_bakedCubeMapRelativePath; + AZ::u64 entityId = (AZ::u64)GetEntityId(); configuration.m_entityId = entityId; } @@ -158,9 +149,55 @@ namespace AZ EditorReflectionProbeBus::Handler::BusDisconnect(GetEntityId()); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); BaseClass::Deactivate(); } + void EditorReflectionProbeComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if (!m_controller.m_featureProcessor) + { + return; + } + + if (m_controller.m_configuration.m_useBakedCubemap) + { + AZStd::string cubeMapRelativePath = m_controller.m_configuration.m_bakedCubeMapRelativePath + ".streamingimage"; + Data::Asset cubeMapAsset; + CubeMapAssetNotificationType notificationType = CubeMapAssetNotificationType::None; + if (m_controller.m_featureProcessor->CheckCubeMapAssetNotification(cubeMapRelativePath, cubeMapAsset, notificationType)) + { + // a cubemap bake is in progress for this entity component + if (notificationType == CubeMapAssetNotificationType::Ready) + { + // bake is complete, update configuration with the new baked cubemap asset + m_controller.m_configuration.m_bakedCubeMapAsset = { cubeMapAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + + // refresh the currently rendered cubemap + m_controller.UpdateCubeMap(); + + // update the UI + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); + } + else if (notificationType == CubeMapAssetNotificationType::Error) + { + // cubemap bake failed + QMessageBox::information( + QApplication::activeWindow(), + "Reflection Probe", + "Reflection Probe cubemap failed to bake, please check the Asset Processor for more information.", + QMessageBox::Ok); + + // clear relative path, this will allow the user to retry + m_controller.m_configuration.m_bakedCubeMapRelativePath.clear(); + + // update the UI + AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); + } + } + } + } + void EditorReflectionProbeComponent::DisplayEntityViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) { // only draw the bounds if selected @@ -276,122 +313,94 @@ namespace AZ return AZ::Edit::PropertyRefreshLevels::None; } - // callback from the EnvironmentCubeMapPass when the cubemap render is complete - BuildCubeMapCallback buildCubeMapCallback = [this](uint8_t* const* cubeMapFaceTextureData, const RHI::Format cubeMapTextureFormat) + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + // retrieve the source cubemap path from the configuration + // we need to make sure to use the same source cubemap for each bake + AZStd::string cubeMapRelativePath = m_controller.m_configuration.m_bakedCubeMapRelativePath; + AZStd::string cubeMapFullPath; + + if (!cubeMapRelativePath.empty()) { - if (!m_bakeInProgress) + // test to see if the cubemap file is actually there, if it was removed we need to + // generate a new filename, otherwise it will cause an error in the asset system + AzFramework::StringFunc::Path::Join(projectPath, cubeMapRelativePath.c_str(), cubeMapFullPath, true, true); + + if (!AZ::IO::FileIOBase::GetInstance()->Exists(cubeMapFullPath.c_str())) { - // user canceled the bake - return; + // clear it to force the generation of a new filename + cubeMapRelativePath.clear(); } + } - char projectPath[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + // build a new cubemap path if necessary + if (cubeMapRelativePath.empty()) + { + // the file name is a combination of the entity name, a UUID, and the filemask + Entity* entity = GetEntity(); + AZ_Assert(entity, "ReflectionProbe entity is null"); - // retrieve the source cubemap path from the configuration - // we need to make sure to use the same source cubemap for each bake - AZStd::string cubeMapRelativePath = m_controller.m_configuration.m_bakedCubeMapRelativePath; - AZStd::string cubeMapFullPath; + AZ::Uuid uuid = AZ::Uuid::CreateRandom(); + AZStd::string uuidString; + uuid.ToString(uuidString); - if (!cubeMapRelativePath.empty()) + cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + "_iblspecularcm.dds"; + + // replace any invalid filename characters + auto invalidCharacters = [](char letter) { - // test to see if the cubemap file is actually there, if it was removed we need to - // generate a new filename, otherwise it will cause an error in the asset system - AzFramework::StringFunc::Path::Join(projectPath, cubeMapRelativePath.c_str(), cubeMapFullPath, true, true); + return + letter == ':' || letter == '"' || letter == '\'' || + letter == '{' || letter == '}' || + letter == '<' || letter == '>'; + }; + AZStd::replace_if(cubeMapRelativePath.begin(), cubeMapRelativePath.end(), invalidCharacters, '_'); - if (!AZ::IO::FileIOBase::GetInstance()->Exists(cubeMapFullPath.c_str())) - { - // clear it to force the generation of a new filename - cubeMapRelativePath.clear(); - } - } + // build the full source path + AzFramework::StringFunc::Path::Join(projectPath, cubeMapRelativePath.c_str(), cubeMapFullPath, true, true); + } - // build a new cubemap path if necessary - if (cubeMapRelativePath.empty()) - { - // the file name is a combination of the entity name, a UUID, and the filemask - Entity* entity = GetEntity(); - AZ_Assert(entity, "ReflectionProbe entity is null"); + // make sure the folder is created + AZStd::string reflectionProbeFolder; + AzFramework::StringFunc::Path::GetFolderPath(cubeMapFullPath.data(), reflectionProbeFolder); + AZ::IO::SystemFile::CreateDir(reflectionProbeFolder.c_str()); - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); + // check out the file in source control + bool checkedOutSuccessfully = false; + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + ApplicationBus::BroadcastResult( + checkedOutSuccessfully, + &ApplicationBus::Events::RequestEditForFileBlocking, + cubeMapFullPath.c_str(), + "Checking out for edit...", + ApplicationBus::Events::RequestEditProgressCallback()); - cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + "_iblspecularcm.dds"; + if (!checkedOutSuccessfully) + { + AZ_Error("ReflectionProbe", false, "Failed to write \"%s\", source control checkout failed", cubeMapFullPath.c_str()); + } - // replace any invalid filename characters - auto invalidCharacters = [](char letter) - { - return - letter == ':' || letter == '"' || letter == '\'' || - letter == '{' || letter == '}' || - letter == '<' || letter == '>'; - }; - AZStd::replace_if(cubeMapRelativePath.begin(), cubeMapRelativePath.end(), invalidCharacters, '_'); + // save the relative source path in the configuration + AzToolsFramework::ScopedUndoBatch undoBatch("Cubemap path changed."); + m_controller.m_configuration.m_bakedCubeMapRelativePath = cubeMapRelativePath; + SetDirty(); - // build the full source path - AzFramework::StringFunc::Path::Join(projectPath, cubeMapRelativePath.c_str(), cubeMapFullPath, true, true); - } - - // make sure the folder is created - AZStd::string reflectionProbeFolder; - AzFramework::StringFunc::Path::GetFolderPath(cubeMapFullPath.data(), reflectionProbeFolder); - AZ::IO::SystemFile::CreateDir(reflectionProbeFolder.c_str()); - - // check out the file in source control - bool checkedOutSuccessfully = false; - using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; - ApplicationBus::BroadcastResult( - checkedOutSuccessfully, - &ApplicationBus::Events::RequestEditForFileBlocking, - cubeMapFullPath.c_str(), - "Checking out for edit...", - ApplicationBus::Events::RequestEditProgressCallback()); - - if (!checkedOutSuccessfully) - { - AZ_Error("ReflectionProbe", false, "Failed to write \"%s\", source control checkout failed", cubeMapFullPath.c_str()); - } + // update UI cubemap path display + m_bakedCubeMapRelativePath = cubeMapRelativePath; + // callback from the EnvironmentCubeMapPass when the cubemap render is complete + BuildCubeMapCallback buildCubeMapCallback = [=](uint8_t* const* cubeMapFaceTextureData, const RHI::Format cubeMapTextureFormat) + { // write the cubemap data to the .dds file WriteOutputFile(cubeMapFullPath.c_str(), cubeMapFaceTextureData, cubeMapTextureFormat); - - // save the relative source path in the configuration - AzToolsFramework::ScopedUndoBatch undoBatch("Cubemap path changed."); - m_controller.m_configuration.m_bakedCubeMapRelativePath = cubeMapRelativePath; - SetDirty(); - - // update UI cubemap path display - m_bakedCubeMapRelativePath = cubeMapRelativePath; - - // call the feature processor to notify when the asset is created and ready - NotifyCubeMapAssetReadyCallback notifyCubeMapAssetReadyCallback = [this](const Data::Asset& cubeMapAsset, CubeMapAssetNotificationType notificationType) - { - // we only need to store the cubemap asset and update the cubemap image on the first bake of the probe, - // otherwise it is a hot-reload of an existing cubemap asset which is handled by the RPI - if (notificationType == CubeMapAssetNotificationType::Ready) - { - // update configuration with the new baked cubemap asset - m_controller.m_configuration.m_bakedCubeMapAsset = { cubeMapAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; - - // refresh the currently rendered cubemap - m_controller.UpdateCubeMap(); - - // update the UI - AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues); - } - - // signal completion - m_bakeInProgress = false; - }; - - AZStd::string cubeMapRelativeAssetPath = cubeMapRelativePath + ".streamingimage"; - m_controller.m_featureProcessor->NotifyCubeMapAssetReady(cubeMapRelativeAssetPath, notifyCubeMapAssetReadyCallback); + m_bakeInProgress = false; }; - // initiate the cubemap bake + // initiate the cubemap bake, this will invoke the buildCubeMapCallback when the cubemap data is ready m_bakeInProgress = true; - m_controller.BakeReflectionProbe(buildCubeMapCallback); + AZStd::string cubeMapRelativeAssetPath = cubeMapRelativePath + ".streamingimage"; + m_controller.BakeReflectionProbe(buildCubeMapCallback, cubeMapRelativeAssetPath); // show a dialog box letting the user know the probe is baking QProgressDialog bakeDialog; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h index eba9575ec4..23fd391818 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include #include @@ -29,6 +30,7 @@ namespace AZ , public EditorReflectionProbeBus::Handler , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -45,8 +47,12 @@ namespace AZ // AzFramework::EntityDebugDisplayEventBus::Handler overrides void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + private: + // AZ::TickBus overrides + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + // validation AZ::Outcome OnUseBakedCubemapValidate(void* newValue, const AZ::Uuid& valueType); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp index c7349adefa..59d1f7afa7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp @@ -111,6 +111,18 @@ namespace AZ m_boxShapeInterface = LmbrCentral::BoxShapeComponentRequestsBus::FindFirstHandler(m_entityId); AZ_Assert(m_boxShapeInterface, "ReflectionProbeComponentController was unable to find box shape component"); + // special handling is required if this component is being cloned in the editor: + // if this probe is using a baked cubemap, check to see if it is already referenced by another probe + if (m_configuration.m_useBakedCubemap) + { + if (m_featureProcessor->IsCubeMapReferenced(m_configuration.m_bakedCubeMapRelativePath)) + { + // clear the cubeMapRelativePath to prevent the newly cloned reflection probe + // from using the same cubemap path as the original reflection probe + m_configuration.m_bakedCubeMapRelativePath = ""; + } + } + // add this reflection probe to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddProbe(transform, m_configuration.m_useParallaxCorrection); @@ -130,12 +142,15 @@ namespace AZ m_configuration.m_useBakedCubemap ? m_configuration.m_bakedCubeMapAsset : m_configuration.m_authoredCubeMapAsset; Data::AssetBus::MultiHandler::BusConnect(cubeMapAsset.GetId()); + const AZStd::string& relativePath = + m_configuration.m_useBakedCubemap ? m_configuration.m_bakedCubeMapRelativePath : m_configuration.m_authoredCubeMapAsset.GetHint(); + if (cubeMapAsset.GetId().IsValid()) { if (cubeMapAsset.IsReady()) { Data::Instance image = RPI::StreamingImage::FindOrCreate(cubeMapAsset); - m_featureProcessor->SetProbeCubeMap(m_handle, image); + m_featureProcessor->SetProbeCubeMap(m_handle, image, relativePath); } else { @@ -169,8 +184,11 @@ namespace AZ return; } + const AZStd::string& relativePath = + m_configuration.m_useBakedCubemap ? m_configuration.m_bakedCubeMapRelativePath : m_configuration.m_authoredCubeMapAsset.GetHint(); + Data::Instance image = RPI::StreamingImage::FindOrCreate(asset); - m_featureProcessor->SetProbeCubeMap(m_handle, image); + m_featureProcessor->SetProbeCubeMap(m_handle, image, relativePath); } void ReflectionProbeComponentController::SetConfiguration(const ReflectionProbeComponentConfig& config) @@ -188,8 +206,11 @@ namespace AZ Data::Asset& cubeMapAsset = m_configuration.m_useBakedCubemap ? m_configuration.m_bakedCubeMapAsset : m_configuration.m_authoredCubeMapAsset; + const AZStd::string& relativePath = + m_configuration.m_useBakedCubemap ? m_configuration.m_bakedCubeMapRelativePath : m_configuration.m_authoredCubeMapAsset.GetHint(); + Data::Instance image = RPI::StreamingImage::FindOrCreate(cubeMapAsset); - m_featureProcessor->SetProbeCubeMap(m_handle, image); + m_featureProcessor->SetProbeCubeMap(m_handle, image, relativePath); } void ReflectionProbeComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world) @@ -240,14 +261,14 @@ namespace AZ m_configuration.m_innerHeight = AZStd::min(m_configuration.m_innerHeight, m_configuration.m_outerHeight); } - void ReflectionProbeComponentController::BakeReflectionProbe(BuildCubeMapCallback callback) + void ReflectionProbeComponentController::BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath) { if (!m_featureProcessor) { return; } - m_featureProcessor->BakeProbe(m_handle, callback); + m_featureProcessor->BakeProbe(m_handle, callback, relativePath); } AZ::Aabb ReflectionProbeComponentController::GetAabb() const diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h index 20d692145c..0a57fde882 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h @@ -79,7 +79,7 @@ namespace AZ AZ::Aabb GetAabb() const; // initiate the reflection probe bake, invokes callback when complete - void BakeReflectionProbe(BuildCubeMapCallback callback); + void BakeReflectionProbe(BuildCubeMapCallback callback, const AZStd::string& relativePath); // update the currently rendering cubemap asset for this probe void UpdateCubeMap();