diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset new file mode 100644 index 0000000000..1bb23c6e96 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset @@ -0,0 +1,45 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "FileMasks": ["_lutr32f"], + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "ios": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "osx_gl": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + }, + "provo": { + "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}", + "Name": "LUT_R32F", + "SourceColor": "Linear", + "DestColor": "Linear", + "PixelFormat": "R32F" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset new file mode 100644 index 0000000000..f36d566d7e --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "ios": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "osx_gl": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + }, + "provo": { + "UUID": "{ABDFCED1-0565-4B7B-9BC1-82C473BCEEA2}", + "Name": "LUT_RGBA16", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16" + ], + "PixelFormat": "R16G16B16A16" + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset new file mode 100644 index 0000000000..367c5101b3 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset @@ -0,0 +1,59 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "PlatformsPresets": { + "es3": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "ios": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "osx_gl": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + }, + "provo": { + "UUID": "{6D75F093-C826-437A-AD94-8631A5A4E8A2}", + "Name": "LUT_RGBA16F", + "SourceColor": "Linear", + "DestColor": "Linear", + "FileMasks": [ + "_lutrgba16f" + ], + "PixelFormat": "R16G16B16A16F" + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h index 15b416597d..cf46383a64 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h @@ -14,6 +14,9 @@ #include #include +#include +#include +#include namespace AZ { @@ -23,6 +26,57 @@ namespace AZ using DiffuseProbeGridHandle = AZStd::shared_ptr; + enum class DiffuseProbeGridMode : uint8_t + { + RealTime, + Baked, + AutoSelect + }; + + enum class DiffuseProbeGridTextureNotificationType + { + None, + Ready, + Error + }; + + struct DiffuseProbeGridTexture + { + const AZStd::shared_ptr> m_data; + RHI::Format m_format; + RHI::Size m_size; + }; + + static const char* DiffuseProbeGridIrradianceFileName = "Irradiance_lutrgba16.dds"; + static const char* DiffuseProbeGridDistanceFileName = "Distance_lutrg32f.dds"; + static const char* DiffuseProbeGridRelocationFileName = "Relocation_lutrgba16f.dds"; + static const char* DiffuseProbeGridClassificationFileName = "Classification_lutr32f.dds"; + + using DiffuseProbeGridBakeTexturesCallback = AZStd::function; + + struct DiffuseProbeGridBakedTextures + { + // irradiance and distance images can be used directly + Data::Instance m_irradianceImage; + AZStd::string m_irradianceImageRelativePath; + + Data::Instance m_distanceImage; + AZStd::string m_distanceImageRelativePath; + + // relocation and classification images need to be recreated as RW textures + RHI::ImageDescriptor m_relocationImageDescriptor; + AZStd::array_view m_relocationImageData; + AZStd::string m_relocationImageRelativePath; + + RHI::ImageDescriptor m_classificationImageDescriptor; + AZStd::array_view m_classificationImageData; + AZStd::string m_classificationImageRelativePath; + }; + // DiffuseProbeGridFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom class DiffuseProbeGridFeatureProcessorInterface : public RPI::FeatureProcessor @@ -44,6 +98,28 @@ namespace AZ virtual void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) = 0; virtual void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) = 0; virtual void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) = 0; + virtual void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) = 0; + virtual void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) = 0; + + virtual void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + + virtual bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) = 0; + + virtual bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) = 0; + }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp index 5be1303434..a55d8fc78c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,11 @@ namespace AZ { namespace Render { + DiffuseProbeGrid::DiffuseProbeGrid() + : m_textureReadback(this) + { + } + DiffuseProbeGrid::~DiffuseProbeGrid() { m_scene->GetCullingScene()->UnregisterCullable(m_cullable); @@ -166,6 +172,84 @@ namespace AZ m_updateRenderObjectSrg = true; } + void DiffuseProbeGrid::SetMode(DiffuseProbeGridMode mode) + { + // handle auto-select + if (mode == DiffuseProbeGridMode::AutoSelect) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + m_mode = (device->GetFeatures().m_rayTracing) ? DiffuseProbeGridMode::RealTime : DiffuseProbeGridMode::Baked; + } + else + { + m_mode = mode; + } + + m_updateTextures = true; + } + + void DiffuseProbeGrid::SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(bakedTextures.m_irradianceImage.get(), "Invalid Irradiance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_distanceImage.get(), "Invalid Distance image passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_relocationImageData.size() > 0, "Invalid Relocation image data passed to SetBakedTextures"); + AZ_Assert(bakedTextures.m_classificationImageData.size() > 0, "Invalid Classification image data passed to SetBakedTextures"); + + m_bakedIrradianceImage = bakedTextures.m_irradianceImage; + m_bakedDistanceImage = bakedTextures.m_distanceImage; + + m_bakedIrradianceRelativePath = bakedTextures.m_irradianceImageRelativePath; + m_bakedDistanceRelativePath = bakedTextures.m_distanceImageRelativePath; + m_bakedRelocationRelativePath = bakedTextures.m_relocationImageRelativePath; + m_bakedClassificationRelativePath = bakedTextures.m_classificationImageRelativePath; + + m_bakedRelocationImageData.resize(bakedTextures.m_relocationImageData.size()); + memcpy(m_bakedRelocationImageData.data(), bakedTextures.m_relocationImageData.data(), bakedTextures.m_relocationImageData.size()); + + m_bakedClassificationImageData.resize(bakedTextures.m_classificationImageData.size()); + memcpy(m_bakedClassificationImageData.data(), bakedTextures.m_classificationImageData.data(), bakedTextures.m_classificationImageData.size()); + + // create the relocation and distance RW textures now, these are needed for shader compatibility + // (image data is copied in UpdateTextures) + { + m_bakedRelocationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedRelocationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_relocationImageDescriptor.m_size.m_width, + bakedTextures.m_relocationImageDescriptor.m_size.m_height, + bakedTextures.m_relocationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Relocation image"); + } + + { + m_bakedClassificationImage = RHI::Factory::Get().CreateImage(); + RHI::ImageInitRequest initRequest; + initRequest.m_image = m_bakedClassificationImage.get(); + initRequest.m_descriptor = RHI::ImageDescriptor::Create2D( + RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, + bakedTextures.m_classificationImageDescriptor.m_size.m_width, + bakedTextures.m_classificationImageDescriptor.m_size.m_height, + bakedTextures.m_classificationImageDescriptor.m_format); + + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(initRequest); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize Classification image"); + } + + m_updateTextures = true; + } + + bool DiffuseProbeGrid::HasValidBakedTextures() const + { + return m_bakedIrradianceImage.get() && + m_bakedDistanceImage.get() && + m_bakedRelocationImage.get() && + m_bakedClassificationImage.get(); + } + uint32_t DiffuseProbeGrid::GetTotalProbeCount() const { return m_probeCountX * m_probeCountY * m_probeCountZ; @@ -188,83 +272,117 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); - // advance to the next image in the frame image array - m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; - - // probe raytrace - { - uint32_t width = m_numRaysPerProbe; - uint32_t height = GetTotalProbeCount(); - - m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - - RHI::ImageInitRequest request; - request.m_image = m_rayTraceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); - } - uint32_t probeCountX; uint32_t probeCountY; GetTexture2DProbeCount(probeCountX, probeCountY); - // probe irradiance + if (m_mode == DiffuseProbeGridMode::RealTime) { - uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); - uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); + // advance to the next image in the frame image array + m_currentImageIndex = (m_currentImageIndex + 1) % ImageFrameCount; - m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // probe raytrace + { + uint32_t width = m_numRaysPerProbe; + uint32_t height = GetTotalProbeCount(); - RHI::ImageInitRequest request; - request.m_image = m_irradianceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); - RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); - request.m_optimizedClearValue = &clearValue; - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); + m_rayTraceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_rayTraceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RayTraceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRayTraceImage image"); + } + + // probe irradiance + { + uint32_t width = probeCountX * (DefaultNumIrradianceTexels + 2); + uint32_t height = probeCountY * (DefaultNumIrradianceTexels + 2); + + m_irradianceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_irradianceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::IrradianceImageFormat); + RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f); + request.m_optimizedClearValue = &clearValue; + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeIrradianceImage image"); + } + + // probe distance + { + uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); + uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); + + m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_distanceImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); + } + + // probe relocation + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; + + m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_relocationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); + } + + // probe classification + { + uint32_t width = probeCountX; + uint32_t height = probeCountY; + + m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + + RHI::ImageInitRequest request; + request.m_image = m_classificationImage[m_currentImageIndex].get(); + request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); + [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + } } - - // probe distance + else if (m_mode == DiffuseProbeGridMode::Baked && HasValidBakedTextures()) { - uint32_t width = probeCountX * (DefaultNumDistanceTexels + 2); - uint32_t height = probeCountY * (DefaultNumDistanceTexels + 2); + // copy the baked relocation and classification texture data to the RW textures + // (these need to be RW for shader compatibility) + RHI::ImageSubresourceRange range{ 0, 0, 0 ,0 }; + RHI::ImageSubresourceLayoutPlaced layout; - m_distanceImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); + // relocation + { + m_bakedRelocationImage->GetSubresourceLayouts(range, &layout, nullptr); - RHI::ImageInitRequest request; - request.m_image = m_distanceImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::DistanceImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeDistanceImage image"); - } + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedRelocationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedRelocationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } - // probe relocation - { - uint32_t width = probeCountX; - uint32_t height = probeCountY; + // classification + { + m_bakedClassificationImage->GetSubresourceLayouts(range, &layout, nullptr); - m_relocationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - - RHI::ImageInitRequest request; - request.m_image = m_relocationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::RelocationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeRelocationImage image"); - } - - // probe classification - { - uint32_t width = probeCountX; - uint32_t height = probeCountY; - - m_classificationImage[m_currentImageIndex] = RHI::Factory::Get().CreateImage(); - - RHI::ImageInitRequest request; - request.m_image = m_classificationImage[m_currentImageIndex].get(); - request.m_descriptor = RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, width, height, DiffuseProbeGridRenderData::ClassificationImageFormat); - [[maybe_unused]] RHI::ResultCode result = m_renderData->m_imagePool->InitImage(request); - AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize m_probeClassificationImage image"); + RHI::ImageUpdateRequest updateRequest; + updateRequest.m_image = m_bakedClassificationImage.get(); + updateRequest.m_sourceSubresourceLayout = layout; + updateRequest.m_sourceData = m_bakedClassificationImageData.data(); + updateRequest.m_imageSubresourcePixelOffset = RHI::Origin(0, 0, 0); + m_renderData->m_imagePool->UpdateImageContents(updateRequest); + } } m_updateTextures = false; @@ -639,16 +757,16 @@ namespace AZ m_renderObjectSrg->SetConstant(constantIndex, m_ambientMultiplier); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeIrradiance")); - m_renderObjectSrg->SetImageView(imageIndex, m_irradianceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetIrradianceImage()->GetImageView(m_renderData->m_probeIrradianceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeDistance")); - m_renderObjectSrg->SetImageView(imageIndex, m_distanceImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetDistanceImage()->GetImageView(m_renderData->m_probeDistanceImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeOffsets")); - m_renderObjectSrg->SetImageView(imageIndex, m_relocationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetRelocationImage()->GetImageView(m_renderData->m_probeRelocationImageViewDescriptor).get()); imageIndex = srgLayout->FindShaderInputImageIndex(Name("m_probeStates")); - m_renderObjectSrg->SetImageView(imageIndex, m_classificationImage[m_currentImageIndex]->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); + m_renderObjectSrg->SetImageView(imageIndex, GetClassificationImage()->GetImageView(m_renderData->m_probeClassificationImageViewDescriptor).get()); SetGridConstants(m_renderObjectSrg); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h index e1ca2123a5..325cbcb616 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace AZ { @@ -30,7 +31,7 @@ namespace AZ static const RHI::Format IrradianceImageFormat = RHI::Format::R16G16B16A16_UNORM; static const RHI::Format DistanceImageFormat = RHI::Format::R32G32_FLOAT; static const RHI::Format RelocationImageFormat = RHI::Format::R16G16B16A16_FLOAT; - static const RHI::Format ClassificationImageFormat = RHI::Format::R8_UINT; + static const RHI::Format ClassificationImageFormat = RHI::Format::R32_FLOAT; // image pool RHI::Ptr m_imagePool; @@ -61,7 +62,7 @@ namespace AZ class DiffuseProbeGrid final { public: - DiffuseProbeGrid() = default; + DiffuseProbeGrid(); ~DiffuseProbeGrid(); void Init(RPI::Scene* scene, DiffuseProbeGridRenderData* diffuseProbeGridRenderData); @@ -96,6 +97,9 @@ namespace AZ bool GetUseDiffuseIbl() const { return m_useDiffuseIbl; } void SetUseDiffuseIbl(bool useDiffuseIbl) { m_useDiffuseIbl = useDiffuseIbl; } + DiffuseProbeGridMode GetMode() const { return m_mode; } + void SetMode(DiffuseProbeGridMode mode); + uint32_t GetNumRaysPerProbe() const { return m_numRaysPerProbe; } uint32_t GetRemainingRelocationIterations() const { return aznumeric_cast(m_remainingRelocationIterations); } @@ -133,11 +137,16 @@ namespace AZ void UpdateRenderObjectSrg(); // textures - const RHI::Ptr& GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } - const RHI::Ptr& GetIrradianceImage() { return m_irradianceImage[m_currentImageIndex]; } - const RHI::Ptr& GetDistanceImage() { return m_distanceImage[m_currentImageIndex]; } - const RHI::Ptr& GetRelocationImage() { return m_relocationImage[m_currentImageIndex]; } - const RHI::Ptr& GetClassificationImage() { return m_classificationImage[m_currentImageIndex]; } + const RHI::Ptr GetRayTraceImage() { return m_rayTraceImage[m_currentImageIndex]; } + const RHI::Ptr GetIrradianceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_irradianceImage[m_currentImageIndex] : m_bakedIrradianceImage->GetRHIImage(); } + const RHI::Ptr GetDistanceImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_distanceImage[m_currentImageIndex] : m_bakedDistanceImage->GetRHIImage(); } + const RHI::Ptr GetRelocationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_relocationImage[m_currentImageIndex] : m_bakedRelocationImage; } + const RHI::Ptr GetClassificationImage() { return m_mode == DiffuseProbeGridMode::RealTime ? m_classificationImage[m_currentImageIndex] : m_bakedClassificationImage; } + + const AZStd::string& GetBakedIrradianceRelativePath() const { return m_bakedIrradianceRelativePath; } + const AZStd::string& GetBakedDistanceRelativePath() const { return m_bakedDistanceRelativePath; } + const AZStd::string& GetBakedRelocationRelativePath() const { return m_bakedRelocationRelativePath; } + const AZStd::string& GetBakedClassificationRelativePath() const { return m_bakedClassificationRelativePath; } // attachment Ids const RHI::AttachmentId GetRayTraceImageAttachmentId() const { return m_rayTraceImageAttachmentId; } @@ -152,6 +161,12 @@ namespace AZ bool GetIrradianceClearRequired() const { return m_irradianceClearRequired; } void ResetIrradianceClearRequired() { m_irradianceClearRequired = false; } + // texture readback + DiffuseProbeGridTextureReadback& GetTextureReadback() { return m_textureReadback; } + + void SetBakedTextures(const DiffuseProbeGridBakedTextures& bakedTextures); + bool HasValidBakedTextures() const; + static constexpr uint32_t DefaultNumIrradianceTexels = 6; static constexpr uint32_t DefaultNumDistanceTexels = 14; static constexpr int32_t DefaultNumRelocationIterations = 100; @@ -221,7 +236,10 @@ namespace AZ // culling RPI::Cullable m_cullable; - // textures + // grid mode (RealTime or Baked) + DiffuseProbeGridMode m_mode = DiffuseProbeGridMode::RealTime; + + // real-time textures static const uint32_t MaxTextureDimension = 8192; static const uint32_t ImageFrameCount = 3; RHI::Ptr m_rayTraceImage[ImageFrameCount]; @@ -233,6 +251,25 @@ namespace AZ bool m_updateTextures = false; bool m_irradianceClearRequired = true; + // baked textures + Data::Instance m_bakedIrradianceImage; + Data::Instance m_bakedDistanceImage; + RHI::Ptr m_bakedRelocationImage; + RHI::Ptr m_bakedClassificationImage; + + // baked texture relative paths + AZStd::string m_bakedIrradianceRelativePath; + AZStd::string m_bakedDistanceRelativePath; + AZStd::string m_bakedRelocationRelativePath; + AZStd::string m_bakedClassificationRelativePath; + + // baked texture data (only needed for the relocation and classification textures) + AZStd::vector m_bakedRelocationImageData; + AZStd::vector m_bakedClassificationImageData; + + // texture readback + DiffuseProbeGridTextureReadback m_textureReadback; + // Srgs Data::Instance m_rayTraceSrg; Data::Instance m_blendIrradianceSrg; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp index 3df13556d3..2a06dacf3f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendDistanceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp index 4e05b8ef31..4818018ea3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp @@ -87,7 +87,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -111,7 +111,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -150,7 +150,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -167,7 +167,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetBlendIrradianceSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp index 59549de331..8821de8a9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp @@ -100,7 +100,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -124,7 +124,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe irradiance image { @@ -153,7 +153,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -173,7 +173,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t probeCountX; uint32_t probeCountY; diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp index db85914cee..65f1c2dd5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -115,7 +115,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -143,7 +143,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -159,7 +159,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetClassificationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp index 1aaa06c797..11d7dc0385 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp @@ -45,6 +45,7 @@ namespace AZ RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get(); m_diffuseProbeGrids.reserve(InitialProbeGridAllocationSize); + m_realTimeDiffuseProbeGrids.reserve(InitialProbeGridAllocationSize); RHI::BufferPoolDescriptor desc; desc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device; @@ -61,7 +62,7 @@ namespace AZ // image pool { RHI::ImagePoolDescriptor imagePoolDesc; - imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite; + imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead; m_probeGridRenderData.m_imagePool = RHI::Factory::Get().CreateImagePool(); [[maybe_unused]] RHI::ResultCode result = m_probeGridRenderData.m_imagePool->Init(*rhiSystem->GetDevice(), imagePoolDesc); @@ -123,6 +124,32 @@ namespace AZ m_needUpdatePipelineStates = false; } + // check pending textures and connect bus for notifications + for (auto& notificationEntry : m_notifyTextureAssets) + { + if (notificationEntry.m_assetId.IsValid()) + { + // asset already has an assetId + continue; + } + + // query for the assetId + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + notificationEntry.m_relativePath.c_str(), + azrtti_typeid(), + false); + + if (assetId.IsValid()) + { + notificationEntry.m_assetId = assetId; + notificationEntry.m_asset.Create(assetId, true); + Data::AssetBus::MultiHandler::BusConnect(assetId); + } + } + // if the volumes changed we need to re-sort the probe list if (m_probeGridSortRequired) { @@ -139,6 +166,7 @@ namespace AZ }; AZStd::sort(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), sortFn); + AZStd::sort(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), sortFn); m_probeGridSortRequired = false; } @@ -160,6 +188,9 @@ namespace AZ diffuseProbeGrid->SetExtents(extents); diffuseProbeGrid->SetProbeSpacing(probeSpacing); m_diffuseProbeGrids.push_back(diffuseProbeGrid); + + UpdateRealTimeList(diffuseProbeGrid); + m_probeGridSortRequired = true; return diffuseProbeGrid; @@ -169,6 +200,7 @@ namespace AZ { AZ_Assert(probeGrid.get(), "RemoveProbeGrid called with an invalid handle"); + // remove from main list auto itEntry = AZStd::find_if(m_diffuseProbeGrids.begin(), m_diffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) { return (entry == probeGrid); @@ -176,6 +208,18 @@ namespace AZ AZ_Assert(itEntry != m_diffuseProbeGrids.end(), "RemoveProbeGrid called with a probe grid that is not in the probe list"); m_diffuseProbeGrids.erase(itEntry); + + // remove from side list of real-time grids + itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == probeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + probeGrid = nullptr; } @@ -247,6 +291,133 @@ namespace AZ probeGrid->SetUseDiffuseIbl(useDiffuseIbl); } + void DiffuseProbeGridFeatureProcessor::BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + AZ_Assert(probeGrid.get(), "BakeTextures called with an invalid handle"); + + AddNotificationEntry(irradianceTextureRelativePath); + AddNotificationEntry(distanceTextureRelativePath); + AddNotificationEntry(relocationTextureRelativePath); + AddNotificationEntry(classificationTextureRelativePath); + + probeGrid->GetTextureReadback().BeginTextureReadback(callback); + } + + void DiffuseProbeGridFeatureProcessor::UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid) + { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::RealTime) + { + // add to side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry == m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.push_back(diffuseProbeGrid); + } + } + else + { + // remove from side list of real-time grids + auto itEntry = AZStd::find_if(m_realTimeDiffuseProbeGrids.begin(), m_realTimeDiffuseProbeGrids.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == diffuseProbeGrid); + }); + + if (itEntry != m_realTimeDiffuseProbeGrids.end()) + { + m_realTimeDiffuseProbeGrids.erase(itEntry); + } + } + } + + void DiffuseProbeGridFeatureProcessor::AddNotificationEntry(const AZStd::string& relativePath) + { + AZStd::string assetPath = relativePath + ".streamingimage"; + + // check to see if this is an existing asset + AZ::Data::AssetId assetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, + &AZ::Data::AssetCatalogRequests::GetAssetIdByPath, + assetPath.c_str(), + azrtti_typeid(), + false); + + // we only track notifications for new texture assets, existing assets are automatically reloaded by the RPI + if (!assetId.IsValid()) + { + m_notifyTextureAssets.push_back({ assetPath, assetId }); + } + } + + bool DiffuseProbeGridFeatureProcessor::CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_relativePath == relativePath) + { + outNotificationType = itNotification->m_notificationType; + if (outNotificationType != DiffuseProbeGridTextureNotificationType::None) + { + outTextureAsset = itNotification->m_asset; + m_notifyTextureAssets.erase(itNotification); + } + + return true; + } + } + + return false; + } + + bool DiffuseProbeGridFeatureProcessor::AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) + { + for (auto& diffuseProbeGrid : m_diffuseProbeGrids) + { + if ((diffuseProbeGrid->GetBakedIrradianceRelativePath() == irradianceTextureRelativePath) || + (diffuseProbeGrid->GetBakedDistanceRelativePath() == distanceTextureRelativePath) || + (diffuseProbeGrid->GetBakedRelocationRelativePath() == relocationTextureRelativePath) || + (diffuseProbeGrid->GetBakedClassificationRelativePath() == classificationTextureRelativePath)) + { + return true; + } + } + + return false; + } + + void DiffuseProbeGridFeatureProcessor::SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) + { + AZ_Assert(probeGrid.get(), "SetMode called with an invalid handle"); + probeGrid->SetMode(mode); + + UpdateRealTimeList(probeGrid); + + m_probeGridSortRequired = true; + } + + void DiffuseProbeGridFeatureProcessor::SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) + { + AZ_Assert(probeGrid.get(), "SetBakedTextures called with an invalid handle"); + probeGrid->SetBakedTextures(bakedTextures); + } + void DiffuseProbeGridFeatureProcessor::CreateBoxMesh() { // vertex positions @@ -418,5 +589,34 @@ namespace AZ } } + void DiffuseProbeGridFeatureProcessor::HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType) + { + for (NotifyTextureAssetVector::iterator itNotification = m_notifyTextureAssets.begin(); itNotification != m_notifyTextureAssets.end(); ++itNotification) + { + if (itNotification->m_assetId == asset.GetId()) + { + // store the texture asset + itNotification->m_asset = Data::static_pointer_cast(asset); + itNotification->m_notificationType = notificationType; + + // stop notifications on this asset + Data::AssetBus::MultiHandler::BusDisconnect(itNotification->m_assetId); + + break; + } + } + } + + void DiffuseProbeGridFeatureProcessor::OnAssetReady(Data::Asset asset) + { + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Ready); + } + + void DiffuseProbeGridFeatureProcessor::OnAssetError(Data::Asset asset) + { + AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to load cubemap [%s]", asset.GetHint().c_str()); + + HandleAssetNotification(asset, DiffuseProbeGridTextureNotificationType::Error); + } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h index ad36f8aafa..19e9bf1b1d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h @@ -22,6 +22,7 @@ namespace AZ //! This class manages DiffuseProbeGrids which generate diffuse global illumination class DiffuseProbeGridFeatureProcessor final : public DiffuseProbeGridFeatureProcessorInterface + , private Data::AssetBus::MultiHandler { public: AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessor, "{BCD232F9-1EBF-4D0D-A5F4-84AEC933A93C}", DiffuseProbeGridFeatureProcessorInterface); @@ -46,6 +47,27 @@ namespace AZ void Enable(const DiffuseProbeGridHandle& probeGrid, bool enable) override; void SetGIShadows(const DiffuseProbeGridHandle& probeGrid, bool giShadows) override; void SetUseDiffuseIbl(const DiffuseProbeGridHandle& probeGrid, bool useDiffuseIbl) override; + void SetMode(const DiffuseProbeGridHandle& probeGrid, DiffuseProbeGridMode mode) override; + void SetBakedTextures(const DiffuseProbeGridHandle& probeGrid, const DiffuseProbeGridBakedTextures& bakedTextures) override; + + void BakeTextures( + const DiffuseProbeGridHandle& probeGrid, + DiffuseProbeGridBakeTexturesCallback callback, + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; + + bool CheckTextureAssetNotification( + const AZStd::string& relativePath, + Data::Asset& outTextureAsset, + DiffuseProbeGridTextureNotificationType& outNotificationType) override; + + bool AreBakedTexturesReferenced( + const AZStd::string& irradianceTextureRelativePath, + const AZStd::string& distanceTextureRelativePath, + const AZStd::string& relocationTextureRelativePath, + const AZStd::string& classificationTextureRelativePath) override; // FeatureProcessor overrides void Activate() override; @@ -56,12 +78,28 @@ namespace AZ using DiffuseProbeGridVector = AZStd::vector>; DiffuseProbeGridVector& GetProbeGrids() { return m_diffuseProbeGrids; } + // retrieve the side list of probe grids that are using real-time (raytraced) mode + DiffuseProbeGridVector& GetRealTimeProbeGrids() { return m_realTimeDiffuseProbeGrids; } + private: AZ_DISABLE_COPY_MOVE(DiffuseProbeGridFeatureProcessor); // create the box vertex and index streams, which are used to render the probe volumes void CreateBoxMesh(); + // AssetBus::MultiHandler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + // updates the real-time list for a specific probe grid + void UpdateRealTimeList(const DiffuseProbeGridHandle& diffuseProbeGrid); + + // adds a notification entry for a new asset + void AddNotificationEntry(const AZStd::string& relativePath); + + // notifies and removes the notification entry + void HandleAssetNotification(Data::Asset asset, DiffuseProbeGridTextureNotificationType notificationType); + // RPI::SceneNotificationBus::Handler overrides void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; @@ -70,10 +108,13 @@ namespace AZ void UpdatePipelineStates(); void UpdatePasses(); - // list of diffuse probe grids + // list of all diffuse probe grids const size_t InitialProbeGridAllocationSize = 64; DiffuseProbeGridVector m_diffuseProbeGrids; + // side list of diffuse probe grids that are in real-time mode (subset of m_diffuseProbeGrids) + DiffuseProbeGridVector m_realTimeDiffuseProbeGrids; + // position structure for the box vertices struct Position { @@ -102,6 +143,17 @@ namespace AZ // indicates the the diffuse probe grid render pipeline state needs to be updated bool m_needUpdatePipelineStates = false; + + // list of texture assets that we need to check during Simulate() to see if they are ready + struct NotifyTextureAssetEntry + { + AZStd::string m_relativePath; + AZ::Data::AssetId m_assetId; + Data::Asset m_asset; + DiffuseProbeGridTextureNotificationType m_notificationType = DiffuseProbeGridTextureNotificationType::None; + }; + typedef AZStd::vector NotifyTextureAssetVector; + NotifyTextureAssetVector m_notifyTextureAssets; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp index 143ae1a08c..1062bedae3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp @@ -132,7 +132,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -210,10 +210,10 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); - frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetProbeGrids().size())); + frameGraph.SetEstimatedItemCount(aznumeric_cast(diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().size())); frameGraph.ExecuteAfter(m_rayTracingScopeProducerShaderTable->GetScopeId()); - for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // TLAS { @@ -318,7 +318,7 @@ namespace AZ rayTracingFeatureProcessor->GetMeshInfoBuffer() && rayTracingFeatureProcessor->GetSubMeshCount()) { - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader // inputs (see line ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -341,7 +341,7 @@ namespace AZ m_rayTracingShaderTable) { // submit the DispatchRaysItem for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroups[] = { diffuseProbeGrid->GetRayTraceSrg()->GetRHIShaderResourceGroup(), diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp index 2bd6595b71..86a26f002d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp @@ -91,7 +91,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty()) + if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids().empty()) { // no diffuse probe grids return; @@ -108,7 +108,7 @@ namespace AZ // create the Relocation Srgs for each DiffuseProbeGrid, and check to see if any grids need relocation bool needRelocation = false; - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { uint32_t rayTracingDataRevision = rayTracingFeatureProcessor->GetRevision(); if (rayTracingDataRevision != m_rayTracingDataRevision) @@ -139,7 +139,7 @@ namespace AZ RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // probe raytrace image { @@ -167,7 +167,7 @@ namespace AZ { RPI::Scene* scene = m_pipeline->GetScene(); DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() in ShaderResourceGroupData.cpp) @@ -187,7 +187,7 @@ namespace AZ DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor(); // submit the DispatchItems for each DiffuseProbeGrid - for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) { const RHI::ShaderResourceGroup* shaderResourceGroup = diffuseProbeGrid->GetRelocationSrg()->GetRHIShaderResourceGroup(); commandList->SetShaderResourceGroupForDispatch(*shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp index af6fce6f6a..4f9221a65f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp @@ -79,6 +79,12 @@ namespace AZ params.m_scissorState = scissor; Base::FrameBeginInternal(params); + + for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetRealTimeProbeGrids()) + { + // process attachment readback + diffuseProbeGrid->GetTextureReadback().FrameBegin(params); + } } void DiffuseProbeGridRenderPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) @@ -88,8 +94,21 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // probe irradiance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the irradiance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetIrradianceImageAttachmentId(), diffuseProbeGrid->GetIrradianceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeIrradianceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetIrradianceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeIrradianceImageViewDescriptor; @@ -100,6 +119,13 @@ namespace AZ // probe distance image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the distance image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetDistanceImageAttachmentId(), diffuseProbeGrid->GetDistanceImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeDistanceImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetDistanceImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeDistanceImageViewDescriptor; @@ -110,6 +136,13 @@ namespace AZ // probe relocation image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the relocation image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetRelocationImageAttachmentId(), diffuseProbeGrid->GetRelocationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeRelocationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetRelocationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeRelocationImageViewDescriptor; @@ -120,6 +153,13 @@ namespace AZ // probe classification image { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked) + { + // import the classification image now, since it is baked and therefore was not imported during the raytracing pass + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportImage(diffuseProbeGrid->GetClassificationImageAttachmentId(), diffuseProbeGrid->GetClassificationImage()); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import probeClassificationImage"); + } + RHI::ImageScopeAttachmentDescriptor desc; desc.m_attachmentId = diffuseProbeGrid->GetClassificationImageAttachmentId(); desc.m_imageViewDescriptor = diffuseProbeGrid->GetRenderData()->m_probeClassificationImageViewDescriptor; @@ -127,6 +167,8 @@ namespace AZ frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite); } + + diffuseProbeGrid->GetTextureReadback().Update(GetName()); } Base::SetupFrameGraphDependencies(frameGraph); @@ -139,6 +181,12 @@ namespace AZ for (auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids()) { + if (diffuseProbeGrid->GetMode() == DiffuseProbeGridMode::Baked && + !diffuseProbeGrid->HasValidBakedTextures()) + { + continue; + } + // the diffuse probe grid Srg must be updated in the Compile phase in order to successfully bind the ReadWrite shader inputs // (see ValidateSetImageView() of ShaderResourceGroupData.cpp) diffuseProbeGrid->UpdateRenderObjectSrg(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp new file mode 100644 index 0000000000..b7fa9c1630 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp @@ -0,0 +1,134 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + DiffuseProbeGridTextureReadback::DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid) + : m_diffuseProbeGrid(diffuseProbeGrid) + { + } + + void DiffuseProbeGridTextureReadback::BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback) + { + AZ_Assert(m_readbackState == DiffuseProbeGridReadbackState::Idle, "DiffuseProbeGridTextureReadback is already processing a readback request"); + + m_callback = callback; + m_readbackState = DiffuseProbeGridReadbackState::Irradiance; + } + + void DiffuseProbeGridTextureReadback::Update(const AZ::Name& passName) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle || m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + return; + } + + if (m_attachmentReadback.get() && m_attachmentReadback->GetReadbackState() > RPI::AttachmentReadback::ReadbackState::Idle) + { + // still processing previous request + return; + } + + AZStd::string readbackName = AZStd::string::format("DiffuseProbeGridReadback_%s", passName.GetCStr()); + RHI::ImageDescriptor descriptor; + RHI::AttachmentId attachmentId; + RPI::AttachmentReadback::CallbackFunction callbackFunction; + + switch (m_readbackState) + { + case DiffuseProbeGridReadbackState::Irradiance: + descriptor = m_diffuseProbeGrid->GetIrradianceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetIrradianceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_irradianceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Distance; + }; + break; + case DiffuseProbeGridReadbackState::Distance: + descriptor = m_diffuseProbeGrid->GetDistanceImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetDistanceImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_distanceReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Relocation; + }; + break; + case DiffuseProbeGridReadbackState::Relocation: + descriptor = m_diffuseProbeGrid->GetRelocationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetRelocationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_relocationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Classification; + }; + break; + case DiffuseProbeGridReadbackState::Classification: + descriptor = m_diffuseProbeGrid->GetClassificationImage()->GetDescriptor(); + attachmentId = m_diffuseProbeGrid->GetClassificationImageAttachmentId(); + callbackFunction = [this](const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) + { + m_classificationReadbackResult = readbackResult; + m_readbackState = DiffuseProbeGridReadbackState::Complete; + }; + break; + default: + AZ_Assert(false, "Unknown readback state"); + } + + m_attachmentReadback = AZStd::make_shared(AZ::RHI::ScopeId{ "DiffuseProbeGridTextureReadBack" }); + m_attachmentReadback->SetCallback(callbackFunction); + + AZ::RPI::PassAttachment passAttachment; + passAttachment.m_descriptor = descriptor; + passAttachment.m_path = attachmentId; + passAttachment.m_name = readbackName; + passAttachment.m_lifetime = RHI::AttachmentLifetimeType::Imported; + + m_attachmentReadback->ReadPassAttachment(&passAttachment, AZ::Name(readbackName)); + } + + void DiffuseProbeGridTextureReadback::FrameBegin(AZ::RPI::Pass::FramePrepareParams& params) + { + if (m_readbackState == DiffuseProbeGridReadbackState::Idle) + { + return; + } + + if (!m_attachmentReadback.get()) + { + return; + } + + if (m_readbackState == DiffuseProbeGridReadbackState::Complete) + { + // readback of all textures is complete, invoke callback and return to Idle state + m_callback( + { m_irradianceReadbackResult.m_dataBuffer, m_irradianceReadbackResult.m_imageDescriptor.m_format, m_irradianceReadbackResult.m_imageDescriptor.m_size }, + { m_distanceReadbackResult.m_dataBuffer, m_distanceReadbackResult.m_imageDescriptor.m_format, m_distanceReadbackResult.m_imageDescriptor.m_size }, + { m_relocationReadbackResult.m_dataBuffer, m_relocationReadbackResult.m_imageDescriptor.m_format, m_relocationReadbackResult.m_imageDescriptor.m_size }, + { m_classificationReadbackResult.m_dataBuffer, m_classificationReadbackResult.m_imageDescriptor.m_format, m_classificationReadbackResult.m_imageDescriptor.m_size }); + + m_readbackState = DiffuseProbeGridReadbackState::Idle; + m_attachmentReadback.reset(); + return; + } + + m_attachmentReadback->FrameBegin(params); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h new file mode 100644 index 0000000000..1becd6fb3e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h @@ -0,0 +1,60 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseProbeGrid; + + enum class DiffuseProbeGridReadbackState + { + Idle, + Irradiance, + Distance, + Relocation, + Classification, + Complete + }; + + //! This class contains functionality necessary to read back the DiffuseProbeGrid textures, which + //! allows them to be saved as assets to run the DiffuseProbeGrid in non-realtime mode. + class DiffuseProbeGridTextureReadback final + { + public: + DiffuseProbeGridTextureReadback(DiffuseProbeGrid* diffuseProbeGrid); + ~DiffuseProbeGridTextureReadback() = default; + + void BeginTextureReadback(DiffuseProbeGridBakeTexturesCallback callback); + void Update(const AZ::Name& passName); + void FrameBegin(AZ::RPI::Pass::FramePrepareParams& params); + + private: + + DiffuseProbeGrid* m_diffuseProbeGrid = nullptr; + DiffuseProbeGridReadbackState m_readbackState = DiffuseProbeGridReadbackState::Idle; + AZStd::shared_ptr m_attachmentReadback; + DiffuseProbeGridBakeTexturesCallback m_callback; + + AZ::RPI::AttachmentReadback::ReadbackResult m_irradianceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_distanceReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_relocationReadbackResult; + AZ::RPI::AttachmentReadback::ReadbackResult m_classificationReadbackResult; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 8926b0c19f..46a4e06ac2 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -135,6 +135,8 @@ set(FILES Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp Source/DiffuseProbeGrid/DiffuseProbeGrid.h + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp index 0ddace1f87..1ec09995fd 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp @@ -44,7 +44,17 @@ namespace AZ ->Field("AmbientMultiplier", &DiffuseProbeGridComponentConfig::m_ambientMultiplier) ->Field("ViewBias", &DiffuseProbeGridComponentConfig::m_viewBias) ->Field("NormalBias", &DiffuseProbeGridComponentConfig::m_normalBias) - ; + ->Field("EditorMode", &DiffuseProbeGridComponentConfig::m_editorMode) + ->Field("RuntimeMode", &DiffuseProbeGridComponentConfig::m_runtimeMode) + ->Field("BakedIrradianceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureRelativePath) + ->Field("BakedDistanceTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureRelativePath) + ->Field("BakedRelocationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureRelativePath) + ->Field("BakedClassificationTextureRelativePath", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureRelativePath) + ->Field("BakedIrradianceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedIrradianceTextureAsset) + ->Field("BakedDistanceTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedDistanceTextureAsset) + ->Field("BakedRelocationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedRelocationTextureAsset) + ->Field("BakedClassificationTextureAsset", &DiffuseProbeGridComponentConfig::m_bakedClassificationTextureAsset) + ; } } @@ -110,6 +120,26 @@ namespace AZ m_boxShapeInterface = LmbrCentral::BoxShapeComponentRequestsBus::FindFirstHandler(m_entityId); AZ_Assert(m_boxShapeInterface, "DiffuseProbeGridComponentController was unable to find box shape component"); + // special handling is required if this component is being cloned in the editor: + // check to see if the baked textures are already referenced by another DiffuseProbeGrid + if (m_featureProcessor->AreBakedTexturesReferenced( + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath)) + { + // clear the baked texture paths and assets + m_configuration.m_bakedIrradianceTextureRelativePath.clear(); + m_configuration.m_bakedDistanceTextureRelativePath.clear(); + m_configuration.m_bakedRelocationTextureRelativePath.clear(); + m_configuration.m_bakedClassificationTextureRelativePath.clear(); + + m_configuration.m_bakedIrradianceTextureAsset.Reset(); + m_configuration.m_bakedDistanceTextureAsset.Reset(); + m_configuration.m_bakedRelocationTextureAsset.Reset(); + m_configuration.m_bakedClassificationTextureAsset.Reset(); + } + // add this diffuse probe grid to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddProbeGrid(transform, m_configuration.m_extents, m_configuration.m_probeSpacing); @@ -118,11 +148,61 @@ namespace AZ m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); + // load the baked texture assets, but only if they are all valid + if (m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() && + m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() && + m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + m_configuration.m_bakedIrradianceTextureAsset.QueueLoad(); + m_configuration.m_bakedDistanceTextureAsset.QueueLoad(); + m_configuration.m_bakedRelocationTextureAsset.QueueLoad(); + m_configuration.m_bakedClassificationTextureAsset.QueueLoad(); + } + else if (m_configuration.m_runtimeMode == DiffuseProbeGridMode::Baked || + m_configuration.m_runtimeMode == DiffuseProbeGridMode::AutoSelect || + m_configuration.m_editorMode == DiffuseProbeGridMode::Baked || + m_configuration.m_editorMode == DiffuseProbeGridMode::AutoSelect) + { + AZ_Error("DiffuseProbeGrid", false, "DiffuseProbeGrid mdoe is set to Baked or Auto-Select, but it does not have baked texture assets. Please re-bake this DiffuseProbeGrid."); + } + + m_featureProcessor->SetMode(m_handle, m_configuration.m_runtimeMode); + // set box shape component dimensions from the configuration // this will invoke the OnShapeChanged() handler and set the outer extents on the feature processor m_boxShapeInterface->SetBoxDimensions(m_configuration.m_extents); } + void DiffuseProbeGridComponentController::OnAssetReady(Data::Asset asset) + { + // if all assets are ready we can set the baked texture images + if (m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_configuration.m_bakedRelocationTextureAsset.IsReady() && + m_configuration.m_bakedClassificationTextureAsset.IsReady()) + { + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedIrradianceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedDistanceTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedRelocationTextureAsset.GetId()); + Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_bakedClassificationTextureAsset.GetId()); + + UpdateBakedTextures(); + } + } + + void DiffuseProbeGridComponentController::OnAssetError(Data::Asset asset) + { + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + AZ_Error("DiffuseProbeGrid", false, "Failed to load baked texture [%s], please re-bake this DiffuseProbeGrid.", asset.GetId().ToString().c_str()); + } + void DiffuseProbeGridComponentController::Deactivate() { if (m_featureProcessor) @@ -212,20 +292,96 @@ namespace AZ void DiffuseProbeGridComponentController::SetAmbientMultiplier(float ambientMultiplier) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_ambientMultiplier = ambientMultiplier; m_featureProcessor->SetAmbientMultiplier(m_handle, m_configuration.m_ambientMultiplier); } void DiffuseProbeGridComponentController::SetViewBias(float viewBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_viewBias = viewBias; m_featureProcessor->SetViewBias(m_handle, m_configuration.m_viewBias); } void DiffuseProbeGridComponentController::SetNormalBias(float normalBias) { + if (!m_featureProcessor) + { + return; + } + m_configuration.m_normalBias = normalBias; m_featureProcessor->SetNormalBias(m_handle, m_configuration.m_normalBias); } + + void DiffuseProbeGridComponentController::SetEditorMode(DiffuseProbeGridMode editorMode) + { + if (!m_featureProcessor) + { + return; + } + + // update the configuration and change the DiffuseProbeGrid mode + m_configuration.m_editorMode = editorMode; + m_featureProcessor->SetMode(m_handle, m_configuration.m_editorMode); + } + + void DiffuseProbeGridComponentController::SetRuntimeMode(DiffuseProbeGridMode runtimeMode) + { + if (!m_featureProcessor) + { + return; + } + + // only update the configuration + m_configuration.m_runtimeMode = runtimeMode; + } + + void DiffuseProbeGridComponentController::BakeTextures(DiffuseProbeGridBakeTexturesCallback callback) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->BakeTextures( + m_handle, + callback, + m_configuration.m_bakedIrradianceTextureRelativePath, + m_configuration.m_bakedDistanceTextureRelativePath, + m_configuration.m_bakedRelocationTextureRelativePath, + m_configuration.m_bakedClassificationTextureRelativePath); + } + + void DiffuseProbeGridComponentController::UpdateBakedTextures() + { + if (!m_featureProcessor) + { + return; + } + + DiffuseProbeGridBakedTextures bakedTextures; + bakedTextures.m_irradianceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedIrradianceTextureAsset); + bakedTextures.m_irradianceImageRelativePath = m_configuration.m_bakedIrradianceTextureRelativePath; + bakedTextures.m_distanceImage = RPI::StreamingImage::FindOrCreate(m_configuration.m_bakedDistanceTextureAsset); + bakedTextures.m_distanceImageRelativePath = m_configuration.m_bakedDistanceTextureRelativePath; + bakedTextures.m_relocationImageDescriptor = m_configuration.m_bakedRelocationTextureAsset->GetImageDescriptor(); + bakedTextures.m_relocationImageData = m_configuration.m_bakedRelocationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_relocationImageRelativePath = m_configuration.m_bakedRelocationTextureRelativePath; + bakedTextures.m_classificationImageDescriptor = m_configuration.m_bakedClassificationTextureAsset->GetImageDescriptor(); + bakedTextures.m_classificationImageData = m_configuration.m_bakedClassificationTextureAsset->GetSubImageData(0, 0); + bakedTextures.m_classificationImageRelativePath = m_configuration.m_bakedClassificationTextureRelativePath; + + m_featureProcessor->SetBakedTextures(m_handle, bakedTextures); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h index 2bcb4132e9..4122a07ba2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h @@ -39,6 +39,19 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + AZStd::string m_bakedIrradianceTextureRelativePath; + AZStd::string m_bakedDistanceTextureRelativePath; + AZStd::string m_bakedRelocationTextureRelativePath; + AZStd::string m_bakedClassificationTextureRelativePath; + + Data::Asset m_bakedIrradianceTextureAsset; + Data::Asset m_bakedDistanceTextureAsset; + Data::Asset m_bakedRelocationTextureAsset; + Data::Asset m_bakedClassificationTextureAsset; }; class DiffuseProbeGridComponentController final @@ -79,12 +92,24 @@ namespace AZ // ShapeComponentNotificationsBus overrides void OnShapeChanged(ShapeChangeReasons changeReason) override; + // AssetBus overrides + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + // Property handlers bool ValidateProbeSpacing(const AZ::Vector3& newSpacing); void SetProbeSpacing(const AZ::Vector3& probeSpacing); void SetAmbientMultiplier(float ambientMultiplier); void SetViewBias(float viewBias); void SetNormalBias(float normalBias); + void SetEditorMode(DiffuseProbeGridMode editorMode); + void SetRuntimeMode(DiffuseProbeGridMode runtimeMode); + + // Bake the diffuse probe grid textures to assets + void BakeTextures(DiffuseProbeGridBakeTexturesCallback callback); + + // Update the baked texture assets from the configuration + void UpdateBakedTextures(); // box shape component, used for defining the outer extents of the probe area LmbrCentral::BoxShapeComponentRequests* m_boxShapeInterface = nullptr; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp index ae22ec06c9..162740fe1e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp @@ -16,6 +16,15 @@ #include #include #include +#include +#include +#include + +AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT +#include +#include +#include +AZ_POP_DISABLE_WARNING namespace AZ { @@ -35,7 +44,9 @@ namespace AZ ->Field("ambientMultiplier", &EditorDiffuseProbeGridComponent::m_ambientMultiplier) ->Field("viewBias", &EditorDiffuseProbeGridComponent::m_viewBias) ->Field("normalBias", &EditorDiffuseProbeGridComponent::m_normalBias) - ; + ->Field("editorMode", &EditorDiffuseProbeGridComponent::m_editorMode) + ->Field("runtimeMode", &EditorDiffuseProbeGridComponent::m_runtimeMode) + ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { @@ -48,25 +59,26 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo::Uuid()) - ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing") + ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing (meters between probes)") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X", "Probe spacing on the X-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y", "Probe spacing on the Y-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z", "Probe spacing on the Z-axis, in meters") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged) ->ClassElement(AZ::Edit::ClassElements::Group, "Grid Settings") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_ambientMultiplier, "Ambient Multiplier", "Multiplier for the irradiance intensity") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged) - ->Attribute(Edit::Attributes::Decimals, 0) - ->Attribute(Edit::Attributes::Step, 1.0f) + ->Attribute(Edit::Attributes::Decimals, 1) + ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 10.0f) ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_viewBias, "View Bias", "View bias adjustment") @@ -81,6 +93,27 @@ namespace AZ ->Attribute(Edit::Attributes::Step, 0.1f) ->Attribute(Edit::Attributes::Min, 0.0f) ->Attribute(Edit::Attributes::Max, 1.0f) + ->ClassElement(AZ::Edit::ClassElements::EditorData, "Grid mode") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_editorMode, "Editor Mode", "Controls whether the editor uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEditorModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_runtimeMode, "Runtime Mode", "Controls whether the runtime uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available") + ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnRuntimeModeChanged) + ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)") + ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked") + ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select") + ->ClassElement(AZ::Edit::ClassElements::Group, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Textures", "Bake the Diffuse Probe Grid textures to static assets that will be used when the mode is set to Baked") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Textures") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting) ; editContext->Class( @@ -90,12 +123,6 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseProbeGridComponentController::m_configuration, "Configuration", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; - - editContext->Class( - "DiffuseProbeGridComponentConfig", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; } } @@ -121,15 +148,73 @@ namespace AZ BaseClass::Activate(); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId()); + AZ::TickBus::Handler::BusConnect(); + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect(); } void EditorDiffuseProbeGridComponent::Deactivate() { + AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect(); + AZ::TickBus::Handler::BusDisconnect(); AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); BaseClass::Deactivate(); } + void EditorDiffuseProbeGridComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + if (!m_controller.m_featureProcessor) + { + return; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // set the editor mode, which will override the runtime mode set by the controller + if (!m_editorModeSet) + { + m_controller.m_featureProcessor->SetMode(m_controller.m_handle, configuration.m_editorMode); + m_editorModeSet = true; + } + + CheckTextureAssetNotification(configuration.m_bakedIrradianceTextureRelativePath, configuration.m_bakedIrradianceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedDistanceTextureRelativePath, configuration.m_bakedDistanceTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedRelocationTextureRelativePath, configuration.m_bakedRelocationTextureAsset); + CheckTextureAssetNotification(configuration.m_bakedClassificationTextureRelativePath, configuration.m_bakedClassificationTextureAsset); + } + + void EditorDiffuseProbeGridComponent::CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset) + { + Data::Asset textureAsset; + DiffuseProbeGridTextureNotificationType notificationType = DiffuseProbeGridTextureNotificationType::None; + if (m_controller.m_featureProcessor->CheckTextureAssetNotification(relativePath + ".streamingimage", textureAsset, notificationType)) + { + if (notificationType == DiffuseProbeGridTextureNotificationType::Ready) + { + // bake is complete, update configuration with the new baked texture asset + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake"); + configurationAsset = { textureAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + SetDirty(); + + if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedDistanceTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedClassificationTextureAsset.IsReady() && + m_controller.m_configuration.m_bakedRelocationTextureAsset.IsReady()) + { + m_controller.UpdateBakedTextures(); + } + } + else if (notificationType == DiffuseProbeGridTextureNotificationType::Error) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Diffuse Probe Grid texture failed to bake, please check the Asset Processor for more information.", + QMessageBox::Ok); + } + } + } + AZ::Aabb EditorDiffuseProbeGridComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) { return m_controller.GetAabb(); @@ -140,11 +225,19 @@ namespace AZ return false; } + void EditorDiffuseProbeGridComponent::OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) + { + if ((GetEntityId() == entityId) && !visible) + { + m_editorModeSet = false; + } + } + AZ::Outcome EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingX = *(reinterpret_cast(newValue)); @@ -152,7 +245,7 @@ namespace AZ Vector3 newSpacing(newProbeSpacingX, m_probeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -162,7 +255,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingY = *(reinterpret_cast(newValue)); @@ -170,7 +263,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, newProbeSpacingY, m_probeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -180,7 +273,7 @@ namespace AZ { if (!m_controller.m_featureProcessor) { - return AZ::Failure(AZStd::string("Unable to adjust probe spacing, please try again")); + return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing.")); } float newProbeSpacingZ = *(reinterpret_cast(newValue)); @@ -188,7 +281,7 @@ namespace AZ Vector3 newSpacing(m_probeSpacingX, m_probeSpacingY, newProbeSpacingZ); if (!m_controller.ValidateProbeSpacing(newSpacing)) { - return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents")); + return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents.")); } return AZ::Success(); @@ -218,5 +311,226 @@ namespace AZ m_controller.SetNormalBias(m_normalBias); return AZ::Edit::PropertyRefreshLevels::None; } + + AZ::u32 EditorDiffuseProbeGridComponent::OnEditorModeChanged() + { + // this will update the configuration and also change the DiffuseProbeGrid mode + m_controller.SetEditorMode(m_editorMode); + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + + AZ::u32 EditorDiffuseProbeGridComponent::OnRuntimeModeChanged() + { + // this will only update the configuration + m_controller.SetRuntimeMode(m_runtimeMode); + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZ::Outcome EditorDiffuseProbeGridComponent::OnModeChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType) + { + DiffuseProbeGridMode newMode = (*(reinterpret_cast(newValue))); + + if (newMode == DiffuseProbeGridMode::Baked || newMode == DiffuseProbeGridMode::AutoSelect) + { + if (!m_controller.m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedRelocationTextureAsset.GetId().IsValid() || + !m_controller.m_configuration.m_bakedClassificationTextureAsset.GetId().IsValid()) + { + return AZ::Failure(AZStd::string("Please bake textures before changing the Diffuse Probe Grid to Baked or Auto-Select mode.")); + } + } + + return AZ::Success(); + } + + AZ::u32 EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting() + { + // the Bake button is visible only when the editor mode is set to RealTime + return m_editorMode == DiffuseProbeGridMode::RealTime ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + AZ::u32 EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid() + { + if (m_bakeInProgress) + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + // retrieve entity visibility + bool isHidden = false; + AzToolsFramework::EditorEntityInfoRequestBus::EventResult( + isHidden, + GetEntityId(), + &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsHidden); + + // the entity must be visible in order to bake + if (isHidden) + { + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "This Diffuse Probe Grid entity is hidden, it must be visible in order to bake textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration; + + // retrieve the source image paths from the configuration + // Note: we need to make sure to use the same source image for each bake + AZStd::string irradianceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedIrradianceTextureRelativePath, DiffuseProbeGridIrradianceFileName); + AZStd::string distanceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedDistanceTextureRelativePath, DiffuseProbeGridDistanceFileName); + AZStd::string relocationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedRelocationTextureRelativePath, DiffuseProbeGridRelocationFileName); + AZStd::string classificationTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedClassificationTextureRelativePath, DiffuseProbeGridClassificationFileName); + + // create the full paths + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + AZStd::string irradianceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true); + AZStd::string distanceTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, distanceTextureRelativePath.c_str(), distanceTextureFullPath, true, true); + AZStd::string relocationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, relocationTextureRelativePath.c_str(), relocationTextureFullPath, true, true); + AZStd::string classificationTextureFullPath; + AzFramework::StringFunc::Path::Join(projectPath, classificationTextureRelativePath.c_str(), classificationTextureFullPath, true, true); + + // make sure the folder is created + AZStd::string diffuseProbeGridFolder; + AzFramework::StringFunc::Path::GetFolderPath(irradianceTextureFullPath.data(), diffuseProbeGridFolder); + AZ::IO::SystemFile::CreateDir(diffuseProbeGridFolder.c_str()); + + // check out the files in source control + CheckoutSourceTextureFile(irradianceTextureFullPath); + CheckoutSourceTextureFile(distanceTextureFullPath); + CheckoutSourceTextureFile(relocationTextureFullPath); + CheckoutSourceTextureFile(classificationTextureFullPath); + + // update the configuration + AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid bake"); + configuration.m_bakedIrradianceTextureRelativePath = irradianceTextureRelativePath; + configuration.m_bakedDistanceTextureRelativePath = distanceTextureRelativePath; + configuration.m_bakedRelocationTextureRelativePath = relocationTextureRelativePath; + configuration.m_bakedClassificationTextureRelativePath = classificationTextureRelativePath; + SetDirty(); + + // callback for the texture readback + DiffuseProbeGridBakeTexturesCallback bakeTexturesCallback = [=]( + DiffuseProbeGridTexture irradianceTexture, + DiffuseProbeGridTexture distanceTexture, + DiffuseProbeGridTexture relocationTexture, + DiffuseProbeGridTexture classificationTexture) + { + // irradiance + { + AZ::DdsFile::DdsFileData fileData = { irradianceTexture.m_size, irradianceTexture.m_format, irradianceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(irradianceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Irradiance texture .dds file [%s]", irradianceTextureFullPath.c_str()); + } + + // distance + { + AZ::DdsFile::DdsFileData fileData = { distanceTexture.m_size, distanceTexture.m_format, distanceTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(distanceTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Distance texture .dds file [%s]", distanceTextureFullPath.c_str()); + } + + // relocation + { + AZ::DdsFile::DdsFileData fileData = { relocationTexture.m_size, relocationTexture.m_format, relocationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(relocationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Relocation texture .dds file [%s]", relocationTextureFullPath.c_str()); + } + + // classification + { + AZ::DdsFile::DdsFileData fileData = { classificationTexture.m_size, classificationTexture.m_format, classificationTexture.m_data.get() }; + [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(classificationTextureFullPath, fileData); + AZ_Assert(outcome.IsSuccess(), "Failed to write Classification texture .dds file [%s]", classificationTextureFullPath.c_str()); + } + + m_bakeInProgress = false; + }; + + m_bakeInProgress = true; + m_controller.BakeTextures(bakeTexturesCallback); + + while (m_bakeInProgress) + { + QApplication::processEvents(); + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100)); + } + + QMessageBox::information( + QApplication::activeWindow(), + "Diffuse Probe Grid", + "Successfully baked Diffuse Probe Grid textures.", + QMessageBox::Ok); + + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::string EditorDiffuseProbeGridComponent::ValidateOrCreateNewTexturePath(const AZStd::string& configurationRelativePath, const char* fileSuffix) + { + AZStd::string relativePath = configurationRelativePath; + AZStd::string fullPath; + + char projectPath[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN); + + if (!relativePath.empty()) + { + // test to see if the texture 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, configurationRelativePath.c_str(), fullPath, true, true); + + if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str())) + { + // file does not exist, clear the relative path so we generate a new name + relativePath.clear(); + } + } + + // build a new image path if necessary + if (relativePath.empty()) + { + // the file name is a combination of the entity name, a UUID, and the filemask + Entity* entity = GetEntity(); + AZ_Assert(entity, "DiffuseProbeGrid entity is null"); + + AZ::Uuid uuid = AZ::Uuid::CreateRandom(); + AZStd::string uuidString; + uuid.ToString(uuidString); + + relativePath = "DiffuseProbeGrids/" + entity->GetName() + uuidString + fileSuffix; + + // replace any invalid filename characters + auto invalidCharacters = [](char letter) + { + return + letter == ':' || letter == '"' || letter == '\'' || + letter == '{' || letter == '}' || + letter == '<' || letter == '>'; + }; + AZStd::replace_if(relativePath.begin(), relativePath.end(), invalidCharacters, '_'); + } + + return relativePath; + } + + void EditorDiffuseProbeGridComponent::CheckoutSourceTextureFile(const AZStd::string& fullPath) + { + bool checkedOutSuccessfully = false; + using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus; + ApplicationBus::BroadcastResult( + checkedOutSuccessfully, + &ApplicationBus::Events::RequestEditForFileBlocking, + fullPath.c_str(), + "Checking out for edit...", + ApplicationBus::Events::RequestEditProgressCallback()); + } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h index 2a50c47b81..15c46d45ba 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h @@ -12,8 +12,10 @@ #pragma once +#include #include #include +#include #include #include #include @@ -26,6 +28,8 @@ namespace AZ : public EditorRenderComponentAdapter , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler + , private AzToolsFramework::EditorEntityInfoNotificationBus::Handler { public: using BaseClass = EditorRenderComponentAdapter; @@ -41,10 +45,22 @@ namespace AZ void Deactivate() override; private: + + // AZ::TickBus overrides + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + // EditorComponentSelectionRequestsBus overrides AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override; bool SupportsEditorRayIntersect() override; + // EditorEntityInfoNotifications overrides + void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible) override; + + // helper functions + AZStd::string ValidateOrCreateNewTexturePath(const AZStd::string& relativePath, const char* fileSuffix); + void CheckoutSourceTextureFile(const AZStd::string& fullPath); + void CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset& configurationAsset); + // property change notifications AZ::Outcome OnProbeSpacingValidateX(void* newValue, const AZ::Uuid& valueType); AZ::Outcome OnProbeSpacingValidateY(void* newValue, const AZ::Uuid& valueType); @@ -53,6 +69,13 @@ namespace AZ AZ::u32 OnAmbientMultiplierChanged(); AZ::u32 OnViewBiasChanged(); AZ::u32 OnNormalBiasChanged(); + AZ::u32 OnEditorModeChanged(); + AZ::u32 OnRuntimeModeChanged(); + AZ::Outcome OnModeChangeValidate(void* newValue, const AZ::Uuid& valueType); + + // Button handler + AZ::u32 BakeDiffuseProbeGrid(); + AZ::u32 GetBakeDiffuseProbeGridVisibilitySetting(); // properties float m_probeSpacingX = DefaultDiffuseProbeGridSpacing; @@ -61,6 +84,12 @@ namespace AZ float m_ambientMultiplier = DefaultDiffuseProbeGridAmbientMultiplier; float m_viewBias = DefaultDiffuseProbeGridViewBias; float m_normalBias = DefaultDiffuseProbeGridNormalBias; + DiffuseProbeGridMode m_editorMode = DiffuseProbeGridMode::RealTime; + DiffuseProbeGridMode m_runtimeMode = DiffuseProbeGridMode::RealTime; + + // flags + bool m_editorModeSet = false; + AZStd::atomic_bool m_bakeInProgress = false; }; } // namespace Render } // namespace AZ