Atom Tools: Combined ME viewport widget and renderer classes

Was looking into options for making any part of this more generic or reusable but 60% of the code is just setup and teardown of processors, handles, entities, and components for the material editor viewport content. The rest is about handling material editors specific setting changes. Most of the general-purpose code has already been moved into atom tools framework render viewport widget. The best reduction or generalization from this point would be using prefabs or some other mechanism to data drive setting up camera, viewport model, IBL, lighting, sky box.

Also discussing options for replacing the material editor viewport camera controller and states with a generic one used in the main editor and animation editor.

Signed-off-by: Guthrie Adams <guthadam@amazon.com>
This commit is contained in:
Guthrie Adams
2022-01-23 16:25:57 -06:00
parent 878ba24a3f
commit aabde7916f
5 changed files with 536 additions and 597 deletions
@@ -1,462 +0,0 @@
/*
* 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
*
*/
#undef RC_INVOKED
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcessing/PostProcessingConstants.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConfig.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.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 <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <Document/MaterialDocumentRequestBus.h>
#include <Viewport/MaterialViewportRenderer.h>
#include <Viewport/MaterialViewportRequestBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Viewport/PerformanceMonitorRequestBus.h>
namespace MaterialEditor
{
static constexpr float DepthNear = 0.01f;
MaterialViewportRenderer::MaterialViewportRenderer(AZStd::shared_ptr<AZ::RPI::WindowContext> windowContext)
: m_windowContext(windowContext)
, m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
{
using namespace AZ;
using namespace Data;
// Create and register a scene with all available feature processors
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
m_scene->EnableAllFeatureProcessors();
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during construction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component initialization");
mainScene->SetSubsystem(m_scene);
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *m_windowContext.get());
pipelineAsset.Release();
m_scene->AddRenderPipeline(m_renderPipeline);
// As part of our initialization we need to create the BRDF texture generation pipeline
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = "BRDFTexturePipeline";
pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
pipelineDesc.m_executeOnce = true;
AZ::RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(brdfTexturePipeline);
// Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be initialized properly
// with pipeline's pass information.
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
// Configure camera
AzFramework::EntityContextRequestBus::EventResult(m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity");
AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity.");
//Add debug camera and controller components
AZ::Debug::CameraComponentConfig cameraConfig(m_windowContext);
cameraConfig.m_fovY = AZ::Constants::HalfPi;
cameraConfig.m_depthNear = DepthNear;
m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid<AZ::Debug::CameraComponent>());
m_cameraComponent->SetConfiguration(cameraConfig);
m_cameraEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_cameraEntity->Activate();
// Connect camera to pipeline's default view after camera entity activated
m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
// Init display mapper processor
m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor<Render::DisplayMapperFeatureProcessorInterface>();
// Init Skybox
m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
m_skyboxFeatureProcessor->Enable(true);
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
// Create model
AzFramework::EntityContextRequestBus::EventResult(m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel");
AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity.");
m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Activate();
// Create shadow catcher
AzFramework::EntityContextRequestBus::EventResult(m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher");
AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity.");
m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::NonUniformScaleComponent>());
m_shadowCatcherEntity->Activate();
AZ::NonUniformScaleRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 });
AZ::Data::AssetId shadowCatcherModelAssetId = RPI::AssetUtils::GetAssetIdForProductPath("materialeditor/viewportmodels/plane_1x1.azmodel", RPI::AssetUtils::TraceLevel::Error);
AZ::Render::MeshComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
&AZ::Render::MeshComponentRequestBus::Events::SetModelAssetId, shadowCatcherModelAssetId);
auto shadowCatcherMaterialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>("materials/special/shadowcatcher.azmaterial", RPI::AssetUtils::TraceLevel::Error);
if (shadowCatcherMaterialAsset)
{
m_shadowCatcherOpacityPropertyIndex = shadowCatcherMaterialAsset->GetMaterialTypeAsset()->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name{ "settings.opacity" });
AZ_Error("MaterialViewportRenderer", m_shadowCatcherOpacityPropertyIndex.IsValid(), "Could not find opacity property");
m_shadowCatcherMaterial = AZ::RPI::Material::Create(shadowCatcherMaterialAsset);
AZ_Error("MaterialViewportRenderer", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, shadowCatcherMaterials);
}
// Create grid
AzFramework::EntityContextRequestBus::EventResult(m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Activate();
OnDocumentOpened(AZ::Uuid::CreateNull());
// Attempt to apply the default lighting preset
AZ::Render::LightingPresetPtr lightingPreset;
MaterialViewportRequestBus::BroadcastResult(lightingPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(lightingPreset);
// Attempt to apply the default model preset
AZ::Render::ModelPresetPtr modelPreset;
MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(modelPreset);
m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId());
// Apply user settinngs restored since last run
AZStd::intrusive_ptr<MaterialViewportSettings> viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
OnGridEnabledChanged(viewportSettings->m_enableGrid);
OnShadowCatcherEnabledChanged(viewportSettings->m_enableShadowCatcher);
OnAlternateSkyboxEnabledChanged(viewportSettings->m_enableAlternateSkybox);
OnFieldOfViewChanged(viewportSettings->m_fieldOfView);
OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
MaterialViewportNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
}
MaterialViewportRenderer::~MaterialViewportRenderer()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity);
m_iblEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity);
m_shadowCatcherEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity);
m_gridEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity);
m_cameraEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity);
m_postProcessEntity = nullptr;
for (DirectionalLightHandle& handle : m_lightHandles)
{
m_directionalLightFeatureProcessor->ReleaseLight(handle);
}
m_lightHandles.clear();
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during destruction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component destruction");
mainScene->UnsetSubsystem(m_scene);
m_swapChainPass = nullptr;
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_scene = nullptr;
}
AZStd::shared_ptr<MaterialEditorViewportInputController> MaterialViewportRenderer::GetController()
{
return m_viewportController;
}
void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
AZ::Render::MaterialAssignmentMap materials;
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
materialAssignment.m_materialInstance = materialInstance;
materialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_modelEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
}
void MaterialViewportRenderer::OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset)
{
if (!preset)
{
return;
}
AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::ImageBasedLightFeatureProcessorInterface>();
AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId())->GetOrCreateExposureControlSettingsInterface();
Camera::Configuration cameraConfig;
Camera::CameraRequestBus::EventResult(cameraConfig, m_cameraEntity->GetId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
bool enableAlternateSkybox = false;
MaterialViewportRequestBus::BroadcastResult(enableAlternateSkybox, &MaterialViewportRequestBus::Events::GetAlternateSkyboxEnabled);
preset->ApplyLightingPreset(
iblFeatureProcessor,
m_skyboxFeatureProcessor,
exposureControlSettingInterface,
m_directionalLightFeatureProcessor,
cameraConfig,
m_lightHandles,
m_shadowCatcherMaterial,
m_shadowCatcherOpacityPropertyIndex,
enableAlternateSkybox);
}
void MaterialViewportRenderer::OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset)
{
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
if (selectedPreset == preset)
{
OnLightingPresetSelected(preset);
}
}
void MaterialViewportRenderer::OnModelPresetSelected(AZ::Render::ModelPresetPtr preset)
{
if (!preset)
{
return;
}
if (!preset->m_modelAsset.GetId().IsValid())
{
AZ_Warning("MaterialViewportRenderer", false, "Attempting to set invalid model for preset: '%s'\n.", preset->m_displayName.c_str());
return;
}
if (preset->m_modelAsset.GetId() == m_modelAssetId)
{
return;
}
AZ::Render::MeshComponentRequestBus::Event(m_modelEntity->GetId(),
&AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, preset->m_modelAsset);
m_modelAssetId = preset->m_modelAsset.GetId();
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(m_modelAssetId);
}
void MaterialViewportRenderer::OnModelPresetChanged(AZ::Render::ModelPresetPtr preset)
{
AZ::Render::ModelPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
if (selectedPreset == preset)
{
OnModelPresetSelected(preset);
}
}
void MaterialViewportRenderer::OnShadowCatcherEnabledChanged(bool enable)
{
AZ::Render::MeshComponentRequestBus::Event(m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetVisibility, enable);
}
void MaterialViewportRenderer::OnGridEnabledChanged(bool enable)
{
if (m_gridEntity)
{
if (enable && m_gridEntity->GetState() == AZ::Entity::State::Init)
{
m_gridEntity->Activate();
}
else if (!enable && m_gridEntity->GetState() == AZ::Entity::State::Active)
{
m_gridEntity->Deactivate();
}
}
}
void MaterialViewportRenderer::OnAlternateSkyboxEnabledChanged(bool enable)
{
AZ_UNUSED(enable);
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(selectedPreset);
}
void MaterialViewportRenderer::OnFieldOfViewChanged(float fieldOfView)
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView);
}
void MaterialViewportRenderer::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType)
{
AZ::Render::DisplayMapperConfigurationDescriptor desc;
desc.m_operationType = operationType;
m_displayMapperFeatureProcessor->RegisterDisplayMapperConfiguration(desc);
}
void MaterialViewportRenderer::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_modelAssetId == asset.GetId())
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset);
AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId());
}
}
void MaterialViewportRenderer::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
m_renderPipeline->AddToRenderTickOnce();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::GatherMetrics);
if (m_shadowCatcherMaterial)
{
// Compile the m_shadowCatcherMaterial in OnTick because changes can only be compiled once per frame.
// This is ignored when a compile isn't needed.
m_shadowCatcherMaterial->Compile();
}
}
void MaterialViewportRenderer::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
{
const AZ::EntityId* currentBusId = AZ::TransformNotificationBus::GetCurrentBusId();
if (m_cameraEntity && currentBusId && *currentBusId == m_cameraEntity->GetId() && m_directionalLightFeatureProcessor)
{
auto transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(
transform,
m_cameraEntity->GetId(),
&AZ::TransformBus::Events::GetWorldTM);
for (const DirectionalLightHandle& id : m_lightHandles)
{
m_directionalLightFeatureProcessor->SetCameraTransform(
id, transform);
}
}
}
}
@@ -1,114 +0,0 @@
/*
* 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/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Base.h>
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/MaterialViewportNotificationBus.h>
namespace AZ
{
namespace Render
{
class DisplayMapperFeatureProcessorInterface;
}
class Entity;
class Component;
namespace RPI
{
class SwapChainPass;
class WindowContext;
}
}
namespace MaterialEditor
{
//! Provides backend logic for MaterialViewport
//! Sets up a scene, camera, loads the model, and applies texture
class MaterialViewportRenderer
: public AZ::Data::AssetBus::Handler
, public AZ::TickBus::Handler
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public MaterialViewportNotificationBus::Handler
, public AZ::TransformNotificationBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0);
MaterialViewportRenderer(AZStd::shared_ptr<AZ::RPI::WindowContext> windowContext);
~MaterialViewportRenderer();
AZStd::shared_ptr<MaterialEditorViewportInputController> GetController();
private:
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// MaterialViewportNotificationBus::Handler interface overrides...
void OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset) override;
void OnModelPresetSelected(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetChanged(AZ::Render::ModelPresetPtr preset) override;
void OnShadowCatcherEnabledChanged(bool enable) override;
void OnGridEnabledChanged(bool enable) override;
void OnAlternateSkyboxEnabledChanged(bool enable) override;
void OnFieldOfViewChanged(float fieldOfView) override;
void OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType) override;
// AZ::Data::AssetBus::Handler interface overrides...
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
// AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// AZ::TransformNotificationBus::MultiHandler overrides...
void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;
AZStd::string m_defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset";
AZStd::shared_ptr<AZ::RPI::WindowContext> m_windowContext;
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ScenePtr m_scene;
AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = nullptr;
AZ::Entity* m_cameraEntity = nullptr;
AZ::Component* m_cameraComponent = nullptr;
bool m_cameraNeedsFullReset = true;
AZ::Entity* m_postProcessEntity = nullptr;
AZ::Entity* m_modelEntity = nullptr;
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = nullptr;
AZ::Entity* m_shadowCatcherEntity = nullptr;
AZ::Data::Instance<AZ::RPI::Material> m_shadowCatcherMaterial;
AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex;
AZStd::vector<DirectionalLightHandle> m_lightHandles;
AZ::Entity* m_iblEntity = nullptr;
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr;
AZStd::shared_ptr<MaterialEditorViewportInputController> m_viewportController;
};
} // namespace MaterialEditor
@@ -6,29 +6,65 @@
*
*/
#undef RC_INVOKED
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcessing/PostProcessingConstants.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/Feature/Utils/ModelPreset.h>
#include <Atom/RHI/Device.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Viewport/MaterialViewportRenderer.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConfig.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.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 <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <Document/MaterialDocumentRequestBus.h>
#include <Viewport/MaterialViewportRequestBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Viewport/MaterialViewportWidget.h>
#include <Viewport/PerformanceMonitorRequestBus.h>
#include <Viewport/ui_MaterialViewportWidget.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWindow>
#include "Viewport/ui_MaterialViewportWidget.h"
AZ_POP_DISABLE_WARNING
#include <AzFramework/Viewport/ViewportControllerList.h>
namespace MaterialEditor
{
static constexpr float DepthNear = 0.01f;
MaterialViewportWidget::MaterialViewportWidget(QWidget* parent)
: AtomToolsFramework::RenderViewportWidget(parent)
, m_ui(new Ui::MaterialViewportWidget)
, m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
{
m_ui->setupUi(this);
@@ -38,7 +74,415 @@ namespace MaterialEditor
const AZ::Name defaultContextName = viewportContextManager->GetDefaultViewportContextName();
viewportContextManager->RenameViewportContext(GetViewportContext(), defaultContextName);
m_renderer = AZStd::make_unique<MaterialViewportRenderer>(GetViewportContext()->GetWindowContext());
GetControllerList()->Add(m_renderer->GetController());
// Create and register a scene with all available feature processors
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
m_scene->EnableAllFeatureProcessors();
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportWidget was unable to get the scene system during construction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component initialization");
mainScene->SetSubsystem(m_scene);
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *GetViewportContext()->GetWindowContext().get());
pipelineAsset.Release();
m_scene->AddRenderPipeline(m_renderPipeline);
// As part of our initialization we need to create the BRDF texture generation pipeline
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = "BRDFTexturePipeline";
pipelineDesc.m_rootPassTemplate = "BRDFTexturePipeline";
pipelineDesc.m_executeOnce = true;
AZ::RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(brdfTexturePipeline);
// Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be
// initialized properly with pipeline's pass information.
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
// Configure camera
AzFramework::EntityContextRequestBus::EventResult(
m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity");
AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity.");
// Add debug camera and controller components
AZ::Debug::CameraComponentConfig cameraConfig(GetViewportContext()->GetWindowContext());
cameraConfig.m_fovY = AZ::Constants::HalfPi;
cameraConfig.m_depthNear = DepthNear;
m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid<AZ::Debug::CameraComponent>());
m_cameraComponent->SetConfiguration(cameraConfig);
m_cameraEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_cameraEntity->Activate();
// Connect camera to pipeline's default view after camera entity activated
m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(
m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
// Init display mapper processor
m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DisplayMapperFeatureProcessorInterface>();
// Init Skybox
m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
m_skyboxFeatureProcessor->Enable(true);
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(
m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
// Create model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel");
AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity.");
m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Activate();
// Create shadow catcher
AzFramework::EntityContextRequestBus::EventResult(
m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher");
AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity.");
m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId);
m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_shadowCatcherEntity->CreateComponent(azrtti_typeid<AzFramework::NonUniformScaleComponent>());
m_shadowCatcherEntity->Activate();
AZ::NonUniformScaleRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 });
AZ::Data::AssetId shadowCatcherModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(
"materialeditor/viewportmodels/plane_1x1.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
AZ::Render::MeshComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAssetId, shadowCatcherModelAssetId);
auto shadowCatcherMaterialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(
"materials/special/shadowcatcher.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Error);
if (shadowCatcherMaterialAsset)
{
m_shadowCatcherOpacityPropertyIndex =
shadowCatcherMaterialAsset->GetMaterialTypeAsset()->GetMaterialPropertiesLayout()->FindPropertyIndex(
AZ::Name{ "settings.opacity" });
AZ_Error("MaterialViewportWidget", m_shadowCatcherOpacityPropertyIndex.IsValid(), "Could not find opacity property");
m_shadowCatcherMaterial = AZ::RPI::Material::Create(shadowCatcherMaterialAsset);
AZ_Error("MaterialViewportWidget", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides,
shadowCatcherMaterials);
}
// Create grid
AzFramework::EntityContextRequestBus::EventResult(
m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Activate();
OnDocumentOpened(AZ::Uuid::CreateNull());
// Attempt to apply the default lighting preset
AZ::Render::LightingPresetPtr lightingPreset;
MaterialViewportRequestBus::BroadcastResult(lightingPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(lightingPreset);
// Attempt to apply the default model preset
AZ::Render::ModelPresetPtr modelPreset;
MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
OnModelPresetSelected(modelPreset);
m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId());
// Apply user settinngs restored since last run
AZStd::intrusive_ptr<MaterialViewportSettings> viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
OnGridEnabledChanged(viewportSettings->m_enableGrid);
OnShadowCatcherEnabledChanged(viewportSettings->m_enableShadowCatcher);
OnAlternateSkyboxEnabledChanged(viewportSettings->m_enableAlternateSkybox);
OnFieldOfViewChanged(viewportSettings->m_fieldOfView);
OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
MaterialViewportNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
GetControllerList()->Add(m_viewportController);
}
MaterialViewportWidget::~MaterialViewportWidget()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity);
m_iblEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity);
m_shadowCatcherEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity);
m_gridEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity);
m_cameraEntity = nullptr;
AzFramework::EntityContextRequestBus::Event(
entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity);
m_postProcessEntity = nullptr;
for (DirectionalLightHandle& handle : m_lightHandles)
{
m_directionalLightFeatureProcessor->ReleaseLight(handle);
}
m_lightHandles.clear();
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "MaterialViewportWidget was unable to get the scene system during destruction.");
AZStd::shared_ptr<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
// This should never happen unless scene creation has changed.
AZ_Assert(mainScene, "Main scenes missing during system component destruction");
mainScene->UnsetSubsystem(m_scene);
m_swapChainPass = nullptr;
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_scene = nullptr;
}
void MaterialViewportWidget::OnDocumentOpened(const AZ::Uuid& documentId)
{
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
AZ::Render::MaterialAssignmentMap materials;
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
materialAssignment.m_materialInstance = materialInstance;
materialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
}
void MaterialViewportWidget::OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset)
{
if (!preset)
{
return;
}
AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::ImageBasedLightFeatureProcessorInterface>();
AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface =
postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId())
->GetOrCreateExposureControlSettingsInterface();
Camera::Configuration cameraConfig;
Camera::CameraRequestBus::EventResult(
cameraConfig, m_cameraEntity->GetId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
bool enableAlternateSkybox = false;
MaterialViewportRequestBus::BroadcastResult(enableAlternateSkybox, &MaterialViewportRequestBus::Events::GetAlternateSkyboxEnabled);
preset->ApplyLightingPreset(
iblFeatureProcessor, m_skyboxFeatureProcessor, exposureControlSettingInterface, m_directionalLightFeatureProcessor,
cameraConfig, m_lightHandles, m_shadowCatcherMaterial, m_shadowCatcherOpacityPropertyIndex, enableAlternateSkybox);
}
void MaterialViewportWidget::OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset)
{
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
if (selectedPreset == preset)
{
OnLightingPresetSelected(preset);
}
}
void MaterialViewportWidget::OnModelPresetSelected(AZ::Render::ModelPresetPtr preset)
{
if (!preset)
{
return;
}
if (!preset->m_modelAsset.GetId().IsValid())
{
AZ_Warning(
"MaterialViewportWidget", false, "Attempting to set invalid model for preset: '%s'\n.", preset->m_displayName.c_str());
return;
}
if (preset->m_modelAsset.GetId() == m_modelAssetId)
{
return;
}
AZ::Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, preset->m_modelAsset);
m_modelAssetId = preset->m_modelAsset.GetId();
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(m_modelAssetId);
}
void MaterialViewportWidget::OnModelPresetChanged(AZ::Render::ModelPresetPtr preset)
{
AZ::Render::ModelPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection);
if (selectedPreset == preset)
{
OnModelPresetSelected(preset);
}
}
void MaterialViewportWidget::OnShadowCatcherEnabledChanged(bool enable)
{
AZ::Render::MeshComponentRequestBus::Event(
m_shadowCatcherEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetVisibility, enable);
}
void MaterialViewportWidget::OnGridEnabledChanged(bool enable)
{
if (m_gridEntity)
{
if (enable && m_gridEntity->GetState() == AZ::Entity::State::Init)
{
m_gridEntity->Activate();
}
else if (!enable && m_gridEntity->GetState() == AZ::Entity::State::Active)
{
m_gridEntity->Deactivate();
}
}
}
void MaterialViewportWidget::OnAlternateSkyboxEnabledChanged(bool enable)
{
AZ_UNUSED(enable);
AZ::Render::LightingPresetPtr selectedPreset;
MaterialViewportRequestBus::BroadcastResult(selectedPreset, &MaterialViewportRequestBus::Events::GetLightingPresetSelection);
OnLightingPresetSelected(selectedPreset);
}
void MaterialViewportWidget::OnFieldOfViewChanged(float fieldOfView)
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(
&MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView);
}
void MaterialViewportWidget::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType)
{
AZ::Render::DisplayMapperConfigurationDescriptor desc;
desc.m_operationType = operationType;
m_displayMapperFeatureProcessor->RegisterDisplayMapperConfiguration(desc);
}
void MaterialViewportWidget::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_modelAssetId == asset.GetId())
{
MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset);
AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId());
}
}
void MaterialViewportWidget::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AtomToolsFramework::RenderViewportWidget::OnTick(deltaTime, time);
m_renderPipeline->AddToRenderTickOnce();
PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::GatherMetrics);
if (m_shadowCatcherMaterial)
{
// Compile the m_shadowCatcherMaterial in OnTick because changes can only be compiled once per frame.
// This is ignored when a compile isn't needed.
m_shadowCatcherMaterial->Compile();
}
}
void MaterialViewportWidget::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
{
const AZ::EntityId* currentBusId = AZ::TransformNotificationBus::GetCurrentBusId();
if (m_cameraEntity && currentBusId && *currentBusId == m_cameraEntity->GetId() && m_directionalLightFeatureProcessor)
{
auto transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, m_cameraEntity->GetId(), &AZ::TransformBus::Events::GetWorldTM);
for (const DirectionalLightHandle& id : m_lightHandles)
{
m_directionalLightFeatureProcessor->SetCameraTransform(id, transform);
}
}
}
} // namespace MaterialEditor
@@ -5,42 +5,115 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/RPI.Public/Base.h>
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
#include <Viewport/MaterialViewportNotificationBus.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
namespace AZ
{
namespace Render
{
class DisplayMapperFeatureProcessorInterface;
}
class Entity;
class Component;
namespace RPI
{
class SwapChainPass;
class WindowContext;
} // namespace RPI
} // namespace AZ
namespace Ui
{
class MaterialViewportWidget;
}
namespace AZ
{
namespace RPI
{
class WindowContext;
}
}
namespace MaterialEditor
{
class MaterialViewportRenderer;
class MaterialViewportWidget
: public AtomToolsFramework::RenderViewportWidget
, public AZ::Data::AssetBus::Handler
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public MaterialViewportNotificationBus::Handler
, public AZ::TransformNotificationBus::MultiHandler
{
public:
MaterialViewportWidget(QWidget* parent = nullptr);
~MaterialViewportWidget();
private:
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// MaterialViewportNotificationBus::Handler interface overrides...
void OnLightingPresetSelected(AZ::Render::LightingPresetPtr preset) override;
void OnLightingPresetChanged(AZ::Render::LightingPresetPtr preset) override;
void OnModelPresetSelected(AZ::Render::ModelPresetPtr preset) override;
void OnModelPresetChanged(AZ::Render::ModelPresetPtr preset) override;
void OnShadowCatcherEnabledChanged(bool enable) override;
void OnGridEnabledChanged(bool enable) override;
void OnAlternateSkyboxEnabledChanged(bool enable) override;
void OnFieldOfViewChanged(float fieldOfView) override;
void OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType) override;
// AZ::Data::AssetBus::Handler interface overrides...
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
// AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// AZ::TransformNotificationBus::MultiHandler overrides...
void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;
AZStd::string m_defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset";
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ScenePtr m_scene;
AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = {};
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = {};
AZ::Entity* m_cameraEntity = {};
AZ::Component* m_cameraComponent = {};
AZ::Entity* m_postProcessEntity = {};
AZ::Entity* m_modelEntity = {};
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = {};
AZ::Entity* m_shadowCatcherEntity = {};
AZ::Data::Instance<AZ::RPI::Material> m_shadowCatcherMaterial;
AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex;
AZStd::vector<DirectionalLightHandle> m_lightHandles;
AZ::Entity* m_iblEntity = {};
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = {};
AZStd::shared_ptr<MaterialEditorViewportInputController> m_viewportController;
QScopedPointer<Ui::MaterialViewportWidget> m_ui;
AZStd::unique_ptr<MaterialViewportRenderer> m_renderer;
};
} // namespace MaterialEditor
@@ -49,8 +49,6 @@ set(FILES
Source/Viewport/MaterialViewportWidget.cpp
Source/Viewport/MaterialViewportWidget.h
Source/Viewport/MaterialViewportWidget.ui
Source/Viewport/MaterialViewportRenderer.cpp
Source/Viewport/MaterialViewportRenderer.h
Source/Viewport/PerformanceMonitorComponent.cpp
Source/Viewport/PerformanceMonitorComponent.h