Merge pull request #537 from aws-lumberyard-dev/Atom/dmcdiar/LYN-3256
[LYN-3256] Atom: Baking Reflection Probe crashes the Editor when Prefabs are enabled
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