Merge remote-tracking branch 'origin' into MultiplayerComponents

This commit is contained in:
karlberg
2021-05-05 20:07:49 -07:00
773 changed files with 5555 additions and 37483 deletions
@@ -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)
@@ -239,14 +260,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();