From cda2bb9d4d080e874cecac5ce0a4345be946b8fd Mon Sep 17 00:00:00 2001 From: rhhong Date: Thu, 9 Sep 2021 21:36:52 -0700 Subject: [PATCH] Add anim viewport renderer Signed-off-by: rhhong --- .../EMotionFXAtom/Code/CMakeLists.txt | 3 + .../Tools/EMStudio/AnimViewportRenderer.cpp | 279 ++++++++++++++++++ .../Tools/EMStudio/AnimViewportRenderer.h | 79 +++++ .../Tools/EMStudio/AnimViewportWidget.cpp | 13 + .../Code/Tools/EMStudio/AnimViewportWidget.h | 5 + .../Code/emotionfx_atom_editor_files.cmake | 2 + 6 files changed, 381 insertions(+) create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp create mode 100644 Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt index dc22c8ee93..cc71e4f237 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt @@ -62,6 +62,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) Gem::EMotionFX.Editor.Static Gem::AtomToolsFramework.Static Gem::AtomToolsFramework.Editor + Gem::Atom_Component_DebugCamera.Static + Gem::Atom_Feature_Common.Static + Gem::AtomLyIntegration_CommonFeatures.Static RUNTIME_DEPENDENCIES Gem::EMotionFX.Editor COMPILE_DEFINITIONS diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp new file mode 100644 index 0000000000..96fae3cdaf --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#pragma optimize("", off) + +namespace EMStudio +{ + static constexpr float DepthNear = 0.01f; + + AnimViewportRenderer::AnimViewportRenderer(AZStd::shared_ptr windowContext) + : m_windowContext(windowContext) + { + // Create a new entity context + m_entityContext = AZStd::make_unique(); + m_entityContext->InitContext(); + + // Create the scene + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Unable to retrieve scene system."); + AZ::Outcome, AZStd::string> createSceneOutcome = sceneSystem->CreateScene("AnimViewport"); + AZ_Assert(createSceneOutcome, "%s", createSceneOutcome.GetError().data()); + m_frameworkScene = createSceneOutcome.TakeValue(); + m_frameworkScene->SetSubsystem(m_entityContext.get()); + + // Create and register a scene with all available feature processors + // TODO: We don't need every procesors. + AZ::RPI::SceneDescriptor sceneDesc; + m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); + m_scene->EnableAllFeatureProcessors(); + + // Link our RPI::Scene to the AzFramework::Scene + m_frameworkScene->SetSubsystem(m_scene); + + // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene + AZStd::string defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset"; + AZ::Data::Asset pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( + 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 = m_entityContext->GetContextId(); + + // 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->CreateComponent(azrtti_typeid()); + m_cameraEntity->Init(); + m_cameraEntity->Activate(); + + // Connect camera to pipeline's default view after camera entity activated + m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId()); + + // Get the FeatureProcessors + m_meshFeatureProcessor = m_scene->GetFeatureProcessor(); + // Helper function to load meshes + /* + const auto LoadMesh = [this](const char* modelPath) -> AZ::Render::MeshFeatureProcessorInterface::MeshHandle + { + AZ_Assert(m_meshFeatureProcessor, "Cannot find mesh feature processor on scene"); + + auto meshAsset = AZ::RPI::AssetUtils::GetAssetByProductPath(modelPath, AZ::RPI::AssetUtils::TraceLevel::Assert); + auto materialAsset = + AZ::RPI::AssetUtils::LoadAssetByProductPath("materials/defaultpbr.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert); + auto material = AZ::RPI::Material::FindOrCreate(materialAsset); + AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = + m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ meshAsset }, material); + + return meshHandle; + }; + LoadMesh("objects/shaderball_simple.azmodel"); + */ + + // 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(); + + // Temp: Load light preset + AZ::Data::Asset lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( + "lightingpresets/default.lightingpreset.azasset", AZ::RPI::AssetUtils::TraceLevel::Warning); + const AZ::Render::LightingPreset* preset = lightingPresetAsset->GetDataAs(); + SetLightingPreset(preset); + + // 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 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.5f, 0.5f, 0.5f, 1.0f); + gridConfig.m_primaryColor = AZ::Color(0.3f, 0.3f, 0.3f, 1.0f); + gridConfig.m_secondaryColor = AZ::Color(0.5f, 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(); + + Reset(); + AZ::TickBus::Handler::BusConnect(); + } + + AnimViewportRenderer::~AnimViewportRenderer() + { + AZ::TickBus::Handler::BusDisconnect(); + } + + void AnimViewportRenderer::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + // m_renderPipeline->AddToRenderTickOnce(); + } + + void AnimViewportRenderer::Reset() + { + // Reset environment + AZ::Transform iblTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform); + + const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); + AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); + + // Reset model + AZ::Transform modelTransform = AZ::Transform::CreateIdentity(); + modelTransform.SetTranslation(AZ::Vector3(1.0f, 2.0f, 0.5f)); + modelTransform.SetUniformScale(3.3f); + // modelTransform.SetUniformScale(50.0f); + AZ::TransformBus::Event(m_modelEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, modelTransform); + + auto modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath( + "objects/shaderball_simple.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert); + AZ::Render::MeshComponentRequestBus::Event( + m_modelEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, modelAsset); + + Camera::Configuration cameraConfig; + Camera::CameraRequestBus::EventResult( + cameraConfig, m_cameraEntity->GetId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration); + + // Reset the camera position + static constexpr float StartingDistanceMultiplier = 2.0f; + static constexpr float StartingRotationAngle = AZ::Constants::QuarterPi / 2.0f; + + AZ::Vector3 targetPosition = modelTransform.GetTranslation(); + const float distance = 1.0f * StartingDistanceMultiplier; + const AZ::Quaternion cameraRotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), StartingRotationAngle); + AZ::Vector3 cameraPosition(targetPosition.GetX(), targetPosition.GetY() - distance, targetPosition.GetZ()); + cameraPosition = cameraRotation.TransformVector(cameraPosition); + AZ::Transform cameraTransform = AZ::Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition); + AZ::TransformBus::Event(m_cameraEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, cameraTransform); + + // Setup primary camera controls + AZ::Debug::CameraControllerRequestBus::Event( + m_cameraEntity->GetId(), &AZ::Debug::CameraControllerRequestBus::Events::Enable, azrtti_typeid()); + } + + void AnimViewportRenderer::SetLightingPreset(const AZ::Render::LightingPreset* preset) + { + if (!preset) + { + AZ_Warning("MaterialViewportRenderer", false, "Attempting to set invalid lighting 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; + + AZStd::vector lightHandles; + preset->ApplyLightingPreset( + iblFeatureProcessor, m_skyboxFeatureProcessor, exposureControlSettingInterface, m_directionalLightFeatureProcessor, + cameraConfig, lightHandles, nullptr, AZ::RPI::MaterialPropertyIndex::Null, enableAlternateSkybox); + } +} // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h new file mode 100644 index 0000000000..9bf92aeff9 --- /dev/null +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace AZ +{ + class Entity; + class Component; + + namespace Render + { + class DisplayMapperFeatureProcessorInterface; + class DirectionalLightFeatureProcessorInterface; + class MeshFeatureProcessorInterface; + } + + namespace RPI + { + class WindowContext; + } +} + +namespace EMStudio +{ + //! + //! + class AnimViewportRenderer + : public AZ::TickBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(AnimViewportRenderer, AZ::SystemAllocator, 0); + + AnimViewportRenderer(AZStd::shared_ptr windowContext); + ~AnimViewportRenderer(); + + private: + // AZ::TickBus::Handler interface overrides... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + void Reset(); + void SetLightingPreset(const AZ::Render::LightingPreset* preset); + + AZStd::shared_ptr m_windowContext; + AZStd::unique_ptr m_entityContext; + AZStd::shared_ptr m_frameworkScene; + AZ::RPI::ScenePtr m_scene; + AZ::RPI::RenderPipelinePtr m_renderPipeline; + AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr; + AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = nullptr; + AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr; + AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; + + AZ::Entity* m_postProcessEntity = nullptr; + AZ::Entity* m_iblEntity = nullptr; + + AZ::Entity* m_cameraEntity = nullptr; + AZ::Component* m_cameraComponent = nullptr; + + AZ::Entity* m_modelEntity = nullptr; + AZ::Data::AssetId m_modelAssetId; + + AZ::Entity* m_gridEntity = nullptr; + }; +} diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 2b961e6e7b..39b45507da 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -6,13 +6,26 @@ * */ +#include + #include +#include namespace EMStudio { AnimViewportWidget::AnimViewportWidget(QWidget* parent) : AtomToolsFramework::RenderViewportWidget(parent) { + setObjectName(QString::fromUtf8("AtomViewportWidget")); + resize(869, 574); + QSizePolicy qSize(QSizePolicy::Preferred, QSizePolicy::Preferred); + qSize.setHorizontalStretch(0); + qSize.setVerticalStretch(0); + qSize.setHeightForWidth(sizePolicy().hasHeightForWidth()); + setSizePolicy(qSize); + setAutoFillBackground(false); + setStyleSheet(QString::fromUtf8("")); + m_renderer = AZStd::make_unique(GetViewportContext()->GetWindowContext()); } } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 59ea5c6680..26bb9838b3 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -11,10 +11,15 @@ namespace EMStudio { + class AnimViewportRenderer; + class AnimViewportWidget : public AtomToolsFramework::RenderViewportWidget { public: AnimViewportWidget(QWidget* parent = nullptr); + + private: + AZStd::unique_ptr m_renderer; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake index 37e69739d0..5c8728a8e9 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/emotionfx_atom_editor_files.cmake @@ -14,4 +14,6 @@ set(FILES Tools/EMStudio/AtomRenderPlugin.cpp Tools/EMStudio/AnimViewportWidget.h Tools/EMStudio/AnimViewportWidget.cpp + Tools/EMStudio/AnimViewportRenderer.h + Tools/EMStudio/AnimViewportRenderer.cpp )