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:
+6
-7
@@ -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;
|
||||
|
||||
+5
-4
@@ -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
|
||||
|
||||
+43
-29
@@ -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
|
||||
|
||||
+117
-108
@@ -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;
|
||||
|
||||
+6
@@ -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);
|
||||
|
||||
|
||||
+26
-5
@@ -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
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user