• Moved everything related to the subject being captured by the preview renderer into a preview render content class which will become an interface in the next iteration

• Extracted all of the thumbnail specific code from the common preview render class as a step towards separating it from the thumbnail system completely
• Created a capture request structure that stores all of the info related to the content being captured and callbacks for success and failure
• Request to capture any kind of content can be added to the renderer using this structure

Signed-off-by: Guthrie Adams <guthadam@amazon.com>
monroegm-disable-blank-issue-2
Guthrie Adams 4 years ago
parent 58194b70c0
commit c90e1da475

@ -16,14 +16,14 @@ namespace AZ
{
namespace Thumbnails
{
//! PreviewerFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
//! PreviewerFeatureProcessorProviderRequests allows registering custom Feature Processors for preview image generation
//! Duplicates will be ignored
//! You can check minimal feature processors that are already registered in CommonPreviewRenderer.cpp
class PreviewerFeatureProcessorProviderRequests
: public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with thumbnail renderer
//! Get a list of custom feature processors to register with preview image renderer
virtual void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const = 0;
};

@ -0,0 +1,170 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <Thumbnails/Rendering/CommonPreviewContent.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
CommonPreviewContent::CommonPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId)
: m_scene(scene)
, m_view(view)
, m_entityContextId(entityContextId)
{
// Connect camera to pipeline's default view after camera entity activated
Matrix4x4 viewToClipMatrix;
MakePerspectiveFovMatrixRH(viewToClipMatrix, FieldOfView, AspectRatio, NearDist, FarDist, true);
m_view->SetViewToClipMatrix(viewToClipMatrix);
// Create preview model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, m_entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ThumbnailPreviewModel");
m_modelEntity->CreateComponent(Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Init();
m_modelEntity->Activate();
m_defaultModelAsset.Create(DefaultModelAssetId, true);
m_defaultMaterialAsset.Create(DefaultMaterialAssetId, true);
m_defaultLightingPresetAsset.Create(DefaultLightingPresetAssetId, true);
m_modelAsset.Create(modelAssetId.IsValid() ? modelAssetId : DefaultModelAssetId, false);
m_materialAsset.Create(materialAssetId.IsValid() ? materialAssetId : DefaultMaterialAssetId, false);
m_lightingPresetAsset.Create(lightingPresetAssetId.IsValid() ? lightingPresetAssetId : DefaultLightingPresetAssetId, false);
}
CommonPreviewContent::~CommonPreviewContent()
{
if (m_modelEntity)
{
AzFramework::EntityContextRequestBus::Event(
m_entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
}
}
void CommonPreviewContent::Load()
{
m_modelAsset.QueueLoad();
m_materialAsset.QueueLoad();
m_lightingPresetAsset.QueueLoad();
}
bool CommonPreviewContent::IsReady() const
{
return m_modelAsset.IsReady() && m_materialAsset.IsReady() && m_lightingPresetAsset.IsReady();
}
bool CommonPreviewContent::IsError() const
{
return m_modelAsset.IsError() || m_materialAsset.IsError() || m_lightingPresetAsset.IsError();
}
void CommonPreviewContent::ReportErrors()
{
AZ_Warning(
"CommonPreviewContent", m_modelAsset.IsReady(), "Asset failed to load in time: %s",
m_modelAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"CommonPreviewContent", m_materialAsset.IsReady(), "Asset failed to load in time: %s",
m_materialAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"CommonPreviewContent", m_lightingPresetAsset.IsReady(), "Asset failed to load in time: %s",
m_lightingPresetAsset.ToString<AZStd::string>().c_str());
}
void CommonPreviewContent::UpdateScene()
{
UpdateModel();
UpdateLighting();
UpdateCamera();
}
void CommonPreviewContent::UpdateModel()
{
Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MeshComponentRequestBus::Events::SetModelAsset, m_modelAsset);
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetDefaultMaterialOverride,
m_materialAsset.GetId());
}
void CommonPreviewContent::UpdateLighting()
{
auto preset = m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
if (preset)
{
auto iblFeatureProcessor = m_scene->GetFeatureProcessor<Render::ImageBasedLightFeatureProcessorInterface>();
auto postProcessFeatureProcessor = m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
auto postProcessSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId());
auto exposureControlSettingInterface = postProcessSettingInterface->GetOrCreateExposureControlSettingsInterface();
auto directionalLightFeatureProcessor =
m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
auto skyboxFeatureProcessor = m_scene->GetFeatureProcessor<Render::SkyBoxFeatureProcessorInterface>();
skyboxFeatureProcessor->Enable(true);
skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap);
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = FieldOfView;
cameraConfig.m_nearClipDistance = NearDist;
cameraConfig.m_farClipDistance = FarDist;
cameraConfig.m_frustumWidth = 100.0f;
cameraConfig.m_frustumHeight = 100.0f;
AZStd::vector<Render::DirectionalLightFeatureProcessorInterface::LightHandle> lightHandles;
preset->ApplyLightingPreset(
iblFeatureProcessor, skyboxFeatureProcessor, exposureControlSettingInterface, directionalLightFeatureProcessor,
cameraConfig, lightHandles);
}
}
void CommonPreviewContent::UpdateCamera()
{
// Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it
Vector3 center = {};
float radius = {};
m_modelAsset->GetAabb().GetAsSphere(center, radius);
const auto distance = radius + NearDist;
const auto cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), CameraRotationAngle);
const auto cameraPosition = center - cameraRotation.TransformVector(Vector3(0.0f, distance, 0.0f));
const auto cameraTransform = Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition);
m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform));
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ

