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.
This commit is contained in:
dmcdiar
2021-05-04 01:24:14 -07:00
parent 781415755d
commit 64f42ba1cb
9 changed files with 210 additions and 156 deletions
@@ -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<RPI::Image>& cubeMapImage) override;
void SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance<RPI::Image>& 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<RPI::StreamingImageAsset>& 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<Data::AssetData> asset) override;
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
// notifies and removes the notification entry
void HandleAssetNotification(Data::Asset<Data::AssetData> asset, CubeMapAssetNotificationType notificationTyp);
void HandleAssetNotification(Data::Asset<Data::AssetData> 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<RPI::StreamingImageAsset> m_asset;
CubeMapAssetNotificationType m_notificationType = CubeMapAssetNotificationType::None;
};
typedef AZStd::vector<NotifyCubeMapAssetEntry> NotifyCubeMapAssetVector;
NotifyCubeMapAssetVector m_notifyCubeMapAssets;
@@ -30,11 +30,11 @@ namespace AZ
enum CubeMapAssetNotificationType
{
None,
Ready,
Error,
Reloaded
};
using NotifyCubeMapAssetReadyCallback = AZStd::function<void(const Data::Asset<RPI::StreamingImageAsset>& 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<RPI::Image>& cubeMapImage) = 0;
virtual void SetProbeCubeMap(const ReflectionProbeHandle& handle, Data::Instance<RPI::Image>& 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<RPI::StreamingImageAsset>& 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;
};
@@ -234,9 +234,10 @@ namespace AZ
m_updateSrg = true;
}
void ReflectionProbe::SetCubeMapImage(const Data::Instance<RPI::Image>& cubeMapImage)
void ReflectionProbe::SetCubeMapImage(const Data::Instance<RPI::Image>& cubeMapImage, const AZStd::string& relativePath)
{
m_cubeMapImage = cubeMapImage;
m_cubeMapRelativePath = relativePath;
m_updateSrg = true;
}
@@ -88,7 +88,9 @@ namespace AZ
const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; }
const Data::Instance<RPI::Image>& GetCubeMapImage() const { return m_cubeMapImage; }
void SetCubeMapImage(const Data::Instance<RPI::Image>& cubeMapImage);
void SetCubeMapImage(const Data::Instance<RPI::Image>& 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<RPI::Image> m_cubeMapImage;
AZStd::string m_cubeMapRelativePath;
bool m_useParallaxCorrection = false;
// probe visualization
@@ -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<RPI::Image>& cubeMapImage)
void ReflectionProbeFeatureProcessor::SetProbeCubeMap(const ReflectionProbeHandle& probe, Data::Instance<RPI::Image>& 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<AZ::RPI::StreamingImageAsset>(),
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<RPI::StreamingImageAsset>& 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<RPI::StreamingImageAsset>(asset), notificationType);
// remove the entry from the list
m_notifyCubeMapAssets.erase(itNotification);
// store the cubemap asset
itNotification->m_asset = Data::static_pointer_cast<RPI::StreamingImageAsset>(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<Data::AssetData> asset)
{
HandleAssetNotification(asset, CubeMapAssetNotificationType::Reloaded);
}
} // namespace Render
} // namespace AZ
@@ -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<RPI::StreamingImageAsset> 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<RPI::StreamingImageAsset>(), 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<RPI::StreamingImageAsset>& 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<RPI::StreamingImageAsset>(), 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;
@@ -13,6 +13,7 @@
#pragma once
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <ReflectionProbe/ReflectionProbeComponent.h>
@@ -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<ReflectionProbeComponentController, ReflectionProbeComponent, ReflectionProbeComponentConfig>;
@@ -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<void, AZStd::string> OnUseBakedCubemapValidate(void* newValue, const AZ::Uuid& valueType);
@@ -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<RPI::Image> 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<RPI::Image> 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<RPI::StreamingImageAsset>& 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<RPI::Image> 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
@@ -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();