From aabde7916fde25177c737c199c9cb8ee94ae2753 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Sun, 23 Jan 2022 16:25:57 -0600 Subject: [PATCH] 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 --- .../Viewport/MaterialViewportRenderer.cpp | 462 ------------------ .../Viewport/MaterialViewportRenderer.h | 114 ----- .../Viewport/MaterialViewportWidget.cpp | 458 ++++++++++++++++- .../Source/Viewport/MaterialViewportWidget.h | 97 +++- .../Code/materialeditor_files.cmake | 2 - 5 files changed, 536 insertions(+), 597 deletions(-) delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp delete mode 100644 Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp deleted file mode 100644 index 478fb92c55..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ /dev/null @@ -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 -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace MaterialEditor -{ - static constexpr float DepthNear = 0.01f; - - MaterialViewportRenderer::MaterialViewportRenderer(AZStd::shared_ptr windowContext) - : m_windowContext(windowContext) - , m_viewportController(AZStd::make_shared()) - { - 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 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 pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath(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()); - m_cameraComponent->SetConfiguration(cameraConfig); - m_cameraEntity->CreateComponent(azrtti_typeid()); - 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()); - m_postProcessEntity->Activate(); - - // Init directional light processor - m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor(); - - // Init display mapper processor - m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor(); - - // Init Skybox - m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor(); - 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()); - 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()); - 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()); - m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); - 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("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()); - 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 viewportSettings = - AZ::UserSettings::CreateFind(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 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 MaterialViewportRenderer::GetController() - { - return m_viewportController; - } - - void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId) - { - AZ::Data::Instance 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::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = m_scene->GetFeatureProcessor(); - - 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 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); - } - } - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h deleted file mode 100644 index 35b7965a2e..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include - -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 windowContext); - ~MaterialViewportRenderer(); - - AZStd::shared_ptr 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 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 m_swapChainPass; - AZStd::string m_defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset"; - AZStd::shared_ptr 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 m_shadowCatcherMaterial; - AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex; - - AZStd::vector m_lightHandles; - - AZ::Entity* m_iblEntity = nullptr; - AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr; - - AZStd::shared_ptr m_viewportController; - }; -} // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index 0322fa194e..dd157c59ac 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -6,29 +6,65 @@ * */ +#undef RC_INVOKED + +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include #include +#include +#include #include #include #include - -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include -#include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING -#include - 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()) { 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(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 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 pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( + 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()); + m_cameraComponent->SetConfiguration(cameraConfig); + m_cameraEntity->CreateComponent(azrtti_typeid()); + 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()); + m_postProcessEntity->Activate(); + + // Init directional light processor + m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor(); + + // Init display mapper processor + m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor(); + + // Init Skybox + m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor(); + 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()); + 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()); + 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()); + m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); + 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( + "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()); + 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 viewportSettings = + AZ::UserSettings::CreateFind(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 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 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::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = + m_scene->GetFeatureProcessor(); + + 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 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 diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h index 2a71dcc1df..8a660c6603 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h @@ -5,42 +5,115 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#include +#include +#include #include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include AZ_POP_DISABLE_WARNING #endif -#include +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 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 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 m_shadowCatcherMaterial; + AZ::RPI::MaterialPropertyIndex m_shadowCatcherOpacityPropertyIndex; + + AZStd::vector m_lightHandles; + + AZ::Entity* m_iblEntity = {}; + AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = {}; + + AZStd::shared_ptr m_viewportController; QScopedPointer m_ui; - AZStd::unique_ptr m_renderer; }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake index 9d1e2b22f8..f7a15d2bcc 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake @@ -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