@ -0,0 +1,79 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! Provides custom rendering of material and model thumbnails
class CommonPreviewContent
{
public:
AZ_CLASS_ALLOCATOR(CommonPreviewContent, AZ::SystemAllocator, 0);
CommonPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId);
~CommonPreviewContent();
void Load();
bool IsReady() const;
bool IsError() const;
void ReportErrors();
void UpdateScene();
private:
void UpdateModel();
void UpdateLighting();
void UpdateCamera();
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = Constants::HalfPi;
static constexpr float CameraRotationAngle = Constants::QuarterPi / 2.0f;
RPI::ScenePtr m_scene;
RPI::ViewPtr m_view;
AZ::Uuid m_entityContextId;
Entity* m_modelEntity = nullptr;
static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
const Data::AssetId DefaultLightingPresetAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath);
Data::Asset<RPI::AnyAsset> m_defaultLightingPresetAsset;
Data::Asset<RPI::AnyAsset> m_lightingPresetAsset;
//! Model asset about to be rendered
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
const Data::AssetId DefaultModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath);
Data::Asset<RPI::ModelAsset> m_defaultModelAsset;
Data::Asset<RPI::ModelAsset> m_modelAsset;
//! Material asset about to be rendered
static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial";
const Data::AssetId DefaultMaterialAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultMaterialPath);
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ

