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:
+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