@ -6,33 +6,17 @@
*
*/
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/EBus/Results.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
@ -51,8 +35,7 @@ namespace AZ
{
CommonPreviewRenderer::CommonPreviewRenderer()
{
// CommonPreviewRenderer supports both models and materials, but we connect on materialAssetType
// since MaterialOrModelThumbnail dispatches event on materialAssetType address too
// CommonPreviewRenderer supports both models and materials
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type());
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type());
PreviewerFeatureProcessorProviderBus::Handler::BusConnect();
@ -72,7 +55,7 @@ namespace AZ
// Bind m_frameworkScene to the entity context's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation.");
AZ_Assert(sceneSystem, "Failed to get scene system implementation.");
Outcome<AZStd::shared_ptr<AzFramework::Scene>, AZStd::string> createSceneOutcome = sceneSystem->CreateScene(m_sceneName);
AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str());
@ -104,23 +87,9 @@ namespace AZ
m_view->SetViewToClipMatrix(viewToClipMatrix);
m_renderPipeline->SetDefaultView(m_view);
// Create preview model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, m_entityContext->GetContextId(), &AzFramework::EntityContextRequestBus::Events::CreateEntity,
"ThumbnailPreviewModel");
m_modelEntity->CreateComponent(Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Init();
m_modelEntity->Activate();
m_defaultLightingPresetAsset.Create(DefaultLightingPresetAssetId, true);
m_defaultMaterialAsset.Create(DefaultMaterialAssetId, true);
m_defaultModelAsset.Create(DefaultModelAssetId, true);
m_steps[CommonPreviewRenderer::State::IdleState] = AZStd::make_shared<CommonPreviewRendererIdleState>(this);
m_steps[CommonPreviewRenderer::State::LoadState] = AZStd::make_shared<CommonPreviewRendererLoadState>(this);
m_steps[CommonPreviewRenderer::State::CaptureState] = AZStd::make_shared<CommonPreviewRendererCaptureState>(this);
m_states[CommonPreviewRenderer::State::IdleState] = AZStd::make_shared<CommonPreviewRendererIdleState>(this);
m_states[CommonPreviewRenderer::State::LoadState] = AZStd::make_shared<CommonPreviewRendererLoadState>(this);
m_states[CommonPreviewRenderer::State::CaptureState] = AZStd::make_shared<CommonPreviewRendererCaptureState>(this);
SetState(CommonPreviewRenderer::State::IdleState);
}
@ -131,13 +100,8 @@ namespace AZ
PreviewerFeatureProcessorProviderBus::Handler::BusDisconnect();
SetState(CommonPreviewRenderer::State::None);
if (m_modelEntity)
{
AzFramework::EntityContextRequestBus::Event(
m_entityContext->GetContextId(), &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
}
m_currentCaptureRequest = {};
m_captureRequestQueue = {};
m_scene->Deactivate();
m_scene->RemoveRenderPipeline(m_renderPipeline->GetId());
@ -146,18 +110,23 @@ namespace AZ
m_frameworkScene->UnsetSubsystem(m_entityContext.get());
}
void CommonPreviewRenderer::AddCaptureRequest(const CaptureRequest& captureRequest)
{
m_captureRequestQueue.push(captureRequest);
}
void CommonPreviewRenderer::SetState(State state)
{
auto stepItr = m_steps.find(m_currentState);
if (stepItr != m_steps.end())
auto stepItr = m_states.find(m_currentState);
if (stepItr != m_states.end())
{
stepItr->second->Stop();
}
m_currentState = state;
stepItr = m_steps.find(m_currentState);
if (stepItr != m_steps.end())
stepItr = m_states.find(m_currentState);
if (stepItr != m_states.end())
{
stepItr->second->Start();
}
@ -168,54 +137,43 @@ namespace AZ
return m_currentState;
}
void CommonPreviewRenderer::SelectThumbnail()
void CommonPreviewRenderer::SelectCaptureRequest()
{
if (!m_thumbnailInfoQueue.empty())
if (!m_captureRequestQueue.empty())
{
// pop the next thumbnailkey to be rendered from the queue
m_currentThubnailInfo = m_thumbnailInfoQueue.front();
m_thumbnailInfoQueue.pop();
// pop the next request to be rendered from the queue
m_currentCaptureRequest = m_captureRequestQueue.front();
m_captureRequestQueue.pop();
SetState(CommonPreviewRenderer::State::LoadState);
}
}
void CommonPreviewRenderer::CancelThumbnail()
void CommonPreviewRenderer::CancelCaptureRequest()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_currentThubnailInfo.m_key, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
m_currentCaptureRequest.m_captureFailedCallback();
SetState(CommonPreviewRenderer::State::IdleState);
}
void CommonPreviewRenderer::CompleteThumbnail()
void CommonPreviewRenderer::CompleteCaptureRequest()
{
SetState(CommonPreviewRenderer::State::IdleState);
}
void CommonPreviewRenderer::LoadAssets()
{
// Determine if thumbnailkey contains a material asset or set a default material
const Data::AssetId materialAssetId = GetAssetId(m_currentThubnailInfo.m_key, RPI::MaterialAsset::RTTI_Type());
m_materialAsset.Create(materialAssetId.IsValid() ? materialAssetId : DefaultMaterialAssetId, true);
// Determine if thumbnailkey contains a model asset or set a default model
const Data::AssetId modelAssetId = GetAssetId(m_currentThubnailInfo.m_key, RPI::ModelAsset::RTTI_Type());
m_modelAsset.Create(modelAssetId.IsValid() ? modelAssetId : DefaultModelAssetId, true);
// Determine if thumbnailkey contains a lighting preset asset or set a default lighting preset
const Data::AssetId lightingPresetAssetId = GetAssetId(m_currentThubnailInfo.m_key, RPI::AnyAsset::RTTI_Type());
m_lightingPresetAsset.Create(lightingPresetAssetId.IsValid() ? lightingPresetAssetId : DefaultLightingPresetAssetId, true);
m_currentCaptureRequest.m_content->Load();
}
void CommonPreviewRenderer::UpdateLoadAssets()
{
if (m_materialAsset.IsReady() && m_modelAsset.IsReady() && m_lightingPresetAsset.IsReady())
if (m_currentCaptureRequest.m_content->IsReady())
{
SetState(CommonPreviewRenderer::State::CaptureState);
return;
}
if (m_materialAsset.IsError() || m_modelAsset.IsError() || m_lightingPresetAsset.IsError())
if (m_currentCaptureRequest.m_content->IsError())
{
CancelLoadAssets();
return;
@ -224,107 +182,35 @@ namespace AZ
void CommonPreviewRenderer::CancelLoadAssets()
{
AZ_Warning(
"CommonPreviewRenderer", m_materialAsset.IsReady(), "Asset failed to load in time: %s",
m_materialAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"CommonPreviewRenderer", m_modelAsset.IsReady(), "Asset failed to load in time: %s",
m_modelAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"CommonPreviewRenderer", m_lightingPresetAsset.IsReady(), "Asset failed to load in time: %s",
m_lightingPresetAsset.ToString<AZStd::string>().c_str());
CancelThumbnail();
m_currentCaptureRequest.m_content->ReportErrors();
CancelCaptureRequest();
}
void CommonPreviewRenderer::UpdateScene()
{
UpdateModel();
UpdateLighting();
UpdateCamera();
}
void CommonPreviewRenderer::UpdateModel()
{
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetDefaultMaterialOverride,
m_materialAsset.GetId());
Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MeshComponentRequestBus::Events::SetModelAsset, m_modelAsset);
}
void CommonPreviewRenderer::UpdateLighting()
{
auto preset = m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
if (preset)
{
auto iblFeatureProcessor = m_scene->GetFeatureProcessor<Render::ImageBasedLightFeatureProcessorInterface>();
auto postProcessFeatureProcessor = m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
auto postProcessSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId());
auto exposureControlSettingInterface = postProcessSettingInterface->GetOrCreateExposureControlSettingsInterface();
auto directionalLightFeatureProcessor =
m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
auto skyboxFeatureProcessor = m_scene->GetFeatureProcessor<Render::SkyBoxFeatureProcessorInterface>();
skyboxFeatureProcessor->Enable(true);
skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap);
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = FieldOfView;
cameraConfig.m_nearClipDistance = NearDist;
cameraConfig.m_farClipDistance = FarDist;
cameraConfig.m_frustumWidth = 100.0f;
cameraConfig.m_frustumHeight = 100.0f;
AZStd::vector<Render::DirectionalLightFeatureProcessorInterface::LightHandle> lightHandles;
preset->ApplyLightingPreset(
iblFeatureProcessor, skyboxFeatureProcessor, exposureControlSettingInterface, directionalLightFeatureProcessor,
cameraConfig, lightHandles);
}
}
void CommonPreviewRenderer::UpdateCamera()
{
// Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it
Vector3 center = {};
float radius = {};
m_modelAsset->GetAabb().GetAsSphere(center, radius);
const auto distance = radius + NearDist;
const auto cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), CameraRotationAngle);
const auto cameraPosition = center - cameraRotation.TransformVector(Vector3(0.0f, distance, 0.0f));
const auto cameraTransform = Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition);
m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform));
m_currentCaptureRequest.m_content->UpdateScene();
}
RPI::AttachmentReadback::CallbackFunction CommonPreviewRenderer::GetCaptureCallback()
bool CommonPreviewRenderer::StartCapture()
{
return [this](const RPI::AttachmentReadback::ReadbackResult& result)
auto captureCallback =
[currentCaptureRequest = m_currentCaptureRequest](const RPI::AttachmentReadback::ReadbackResult& result)
{
if (result.m_dataBuffer)
{
QImage image(
currentCaptureRequest.m_captureCompleteCallback(QImage(
result.m_dataBuffer.get()->data(), result.m_imageDescriptor.m_size.m_width,
result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888);
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_currentThubnailInfo.m_key,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, QPixmap::fromImage(image));
result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888));
}
else
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_currentThubnailInfo.m_key,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
currentCaptureRequest.m_captureFailedCallback();
}
};
}
bool CommonPreviewRenderer::StartCapture()
{
if (auto renderToTexturePass = azrtti_cast<AZ::RPI::RenderToTexturePass*>(m_renderPipeline->GetRootPass().get()))
{
renderToTexturePass->ResizeOutput(m_currentThubnailInfo.m_size, m_currentThubnailInfo.m_size);
renderToTexturePass->ResizeOutput(m_currentCaptureRequest.m_size, m_currentCaptureRequest.m_size);
}
m_renderPipeline->AddToRenderTickOnce();
@ -332,7 +218,7 @@ namespace AZ
bool startedCapture = false;
Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture, &Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy,
AZStd::string("Output"), GetCaptureCallback(), RPI::PassAttachmentReadbackOption::Output);
AZStd::string("Output"), captureCallback, RPI::PassAttachmentReadbackOption::Output);
return startedCapture;
}
@ -341,11 +227,6 @@ namespace AZ
m_renderPipeline->RemoveFromRenderTick();
}
bool CommonPreviewRenderer::Installed() const
{
return true;
}
void CommonPreviewRenderer::OnSystemTick()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
@ -372,10 +253,32 @@ namespace AZ
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor" });
}
void CommonPreviewRenderer::RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize)
{
m_thumbnailInfoQueue.push({ thumbnailKey, thumbnailSize });
AddCaptureRequest(
{ thumbnailSize,
AZStd::make_shared<CommonPreviewContent>(
m_scene, m_view, m_entityContext->GetContextId(),
GetAssetId(thumbnailKey, RPI::ModelAsset::RTTI_Type()),
GetAssetId(thumbnailKey, RPI::MaterialAsset::RTTI_Type()),
GetAssetId(thumbnailKey, RPI::AnyAsset::RTTI_Type())),
[thumbnailKey]()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
},
[thumbnailKey](const QImage& image)
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered,
QPixmap::fromImage(image));
} });
}
bool CommonPreviewRenderer::Installed() const
{
return true;
}
} // namespace Thumbnails
} // namespace LyIntegration

@ -10,13 +10,11 @@
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/PreviewerFeatureProcessorProviderBus.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Rendering/CommonPreviewContent.h>
#include <Thumbnails/Thumbnail.h>
namespace AzFramework
@ -46,11 +44,21 @@ namespace AZ
, public PreviewerFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(CommonPreviewRenderer, AZ::SystemAllocator, 0)
AZ_CLASS_ALLOCATOR(CommonPreviewRenderer, AZ::SystemAllocator, 0);
CommonPreviewRenderer();
~CommonPreviewRenderer();
struct CaptureRequest
{
int m_size = 512;
AZStd::shared_ptr<CommonPreviewContent> m_content;
AZStd::function<void()> m_captureFailedCallback;
AZStd::function<void(const QImage&)> m_captureCompleteCallback;
};
void AddCaptureRequest(const CaptureRequest& captureRequest);
enum class State : AZ::s8
{
None,
@ -62,39 +70,34 @@ namespace AZ
void SetState(State state);
State GetState() const;
void SelectThumbnail();
void CancelThumbnail();
void CompleteThumbnail();
void SelectCaptureRequest();
void CancelCaptureRequest();
void CompleteCaptureRequest();
void LoadAssets();
void UpdateLoadAssets();
void CancelLoadAssets();
void UpdateScene();
void UpdateModel();
void UpdateLighting();
void UpdateCamera();
RPI::AttachmentReadback::CallbackFunction GetCaptureCallback();
bool StartCapture();
void EndCapture();
private:
//! ThumbnailerRendererRequestsBus::Handler interface overrides...
void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override;
bool Installed() const override;
//! SystemTickBus::Handler interface overrides...
void OnSystemTick() override;
//! Render::PreviewerFeatureProcessorProviderBus::Handler interface overrides...
void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const override;
//! ThumbnailerRendererRequestsBus::Handler interface overrides...
void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override;
bool Installed() const override;
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = Constants::HalfPi;
static constexpr float CameraRotationAngle = Constants::QuarterPi / 2.0f;
RPI::ScenePtr m_scene;
AZStd::string m_sceneName = "Material Thumbnail Scene";
@ -105,36 +108,12 @@ namespace AZ
AZStd::vector<AZStd::string> m_passHierarchy;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
//! Incoming thumbnail requests are appended to this queue and processed one at a time in OnTick function.
struct ThumbnailInfo
{
AzToolsFramework::Thumbnailer::SharedThumbnailKey m_key;
int m_size = 512;
};
AZStd::queue<ThumbnailInfo> m_thumbnailInfoQueue;
ThumbnailInfo m_currentThubnailInfo;
//! Incoming requests are appended to this queue and processed one at a time in OnTick function.
AZStd::queue<CaptureRequest> m_captureRequestQueue;
CaptureRequest m_currentCaptureRequest;
AZStd::unordered_map<State, AZStd::shared_ptr<CommonPreviewRendererState>> m_steps;
AZStd::unordered_map<State, AZStd::shared_ptr<CommonPreviewRendererState>> m_states;
State m_currentState = CommonPreviewRenderer::State::None;
static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
const Data::AssetId DefaultLightingPresetAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath);
Data::Asset<RPI::AnyAsset> m_defaultLightingPresetAsset;
Data::Asset<RPI::AnyAsset> m_lightingPresetAsset;
//! Model asset about to be rendered
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
const Data::AssetId DefaultModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath);
Data::Asset<RPI::ModelAsset> m_defaultModelAsset;
Data::Asset<RPI::ModelAsset> m_modelAsset;
//! Material asset about to be rendered
static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial";
const Data::AssetId DefaultMaterialAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultMaterialPath);
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Entity* m_modelEntity = nullptr;
};
} // namespace Thumbnails
} // namespace LyIntegration

@ -50,7 +50,7 @@ namespace AZ
void CommonPreviewRendererCaptureState::OnCaptureFinished(
[[maybe_unused]] Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
{
m_renderer->CompleteThumbnail();
m_renderer->CompleteCaptureRequest();
}
} // namespace Thumbnails
} // namespace LyIntegration

@ -32,7 +32,7 @@ namespace AZ
void CommonPreviewRendererIdleState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{
m_renderer->SelectThumbnail();
m_renderer->SelectCaptureRequest();
}
} // namespace Thumbnails
} // namespace LyIntegration

@ -102,6 +102,8 @@ set(FILES
Source/Thumbnails/Preview/CommonPreviewer.ui
Source/Thumbnails/Preview/CommonPreviewerFactory.cpp
Source/Thumbnails/Preview/CommonPreviewerFactory.h
Source/Thumbnails/Rendering/CommonPreviewContent.cpp
Source/Thumbnails/Rendering/CommonPreviewContent.h
Source/Thumbnails/Rendering/CommonPreviewRenderer.cpp
Source/Thumbnails/Rendering/CommonPreviewRenderer.h
Source/Thumbnails/Rendering/CommonPreviewRendererState.h

Loading…
Cancel
Save