From 4c2933b38d36dca8e2e0a2d412114a1bd1ab0d92 Mon Sep 17 00:00:00 2001 From: mgwynn Date: Mon, 17 May 2021 18:44:32 -0400 Subject: [PATCH 001/105] Adding gems metadata query --- cmake/Tools/registration.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/cmake/Tools/registration.py b/cmake/Tools/registration.py index 184d2cdb31..70fbc5ba90 100755 --- a/cmake/Tools/registration.py +++ b/cmake/Tools/registration.py @@ -2216,6 +2216,19 @@ def get_gem_data(gem_name: str = None, return None +def get_gems_metadata(): + gem_list = get_gems() + get_engine_gems() + gem_data_dict = {} + for gem in gem_list: + json_path = os.path.join(gem, 'gem.json') + if (os.path.exists(json_path)): + with open(json_path, 'r') as gem_json: + parsed_meta_data = json.loads(gem_json.read()) + gem_data_dict[str(parsed_meta_data['gem_name'])] = parsed_meta_data + else: + logger.error(f'Gem json {gem_json} is not present.') + json_result = json.dumps(gem_data_dict, indent = 4) + return json_result def get_template_data(template_name: str = None, template_path: str or pathlib.Path = None, ) -> dict or None: @@ -2410,6 +2423,12 @@ def print_downloadables(verbose: int) -> None: print_templates_data(downloadable_data['templates']) print_restricted_data(downloadable_data['templates']) +def print_gems_metadata(verbose: int) -> None: + gems_data = get_gems_metadata() + print(gems_data) + if verbose > 0: + gem_list = get_gems() + get_engine_gems() + print(gem_list) def download_engine(engine_name: str, dest_path: str) -> int: @@ -3833,7 +3852,6 @@ def _run_register_show(args: argparse) -> int: if args.this_engine: print_this_engine(args.verbose) return 0 - elif args.engines: print_engines(args.verbose) return 0 @@ -3897,6 +3915,9 @@ def _run_register_show(args: argparse) -> int: elif args.downloadable_templates: print_downloadable_templates(args.verbose) return 0 + elif args.gems_data: + print_gems_metadata(args.verbose) + return 0 else: register_show(args.verbose) return 0 @@ -4168,6 +4189,9 @@ def add_args(parser, subparsers) -> None: group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False, default=False, help='Combine all repos templates into a single list of resources.') + group.add_argument('-gd', '--gems-data', action='store_true', required=False, + default=False, + help='Returns a json formatted string of meta data for all local and engine gems.') register_show_subparser.add_argument('-v', '--verbose', action='count', required=False, default=0, From 17e9c17f311bca70b61b3295c4aef49d23a2dd46 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 26 May 2021 19:18:18 -0700 Subject: [PATCH 002/105] Added Occlusion Culling Planes and RPI Culling support for Masked Occlusion Culling --- ...ionCullingPlaneFeatureProcessorInterface.h | 41 +++++ .../Code/Source/CommonSystemComponent.cpp | 4 + .../OcclusionCullingPlaneFeatureProcessor.cpp | 96 ++++++++++ .../OcclusionCullingPlaneFeatureProcessor.h | 75 ++++++++ .../Code/atom_feature_common_files.cmake | 2 + .../atom_feature_common_public_files.cmake | 1 + .../Code/Include/Atom/RPI.Public/Culling.h | 12 +- .../RPI/Code/Include/Atom/RPI.Public/View.h | 12 +- .../Source/Platform/Windows/PAL_windows.cmake | 13 ++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 171 +++++++++++++----- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 3 +- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 68 ++++++- .../Atom/RPI/Code/atom_rpi_public_files.cmake | 4 + .../CommonFeatures/Code/Source/Module.cpp | 4 + .../EditorOcclusionCullingPlaneComponent.cpp | 91 ++++++++++ .../EditorOcclusionCullingPlaneComponent.h | 43 +++++ .../OcclusionCullingPlaneComponent.cpp | 43 +++++ .../OcclusionCullingPlaneComponent.h | 37 ++++ .../OcclusionCullingPlaneComponentConstants.h | 22 +++ ...clusionCullingPlaneComponentController.cpp | 137 ++++++++++++++ ...OcclusionCullingPlaneComponentController.h | 78 ++++++++ ...egration_commonfeatures_editor_files.cmake | 2 + ...omlyintegration_commonfeatures_files.cmake | 4 + 23 files changed, 913 insertions(+), 50 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h new file mode 100644 index 0000000000..07a6179e78 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h @@ -0,0 +1,41 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlane; + + using OcclusionCullingPlaneHandle = AZStd::shared_ptr; + + // OcclusionCullingPlaneFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom + class OcclusionCullingPlaneFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessorInterface, "{50F6B45E-A622-44EC-B962-DE25FBD44095}"); + + virtual OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) = 0; + virtual void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) = 0; + virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0; + virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0; + virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 089a6168b1..614e614c06 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -102,6 +102,7 @@ #include #include #include +#include namespace AZ { @@ -137,6 +138,7 @@ namespace AZ ModelPreset::Reflect(context); DiffuseProbeGridFeatureProcessor::Reflect(context); RayTracingFeatureProcessor::Reflect(context); + OcclusionCullingPlaneFeatureProcessor::Reflect(context); if (SerializeContext* serialize = azrtti_cast(context)) { @@ -193,6 +195,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); // Add SkyBox pass auto* passSystem = RPI::PassSystemInterface::Get(); @@ -295,6 +298,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } void CommonSystemComponent::LoadPassTemplateMappings() diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp new file mode 100644 index 0000000000..d4a1a37521 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -0,0 +1,96 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void OcclusionCullingPlaneFeatureProcessor::Activate() + { + m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + + EnableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::Deactivate() + { + AZ_Warning("OcclusionCullingPlaneFeatureProcessor", m_occlusionCullingPlanes.size() == 0, + "Deactivating the OcclusionCullingPlaneFeatureProcessor, but there are still outstanding occlusion planes. Components\n" + "using OcclusionCullingPlaneHandles should free them before the OcclusionCullingPlaneFeatureProcessor is deactivated.\n" + ); + + DisableSceneNotification(); + } + + void OcclusionCullingPlaneFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) + { + AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + + AZStd::vector occlusionCullingPlanes; + for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + occlusionCullingPlanes.push_back(occlusionCullingPlane->GetTransform()); + } + GetParentScene()->GetCullingScene()->SetOcclusionCullingPlanes(occlusionCullingPlanes); + } + + OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) + { + AZStd::shared_ptr occlusionCullingPlane = AZStd::make_shared(); + occlusionCullingPlane->SetTransform(transform); + m_occlusionCullingPlanes.push_back(occlusionCullingPlane); + return occlusionCullingPlane; + } + + void OcclusionCullingPlaneFeatureProcessor::RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& occlusionCullingPlane) + { + AZ_Assert(occlusionCullingPlane.get(), "RemoveOcclusionCullingPlane called with an invalid handle"); + + auto itEntry = AZStd::find_if(m_occlusionCullingPlanes.begin(), m_occlusionCullingPlanes.end(), [&](AZStd::shared_ptr const& entry) + { + return (entry == occlusionCullingPlane); + }); + + AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list"); + m_occlusionCullingPlanes.erase(itEntry); + occlusionCullingPlane = nullptr; + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle"); + occlusionCullingPlane->SetTransform(transform); + } + + void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) + { + AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); + occlusionCullingPlane->SetEnabled(enabled); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h new file mode 100644 index 0000000000..c54c816bfd --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -0,0 +1,75 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum + class OcclusionCullingPlane final + { + public: + OcclusionCullingPlane() = default; + ~OcclusionCullingPlane() = default; + + void SetTransform(const AZ::Transform& transform) { m_transform = transform; } + const AZ::Transform& GetTransform() const { return m_transform; } + + void SetEnabled(bool enabled) { m_enabled = enabled; } + bool GetEnabled() const { return m_enabled; } + + private: + AZ::Transform m_transform; + bool m_enabled = true; + }; + + //! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum + class OcclusionCullingPlaneFeatureProcessor final + : public OcclusionCullingPlaneFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessor, "{C3DE91D7-EF7A-4A82-A55F-E22BC52074EA}", OcclusionCullingPlaneFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + OcclusionCullingPlaneFeatureProcessor() = default; + virtual ~OcclusionCullingPlaneFeatureProcessor() = default; + + // OcclusionCullingPlaneFeatureProcessorInterface overrides + OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) override; + void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) override; + bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); } + void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override; + void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override; + + // FeatureProcessor overrides + void Activate() override; + void Deactivate() override; + void Simulate(const FeatureProcessor::SimulatePacket& packet) override; + + // retrieve the full list of occlusion planes + using OcclusionCullingPlaneVector = AZStd::vector>; + OcclusionCullingPlaneVector& GetOcclusionCullingPlanes() { return m_occlusionCullingPlanes; } + + private: + AZ_DISABLE_COPY_MOVE(OcclusionCullingPlaneFeatureProcessor); + + // list of occlusion planes + const size_t InitialOcclusionCullingPlanesAllocationSize = 64; + OcclusionCullingPlaneVector m_occlusionCullingPlanes; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 8926b0c19f..f3ee1b4757 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -175,6 +175,8 @@ set(FILES Source/MorphTargets/MorphTargetComputePass.h Source/MorphTargets/MorphTargetDispatchItem.cpp Source/MorphTargets/MorphTargetDispatchItem.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp Source/PostProcess/PostProcessBase.cpp Source/PostProcess/PostProcessBase.h Source/PostProcess/PostProcessFeatureProcessor.cpp diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 9034859707..3df15f9a70 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -44,6 +44,7 @@ set(FILES Include/Atom/Feature/ParamMacros/StartParamFunctionsVirtual.inl Include/Atom/Feature/ParamMacros/StartParamMembers.inl Include/Atom/Feature/ParamMacros/StartParamSerializeContext.inl + Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostProcessParams.inl Include/Atom/Feature/PostProcess/PostProcessSettings.inl diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index c7e9cc4706..17e0a1f82d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -31,7 +31,7 @@ #include #include - +#include #include #include @@ -213,8 +213,11 @@ namespace AZ void Activate(const class Scene* parentScene); void Deactivate(); + //! Sets a list of occlusion planes to be used during the culling process. + void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } + //! Notifies the CullingScene that culling will begin for this frame. - void BeginCulling(const AZStd::vector& views); + void BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines); //! Notifies the CullingScene that the culling is done for this frame. void EndCulling(); @@ -251,12 +254,9 @@ namespace AZ const Scene* m_parentScene = nullptr; AzFramework::IVisibilityScene* m_visScene = nullptr; - CullingDebugContext m_debugCtx; - AZStd::concurrency_checker m_cullDataConcurrencyCheck; - - AZStd::mutex m_mutex; + AZStd::vector m_occlusionCullingPlanes; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index aad099dc23..74c841d2a5 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -57,7 +58,7 @@ namespace AZ //! Only use this function to create a new view object. And force using smart pointer to manage view's life time static ViewPtr CreateView(const AZ::Name& name, UsageFlags usage); - ~View() = default; + ~View(); void SetDrawListMask(const RHI::DrawListMask& drawListMask); RHI::DrawListMask GetDrawListMask() const { return m_drawListMask; } @@ -126,6 +127,12 @@ namespace AZ //! Notifies consumers when the world to clip matrix has changed. void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler); + //! Prepare for view culling + void BeginCulling(const AZStd::vector& activePipelines); + + //! Returns the masked occlusion culling interface + MaskedOcclusionCulling* GetMaskedOcclusionCulling(); + private: View() = delete; View(const AZ::Name& name, UsageFlags usage); @@ -193,6 +200,9 @@ namespace AZ MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; + + // Software occlusion culling + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; }; AZ_DEFINE_ENUM_BITWISE_OPERATORS(View::UsageFlags); diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake index c060b8bbaa..51e42d5216 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,3 +10,16 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) + +ly_add_source_properties( + SOURCES Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + PROPERTY COMPILE_OPTIONS + VALUES /arch:AVX2 /W3 +) +ly_add_source_properties( + SOURCES + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + PROPERTY COMPILE_OPTIONS + VALUES /W3 +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index c64f08e4f8..ab25fb14fb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -262,21 +262,24 @@ namespace AZ public: AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0); + struct JobData + { + CullingDebugContext* m_debugCtx = nullptr; + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; + const Scene* m_scene = nullptr; + View* m_view = nullptr; + Frustum m_frustum; + }; + private: - CullingDebugContext* m_debugCtx; - const Scene* m_scene; - View* m_view; - Frustum m_frustum; + const AZStd::shared_ptr m_jobData; CullingScene::WorkListType m_worklist; public: - AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingScene::WorkListType& worklist) + AddObjectsToViewJob(const AZStd::shared_ptr& jobData, CullingScene::WorkListType& worklist) : Job(true, nullptr) //auto-deletes, no JobContext - , m_debugCtx(&debugCtx) - , m_scene(&scene) - , m_view(&view) - , m_frustum(frustum) //capture by value - , m_worklist(AZStd::move(worklist)) //capture by value + , m_jobData(jobData) + , m_worklist(worklist) { } @@ -285,37 +288,40 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - const View::UsageFlags viewFlags = m_view->GetUsageFlags(); - const RHI::DrawListMask drawListMask = m_view->GetDrawListMask(); + const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags(); + const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask(); uint32_t numDrawPackets = 0; uint32_t numVisibleCullables = 0; for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist) { //If a node is entirely contained within the frustum, then we can skip the fine grained culling. - bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_frustum, nodeData.m_bounds); + bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds); #ifdef AZ_CULL_PROFILE_VERBOSE AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "process node (view: %s, skip fine cull: %d", m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0); #endif - if (nodeIsContainedInFrustum || !m_debugCtx->m_enableFrustumCulling) + if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling) { //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { - if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) { - Cullable* c = static_cast(visibleEntry->m_userData); - if ((c->m_cullData.m_drawListMask & drawListMask).none() || - c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { - continue; + Cullable* c = static_cast(visibleEntry->m_userData); + if ((c->m_cullData.m_drawListMask & drawListMask).none() || + c->m_cullData.m_hideFlags & viewFlags || + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + { + continue; + } + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; } - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; } } } @@ -329,66 +335,69 @@ namespace AZ Cullable* c = static_cast(visibleEntry->m_userData); if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || - c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this + c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this { continue; } - IntersectResult res = ShapeIntersection::Classify(m_frustum, c->m_cullData.m_boundingSphere); + IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere); if (res == IntersectResult::Exterior) { continue; } - else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_frustum, c->m_cullData.m_boundingObb)) + else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb)) { - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view); - ++numVisibleCullables; + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + } } } } } - if (m_debugCtx->m_debugDraw && (m_view->GetName() == m_debugCtx->m_currentViewSelectionName)) + if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName)) { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "debug draw culling"); - AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_scene); + AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene); if (auxGeomPtr) { //Draw the node bounds // "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum. // Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so // we have a few debug booleans to toggle which ones to draw. - if (nodeIsContainedInFrustum && m_debugCtx->m_drawFullyVisibleNodes) + if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } - else if (!nodeIsContainedInFrustum && m_debugCtx->m_drawPartiallyVisibleNodes) + else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes) { auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off); } //Draw bounds on individual objects - if (m_debugCtx->m_drawBoundingBoxes || m_debugCtx->m_drawBoundingSpheres || m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii) { for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); - if (m_debugCtx->m_drawBoundingBoxes) + if (m_jobData->m_debugCtx->m_drawBoundingBoxes) { auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(), nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line); } - if (m_debugCtx->m_drawBoundingSpheres) + if (m_jobData->m_debugCtx->m_drawBoundingSpheres) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(), Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded); } - if (m_debugCtx->m_drawLodRadii) + if (m_jobData->m_debugCtx->m_drawLodRadii) { auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData.m_lodSelectionRadius, @@ -401,9 +410,9 @@ namespace AZ } } - if (m_debugCtx->m_enableStats) + if (m_jobData->m_debugCtx->m_enableStats) { - CullingDebugContext::CullStats& cullStats = m_debugCtx->GetCullStatsForView(m_view); + CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view); //no need for mutex here since these are all atomics cullStats.m_numVisibleDrawPackets += numDrawPackets; @@ -411,6 +420,29 @@ namespace AZ ++cullStats.m_numJobs; } } + + MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry) + { + if (!m_jobData->m_maskedOcclusionCulling) + { + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } + + // convert the bounding box of the visibility entry to NDC + AZ::Vector4 clipSpaceMin = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMin()); + float depth = clipSpaceMin.GetW(); + AZ::Vector4 ndcMin = clipSpaceMin / clipSpaceMin.GetW(); + + AZ::Vector4 clipSpaceMax = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMax()); + depth = AZStd::min(depth, clipSpaceMax.GetW()); + AZ::Vector4 ndcMax = clipSpaceMax / clipSpaceMax.GetW(); + + Vector2 rectMin(AZStd::min(ndcMin.GetX(), ndcMax.GetX()), AZStd::min(ndcMin.GetY(), ndcMax.GetY())); + Vector2 rectMax(AZStd::max(ndcMin.GetX(), ndcMax.GetX()), AZStd::max(ndcMin.GetY(), ndcMax.GetY())); + + // test against the occlusion buffer, which contains only the manually placed occlusion planes + return m_jobData->m_maskedOcclusionCulling->TestRect(rectMin.GetX(), rectMin.GetY(), rectMax.GetX(), rectMax.GetY(), depth); + } }; void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob) @@ -444,8 +476,53 @@ namespace AZ cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } + // setup occlusion culling, if necessary + MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); + if (maskedOcclusionCulling) + { + for (const AZ::Transform& transform : m_occlusionCullingPlanes) + { + // find the corners of the plane + static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); + static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); + static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); + static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + + Vector3 planeBL = transform.TransformPoint(BL); + Vector3 planeBR = transform.TransformPoint(BR); + Vector3 planeTL = transform.TransformPoint(TL); + Vector3 planeTR = transform.TransformPoint(TR); + + // convert to clip-space + Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(planeBL); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(planeBR); + Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(planeTL); + Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(planeTR); + + // store to float array + float verts[16]; + projectedBL.StoreToFloat4(&verts[0]); + projectedBR.StoreToFloat4(&verts[4]); + projectedTL.StoreToFloat4(&verts[8]); + projectedTR.StoreToFloat4(&verts[12]); + + static uint32_t indices[6] = { 0, 2, 1, 2, 3, 1 }; + + // render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder + maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); + } + } + WorkListType worklist; - auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void + + AZStd::shared_ptr jobData = AZStd::make_shared(); + jobData->m_debugCtx = &m_debugCtx; + jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; + jobData->m_scene = &scene; + jobData->m_view = &view; + jobData->m_frustum = frustum; + + auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()"); AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries"); @@ -458,7 +535,7 @@ namespace AZ if (worklist.size() == worklist.capacity()) { //Kick off a job to process the (full) worklist - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes worklist.clear(); parentJob.SetContinuation(job); job->Start(); @@ -476,8 +553,15 @@ namespace AZ if (worklist.size() > 0) { + AZStd::shared_ptr remainingJobData = AZStd::make_shared(); + remainingJobData->m_debugCtx = &m_debugCtx; + remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; + remainingJobData->m_scene = &scene; + remainingJobData->m_view = &view; + remainingJobData->m_frustum = frustum; + //Kick off a job to process any remaining workitems - AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes + AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes parentJob.SetContinuation(job); job->Start(); } @@ -559,13 +643,18 @@ namespace AZ } } - void CullingScene::BeginCulling(const AZStd::vector& views) + void CullingScene::BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines) { m_cullDataConcurrencyCheck.soft_lock(); m_debugCtx.ResetCullStats(); m_debugCtx.m_numCullablesInScene = GetNumCullables(); + for (auto& view : views) + { + view->BeginCulling(activePipelines); + } + AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 7e33750eb5..16c2189a00 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -499,7 +500,7 @@ namespace AZ } // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) - m_cullingScene->BeginCulling(m_renderPacket.m_views); + m_cullingScene->BeginCulling(m_renderPacket.m_views, activePipelines); for (ViewPtr& viewPtr : m_renderPacket.m_views) { AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 21a46693d5..3060fc34a3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -15,7 +15,8 @@ #include #include #include - +#include +#include #include #include @@ -51,6 +52,18 @@ namespace AZ { m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset); } + + m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); + m_maskedOcclusionCulling->SetNearClipPlane(0.1f); + } + + View::~View() + { + if (m_maskedOcclusionCulling) + { + MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling); + m_maskedOcclusionCulling = nullptr; + } } void View::SetDrawListMask(const RHI::DrawListMask& drawListMask) @@ -374,5 +387,58 @@ namespace AZ m_shaderResourceGroup->Compile(); m_needBuildSrg = false; } + + void View::BeginCulling(const AZStd::vector& activePipelines) + { + // retrieve current resolution + Vector2 resolution(0.0f, 0.0f); + for (auto& pipeline : activePipelines) + { + ViewPtr pipelineView = pipeline->GetDefaultView(); + if (pipelineView.get() == this) + { + RPI::SwapChainPass* pass = AZ::RPI::PassSystemInterface::Get()->FindSwapChainPass(pipeline->GetWindowHandle()); + if (pass) + { + const RHI::Viewport& viewport = pass->GetViewport(); + resolution.SetX(viewport.m_maxX); + resolution.SetY(viewport.m_maxY); + } + break; + } + } + + // calculate culling resolution based on required tile size for MaskedOcclusionCulling + static const uint32_t MaskedOcclusionCullingSubTileWidth = 8; + static const uint32_t MaskedOcclusionCullingSubTileHeight = 4; + + uint32_t cullingWidth = RHI::AlignUp(resolution.GetX(), MaskedOcclusionCullingSubTileWidth); + uint32_t cullingHeight = RHI::AlignUp(resolution.GetY(), MaskedOcclusionCullingSubTileHeight); + + m_maskedOcclusionCulling->SetResolution(cullingWidth, cullingHeight); + + if (cullingWidth > 0 && cullingHeight > 0) + { + m_maskedOcclusionCulling->ClearBuffer(); + } + } + + MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() + { + if (m_maskedOcclusionCulling) + { + uint32_t width = 0; + uint32_t height = 0; + + m_maskedOcclusionCulling->GetResolution(width, height); + if (width > 0 && height > 0) + { + return m_maskedOcclusionCulling; + } + } + + return nullptr; + } + } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 0d5c19758b..5ae72fe42f 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -101,6 +101,7 @@ set(FILES Include/Atom/RPI.Public/GpuQuery/Query.h Include/Atom/RPI.Public/GpuQuery/QueryPool.h Include/Atom/RPI.Public/GpuQuery/TimestampQueryPool.h + Include/Atom/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h Source/RPI.Public/Culling.cpp Source/RPI.Public/FeatureProcessor.cpp Source/RPI.Public/FeatureProcessorFactory.cpp @@ -178,4 +179,7 @@ set(FILES Source/RPI.Public/GpuQuery/Query.cpp Source/RPI.Public/GpuQuery/QueryPool.cpp Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp ) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index 2ef4e1e229..368d8e76e7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +116,7 @@ namespace AZ DeferredFogComponent::CreateDescriptor(), SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(), AttachmentComponent::CreateDescriptor(), + OcclusionCullingPlaneComponent::CreateDescriptor(), #ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR EditorAreaLightComponent::CreateDescriptor(), @@ -145,6 +148,7 @@ namespace AZ EditorDeferredFogComponent::CreateDescriptor(), SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(), EditorAttachmentComponent::CreateDescriptor(), + EditorOcclusionCullingPlaneComponent::CreateDescriptor(), #endif }); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..9a655727d6 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -0,0 +1,91 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorOcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1, ConvertToEditorRenderComponentAdapter<1>) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Occlusion Culling Plane", "The OcclusionCullingPlane component is used to cull meshes that are inside the view frustum and behind the occlusion plane") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentController", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OcclusionCullingPlaneComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class( + "OcclusionCullingPlaneComponentConfig", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorOcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(EditorOcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent() + { + } + + EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void EditorOcclusionCullingPlaneComponent::Activate() + { + BaseClass::Activate(); + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + } + + void EditorOcclusionCullingPlaneComponent::Deactivate() + { + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + BaseClass::Deactivate(); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..8070c1d553 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorOcclusionCullingPlaneComponent final + : public EditorRenderComponentAdapter + , private AzFramework::EntityDebugDisplayEventBus::Handler + { + public: + using BaseClass = EditorRenderComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorOcclusionCullingPlaneComponent, EditorOcclusionCullingPlaneComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorOcclusionCullingPlaneComponent(); + EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + // AZ::Component overrides + void Activate() override; + void Deactivate() override; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp new file mode 100644 index 0000000000..567809266d --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace AZ +{ + namespace Render + { + OcclusionCullingPlaneComponent::OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config) + : BaseClass(config) + { + } + + void OcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ; + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("OcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(OcclusionCullingPlaneComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h new file mode 100644 index 0000000000..7e7b48bd45 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentTypeId, BaseClass); + + OcclusionCullingPlaneComponent() = default; + OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h new file mode 100644 index 0000000000..59276de9ee --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h @@ -0,0 +1,22 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const OcclusionCullingPlaneComponentTypeId = "{F7537387-15A8-48F0-A1F3-D19C5886B886}"; + static constexpr const char* const EditorOcclusionCullingPlaneComponentTypeId = "{BE7CC17B-32EB-49B0-BAD9-D26E3A059012}"; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp new file mode 100644 index 0000000000..bd03bec0f4 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp @@ -0,0 +1,137 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace AZ +{ + namespace Render + { + void OcclusionCullingPlaneComponentConfig::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ; + } + } + + void OcclusionCullingPlaneComponentController::Reflect(ReflectContext* context) + { + OcclusionCullingPlaneComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &OcclusionCullingPlaneComponentController::m_configuration); + } + } + + void OcclusionCullingPlaneComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50)); + } + + void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); + } + + void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC("TransformService")); + } + + OcclusionCullingPlaneComponentController::OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config) + : m_configuration(config) + { + } + + void OcclusionCullingPlaneComponentController::Activate(AZ::EntityId entityId) + { + m_entityId = entityId; + + TransformNotificationBus::Handler::BusConnect(m_entityId); + + m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); + AZ_Assert(m_featureProcessor, "OcclusionCullingPlaneComponentController was unable to find a OcclusionCullingPlaneFeatureProcessor on the EntityContext provided."); + + m_transformInterface = TransformBus::FindFirstHandler(entityId); + AZ_Assert(m_transformInterface, "Unable to attach to a TransformBus handler"); + if (!m_transformInterface) + { + return; + } + + // add this occlusion plane to the feature processor + const AZ::Transform& transform = m_transformInterface->GetWorldTM(); + m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform); + } + + void OcclusionCullingPlaneComponentController::Deactivate() + { + if (m_featureProcessor) + { + m_featureProcessor->RemoveOcclusionCullingPlane(m_handle); + } + + Data::AssetBus::MultiHandler::BusDisconnect(); + TransformNotificationBus::Handler::BusDisconnect(); + + m_transformInterface = nullptr; + m_featureProcessor = nullptr; + } + + void OcclusionCullingPlaneComponentController::SetConfiguration(const OcclusionCullingPlaneComponentConfig& config) + { + m_configuration = config; + } + + const OcclusionCullingPlaneComponentConfig& OcclusionCullingPlaneComponentController::GetConfiguration() const + { + return m_configuration; + } + + void OcclusionCullingPlaneComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world) + { + if (!m_featureProcessor) + { + return; + } + + m_featureProcessor->SetTransform(m_handle, world); + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h new file mode 100644 index 0000000000..5f0be5315f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h @@ -0,0 +1,78 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class OcclusionCullingPlaneComponentConfig final + : public AZ::ComponentConfig + { + public: + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentConfig, "{D0E107CA-5AFB-4675-BC97-94BCA5F248DB}", ComponentConfig); + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0); + static void Reflect(AZ::ReflectContext* context); + + OcclusionCullingPlaneComponentConfig() = default; + }; + + class OcclusionCullingPlaneComponentController final + : public Data::AssetBus::MultiHandler + , private TransformNotificationBus::Handler + { + public: + friend class EditorOcclusionCullingPlaneComponent; + + AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentController, AZ::SystemAllocator, 0); + AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentController, "{8EDA3C7D-5171-4843-9969-4D84DB13F221}"); + + static void Reflect(AZ::ReflectContext* context); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + OcclusionCullingPlaneComponentController() = default; + OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config); + + void Activate(AZ::EntityId entityId); + void Deactivate(); + void SetConfiguration(const OcclusionCullingPlaneComponentConfig& config); + const OcclusionCullingPlaneComponentConfig& GetConfiguration() const; + + private: + + AZ_DISABLE_COPY(OcclusionCullingPlaneComponentController); + + // TransformNotificationBus overrides + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + + // handle for this occlusion plane in the feature processor + OcclusionCullingPlaneHandle m_handle; + + OcclusionCullingPlaneFeatureProcessorInterface* m_featureProcessor = nullptr; + TransformInterface* m_transformInterface = nullptr; + AZ::EntityId m_entityId; + OcclusionCullingPlaneComponentConfig m_configuration; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index e58f72a121..360511aaea 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -53,6 +53,8 @@ set(FILES Source/Mesh/EditorMeshSystemComponent.h Source/Mesh/MeshThumbnail.h Source/Mesh/MeshThumbnail.cpp + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.cpp Source/PostProcess/EditorPostFxLayerComponent.h Source/PostProcess/Bloom/EditorBloomComponent.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e13d1d37d6..deb8ab1b74 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -66,6 +66,10 @@ set(FILES Source/Mesh/MeshComponent.cpp Source/Mesh/MeshComponentController.h Source/Mesh/MeshComponentController.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h + Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp Source/PostProcess/PostFxLayerComponent.cpp Source/PostProcess/PostFxLayerComponent.h Source/PostProcess/PostFxLayerComponentConfig.cpp From bee811ae4ffa6767853233bfe32416ba2a709450 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 27 May 2021 19:22:46 -0700 Subject: [PATCH 003/105] Moved Simulate to OnBeginPrepareRender. --- .../OcclusionCullingPlaneFeatureProcessor.cpp | 6 ++---- .../OcclusionCullingPlaneFeatureProcessor.h | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index d4a1a37521..bed008a3da 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -47,10 +47,8 @@ namespace AZ DisableSceneNotification(); } - void OcclusionCullingPlaneFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) - { - AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - + void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender() + { AZStd::vector occlusionCullingPlanes; for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) { diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index c54c816bfd..5319666745 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -58,7 +58,9 @@ namespace AZ // FeatureProcessor overrides void Activate() override; void Deactivate() override; - void Simulate(const FeatureProcessor::SimulatePacket& packet) override; + + // RPI::SceneNotificationBus overrides ... + void OnBeginPrepareRender() override; // retrieve the full list of occlusion planes using OcclusionCullingPlaneVector = AZStd::vector>; From c84882869bc57887d7e534e803a0c006d516d9fd Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 28 May 2021 03:41:44 -0700 Subject: [PATCH 004/105] Changed to a fixed-size occlusion buffer --- .../Code/Include/Atom/RPI.Public/Culling.h | 2 +- .../RPI/Code/Include/Atom/RPI.Public/View.h | 2 +- .../RPI/Code/Source/RPI.Public/Culling.cpp | 4 +- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 50 ++----------------- 5 files changed, 9 insertions(+), 51 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 17e0a1f82d..8892266683 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -217,7 +217,7 @@ namespace AZ void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } //! Notifies the CullingScene that culling will begin for this frame. - void BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines); + void BeginCulling(const AZStd::vector& views); //! Notifies the CullingScene that the culling is done for this frame. void EndCulling(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 74c841d2a5..0b6b41c8b0 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -128,7 +128,7 @@ namespace AZ void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler); //! Prepare for view culling - void BeginCulling(const AZStd::vector& activePipelines); + void BeginCulling(); //! Returns the masked occlusion culling interface MaskedOcclusionCulling* GetMaskedOcclusionCulling(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index ab25fb14fb..7bed2d3232 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -643,7 +643,7 @@ namespace AZ } } - void CullingScene::BeginCulling(const AZStd::vector& views, const AZStd::vector& activePipelines) + void CullingScene::BeginCulling(const AZStd::vector& views) { m_cullDataConcurrencyCheck.soft_lock(); @@ -652,7 +652,7 @@ namespace AZ for (auto& view : views) { - view->BeginCulling(activePipelines); + view->BeginCulling(); } AuxGeomDrawPtr auxGeom; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 16c2189a00..c02ac0713c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -500,7 +500,7 @@ namespace AZ } // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) - m_cullingScene->BeginCulling(m_renderPacket.m_views, activePipelines); + m_cullingScene->BeginCulling(m_renderPacket.m_views); for (ViewPtr& viewPtr : m_renderPacket.m_views) { AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 3060fc34a3..edae0f88b7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -54,7 +54,7 @@ namespace AZ } m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); - m_maskedOcclusionCulling->SetNearClipPlane(0.1f); + m_maskedOcclusionCulling->SetResolution(1920, 1080); } View::~View() @@ -388,56 +388,14 @@ namespace AZ m_needBuildSrg = false; } - void View::BeginCulling(const AZStd::vector& activePipelines) + void View::BeginCulling() { - // retrieve current resolution - Vector2 resolution(0.0f, 0.0f); - for (auto& pipeline : activePipelines) - { - ViewPtr pipelineView = pipeline->GetDefaultView(); - if (pipelineView.get() == this) - { - RPI::SwapChainPass* pass = AZ::RPI::PassSystemInterface::Get()->FindSwapChainPass(pipeline->GetWindowHandle()); - if (pass) - { - const RHI::Viewport& viewport = pass->GetViewport(); - resolution.SetX(viewport.m_maxX); - resolution.SetY(viewport.m_maxY); - } - break; - } - } - - // calculate culling resolution based on required tile size for MaskedOcclusionCulling - static const uint32_t MaskedOcclusionCullingSubTileWidth = 8; - static const uint32_t MaskedOcclusionCullingSubTileHeight = 4; - - uint32_t cullingWidth = RHI::AlignUp(resolution.GetX(), MaskedOcclusionCullingSubTileWidth); - uint32_t cullingHeight = RHI::AlignUp(resolution.GetY(), MaskedOcclusionCullingSubTileHeight); - - m_maskedOcclusionCulling->SetResolution(cullingWidth, cullingHeight); - - if (cullingWidth > 0 && cullingHeight > 0) - { - m_maskedOcclusionCulling->ClearBuffer(); - } + m_maskedOcclusionCulling->ClearBuffer(); } MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() { - if (m_maskedOcclusionCulling) - { - uint32_t width = 0; - uint32_t height = 0; - - m_maskedOcclusionCulling->GetResolution(width, height); - if (width > 0 && height > 0) - { - return m_maskedOcclusionCulling; - } - } - - return nullptr; + return m_maskedOcclusionCulling; } } // namespace RPI From ab45ea7efa3cacea1e0e809a3bc8e638bf183900 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Fri, 28 May 2021 17:15:20 -0700 Subject: [PATCH 005/105] [ATOM-15631] First pass on exposing Display Mapper properties to Behavior Context --- .../Common/Code/3rdParty/ACES/ACES/Aces.h | 1 + .../DisplayMapperConfigurationDescriptor.h | 8 ++- .../DisplayMapperConfigurationDescriptor.cpp | 26 +++++++++ .../DisplayMapper/DisplayMapperComponentBus.h | 55 +++++++++++++++++++ .../DisplayMapper/DisplayMapperComponent.cpp | 1 - .../DisplayMapperComponentController.cpp | 33 +++++++++++ .../DisplayMapperComponentController.h | 10 ++++ .../EditorDisplayMapperComponent.cpp | 13 ++++- ...egration_commonfeatures_public_files.cmake | 1 + 9 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h diff --git a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h index 472ef160be..cd9dca68e3 100644 --- a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h +++ b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h @@ -177,4 +177,5 @@ namespace AZ } // namespace Render AZ_TYPE_INFO_SPECIALIZE(Render::DisplayMapperOperationType, "{41CA80B1-9E0D-41FB-A235-9638D2A905A5}"); + AZ_TYPE_INFO_SPECIALIZE(Render::OutputDeviceTransformType, "{B94085B7-C0D4-466A-A791-188A4559EC8D}"); } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h index 4dc090b831..22c866447e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h @@ -12,11 +12,13 @@ #pragma once +#include + #include + #include #include #include -#include namespace AZ { @@ -33,6 +35,7 @@ namespace AZ AZ_TYPE_INFO(AcesParameterOverrides, "{3EE8C0D4-3792-46C0-B91C-B89A81C36B91}"); static void Reflect(ReflectContext* context); + // Load preconfigured preset for specific ODT mode defined by m_preset void LoadPreset(); // When enabled allows parameter overrides for ACES configuration @@ -98,6 +101,5 @@ namespace AZ DisplayMapperConfigurationDescriptor m_config; }; - - } // namespace RPI + } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index e91e125b40..0858381fc5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -23,6 +23,15 @@ namespace AZ { if (auto serializeContext = azrtti_cast(context)) { + serializeContext->Enum() + ->Version(0) + ->Value("48Nits", OutputDeviceTransformType::OutputDeviceTransformType_48Nits) + ->Value("1000Nits", OutputDeviceTransformType::OutputDeviceTransformType_1000Nits) + ->Value("2000Nits", OutputDeviceTransformType::OutputDeviceTransformType_2000Nits) + ->Value("4000Nits", OutputDeviceTransformType::OutputDeviceTransformType_4000Nits) + ->Value("NumOutputDeviceTransformTypes", OutputDeviceTransformType::NumOutputDeviceTransformTypes) + ; + serializeContext->Class() ->Version(0) ->Field("OverrideDefaults", &AcesParameterOverrides::m_overrideDefaults) @@ -38,6 +47,22 @@ namespace AZ ->Field("SurroundGamma", &AcesParameterOverrides::m_surroundGamma) ->Field("Gamma", &AcesParameterOverrides::m_gamma); } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class("AcesParameterOverrides") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "render") + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Constructor() + ->Method("LoadPreset", &AcesParameterOverrides::LoadPreset) + ->Property("overrideDefaults", BehaviorValueProperty(&AcesParameterOverrides::m_overrideDefaults)) + ->Property("preset", BehaviorValueProperty(&AcesParameterOverrides::m_preset)) + ->Property("alterSurround", BehaviorValueProperty(&AcesParameterOverrides::m_alterSurround)) + ->Property("applyDesaturation", BehaviorValueProperty(&AcesParameterOverrides::m_applyDesaturation)) + ->Property("applyCATD60toD65", BehaviorValueProperty(&AcesParameterOverrides::m_applyCATD60toD65)) + ; + } } void AcesParameterOverrides::LoadPreset() @@ -76,6 +101,7 @@ namespace AZ ->Field("OperationType", &DisplayMapperConfigurationDescriptor::m_operationType) ->Field("LdrGradingLutEnabled", &DisplayMapperConfigurationDescriptor::m_ldrGradingLutEnabled) ->Field("LdrColorGradingLut", &DisplayMapperConfigurationDescriptor::m_ldrColorGradingLut) + ->Field("AcesParameterOverrides", &DisplayMapperConfigurationDescriptor::m_acesParameterOverrides) ; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h new file mode 100644 index 0000000000..af57b69d3b --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + struct AcesParameterOverrides; + + //! DisplayMapperComponentRequests provides an interface to request operations on a DisplayMapperComponent + class DisplayMapperComponentRequests + : public ComponentBus + { + public: + //! Load preconfigured preset for specific ODT mode + virtual void LoadPreset(OutputDeviceTransformType preset) = 0; + //! Set display mapper type + virtual void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) = 0; + //! Set custom ACES parameters for ACES mapping, display mapper must be set to Aces to see the difference + virtual void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) = 0; + }; + using DisplayMapperComponentRequestBus = EBus; + + //! DisplayMapperComponent can send out notifications on the DisplayMapperComponentNotifications + class DisplayMapperComponentNotifications : public ComponentBus + { + public: + //! Notifies that display mapper type changed + virtual void OntDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) + { + } + + //! Notifies that ACES parameter overrides changed + virtual void OnAcesParameterOverridesUpdated([[maybe_unused]] const AcesParameterOverrides& acesParameterOverrides) + { + } + }; + using DisplayMapperComponentNotificationBus = EBus; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp index 9642072cd2..b8d8336c30 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponent.cpp @@ -39,6 +39,5 @@ namespace AZ ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); } } - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 0e283199e7..7831c0a4c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -76,6 +76,39 @@ namespace AZ return m_configuration; } + void DisplayMapperComponentController::LoadPreset(OutputDeviceTransformType preset) + { + AcesParameterOverrides propertyOverrides; + propertyOverrides.m_preset = preset; + propertyOverrides.m_overrideDefaults = true; + propertyOverrides.LoadPreset(); + SetAcesParameterOverrides(propertyOverrides); + } + + void DisplayMapperComponentController::SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) + { + if (m_configuration.m_displayMapperOperation != displayMapperOperationType) + { + m_configuration.m_displayMapperOperation = displayMapperOperationType; + OnConfigChanged(); + DisplayMapperComponentNotificationBus::Broadcast( + &DisplayMapperComponentNotificationBus::Handler::OntDisplayMapperOperationTypeUpdated, + m_configuration.m_displayMapperOperation); + } + } + + void DisplayMapperComponentController::SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) + { + m_configuration.m_acesParameterOverrides = parameterOverrides; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + DisplayMapperComponentNotificationBus::Broadcast( + &DisplayMapperComponentNotificationBus::Handler::OnAcesParameterOverridesUpdated, + m_configuration.m_acesParameterOverrides); + } + void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h index 3cc6a7e5d9..efa2070828 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h @@ -12,10 +12,12 @@ #pragma once + #include #include #include +#include #include #include @@ -24,7 +26,10 @@ namespace AZ { namespace Render { + struct AcesParameterOverrides; + class DisplayMapperComponentController final + : DisplayMapperComponentRequestBus::Handler { public: friend class EditorDisplayMapperComponent; @@ -43,6 +48,11 @@ namespace AZ void SetConfiguration(const DisplayMapperComponentConfig& config); const DisplayMapperComponentConfig& GetConfiguration() const; + //! DisplayMapperComponentRequestBus::Handler overrides... + void LoadPreset(OutputDeviceTransformType preset) override; + void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) override; + void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) override; + private: AZ_DISABLE_COPY(DisplayMapperComponentController); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index 64cd450940..aadb0cc22b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -76,23 +76,32 @@ namespace AZ Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsBlack, "Cinema Limit (black)", "Reference black luminance value") + ->Attribute(AZ::Edit::Attributes::Min, 0.02f) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_cinemaLimitsWhite) ->DataElement( Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsWhite, "Cinema Limit (white)", "Reference white luminance value") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_cinemaLimitsBlack) + ->Attribute(AZ::Edit::Attributes::Max, 4000) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", "Linear extension below this") + ->Attribute(AZ::Edit::Attributes::Min, 0.002f) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_midPoint) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", - "Middle gray") + Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", "Middle gray") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_minPoint) + ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_maxPoint) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", "Linear extension above this") + ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_midPoint) + ->Attribute(AZ::Edit::Attributes::Max, 4000) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake index 7b8d0a6e21..31a90e7ceb 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake @@ -36,6 +36,7 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/PostProcess/Bloom/BloomComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DepthOfField/DepthOfFieldBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DepthOfField/DepthOfFieldComponentConfig.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConstants.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h From ddbed2f222ec279ef5472df56cc305ef8b29df72 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 28 May 2021 17:31:33 -0700 Subject: [PATCH 006/105] Sorting occlusion planes. --- .../RPI/Code/Source/RPI.Public/Culling.cpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 7bed2d3232..e105f7bca5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -480,8 +480,33 @@ namespace AZ MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { + // frustum cull and sort the occlusion planes by view space distance, front-to-back + using OccluderEntry = AZStd::pair; + AZStd::vector visibleOccluders; for (const AZ::Transform& transform : m_occlusionCullingPlanes) { + Aabb occluderAabb = Aabb::CreateCenterHalfExtents(transform.GetTranslation(), AZ::Vector3(AZ::Vector2(transform.GetUniformScale() / 2.0f))); + occluderAabb.SetMin(transform.TransformPoint(occluderAabb.GetMin())); + occluderAabb.SetMax(transform.TransformPoint(occluderAabb.GetMax())); + if (ShapeIntersection::Contains(frustum, occluderAabb)) + { + // occluder is visible, compute view space distance and add to list + float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); + depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occluderAabb.GetMax()).GetZ()); + + visibleOccluders.push_back(AZStd::make_pair(transform, depth)); + } + } + + AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) + { + return LHS.second < RHS.second; + }); + + for (const OccluderEntry& occluder : visibleOccluders) + { + const AZ::Transform& transform = occluder.first; + // find the corners of the plane static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); From 59ab6edaefc08768f2b1f933097df07c339103fa Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Mon, 31 May 2021 01:38:13 -0700 Subject: [PATCH 007/105] Added occlusion culling plane visualization --- ...lingPlaneTransparentVisualization.material | 22 ++++ ...cclusionCullingPlaneVisualization.material | 22 ++++ .../Assets/Models/OcclusionCullingPlane.fbx | 3 + ...ionCullingPlaneFeatureProcessorInterface.h | 2 + .../OcclusionCullingPlane.cpp | 113 ++++++++++++++++++ .../OcclusionCullingPlane.h | 65 ++++++++++ .../OcclusionCullingPlaneFeatureProcessor.cpp | 13 ++ .../OcclusionCullingPlaneFeatureProcessor.h | 21 +--- .../Code/atom_feature_common_files.cmake | 2 + .../RPI/Code/Source/RPI.Public/Culling.cpp | 72 ++++++++--- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 6 +- .../EditorOcclusionCullingPlaneComponent.cpp | 6 +- ...clusionCullingPlaneComponentController.cpp | 8 +- ...OcclusionCullingPlaneComponentController.h | 3 + 14 files changed, 321 insertions(+), 37 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material create mode 100644 Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material create mode 100644 Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material new file mode 100644 index 0000000000..981e392eef --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 0.25, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material new file mode 100644 index 0000000000..4446cc2d9d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.material @@ -0,0 +1,22 @@ +{ + "materialType": "Materials\\Types\\StandardPBR.materialtype", + "propertyLayoutVersion": 3, + "properties": { + "general": { + "enableShadows": false, + "enableDirectionalLights": false, + "enablePunctualLights": false, + "enableAreaLights": false, + "enableIBL": true + }, + "baseColor": { + "color": [ 0.0, 1.0, 0.0 ] + }, + "opacity": { + "alphaSource": "None", + "doubleSided": true, + "factor": 1.0, + "mode": "TintedTransparent" + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx new file mode 100644 index 0000000000..b274bfa282 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a1f8d75dcd85e8b4aa57f6c0c81af0300ff96915ba3c2b591095c215d5e1d8c +size 12072 diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h index 07a6179e78..8ffbb7f235 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h @@ -36,6 +36,8 @@ namespace AZ virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0; virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0; virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0; + virtual void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) = 0; + virtual void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) = 0; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp new file mode 100644 index 0000000000..10004a72e4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + static const char* OcclusionCullingPlaneDrawListTag("occlusioncullingplanevisualization"); + + OcclusionCullingPlane::~OcclusionCullingPlane() + { + Data::AssetBus::MultiHandler::BusDisconnect(); + m_meshFeatureProcessor->ReleaseMesh(m_visualizationMeshHandle); + } + + void OcclusionCullingPlane::Init(RPI::Scene* scene) + { + AZ_Assert(scene, "OcclusionCullingPlane::Init called with a null Scene pointer"); + + m_meshFeatureProcessor = scene->GetFeatureProcessor(); + + // load visualization plane model and material + m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath( + "Models/OcclusionCullingPlane.azmodel", + AZ::RPI::AssetUtils::TraceLevel::Assert); + + m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset); + m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true); + m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false); + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity()); + + SetVisualizationMaterial(); + } + + void OcclusionCullingPlane::SetVisualizationMaterial() + { + AZStd::string materialAssetPath; + if (m_transparentVisualization) + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.azmaterial"; + } + else + { + materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.azmaterial"; + } + + RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert; + m_visualizationMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath(materialAssetPath.c_str(), traceLevel); + m_visualizationMaterialAsset.QueueLoad(); + Data::AssetBus::MultiHandler::BusConnect(m_visualizationMaterialAsset.GetId()); + } + + void OcclusionCullingPlane::OnAssetReady(Data::Asset asset) + { + if (m_visualizationMaterialAsset.GetId() == asset.GetId()) + { + m_visualizationMaterialAsset = asset; + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + + m_visualizationMaterial = AZ::RPI::Material::FindOrCreate(m_visualizationMaterialAsset); + m_meshFeatureProcessor->SetMaterialAssignmentMap(m_visualizationMeshHandle, m_visualizationMaterial); + } + } + + void OcclusionCullingPlane::OnAssetError(Data::Asset asset) + { + AZ_Error("OcclusionCullingPlane", false, "Failed to load OcclusionCullingPlane visualization asset %s", asset.ToString().c_str()); + Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId()); + } + + void OcclusionCullingPlane::SetTransform(const AZ::Transform& transform) + { + m_transform = transform; + + // update visualization plane transform + m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, transform); + } + + void OcclusionCullingPlane::ShowVisualization(bool showVisualization) + { + if (m_showVisualization != showVisualization) + { + m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization); + SetVisualizationMaterial(); + } + } + + void OcclusionCullingPlane::SetTransparentVisualization(bool transparentVisualization) + { + if (m_transparentVisualization != transparentVisualization) + { + m_transparentVisualization = transparentVisualization; + SetVisualizationMaterial(); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h new file mode 100644 index 0000000000..4701c4977f --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlane.h @@ -0,0 +1,65 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum + class OcclusionCullingPlane final + : public AZ::Data::AssetBus::MultiHandler + { + public: + OcclusionCullingPlane() = default; + ~OcclusionCullingPlane(); + + void Init(RPI::Scene* scene); + + void SetTransform(const AZ::Transform& transform); + const AZ::Transform& GetTransform() const { return m_transform; } + + void SetEnabled(bool enabled) { m_enabled = enabled; } + bool GetEnabled() const { return m_enabled; } + + // enables or disables rendering of the visualization plane + void ShowVisualization(bool showVisualization); + + // sets the visualization to transparent mode + void SetTransparentVisualization(bool transparentVisualization); + + private: + + void SetVisualizationMaterial(); + + // AZ::Data::AssetBus::Handler overrides... + void OnAssetReady(Data::Asset asset) override; + void OnAssetError(Data::Asset asset) override; + + AZ::Transform m_transform; + bool m_enabled = true; + bool m_showVisualization = true; + bool m_transparentVisualization = false; + + // visualization + AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; + Data::Asset m_visualizationModelAsset; + Data::Asset m_visualizationMaterialAsset; + Data::Instance m_visualizationMaterial; + AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_visualizationMeshHandle; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index bed008a3da..b9866a925f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -60,6 +60,7 @@ namespace AZ OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) { AZStd::shared_ptr occlusionCullingPlane = AZStd::make_shared(); + occlusionCullingPlane->Init(GetParentScene()); occlusionCullingPlane->SetTransform(transform); m_occlusionCullingPlanes.push_back(occlusionCullingPlane); return occlusionCullingPlane; @@ -90,5 +91,17 @@ namespace AZ AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); occlusionCullingPlane->SetEnabled(enabled); } + + void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "ShowVisualization called with an invalid handle"); + occlusionCullingPlane->ShowVisualization(showVisualization); + } + + void OcclusionCullingPlaneFeatureProcessor::SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) + { + AZ_Assert(occlusionCullingPlane.get(), "SetTransparentVisualization called with an invalid handle"); + occlusionCullingPlane->SetTransparentVisualization(transparentVisualization); + } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index 5319666745..211254742f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -13,29 +13,12 @@ #pragma once #include +#include namespace AZ { namespace Render { - //! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum - class OcclusionCullingPlane final - { - public: - OcclusionCullingPlane() = default; - ~OcclusionCullingPlane() = default; - - void SetTransform(const AZ::Transform& transform) { m_transform = transform; } - const AZ::Transform& GetTransform() const { return m_transform; } - - void SetEnabled(bool enabled) { m_enabled = enabled; } - bool GetEnabled() const { return m_enabled; } - - private: - AZ::Transform m_transform; - bool m_enabled = true; - }; - //! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum class OcclusionCullingPlaneFeatureProcessor final : public OcclusionCullingPlaneFeatureProcessorInterface @@ -54,6 +37,8 @@ namespace AZ bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); } void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override; void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override; + void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) override; + void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) override; // FeatureProcessor overrides void Activate() override; diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index c545a2c974..b796a5b356 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -175,6 +175,8 @@ set(FILES Source/MorphTargets/MorphTargetDispatchItem.h Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp + Source/OcclusionCullingPlane/OcclusionCullingPlane.h + Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp Source/PostProcess/PostProcessBase.cpp Source/PostProcess/PostProcessBase.h Source/PostProcess/PostProcessFeatureProcessor.cpp diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index e105f7bca5..d8ed850309 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -428,20 +428,52 @@ namespace AZ return MaskedOcclusionCulling::CullingResult::VISIBLE; } - // convert the bounding box of the visibility entry to NDC - AZ::Vector4 clipSpaceMin = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMin()); - float depth = clipSpaceMin.GetW(); - AZ::Vector4 ndcMin = clipSpaceMin / clipSpaceMin.GetW(); + if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation())) + { + // camera is inside bounding volume + return MaskedOcclusionCulling::CullingResult::VISIBLE; + } - AZ::Vector4 clipSpaceMax = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(visibleEntry->m_boundingVolume.GetMax()); - depth = AZStd::min(depth, clipSpaceMax.GetW()); - AZ::Vector4 ndcMax = clipSpaceMax / clipSpaceMax.GetW(); + const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin(); + const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax(); - Vector2 rectMin(AZStd::min(ndcMin.GetX(), ndcMax.GetX()), AZStd::min(ndcMin.GetY(), ndcMax.GetY())); - Vector2 rectMax(AZStd::max(ndcMin.GetX(), ndcMax.GetX()), AZStd::max(ndcMin.GetY(), ndcMax.GetY())); + // compute bounding volume corners + Vector4 corners[8]; + corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f); + corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f); + corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f); + corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); + // find min clip-space depth and NDC min/max + float ndcMinX = FLT_MAX; + float ndcMinY = FLT_MAX; + float ndcMaxX = -FLT_MAX; + float ndcMaxY = -FLT_MAX; + float minDepth = FLT_MAX; + for (uint32_t index = 0; index < 8; ++index) + { + minDepth = AZStd::min(minDepth, corners[index].GetW()); + + // convert to NDC + corners[index] /= corners[index].GetW(); + + ndcMinX = AZStd::min(ndcMinX, corners[index].GetX()); + ndcMinY = AZStd::min(ndcMinY, corners[index].GetY()); + ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX()); + ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY()); + } + + if (minDepth < 0.00000001f) + { + return MaskedOcclusionCulling::VISIBLE; + } + // test against the occlusion buffer, which contains only the manually placed occlusion planes - return m_jobData->m_maskedOcclusionCulling->TestRect(rectMin.GetX(), rectMin.GetY(), rectMax.GetX(), rectMax.GetY(), depth); + return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth); } }; @@ -480,15 +512,22 @@ namespace AZ MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { - // frustum cull and sort the occlusion planes by view space distance, front-to-back + // frustum cull occlusion planes using OccluderEntry = AZStd::pair; AZStd::vector visibleOccluders; for (const AZ::Transform& transform : m_occlusionCullingPlanes) { - Aabb occluderAabb = Aabb::CreateCenterHalfExtents(transform.GetTranslation(), AZ::Vector3(AZ::Vector2(transform.GetUniformScale() / 2.0f))); - occluderAabb.SetMin(transform.TransformPoint(occluderAabb.GetMin())); - occluderAabb.SetMax(transform.TransformPoint(occluderAabb.GetMax())); - if (ShapeIntersection::Contains(frustum, occluderAabb)) + static const AZ::Vector3 BL(-0.5f, -0.5f, 0.0f); + static const AZ::Vector3 TR(0.5f, 0.5f, 0.0f); + + AZ::Vector3 P1 = transform.TransformPoint(BL); + AZ::Vector3 P2 = transform.TransformPoint(TR); + + AZ::Vector3 aabbMin = P1.GetMin(P2); + AZ::Vector3 aabbMax = P1.GetMax(P2); + + AZ::Aabb occluderAabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); + if (ShapeIntersection::Overlaps(frustum, occluderAabb)) { // occluder is visible, compute view space distance and add to list float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); @@ -498,9 +537,10 @@ namespace AZ } } + // sort the occlusion planes by view space distance, front-to-back AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) { - return LHS.second < RHS.second; + return LHS.second > RHS.second; }); for (const OccluderEntry& occluder : visibleOccluders) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index edae0f88b7..6e4dec112f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -28,6 +28,10 @@ namespace AZ { namespace RPI { + // fixed-size software occlusion culling buffer + const uint32_t MaskedSoftwareOcclusionCullingWidth = 1920; + const uint32_t MaskedSoftwareOcclusionCullingHeight = 1080; + ViewPtr View::CreateView(const AZ::Name& name, UsageFlags usage) { View* view = aznew View(name, usage); @@ -54,7 +58,7 @@ namespace AZ } m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); - m_maskedOcclusionCulling->SetResolution(1920, 1080); + m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight); } View::~View() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp index 9a655727d6..b027be3171 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp @@ -53,8 +53,12 @@ namespace AZ editContext->Class( "OcclusionCullingPlaneComponentConfig", "") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->ClassElement(AZ::Edit::ClassElements::Group, "Settings") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_showVisualization, "Show Visualization", "Show the occlusion culling plane visualization") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_transparentVisualization, "Transparent Visualization", "Sets the occlusion culling plane visualization as transparent") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp index bd03bec0f4..cf8107776f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp @@ -38,7 +38,9 @@ namespace AZ { serializeContext->Class() ->Version(0) - ; + ->Field("ShowVisualization", &OcclusionCullingPlaneComponentConfig::m_showVisualization) + ->Field("TransparentVisualization", &OcclusionCullingPlaneComponentConfig::m_transparentVisualization) + ; } } @@ -98,6 +100,10 @@ namespace AZ // add this occlusion plane to the feature processor const AZ::Transform& transform = m_transformInterface->GetWorldTM(); m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform); + + // set visualization + m_featureProcessor->ShowVisualization(m_handle, m_configuration.m_showVisualization); + m_featureProcessor->SetTransparentVisualization(m_handle, m_configuration.m_transparentVisualization); } void OcclusionCullingPlaneComponentController::Deactivate() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h index 5f0be5315f..2d977a2cf3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h @@ -32,6 +32,9 @@ namespace AZ AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0); static void Reflect(AZ::ReflectContext* context); + bool m_showVisualization = true; + bool m_transparentVisualization = false; + OcclusionCullingPlaneComponentConfig() = default; }; From a9a42a540550507258b77b5fed933bf73a267734 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Tue, 1 Jun 2021 12:17:48 -0700 Subject: [PATCH 008/105] Added AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED --- Gems/Atom/RPI/Code/CMakeLists.txt | 17 ++++++++-- .../Code/Include/Atom/RPI.Public/Culling.h | 1 - .../RPI/Code/Include/Atom/RPI.Public/View.h | 3 +- .../Android/Atom_RPI_Traits_Android.h | 14 +++++++++ .../Android/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Android/platform_android_files.cmake | 15 +++++++++ .../Platform/Linux/Atom_RPI_Traits_Linux.h | 14 +++++++++ .../Platform/Linux/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Platform/Linux/platform_linux_files.cmake | 15 +++++++++ .../Source/Platform/Mac/Atom_RPI_Traits_Mac.h | 14 +++++++++ .../Platform/Mac/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Platform/Mac/platform_mac_files.cmake | 15 +++++++++ .../Windows/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Windows/Atom_RPI_Traits_Windows.h | 14 +++++++++ .../Source/Platform/Windows/PAL_windows.cmake | 7 +++-- .../Windows/platform_windows_files.cmake | 15 +++++++++ .../Platform/iOS/Atom_RPI_Traits_Platform.h | 14 +++++++++ .../Source/Platform/iOS/Atom_RPI_Traits_iOS.h | 14 +++++++++ .../Platform/iOS/platform_ios_files.cmake | 15 +++++++++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 31 +++++++++++++++---- Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp | 13 ++++++-- .../atom_rpi_masked_occlusion_files.cmake | 18 +++++++++++ .../Atom/RPI/Code/atom_rpi_public_files.cmake | 6 +--- 23 files changed, 291 insertions(+), 20 deletions(-) create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake create mode 100644 Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index d2b7fba071..2898967add 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -9,6 +9,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) + +#for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED and PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED +include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +if(PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED) + set(MASKED_OCCLUSION_CULLING_FILES "atom_rpi_masked_occlusion_files.cmake") +else() + set(MASKED_OCCLUSION_CULLING_FILES "") +endif() + ly_add_target( NAME Atom_RPI.Public STATIC NAMESPACE Gem @@ -16,11 +27,15 @@ ly_add_target( atom_rpi_reflect_files.cmake atom_rpi_public_files.cmake ../Assets/atom_rpi_asset_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${MASKED_OCCLUSION_CULLING_FILES} INCLUDE_DIRECTORIES PRIVATE Source + ${pal_source_dir} PUBLIC Include + External BUILD_DEPENDENCIES PRIVATE AZ::AtomCore @@ -159,8 +174,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common) - include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) #for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED - if(NOT PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED) # Create a stub diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 8892266683..3f03fb9dcb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 0b6b41c8b0..7d192ea62a 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -18,13 +18,14 @@ #include #include #include -#include #include #include #include #include +class MaskedOcclusionCulling; + namespace AZ { namespace RHI diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Android.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..27e0af7f35 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Android.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake new file mode 100644 index 0000000000..357d8f0381 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Android.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Linux.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..39c6a3e572 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Linux.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake new file mode 100644 index 0000000000..19be7951f6 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Linux.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Mac.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..19816f2bd1 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_Mac.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake new file mode 100644 index 0000000000..bde67ff340 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_Mac.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..dc655ed3a9 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_RPI_Traits_Windows.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h new file mode 100644 index 0000000000..0deebe4706 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/Atom_RPI_Traits_Windows.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 1 diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake index 51e42d5216..b989233ccd 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/PAL_windows.cmake @@ -10,16 +10,17 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED TRUE) ly_add_source_properties( - SOURCES Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp PROPERTY COMPILE_OPTIONS VALUES /arch:AVX2 /W3 ) ly_add_source_properties( SOURCES - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp PROPERTY COMPILE_OPTIONS VALUES /W3 ) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake new file mode 100644 index 0000000000..e49944d8ef --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Windows.h +) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h new file mode 100644 index 0000000000..4403d741dc --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include "Atom_Feature_Traits_iOS.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h new file mode 100644 index 0000000000..e1c8d827bf --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_iOS.h @@ -0,0 +1,14 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0 diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake new file mode 100644 index 0000000000..7f603e4bfd --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake @@ -0,0 +1,15 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + Atom_Feature_Traits_Platform.h + Atom_Feature_Traits_iOS.h +) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index d8ed850309..79d152f661 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -20,15 +20,20 @@ #include +#include #include #include - #include #include #include #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif //Enables more inner-loop profiling scopes (can create high overhead in RadTelemetry if there are many-many objects in a scene) //#define AZ_CULL_PROFILE_DETAILED @@ -265,10 +270,12 @@ namespace AZ struct JobData { CullingDebugContext* m_debugCtx = nullptr; - MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; const Scene* m_scene = nullptr; View* m_view = nullptr; Frustum m_frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; +#endif }; private: @@ -308,7 +315,9 @@ namespace AZ //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { @@ -347,7 +356,9 @@ namespace AZ } else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb)) { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif { numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); ++numVisibleCullables; @@ -421,6 +432,7 @@ namespace AZ } } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry) { if (!m_jobData->m_maskedOcclusionCulling) @@ -471,10 +483,11 @@ namespace AZ { return MaskedOcclusionCulling::VISIBLE; } - + // test against the occlusion buffer, which contains only the manually placed occlusion planes return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth); } +#endif }; void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob) @@ -508,6 +521,7 @@ namespace AZ cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix(); } +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED // setup occlusion culling, if necessary MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) @@ -577,15 +591,19 @@ namespace AZ maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); } } +#endif WorkListType worklist; AZStd::shared_ptr jobData = AZStd::make_shared(); jobData->m_debugCtx = &m_debugCtx; - jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; jobData->m_scene = &scene; jobData->m_view = &view; jobData->m_frustum = frustum; +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif + auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { @@ -620,11 +638,12 @@ namespace AZ { AZStd::shared_ptr remainingJobData = AZStd::make_shared(); remainingJobData->m_debugCtx = &m_debugCtx; - remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; remainingJobData->m_scene = &scene; remainingJobData->m_view = &view; remainingJobData->m_frustum = frustum; - +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling; +#endif //Kick off a job to process any remaining workitems AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes parentJob.SetContinuation(job); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index 6e4dec112f..bd0e15fb2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -23,6 +23,11 @@ #include #include #include +#include + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED +#include +#endif namespace AZ { @@ -56,18 +61,21 @@ namespace AZ { m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset); } - +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED m_maskedOcclusionCulling = MaskedOcclusionCulling::Create(); m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight); +#endif } View::~View() { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED if (m_maskedOcclusionCulling) { MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling); m_maskedOcclusionCulling = nullptr; } +#endif } void View::SetDrawListMask(const RHI::DrawListMask& drawListMask) @@ -394,13 +402,14 @@ namespace AZ void View::BeginCulling() { +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED m_maskedOcclusionCulling->ClearBuffer(); +#endif } MaskedOcclusionCulling* View::GetMaskedOcclusionCulling() { return m_maskedOcclusionCulling; } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake new file mode 100644 index 0000000000..5828848c81 --- /dev/null +++ b/Gems/Atom/RPI/Code/atom_rpi_masked_occlusion_files.cmake @@ -0,0 +1,18 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(FILES + External/MaskedOcclusionCulling/MaskedOcclusionCulling.h + External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl + External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp + External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp +) \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 35003d268a..a1d98bbd38 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -102,7 +102,6 @@ set(FILES Include/Atom/RPI.Public/GpuQuery/Query.h Include/Atom/RPI.Public/GpuQuery/QueryPool.h Include/Atom/RPI.Public/GpuQuery/TimestampQueryPool.h - Include/Atom/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h Source/RPI.Public/Culling.cpp Source/RPI.Public/FeatureProcessor.cpp Source/RPI.Public/FeatureProcessorFactory.cpp @@ -181,7 +180,4 @@ set(FILES Source/RPI.Public/GpuQuery/Query.cpp Source/RPI.Public/GpuQuery/QueryPool.cpp Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp - Source/RPI.Public/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp -) +) \ No newline at end of file From 38819c630aa7313c49cb8073876b6f401df95efb Mon Sep 17 00:00:00 2001 From: mnaumov Date: Tue, 1 Jun 2021 15:34:01 -0700 Subject: [PATCH 009/105] PR feedback --- .../DisplayMapper/DisplayMapperComponentBus.h | 63 ++++- .../DisplayMapperComponentController.cpp | 231 +++++++++++++++++- .../DisplayMapperComponentController.h | 24 ++ .../EditorDisplayMapperComponent.cpp | 60 +++-- 4 files changed, 359 insertions(+), 19 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h index af57b69d3b..4a01f71325 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -30,8 +30,67 @@ namespace AZ virtual void LoadPreset(OutputDeviceTransformType preset) = 0; //! Set display mapper type virtual void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) = 0; - //! Set custom ACES parameters for ACES mapping, display mapper must be set to Aces to see the difference + //! Get display mapper type + virtual DisplayMapperOperationType GetDisplayMapperOperationType() const = 0; + //! Set ACES parameter overrides for ACES mapping, display mapper must be set to Aces to see the difference virtual void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) = 0; + //! Get ACES parameter overrides + virtual const AcesParameterOverrides& GetAcesParameterOverrides() const = 0; + + // Enable or disable ACES parameter overrides + virtual void SetOverrideAcesParameters(bool value) = 0; + // Check if ACES parameters are overriding default preset values + virtual bool GetOverrideAcesParameters() const = 0; + + // Set gamma adjustment to compensate for dim surround + virtual void SetAlterSurround(bool value) = 0; + // Get gamma adjustment to compensate for dim surround + virtual bool GetAlterSurround() const = 0; + + // Set desaturation to compensate for luminance difference + virtual void SetApplyDesaturation(bool value) = 0; + // Get desaturation to compensate for luminance difference + virtual bool GetApplyDesaturation() const = 0; + + // Set color appearance transform (CAT) from ACES white point to assumed observer adapted white point + virtual void SetApplyCATD60toD65(bool value) = 0; + // Get color appearance transform (CAT) from ACES white point to assumed observer adapted white point + virtual bool GetApplyCATD60toD65() const = 0; + + // Set reference black luminance value + virtual void SetCinemaLimitsBlack(float value) = 0; + // Get reference black luminance value + virtual float GetCinemaLimitsBlack() const = 0; + + // Set reference white luminance value + virtual void SetCinemaLimitsWhite(float value) = 0; + // Get reference white luminance value + virtual float GetCinemaLimitsWhite() const = 0; + + // Set min luminance value + virtual void SetMinPoint(float value) = 0; + // Get min luminance value + virtual float GetMinPoint() const = 0; + + // Set mid luminance value + virtual void SetMidPoint(float value) = 0; + // Get mid luminance value + virtual float GetMidPoint() const = 0; + + // Set max luminance value + virtual void SetMaxPoint(float value) = 0; + // Get max luminance value + virtual float GetMaxPoint() const = 0; + + // Set gamma adjustment value + virtual void SetSurroundGamma(float value) = 0; + // Get gamma adjustment value + virtual float GetSurroundGamma() const = 0; + + // Set optional gamma value that is applied as basic gamma curve OETF + virtual void SetGamma(float value) = 0; + // Get optional gamma value that is applied as basic gamma curve OETF + virtual float GetGamma() const = 0; }; using DisplayMapperComponentRequestBus = EBus; @@ -40,7 +99,7 @@ namespace AZ { public: //! Notifies that display mapper type changed - virtual void OntDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) + virtual void OnDisplayMapperOperationTypeUpdated([[maybe_unused]] const DisplayMapperOperationType& displayMapperOperationType) { } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 7831c0a4c6..06c549f560 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -10,6 +10,8 @@ * */ +#include "AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h" + #include #include @@ -32,6 +34,69 @@ namespace AZ ->Version(0) ->Field("Configuration", &DisplayMapperComponentController::m_configuration); } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("DisplayMapperComponentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "render") + ->Attribute(AZ::Script::Attributes::Module, "render") + // LoadPreset + ->Event("LoadPreset", &DisplayMapperComponentRequestBus::Events::LoadPreset) + // DisplayMapperOperationType + ->Event("SetDisplayMapperOperationType", &DisplayMapperComponentRequestBus::Events::SetDisplayMapperOperationType) + ->Event("GetDisplayMapperOperationType", &DisplayMapperComponentRequestBus::Events::GetDisplayMapperOperationType) + ->VirtualProperty("DisplayMapperOperationType", "GetDisplayMapperOperationType", "SetDisplayMapperOperationType") + // AcesParameterOverrides + ->Event("SetAcesParameterOverrides", &DisplayMapperComponentRequestBus::Events::SetAcesParameterOverrides) + ->Event("GetAcesParameterOverrides", &DisplayMapperComponentRequestBus::Events::GetAcesParameterOverrides) + ->VirtualProperty("AcesParameterOverrides", "GetAcesParameterOverrides", "SetAcesParameterOverrides") + // OverrideAcesParameters + ->Event("SetOverrideAcesParameters", &DisplayMapperComponentRequestBus::Events::SetOverrideAcesParameters) + ->Event("GetOverrideAcesParameters", &DisplayMapperComponentRequestBus::Events::GetOverrideAcesParameters) + ->VirtualProperty("OverrideAcesParameters", "GetOverrideAcesParameters", "SetOverrideAcesParameters") + // AlterSurround + ->Event("SetAlterSurround", &DisplayMapperComponentRequestBus::Events::SetAlterSurround) + ->Event("GetAlterSurround", &DisplayMapperComponentRequestBus::Events::GetAlterSurround) + ->VirtualProperty("AlterSurround", "GetAlterSurround", "SetAlterSurround") + // ApplyDesaturation + ->Event("SetApplyDesaturation", &DisplayMapperComponentRequestBus::Events::SetApplyDesaturation) + ->Event("GetApplyDesaturation", &DisplayMapperComponentRequestBus::Events::GetApplyDesaturation) + ->VirtualProperty("ApplyDesaturation", "GetApplyDesaturation", "SetApplyDesaturation") + // ApplyCATD60toD65 + ->Event("SetApplyCATD60toD65", &DisplayMapperComponentRequestBus::Events::SetApplyCATD60toD65) + ->Event("GetApplyCATD60toD65", &DisplayMapperComponentRequestBus::Events::GetApplyCATD60toD65) + ->VirtualProperty("ApplyCATD60toD65", "GetApplyCATD60toD65", "SetApplyCATD60toD65") + // CinemaLimitsBlack + ->Event("SetCinemaLimitsBlack", &DisplayMapperComponentRequestBus::Events::SetCinemaLimitsBlack) + ->Event("GetCinemaLimitsBlack", &DisplayMapperComponentRequestBus::Events::GetCinemaLimitsBlack) + ->VirtualProperty("CinemaLimitsBlack", "GetCinemaLimitsBlack", "SetCinemaLimitsBlack") + // CinemaLimitsWhite + ->Event("SetCinemaLimitsWhite", &DisplayMapperComponentRequestBus::Events::SetCinemaLimitsWhite) + ->Event("GetCinemaLimitsWhite", &DisplayMapperComponentRequestBus::Events::GetCinemaLimitsWhite) + ->VirtualProperty("CinemaLimitsWhite", "GetCinemaLimitsWhite", "SetCinemaLimitsWhite") + // MinPoint + ->Event("SetMinPoint", &DisplayMapperComponentRequestBus::Events::SetMinPoint) + ->Event("GetMinPoint", &DisplayMapperComponentRequestBus::Events::GetMinPoint) + ->VirtualProperty("MinPoint", "GetMinPoint", "SetMinPoint") + // MidPoint + ->Event("SetMidPoint", &DisplayMapperComponentRequestBus::Events::SetMidPoint) + ->Event("GetMidPoint", &DisplayMapperComponentRequestBus::Events::GetMidPoint) + ->VirtualProperty("MidPoint", "GetMidPoint", "SetMidPoint") + // MaxPoint + ->Event("SetMaxPoint", &DisplayMapperComponentRequestBus::Events::SetMaxPoint) + ->Event("GetMaxPoint", &DisplayMapperComponentRequestBus::Events::GetMaxPoint) + ->VirtualProperty("MaxPoint", "GetMaxPoint", "SetMaxPoint") + // SurroundGamma + ->Event("SetSurroundGamma", &DisplayMapperComponentRequestBus::Events::SetSurroundGamma) + ->Event("GetSurroundGamma", &DisplayMapperComponentRequestBus::Events::GetSurroundGamma) + ->VirtualProperty("SurroundGamma", "GetSurroundGamma", "SetSurroundGamma") + // Gamma + ->Event("SetGamma", &DisplayMapperComponentRequestBus::Events::SetGamma) + ->Event("GetGamma", &DisplayMapperComponentRequestBus::Events::GetGamma) + ->VirtualProperty("Gamma", "GetGamma", "SetGamma") + ; + } } void DisplayMapperComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) @@ -92,11 +157,16 @@ namespace AZ m_configuration.m_displayMapperOperation = displayMapperOperationType; OnConfigChanged(); DisplayMapperComponentNotificationBus::Broadcast( - &DisplayMapperComponentNotificationBus::Handler::OntDisplayMapperOperationTypeUpdated, + &DisplayMapperComponentNotificationBus::Handler::OnDisplayMapperOperationTypeUpdated, m_configuration.m_displayMapperOperation); } } + DisplayMapperOperationType DisplayMapperComponentController::GetDisplayMapperOperationType() const + { + return m_configuration.m_displayMapperOperation; + } + void DisplayMapperComponentController::SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) { m_configuration.m_acesParameterOverrides = parameterOverrides; @@ -109,6 +179,165 @@ namespace AZ m_configuration.m_acesParameterOverrides); } + const AcesParameterOverrides& DisplayMapperComponentController::GetAcesParameterOverrides() const + { + return m_configuration.m_acesParameterOverrides; + } + + void DisplayMapperComponentController::SetOverrideAcesParameters(bool value) + { + m_configuration.m_acesParameterOverrides.m_overrideDefaults = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetOverrideAcesParameters() const + { + return m_configuration.m_acesParameterOverrides.m_overrideDefaults; + } + + void DisplayMapperComponentController::SetAlterSurround(bool value) + { + m_configuration.m_acesParameterOverrides.m_alterSurround = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetAlterSurround() const + { + return m_configuration.m_acesParameterOverrides.m_alterSurround; + } + + void DisplayMapperComponentController::SetApplyDesaturation(bool value) + { + m_configuration.m_acesParameterOverrides.m_applyDesaturation = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetApplyDesaturation() const + { + return m_configuration.m_acesParameterOverrides.m_applyDesaturation; + } + + void DisplayMapperComponentController::SetApplyCATD60toD65(bool value) + { + m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + bool DisplayMapperComponentController::GetApplyCATD60toD65() const + { + return m_configuration.m_acesParameterOverrides.m_applyCATD60toD65; + } + + void DisplayMapperComponentController::SetCinemaLimitsBlack(float value) + { + m_configuration.m_acesParameterOverrides.m_cinemaLimitsBlack = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetCinemaLimitsBlack() const + { + return m_configuration.m_acesParameterOverrides.m_cinemaLimitsBlack; + } + + void DisplayMapperComponentController::SetCinemaLimitsWhite(float value) + { + m_configuration.m_acesParameterOverrides.m_cinemaLimitsWhite = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetCinemaLimitsWhite() const + { + return m_configuration.m_acesParameterOverrides.m_cinemaLimitsWhite; + } + + void DisplayMapperComponentController::SetMinPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_minPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMinPoint() const + { + return m_configuration.m_acesParameterOverrides.m_minPoint; + } + + void DisplayMapperComponentController::SetMidPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_midPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMidPoint() const + { + return m_configuration.m_acesParameterOverrides.m_midPoint; + } + + void DisplayMapperComponentController::SetMaxPoint(float value) + { + m_configuration.m_acesParameterOverrides.m_maxPoint = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetMaxPoint() const + { + return m_configuration.m_acesParameterOverrides.m_maxPoint; + } + + void DisplayMapperComponentController::SetSurroundGamma(float value) + { + m_configuration.m_acesParameterOverrides.m_surroundGamma = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetSurroundGamma() const + { + return m_configuration.m_acesParameterOverrides.m_surroundGamma; + } + + void DisplayMapperComponentController::SetGamma(float value) + { + m_configuration.m_acesParameterOverrides.m_gamma = value; + if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) + { + OnConfigChanged(); + } + } + + float DisplayMapperComponentController::GetGamma() const + { + return m_configuration.m_acesParameterOverrides.m_gamma; + } + void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h index efa2070828..412bdc8524 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.h @@ -51,7 +51,31 @@ namespace AZ //! DisplayMapperComponentRequestBus::Handler overrides... void LoadPreset(OutputDeviceTransformType preset) override; void SetDisplayMapperOperationType(DisplayMapperOperationType displayMapperOperationType) override; + DisplayMapperOperationType GetDisplayMapperOperationType() const override; void SetAcesParameterOverrides(const AcesParameterOverrides& parameterOverrides) override; + const AcesParameterOverrides& GetAcesParameterOverrides() const override; + void SetOverrideAcesParameters(bool value) override; + bool GetOverrideAcesParameters() const override; + void SetAlterSurround(bool value) override; + bool GetAlterSurround() const override; + void SetApplyDesaturation(bool value) override; + bool GetApplyDesaturation() const override; + void SetApplyCATD60toD65(bool value) override; + bool GetApplyCATD60toD65() const override; + void SetCinemaLimitsBlack(float value) override; + float GetCinemaLimitsBlack() const override; + void SetCinemaLimitsWhite(float value) override; + float GetCinemaLimitsWhite() const override; + void SetMinPoint(float value) override; + float GetMinPoint() const override; + void SetMidPoint(float value) override; + float GetMidPoint() const override; + void SetMaxPoint(float value) override; + float GetMaxPoint() const override; + void SetSurroundGamma(float value) override; + float GetSurroundGamma() const override; + void SetGamma(float value) override; + float GetGamma() const override; private: AZ_DISABLE_COPY(DisplayMapperComponentController); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index aadb0cc22b..4a03c6f712 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -54,63 +54,89 @@ namespace AZ ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + // m_overrideDefaults ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_overrideDefaults, "Override Defaults", "When enabled allows parameter overrides for ACES configuration") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + // m_alterSurround ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_alterSurround, "Alter Surround", "Apply gamma adjustment to compensate for dim surround") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_applyDesaturation ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyDesaturation, "Alter Desaturation", "Apply desaturation to compensate for luminance difference") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_applyCATD60toD65 ->DataElement( AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyCATD60toD65, "Alter CAT D60 to D65", "Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point") ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - + + // m_cinemaLimitsBlack ->DataElement( - Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsBlack, + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_cinemaLimitsBlack, "Cinema Limit (black)", "Reference black luminance value") ->Attribute(AZ::Edit::Attributes::Min, 0.02f) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_cinemaLimitsWhite) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // m_cinemaLimitsWhite ->DataElement( - Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsWhite, + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_cinemaLimitsWhite, "Cinema Limit (white)", "Reference white luminance value") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_cinemaLimitsBlack) - ->Attribute(AZ::Edit::Attributes::Max, 4000) + ->Attribute(AZ::Edit::Attributes::Max, 4000.f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + // m_minPoint ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", "Linear extension below this") ->Attribute(AZ::Edit::Attributes::Min, 0.002f) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_midPoint) - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", "Middle gray") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + + // m_midPoint + ->DataElement(Edit::UIHandlers::Slider, &AcesParameterOverrides::m_midPoint, + "Mid Point (luminance)", "Middle gray") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_minPoint) ->Attribute(AZ::Edit::Attributes::Max, &AcesParameterOverrides::m_maxPoint) - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + + // m_maxPoint ->DataElement( - Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", + Edit::UIHandlers::Slider, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", "Linear extension above this") ->Attribute(AZ::Edit::Attributes::Min, &AcesParameterOverrides::m_midPoint) - ->Attribute(AZ::Edit::Attributes::Max, 4000) + ->Attribute(AZ::Edit::Attributes::Max, 4000.f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues) + + // m_surroundGamma + ->DataElement( + AZ::Edit::UIHandlers::Slider, &AcesParameterOverrides::m_surroundGamma, "Surround Gamma", + "Gamma adjustment to be applied to compensate for the condition of the viewing environment") + ->Attribute(AZ::Edit::Attributes::Min, 0.6f) + ->Attribute(AZ::Edit::Attributes::Max, 1.2f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + // m_gamma ->DataElement( - AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_surroundGamma, "Surround Gamma", - "Gamma adjustment to be applied to compensate for the condition of the viewing environment") - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->DataElement( - AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_gamma, "Gamma", + AZ::Edit::UIHandlers::Slider, &AcesParameterOverrides::m_gamma, "Gamma", "Optional gamma value that is applied as basic gamma curve OETF") + ->Attribute(AZ::Edit::Attributes::Min, 0.2f) + ->Attribute(AZ::Edit::Attributes::Max, 4.0f) + ->Attribute(AZ::Edit::Attributes::Step, 0.005f) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) // Load preset group @@ -154,6 +180,8 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { + behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); + behaviorContext->ConstantProperty("EditorDisplayMapperComponentTypeId", BehaviorConstant(Uuid(EditorDisplayMapperComponentTypeId))) ->Attribute(AZ::Script::Attributes::Module, "render") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); From 3fcc1b64fce369a6129b9419187f2d78d7d39339 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Tue, 1 Jun 2021 20:24:23 -0700 Subject: [PATCH 010/105] [ATOM-15692] Ebus for registering custom feature processors for thumbnail generation --- .../ThumbnailFeatureProcessorProviderBus.h | 37 +++++++++++++++ .../Rendering/CommonThumbnailRenderer.cpp | 28 ++++++++++++ .../Rendering/CommonThumbnailRenderer.h | 11 ++++- .../ThumbnailRendererSteps/InitializeStep.cpp | 45 ++++++++++--------- 4 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h new file mode 100644 index 0000000000..71e101bcef --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ +{ + namespace LyIntegration + { + namespace Thumbnails + { + //! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation + //! Duplicates will be ignored + //! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp + class ThumbnailFeatureProcessorProviderRequests + : public AZ::EBusTraits + { + public: + //! Get a list of custom feature processors to register with thumbnail renderer + virtual const AZStd::vector& GetCustomFeatureProcessors() const = 0; + }; + + using ThumbnailFeatureProcessorProviderBus = AZ::EBus; + } // namespace Thumbnails + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp index 06d54a20b3..b002a0bb66 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp @@ -34,12 +34,34 @@ namespace AZ AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type()); AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type()); SystemTickBus::Handler::BusConnect(); + ThumbnailFeatureProcessorProviderBus::Handler::BusConnect(); m_steps[Step::Initialize] = AZStd::make_shared(this); m_steps[Step::FindThumbnailToRender] = AZStd::make_shared(this); m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared(this); m_steps[Step::Capture] = AZStd::make_shared(this); m_steps[Step::ReleaseResources] = AZStd::make_shared(this); + + m_minimalFeatureProcessors = + { + "AZ::Render::TransformServiceFeatureProcessor", + "AZ::Render::MeshFeatureProcessor", + "AZ::Render::SimplePointLightFeatureProcessor", + "AZ::Render::SimpleSpotLightFeatureProcessor", + "AZ::Render::PointLightFeatureProcessor", + // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow + // flickering [ATOM-13568] + // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. + // Possibly re-enable with [GFX TODO][ATOM-13639] + // "AZ::Render::DirectionalLightFeatureProcessor", + "AZ::Render::DiskLightFeatureProcessor", + "AZ::Render::CapsuleLightFeatureProcessor", + "AZ::Render::QuadLightFeatureProcessor", + "AZ::Render::DecalTextureArrayFeatureProcessor", + "AZ::Render::ImageBasedLightFeatureProcessor", + "AZ::Render::PostProcessFeatureProcessor", + "AZ::Render::SkyBoxFeatureProcessor" + }; } CommonThumbnailRenderer::~CommonThumbnailRenderer() @@ -50,6 +72,7 @@ namespace AZ } AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect(); SystemTickBus::Handler::BusDisconnect(); + ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect(); } void CommonThumbnailRenderer::SetStep(Step step) @@ -77,6 +100,11 @@ namespace AZ AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents(); } + const AZStd::vector& CommonThumbnailRenderer::GetCustomFeatureProcessors() const + { + return m_minimalFeatureProcessors; + } + AZStd::shared_ptr CommonThumbnailRenderer::GetData() const { return m_data; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h index 50e73f4391..249a1f1343 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h @@ -17,6 +17,8 @@ #include #include +#include + // Disables warning messages triggered by the Qt library // 4251: class needs to have dll-interface to be used by clients of class // 4800: forcing value to bool 'true' or 'false' (performance warning) @@ -34,9 +36,10 @@ namespace AZ //! Provides custom rendering of material and model thumbnails class CommonThumbnailRenderer - : private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler + : public ThumbnailRendererContext + , private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler , private SystemTickBus::Handler - , public ThumbnailRendererContext + , private ThumbnailFeatureProcessorProviderBus::Handler { public: AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0) @@ -57,9 +60,13 @@ namespace AZ //! SystemTickBus::Handler interface overrides... void OnSystemTick() override; + //! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides... + const AZStd::vector& GetCustomFeatureProcessors() const override; + AZStd::unordered_map> m_steps; Step m_currentStep = Step::None; AZStd::shared_ptr m_data; + AZStd::vector m_minimalFeatureProcessors; }; } // namespace Thumbnails } // namespace LyIntegration diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index 322c765f1b..c35c33017a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -11,10 +11,16 @@ */ +#include +#include + +#include + #include #include #include #include + #include #include #include @@ -23,10 +29,11 @@ #include #include #include + #include #include -#include -#include +#include + #include #include #include @@ -37,7 +44,6 @@ namespace AZ { namespace Thumbnails { - InitializeStep::InitializeStep(ThumbnailRendererContext* context) : ThumbnailRendererStep(context) { @@ -50,24 +56,23 @@ namespace AZ data->m_entityContext = AZStd::make_unique(); data->m_entityContext->InitContext(); - // Create and register a scene with minimum required feature processors + // Create and register a scene with all required feature processors RPI::SceneDescriptor sceneDesc; - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor"); - // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568] - // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. - // Possibly re-enable with [GFX TODO][ATOM-13639] - // sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DirectionalLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DiskLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::CapsuleLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::QuadLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DecalTextureArrayFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::ImageBasedLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PostProcessFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SkyBoxFeatureProcessor"); + + AZ::EBusAggregateResults> results; + ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors); + + AZStd::set featureProcessorNames; + for (auto& resultCollection : results.values) + { + for (auto& featureProcessorName : resultCollection) + { + if (featureProcessorNames.emplace(featureProcessorName).second) + { + sceneDesc.m_featureProcessorNames.push_back(featureProcessorName); + } + } + } data->m_scene = RPI::Scene::CreateScene(sceneDesc); From d7ae88c17b1ce1c5c59f293a0575cee9f4ea0937 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Tue, 1 Jun 2021 20:24:52 -0700 Subject: [PATCH 011/105] Adding cmake change --- .../Code/atomlyintegration_commonfeatures_editor_files.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index e58f72a121..9072cd54f2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -10,11 +10,12 @@ # set(FILES + Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h + Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h + Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h Source/Module.cpp Source/Animation/EditorAttachmentComponent.h Source/Animation/EditorAttachmentComponent.cpp - Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h - Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h Source/EditorCommonFeaturesSystemComponent.h Source/EditorCommonFeaturesSystemComponent.cpp Source/CoreLights/EditorAreaLightComponent.h From 982c30eefdbe3364c55265e690bcec576b6a2dc6 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Tue, 1 Jun 2021 23:43:27 -0700 Subject: [PATCH 012/105] Added a visibility result flag to RPI::Cullable, set to true if the object passed all culling tests. --- .../Code/Include/Atom/RPI.Public/Culling.h | 3 +++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 25 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 3f03fb9dcb..295797d2dd 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -96,6 +96,9 @@ namespace AZ }; LodData m_lodData; + //! Flag indicating if the object is visible, i.e., was not culled out in the last frame + bool m_isVisible = true; + void SetDebugName([[maybe_unused]] const AZ::Name& debugName) { #ifdef AZ_CULL_DEBUG_ENABLED diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 79d152f661..85b6bf07b8 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -315,21 +315,29 @@ namespace AZ //Add all objects within this node to the view, without any extra culling for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries) { -#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED - if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) -#endif { if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this { continue; } - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); - ++numVisibleCullables; + +#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED + if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) +#endif + { + numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); + ++numVisibleCullables; + c->m_isVisible = true; + } } } } @@ -342,6 +350,10 @@ namespace AZ if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable) { Cullable* c = static_cast(visibleEntry->m_userData); + + // reset visibility flag to false, update to true if all culling checks pass + c->m_isVisible = false; + if ((c->m_cullData.m_drawListMask & drawListMask).none() || c->m_cullData.m_hideFlags & viewFlags || c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this @@ -362,6 +374,7 @@ namespace AZ { numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view); ++numVisibleCullables; + c->m_isVisible = true; } } } @@ -461,11 +474,11 @@ namespace AZ corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f); // find min clip-space depth and NDC min/max + float minDepth = FLT_MAX; float ndcMinX = FLT_MAX; float ndcMinY = FLT_MAX; float ndcMaxX = -FLT_MAX; float ndcMaxY = -FLT_MAX; - float minDepth = FLT_MAX; for (uint32_t index = 0; index < 8; ++index) { minDepth = AZStd::min(minDepth, corners[index].GetW()); From a1b8d1233cb75a330adfd260ad85c827f163f84d Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 11:28:22 -0700 Subject: [PATCH 013/105] [cpack_installer] initial work for installer Jenkins jobs --- .../build/Platform/Windows/build_config.json | 15 ++++++++ .../Windows/build_installer_windows.cmd | 22 +++++++++++ .../build/Platform/Windows/build_windows.cmd | 9 +++++ .../Platform/Windows/installer_windows.cmd | 38 +++++++++++++++++++ .../Platform/Windows/install_utiltools.ps1 | 3 ++ 5 files changed, 87 insertions(+) create mode 100644 scripts/build/Platform/Windows/build_installer_windows.cmd create mode 100644 scripts/build/Platform/Windows/installer_windows.cmd diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 38cd7d6ad8..b0d16f1fb6 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -306,6 +306,21 @@ "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } }, + "windows_installer": { + "TAGS": [ + "package" + ], + "COMMAND": "build_installer_windows.cmd", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build\\windows_vs2019", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE", + "CMAKE_INCLUDE_WIX": "True", + "CMAKE_LY_PROJECTS": "", + "CMAKE_TARGET": "ALL_BUILD", + "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" + } + }, "project_enginesource_profile_vs2019": { "TAGS": [ "project" diff --git a/scripts/build/Platform/Windows/build_installer_windows.cmd b/scripts/build/Platform/Windows/build_installer_windows.cmd new file mode 100644 index 0000000000..0d50f4b57a --- /dev/null +++ b/scripts/build/Platform/Windows/build_installer_windows.cmd @@ -0,0 +1,22 @@ +@ECHO OFF +REM +REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +REM its licensors. +REM +REM For complete copyright and license terms please see the LICENSE at the root of this +REM distribution (the "License"). All use of this software is governed by the License, +REM or, if provided, by the license below or the license accompanying this file. Do not +REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM + +CALL "%~dp0build_windows.cmd" +IF NOT %ERRORLEVEL%==0 GOTO :error + +CALL "%~dp0installer_windows.cmd" +IF NOT %ERRORLEVEL%==0 GOTO :error + +EXIT /b 0 + +:error +EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 3e995e1905..109db3438e 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,6 +38,15 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 +IF %CMAKE_INCLUDE_WIX%=="True" ( + REM Explicitly enable wix via command line arg for forensic logging + SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" +) +ELSE ( + REM Disable implicit enabling of windows packing by clearing out the wix variable + SET WIX= +) + SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS% IF NOT EXIST CMakeCache.txt ( diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd new file mode 100644 index 0000000000..c613f0a1e3 --- /dev/null +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -0,0 +1,38 @@ +@ECHO OFF +REM +REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +REM its licensors. +REM +REM For complete copyright and license terms please see the LICENSE at the root of this +REM distribution (the "License"). All use of this software is governed by the License, +REM or, if provided, by the license below or the license accompanying this file. Do not +REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM + +SETLOCAL EnableDelayedExpansion + +CALL %~dp0env_windows.cmd + +IF NOT EXIST %OUTPUT_DIRECTORY% ( + ECHO [ci_build] Error: $OUTPUT_DIRECTORY was not found + GOTO :error +) +PUSHD %OUTPUT_DIRECTORY% + +REM Override the temporary directory used by wix to the EBS volume +SET "WIX_TEMP=!WORKSPACE!/temp/wix" + +REM Run cpack +ECHO [ci_build] cpack -C %CONFIGURATION% +cpack -C %CONFIGURATION% +IF NOT %ERRORLEVEL%==0 GOTO :popd_error + +POPD +EXIT /b 0 + +:popd_error +POPD + +:error +EXIT /b 1 \ No newline at end of file diff --git a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 index 4e3695d35d..35ae04cc09 100644 --- a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 @@ -29,3 +29,6 @@ choco install corretto8jdk -y --ia INSTALLDIR="c:\jdk8" # Custom directory to ha # Install CMake choco install cmake -y --installargs 'ADD_CMAKE_TO_PATH=System' + +# Install WIX +choco install wixtoolset -y From ee0ecc2fa03b73cb95e3987d56a63441cfc8fe0a Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 2 Jun 2021 11:49:08 -0700 Subject: [PATCH 014/105] [ATOM-15711] Changing thumbmail sphere to polar sphere --- Gems/Atom/Feature/Common/Assets/Models/sphere.fbx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx b/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx index 5c8f550c8c..eb02c68394 100644 --- a/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx +++ b/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a476e99b55cf2a76fef6775c5a57dad29f8ffcb942c625bab04c89051a72a560 -size 62626 +oid sha256:838830c99f344f5b68e5e85c9bc52751350caf48e662c9c2b767ab77039bbd8f +size 103472 From cfd06f2e4a46869052dd5d6e5baee03d21860d35 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 13:10:11 -0700 Subject: [PATCH 015/105] [cpack_installer] added check for desired cmake version to be at least greater than minimum required plus minor cleanup --- cmake/Packaging.cmake | 10 ++++++++-- cmake/Platform/Windows/Packaging_windows.cmake | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 84bad13687..e7136eab12 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -51,6 +51,12 @@ if(NOT CPACK_GENERATOR) return() endif() +if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) + message(FATAL_ERROR + "The desired version of CMake to be included in the package is " + "is below the minium required version of CMake to run") +endif() + # pull down the desired copy of CMake so it can be included in the package if(NOT (CPACK_CMAKE_PACKAGE_FILE AND CPACK_CMAKE_PACKAGE_HASH)) message(FATAL_ERROR @@ -67,7 +73,7 @@ list(GET _version_componets 1 _minor_version) set(_url_version_tag "v${_major_version}.${_minor_version}") set(_package_url "https://cmake.org/files/${_url_version_tag}/${CPACK_CMAKE_PACKAGE_FILE}") -message(STATUS "Ensuring CMake ${CPACK_DESIRED_CMAKE_VERSION} is available for packaging...") +message(STATUS "Downloading CMake ${CPACK_DESIRED_CMAKE_VERSION} for packaging...") download_file( URL ${_package_url} TARGET_FILE ${_cmake_package_dest} @@ -77,7 +83,7 @@ download_file( list(GET _results 0 _status_code) if (${_status_code} EQUAL 0 AND EXISTS ${_cmake_package_dest}) - message(STATUS "-> Package found and verified!") + message(STATUS "Package found and verified!") else() file(REMOVE ${_cmake_package_dest}) list(REMOVE_AT _results 0) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index db9c7fc906..5210c24e7b 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -12,7 +12,7 @@ set(LY_WIX_PATH "" CACHE PATH "Path to the WiX install path") if(LY_WIX_PATH) - file(TO_CMAKE_PATH ${LY_QTIFW_PATH} CPACK_WIX_ROOT) + file(TO_CMAKE_PATH ${LY_WIX_PATH} CPACK_WIX_ROOT) elseif(DEFINED ENV{WIX}) file(TO_CMAKE_PATH $ENV{WIX} CPACK_WIX_ROOT) endif() @@ -26,7 +26,7 @@ else() return() endif() -set(CPACK_GENERATOR "WIX") +set(CPACK_GENERATOR WIX) set(_cmake_package_name "cmake-${CPACK_DESIRED_CMAKE_VERSION}-windows-x86_64") set(CPACK_CMAKE_PACKAGE_FILE "${_cmake_package_name}.zip") From 12cdaed03e0c968630c4b27d25d86cdeede3c389 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 13:58:37 -0700 Subject: [PATCH 016/105] [cpack_installer] updated installer icon/logo --- cmake/Platform/Windows/Packaging/product_icon.ico | 4 ++-- cmake/Platform/Windows/Packaging/product_logo.png | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Platform/Windows/Packaging/product_icon.ico b/cmake/Platform/Windows/Packaging/product_icon.ico index 0680ceea19..e7b77c35bf 100644 --- a/cmake/Platform/Windows/Packaging/product_icon.ico +++ b/cmake/Platform/Windows/Packaging/product_icon.ico @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a -size 107016 +oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266 +size 106449 diff --git a/cmake/Platform/Windows/Packaging/product_logo.png b/cmake/Platform/Windows/Packaging/product_logo.png index d5fd60ffb8..ac9c06f8f1 100644 --- a/cmake/Platform/Windows/Packaging/product_logo.png +++ b/cmake/Platform/Windows/Packaging/product_logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac0348c906c91de864cba91c0231b4794d8a00fafa630d13f2232351b90aa59b +oid sha256:8c804a6be619b9f35cad46eab30b94def7a4ac7142a92cb3f7c78a659381d834 size 11074 From 4b40f23d0b63cdf5b75188000c843e08c168c139 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 14:06:27 -0700 Subject: [PATCH 017/105] [cpack_installer] couple small fixes to installer Jenkins scripts --- scripts/build/Platform/Windows/build_windows.cmd | 5 ++--- scripts/build/Platform/Windows/installer_windows.cmd | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 109db3438e..a2f42b75cf 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,11 +38,10 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 -IF %CMAKE_INCLUDE_WIX%=="True" ( +IF "%CMAKE_INCLUDE_WIX%"=="True" ( REM Explicitly enable wix via command line arg for forensic logging SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" -) -ELSE ( +) ELSE ( REM Disable implicit enabling of windows packing by clearing out the wix variable SET WIX= ) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index c613f0a1e3..e8ce10ff14 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -22,6 +22,9 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" +IF NOT EXIST "%WIX_TEMP%" ( + MKDIR %WIX_TEMP% +) REM Run cpack ECHO [ci_build] cpack -C %CONFIGURATION% From c6e4e3ed1fd549d88e27a0aac254ec3ab267bc98 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 14:57:18 -0700 Subject: [PATCH 018/105] [cpack_installer] few more small fixes to installer Jenkins scripts --- scripts/build/Platform/Windows/build_windows.cmd | 2 +- scripts/build/build_node/Platform/Windows/install_utiltools.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index a2f42b75cf..799e6828b2 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -40,7 +40,7 @@ SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 IF "%CMAKE_INCLUDE_WIX%"=="True" ( REM Explicitly enable wix via command line arg for forensic logging - SET EXTRA_CMAKE_OPTIONS=%EXTRA_CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" + SET CMAKE_OPTIONS=%CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" ) ELSE ( REM Disable implicit enabling of windows packing by clearing out the wix variable SET WIX= diff --git a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 index 35ae04cc09..050e446f52 100644 --- a/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 +++ b/scripts/build/build_node/Platform/Windows/install_utiltools.ps1 @@ -30,5 +30,5 @@ choco install corretto8jdk -y --ia INSTALLDIR="c:\jdk8" # Custom directory to ha # Install CMake choco install cmake -y --installargs 'ADD_CMAKE_TO_PATH=System' -# Install WIX +# Install Windows Installer XML toolkit (WiX) choco install wixtoolset -y From 134258c18acff77588b7f57d8200d069144ecc2a Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 15:03:26 -0700 Subject: [PATCH 019/105] [cpack_installer] add trailing newline to some new files --- scripts/build/Platform/Windows/build_installer_windows.cmd | 2 +- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/build_installer_windows.cmd b/scripts/build/Platform/Windows/build_installer_windows.cmd index 0d50f4b57a..4f31fee085 100644 --- a/scripts/build/Platform/Windows/build_installer_windows.cmd +++ b/scripts/build/Platform/Windows/build_installer_windows.cmd @@ -19,4 +19,4 @@ IF NOT %ERRORLEVEL%==0 GOTO :error EXIT /b 0 :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index e8ce10ff14..e3a60fee1c 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -38,4 +38,4 @@ EXIT /b 0 POPD :error -EXIT /b 1 \ No newline at end of file +EXIT /b 1 From 197241f16d4a7f0ec6bfc33af715d43aff93e6e8 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 15:40:41 -0700 Subject: [PATCH 020/105] [cpack_installer] fixed issue with cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index e3a60fee1c..824a461a4b 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -26,9 +26,17 @@ IF NOT EXIST "%WIX_TEMP%" ( MKDIR %WIX_TEMP% ) +REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty +IF "%LY_CMAKE_PATH%"=="" ( + for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" + for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" +) ELSE ( + SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" +) + REM Run cpack -ECHO [ci_build] cpack -C %CONFIGURATION% -cpack -C %CONFIGURATION% +ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% +"%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From fd8cff6aecb2c93b93e6e09f9927feac563392c7 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 16:10:41 -0700 Subject: [PATCH 021/105] [cpack_installer] second attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 824a461a4b..b1fecaa2cb 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -23,10 +23,11 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( - MKDIR %WIX_TEMP% + MKDIR "WIX_TEMP%" ) REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty +SET CMAKE_INSTALL_PATH= IF "%LY_CMAKE_PATH%"=="" ( for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" @@ -34,6 +35,11 @@ IF "%LY_CMAKE_PATH%"=="" ( SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" ) +IF "%CMAKE_INSTALL_PATH%"=="" ( + ECHO [ci_build] CPack path not found + GOTO :popd_error +) + REM Run cpack ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% From 201d6b1b72ec579c980c5e38d58fc354bfcc9a29 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 17:29:50 -0700 Subject: [PATCH 022/105] [cpack_installer] third attempt to fix cpack selection --- .../Platform/Windows/installer_windows.cmd | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index b1fecaa2cb..41cc21b35b 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -26,23 +26,29 @@ IF NOT EXIST "%WIX_TEMP%" ( MKDIR "WIX_TEMP%" ) -REM Make sure we are using the CMake version of CPack and not the one that comes with chocolaty -SET CMAKE_INSTALL_PATH= +REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey +SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( - for /f %%i in ('where cmake') do SET "CMAKE_EXE_PATH=%%i" - for %%F in ("%CMAKE_EXE_PATH%") do SET "CMAKE_INSTALL_PATH=%%~dpF" + FOR /F %%i in ('where cpack') DO ( + REM The cpack in chocolatey expects a number supplied with --version so it will error + %%i --version > NUL + IF !ERRORLEVEL!==0 ( + SET "CPACK_PATH=%%i" + ) + ) ) ELSE ( - SET "CMAKE_INSTALL_PATH=%LY_CMAKE_PATH%\" + SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) -IF "%CMAKE_INSTALL_PATH%"=="" ( - ECHO [ci_build] CPack path not found - GOTO :popd_error +ECHO [ci_build] "%CPACK_PATH%" --version +"%CPACK_PATH%" --version +IF ERRORLEVEL 1 ( + ECHO [ci_build] CPack not found! + exit /b 1 ) - REM Run cpack -ECHO [ci_build] "%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% -"%CMAKE_INSTALL_PATH%cpack" -C %CONFIGURATION% +ECHO [ci_build] "%CPACK_PATH%" -C %CONFIGURATION% +"%CPACK_PATH%" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From 01f3ba560819fcba0d3a5575510e597904cced4f Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 17:58:58 -0700 Subject: [PATCH 023/105] [cpack_installer] fourth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 41cc21b35b..ef284290f3 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -23,7 +23,7 @@ PUSHD %OUTPUT_DIRECTORY% REM Override the temporary directory used by wix to the EBS volume SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( - MKDIR "WIX_TEMP%" + MKDIR "%WIX_TEMP%" ) REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey @@ -40,15 +40,15 @@ IF "%LY_CMAKE_PATH%"=="" ( SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) -ECHO [ci_build] "%CPACK_PATH%" --version -"%CPACK_PATH%" --version +ECHO [ci_build] "!CPACK_PATH!" --version +"!CPACK_PATH!" --version IF ERRORLEVEL 1 ( ECHO [ci_build] CPack not found! exit /b 1 ) -REM Run cpack -ECHO [ci_build] "%CPACK_PATH%" -C %CONFIGURATION% -"%CPACK_PATH%" -C %CONFIGURATION% + +ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% +"!CPACK_PATH!" -C %CONFIGURATION% IF NOT %ERRORLEVEL%==0 GOTO :popd_error POPD From 9dbe596e400bbeac8fcecc34227821fceab378b1 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 18:39:05 -0700 Subject: [PATCH 024/105] Added reflection probe cubemap quality levels --- .../Config/IBLSpecular.preset | 15 +- .../Config/IBLSpecularHigh.preset | 135 ++++++++++++++++++ .../Config/IBLSpecularLow.preset | 135 ++++++++++++++++++ .../Config/IBLSpecularVeryHigh.preset | 135 ++++++++++++++++++ .../Config/IBLSpecularVeryLow.preset | 135 ++++++++++++++++++ .../EditorReflectionProbeComponent.cpp | 20 ++- .../EditorReflectionProbeComponent.h | 1 + .../ReflectionProbeComponentController.cpp | 1 + .../ReflectionProbeComponentController.h | 24 ++++ 9 files changed, 595 insertions(+), 6 deletions(-) create mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularHigh.preset create mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularLow.preset create mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryHigh.preset create mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryLow.preset diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset index d940f425c2..4f935c73ff 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset @@ -8,7 +8,8 @@ "Name": "IBLSpecular", "Description": "The input cubemap generates an IBL specular output cubemap.", "FileMasks": [ - "_iblspecularcm" + "_iblspecularcm", + "_iblspecularcm256" ], "SourceColor": "Linear", "DestColor": "Linear", @@ -34,7 +35,8 @@ "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ - "_iblspecularcm" + "_iblspecularcm", + "_iblspecularcm256" ], "SourceColor": "Linear", "DestColor": "Linear", @@ -59,7 +61,8 @@ "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ - "_iblspecularcm" + "_iblspecularcm", + "_iblspecularcm256" ], "SourceColor": "Linear", "DestColor": "Linear", @@ -84,7 +87,8 @@ "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ - "_iblspecularcm" + "_iblspecularcm", + "_iblspecularcm256" ], "SourceColor": "Linear", "DestColor": "Linear", @@ -109,7 +113,8 @@ "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}", "Name": "IBLSpecular", "FileMasks": [ - "_iblspecularcm" + "_iblspecularcm", + "_iblspecularcm256" ], "SourceColor": "Linear", "DestColor": "Linear", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularHigh.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularHigh.preset new file mode 100644 index 0000000000..ff4e143326 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularHigh.preset @@ -0,0 +1,135 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}", + "Name": "IBLSpecularHigh", + "Description": "The input cubemap generates an IBL specular output cubemap.", + "FileMasks": [ + "_iblspecularcm512" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 512, + "MaxTextureSize": 512, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "PlatformsPresets": { + "android": { + "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}", + "Name": "IBLSpecularHigh", + "FileMasks": [ + "_iblspecularcm512" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 512, + "MaxTextureSize": 512, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "ios": { + "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}", + "Name": "IBLSpecularHigh", + "FileMasks": [ + "_iblspecularcm512" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 512, + "MaxTextureSize": 512, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "mac": { + "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}", + "Name": "IBLSpecularHigh", + "FileMasks": [ + "_iblspecularcm512" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 512, + "MaxTextureSize": 512, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "provo": { + "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}", + "Name": "IBLSpecularHigh", + "FileMasks": [ + "_iblspecularcm512" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 512, + "MaxTextureSize": 512, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularLow.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularLow.preset new file mode 100644 index 0000000000..ee9ddd6ac7 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularLow.preset @@ -0,0 +1,135 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}", + "Name": "IBLSpecularLow", + "Description": "The input cubemap generates an IBL specular output cubemap.", + "FileMasks": [ + "_iblspecularcm128" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 128, + "MaxTextureSize": 128, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "PlatformsPresets": { + "android": { + "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}", + "Name": "IBLSpecularLow", + "FileMasks": [ + "_iblspecularcm128" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 128, + "MaxTextureSize": 128, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "ios": { + "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}", + "Name": "IBLSpecularLow", + "FileMasks": [ + "_iblspecularcm128" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 128, + "MaxTextureSize": 128, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "mac": { + "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}", + "Name": "IBLSpecularLow", + "FileMasks": [ + "_iblspecularcm128" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 128, + "MaxTextureSize": 128, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "provo": { + "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}", + "Name": "IBLSpecularLow", + "FileMasks": [ + "_iblspecularcm128" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 128, + "MaxTextureSize": 128, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryHigh.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryHigh.preset new file mode 100644 index 0000000000..08d9416935 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryHigh.preset @@ -0,0 +1,135 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}", + "Name": "IBLSpecularVeryHigh", + "Description": "The input cubemap generates an IBL specular output cubemap.", + "FileMasks": [ + "_iblspecularcm1024" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 1024, + "MaxTextureSize": 1024, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "PlatformsPresets": { + "android": { + "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}", + "Name": "IBLSpecularVeryHigh", + "FileMasks": [ + "_iblspecularcm1024" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 1024, + "MaxTextureSize": 1024, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "ios": { + "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}", + "Name": "IBLSpecularVeryHigh", + "FileMasks": [ + "_iblspecularcm1024" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 1024, + "MaxTextureSize": 1024, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "mac": { + "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}", + "Name": "IBLSpecularVeryHigh", + "FileMasks": [ + "_iblspecularcm1024" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 1024, + "MaxTextureSize": 1024, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "provo": { + "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}", + "Name": "IBLSpecularVeryHigh", + "FileMasks": [ + "_iblspecularcm1024" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 1024, + "MaxTextureSize": 1024, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + } + } + } +} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryLow.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryLow.preset new file mode 100644 index 0000000000..c5c0788848 --- /dev/null +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryLow.preset @@ -0,0 +1,135 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "MultiplatformPresetSettings", + "ClassData": { + "DefaultPreset": { + "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}", + "Name": "IBLSpecularVeryLow", + "Description": "The input cubemap generates an IBL specular output cubemap.", + "FileMasks": [ + "_iblspecularcm64" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 64, + "MaxTextureSize": 64, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "PlatformsPresets": { + "android": { + "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}", + "Name": "IBLSpecularVeryLow", + "FileMasks": [ + "_iblspecularcm64" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 64, + "MaxTextureSize": 64, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "ios": { + "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}", + "Name": "IBLSpecularVeryLow", + "FileMasks": [ + "_iblspecularcm64" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 64, + "MaxTextureSize": 64, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "mac": { + "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}", + "Name": "IBLSpecularVeryLow", + "FileMasks": [ + "_iblspecularcm64" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 64, + "MaxTextureSize": 64, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + }, + "provo": { + "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}", + "Name": "IBLSpecularVeryLow", + "FileMasks": [ + "_iblspecularcm64" + ], + "SourceColor": "Linear", + "DestColor": "Linear", + "SuppressEngineReduce": true, + "PixelFormat": "R9G9B9E5", + "DiscardAlpha": true, + "MinTextureSize": 64, + "MaxTextureSize": 64, + "IsPowerOf2": true, + "CubemapSettings": { + "Filter": "GGX", + "MipAngle": 7.0, + "MipSlope": 2.299999952316284, + "EdgeFixup": -431602080.0, + "SubId": 2000 + }, + "MipMapSetting": { + "MipGenType": "Box" + } + } + } + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 7880d5e88c..f3932322b1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -39,6 +39,7 @@ namespace AZ serializeContext->Class() ->Version(2, ConvertToEditorRenderComponentAdapter<1>) ->Field("useBakedCubemap", &EditorReflectionProbeComponent::m_useBakedCubemap) + ->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel) ->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath) ->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset) ; @@ -67,6 +68,13 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_useBakedCubemap, "Use Baked Cubemap", "Selects between a cubemap that captures the environment at location in the scene or a preauthored cubemap") ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorReflectionProbeComponent::OnUseBakedCubemapValidate) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnUseBakedCubemapChanged) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel, "Baked Cubemap Quality", "Resolution of the baked cubemap") + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting) + ->EnumAttribute(BakedCubeMapQualityLevel::VeryLow, "Very Low") + ->EnumAttribute(BakedCubeMapQualityLevel::Low, "Low") + ->EnumAttribute(BakedCubeMapQualityLevel::Medium, "Medium") + ->EnumAttribute(BakedCubeMapQualityLevel::High, "High") + ->EnumAttribute(BakedCubeMapQualityLevel::VeryHigh, "Very High") ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath, "Baked Cubemap Path", "Baked Cubemap Path") ->Attribute(AZ::Edit::Attributes::ReadOnly, true) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting) @@ -332,6 +340,12 @@ namespace AZ // clear it to force the generation of a new filename cubeMapRelativePath.clear(); } + + // if the quality level changed we need to generate a new filename + if (m_controller.m_configuration.m_bakedCubeMapQualityLevel != m_bakedCubeMapQualityLevel) + { + cubeMapRelativePath.clear(); + } } // build a new cubemap path if necessary @@ -345,7 +359,10 @@ namespace AZ AZStd::string uuidString; uuid.ToString(uuidString); - cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + "_iblspecularcm.dds"; + // determine the filemask suffix from the cubemap quality level setting + AZStd::string fileSuffix = BakedCubeMapFileSuffixes[aznumeric_cast(m_bakedCubeMapQualityLevel)]; + + cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + fileSuffix; // replace any invalid filename characters auto invalidCharacters = [](char letter) @@ -384,6 +401,7 @@ namespace AZ // save the relative source path in the configuration AzToolsFramework::ScopedUndoBatch undoBatch("Cubemap path changed."); m_controller.m_configuration.m_bakedCubeMapRelativePath = cubeMapRelativePath; + m_controller.m_configuration.m_bakedCubeMapQualityLevel = m_bakedCubeMapQualityLevel; SetDirty(); // update UI cubemap path display diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h index 23fd391818..bc45eae11b 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.h @@ -77,6 +77,7 @@ namespace AZ // UI settings // the user can select between a baked cubemap or an authored cubemap asset bool m_useBakedCubemap = true; + BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium; AZStd::string m_bakedCubeMapRelativePath; Data::Asset m_bakedCubeMapAsset; Data::Asset m_authoredCubeMapAsset; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp index 9b0ff29bb8..873810c016 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp @@ -46,6 +46,7 @@ namespace AZ ->Field("InnerLength", &ReflectionProbeComponentConfig::m_innerLength) ->Field("InnerWidth", &ReflectionProbeComponentConfig::m_innerWidth) ->Field("UseBakedCubemap", &ReflectionProbeComponentConfig::m_useBakedCubemap) + ->Field("BakedCubemapQualityLevel", &ReflectionProbeComponentConfig::m_bakedCubeMapQualityLevel) ->Field("BakedCubeMapRelativePath", &ReflectionProbeComponentConfig::m_bakedCubeMapRelativePath) ->Field("BakedCubeMapAsset", &ReflectionProbeComponentConfig::m_bakedCubeMapAsset) ->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h index 0a57fde882..97a7fbebb4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.h @@ -24,6 +24,29 @@ namespace AZ { namespace Render { + enum class BakedCubeMapQualityLevel : uint32_t + { + VeryLow, // 64 + Low, // 128 + Medium, // 256 + High, // 512 + VeryHigh, // 1024 + + Count + }; + + static const char* BakedCubeMapFileSuffixes[] = + { + "_iblspecularcm64.dds", + "_iblspecularcm128.dds", + "_iblspecularcm256.dds", + "_iblspecularcm512.dds", + "_iblspecularcm1024.dds" + }; + + static_assert(AZ_ARRAY_SIZE(BakedCubeMapFileSuffixes) == aznumeric_cast(BakedCubeMapQualityLevel::Count), + "BakedCubeMapFileSuffixes must have the same number of entries as BakedCubeMapQualityLevel"); + class ReflectionProbeComponentConfig final : public AZ::ComponentConfig { @@ -43,6 +66,7 @@ namespace AZ bool m_showVisualization = true; bool m_useBakedCubemap = true; + BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium; AZStd::string m_bakedCubeMapRelativePath; Data::Asset m_bakedCubeMapAsset; Data::Asset m_authoredCubeMapAsset; From 3f9811e498efdb07e9275a3395f201b65ce8aa0f Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 18:40:22 -0700 Subject: [PATCH 025/105] [cpack_installer] fifth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index ef284290f3..73d1a3d88c 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -31,7 +31,7 @@ SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( FOR /F %%i in ('where cpack') DO ( REM The cpack in chocolatey expects a number supplied with --version so it will error - %%i --version > NUL + "%%i" --version > NUL IF !ERRORLEVEL!==0 ( SET "CPACK_PATH=%%i" ) From c3df73bed8f4b052659086e69127e90ae794bb46 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 2 Jun 2021 18:48:50 -0700 Subject: [PATCH 026/105] PR feedback and fixing TrackView --- .../DisplayMapperConfigurationDescriptor.cpp | 2 +- .../DisplayMapper/DisplayMapperComponentBus.h | 5 +++++ .../DisplayMapperComponentController.cpp | 20 +++++++++++++++++++ .../EditorDisplayMapperComponent.cpp | 5 ++--- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index 0858381fc5..a064b61a1a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -96,7 +96,7 @@ namespace AZ ; serializeContext->Class() - ->Version(1) + ->Version(2) ->Field("Name", &DisplayMapperConfigurationDescriptor::m_name) ->Field("OperationType", &DisplayMapperConfigurationDescriptor::m_operationType) ->Field("LdrGradingLutEnabled", &DisplayMapperConfigurationDescriptor::m_ldrGradingLutEnabled) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h index 4a01f71325..448c6cd32a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentBus.h @@ -26,6 +26,11 @@ namespace AZ : public ComponentBus { public: + AZ_RTTI(AZ::Render::DisplayMapperComponentRequests, "{9E2E8AF5-1176-44B4-A461-E09867753349}"); + + /// Overrides the default AZ::EBusTraits handler policy to allow one listener only. + static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Single; + //! Load preconfigured preset for specific ODT mode virtual void LoadPreset(OutputDeviceTransformType preset) = 0; //! Set display mapper type diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index 06c549f560..30c0ac8b1d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -122,10 +122,14 @@ namespace AZ void DisplayMapperComponentController::Activate(EntityId entityId) { m_entityId = entityId; + + DisplayMapperComponentRequestBus::Handler::BusConnect(m_entityId); } void DisplayMapperComponentController::Deactivate() { + DisplayMapperComponentRequestBus::Handler::BusDisconnect(m_entityId); + m_postProcessInterface = nullptr; m_entityId.SetInvalid(); } @@ -186,6 +190,10 @@ namespace AZ void DisplayMapperComponentController::SetOverrideAcesParameters(bool value) { + if (m_configuration.m_acesParameterOverrides.m_overrideDefaults == value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_overrideDefaults = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -200,6 +208,10 @@ namespace AZ void DisplayMapperComponentController::SetAlterSurround(bool value) { + if (m_configuration.m_acesParameterOverrides.m_alterSurround != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_alterSurround = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -214,6 +226,10 @@ namespace AZ void DisplayMapperComponentController::SetApplyDesaturation(bool value) { + if (m_configuration.m_acesParameterOverrides.m_applyDesaturation != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_applyDesaturation = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { @@ -228,6 +244,10 @@ namespace AZ void DisplayMapperComponentController::SetApplyCATD60toD65(bool value) { + if (m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 != value) + { + return; // prevents flickering when set via TrackView + } m_configuration.m_acesParameterOverrides.m_applyCATD60toD65 = value; if (m_configuration.m_displayMapperOperation == DisplayMapperOperationType::Aces) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index 4a03c6f712..8b59cfc9ea 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -10,10 +10,9 @@ * */ -#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" - #include #include +#include namespace AZ { @@ -180,7 +179,7 @@ namespace AZ if (auto behaviorContext = azrtti_cast(context)) { - behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); + behaviorContext->Class()->RequestBus("DisplayMapperComponentRequestBus"); behaviorContext->ConstantProperty("EditorDisplayMapperComponentTypeId", BehaviorConstant(Uuid(EditorDisplayMapperComponentTypeId))) ->Attribute(AZ::Script::Attributes::Module, "render") From 35fed7722305c8a3eaadb91f45836628d5d2ba33 Mon Sep 17 00:00:00 2001 From: mgwynn Date: Wed, 2 Jun 2021 21:56:03 -0400 Subject: [PATCH 027/105] Adding CLI script for modifying project properties (LYN-3918). Updating O3de to support it. Fixing some typo errors in manifest.py and minor optimizations --- scripts/o3de.py | 7 ++- scripts/o3de/o3de/manifest.py | 12 +--- scripts/o3de/o3de/project_properties.py | 82 +++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 scripts/o3de/o3de/project_properties.py diff --git a/scripts/o3de.py b/scripts/o3de.py index 8d7532878c..85d49ec268 100755 --- a/scripts/o3de.py +++ b/scripts/o3de.py @@ -32,7 +32,7 @@ def add_args(parser, subparsers) -> None: # add the scripts/o3de directory to the front of the sys.path sys.path.insert(0, str(o3de_package_dir)) from o3de import engine_template, global_project, register, print_registration, get_registration, \ - enable_gem, disable_gem, sha256 + enable_gem, disable_gem, project_properties, sha256 # Remove the temporarily added path sys.path = sys.path[1:] @@ -55,7 +55,10 @@ def add_args(parser, subparsers) -> None: # remove a gem from a project disable_gem.add_args(subparsers) - + + # modify project properties + project_properties.add_args(subparsers) + # sha256 sha256.add_args(subparsers) diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index 2a7e5bba11..edcd44c525 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -573,9 +573,7 @@ def get_registered(engine_name: str = None, return engine_path elif isinstance(project_name, str): - enging_projects = get_engine_projects() - projects = json_data['projects'].copy() - projects.extend(engine_object['projects']) + projects = get_all_projects() for project_path in projects: project_path = pathlib.Path(project_path).resolve() project_json = project_path / 'project.json' @@ -605,9 +603,7 @@ def get_registered(engine_name: str = None, return gem_path elif isinstance(template_name, str): - engine_templates = get_engine_templates() - templates = json_data['templates'].copy() - templates.extend(engine_templates) + templates = get_all_templates() for template_path in templates: template_path = pathlib.Path(template_path).resolve() template_json = template_path / 'template.json' @@ -622,9 +618,7 @@ def get_registered(engine_name: str = None, return template_path elif isinstance(restricted_name, str): - engine_restricted = get_engine_restricted() - restricted = json_data['restricted'].copy() - restricted.extend(engine_restricted) + restricted = get_all_restricted() for restricted_path in restricted: restricted_path = pathlib.Path(restricted_path).resolve() restricted_json = restricted_path / 'restricted.json' diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py new file mode 100644 index 0000000000..10153ff833 --- /dev/null +++ b/scripts/o3de/o3de/project_properties.py @@ -0,0 +1,82 @@ +import argparse +import json +import os +import pathlib +import sys +import logging + +from o3de import manifest + +logger = logging.getLogger() +logging.basicConfig() + +def get_project_props(name: str = None, path: pathlib.Path = None) -> dict: + proj_json = manifest.get_project_json_data(project_name=name, project_path=path) + if not proj_json: + logger.error('Could not retrieve project.json file') + return None + return proj_json + +def edit_project_props(proj_path, proj_name, new_origin, new_display, + new_summary, new_icon, new_tag) -> int: + proj_json = get_project_props(proj_name, proj_path) + + try: + if new_origin and 'origin' in proj_json: + proj_json['origin'] = new_origin + if new_display and 'display_name' in proj_json: + proj_json['display_name'] = new_display + if new_summary and 'summary' in proj_json: + proj_json['summary'] = new_summary + if new_icon and 'icon_path' in proj_json: + proj_json['icon_path'] = new_icon + if new_tag and 'user_tags' in proj_json: + proj_json['user_tags'].append(new_tag) + except Exception as e: + logger.error(e) + return 1 + + manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path)/'project.json') + return 0 + +def _edit_project_props(args: argparse) -> int: + return edit_project_props(args.project_path, + args.project_name, + args.project_origin, + args.project_display, + args.project_summary, + args.project_icon, + args.project_tag) + +def add_parser_args(parser): + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False, + help='The path to the project.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project.') + group = parser.add_argument_group('properties', 'arguments for modifying individual project properties.') + group.add_argument('-po', '--project-origin', type=str, required=False, + help='Sets description or url for project origin.') + group.add_argument('-pd', '--project-display', type=str, required=False, + help='Sets the project display name.') + group.add_argument('-ps', '--project-summary', type=str, required=False, + help='Sets the summary description of the project.') + group.add_argument('-pi', '--project-icon', type=str, required=False, + help='Sets the path to the projects icon resource.') + group.add_argument('-pt', '--project-tag', type=str, required=False, + help='Adds a tag to canonical user tags.') + parser.set_defaults(func=_edit_project_props) + +def add_args(subparsers) -> None: + enable_project_props_subparser = subparsers.add_parser('edit-project-props') + add_parser_args(enable_project_props_subparser) + +def main(): + the_parser = argparse.ArgumentParser() + add_parser_args(the_parser) + the_args = the_parser.parse_args() + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + sys.exit(ret) + +if __name__ == "__main__": + main() \ No newline at end of file From 86234841689b7c48de57b3d7b9c3a60f129637a0 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:02:42 -0700 Subject: [PATCH 028/105] Added Masked Occlusion Culling external files --- .../CompilerSpecific.inl | 98 + .../MaskedOcclusionCulling/LICENSE.txt | 181 ++ .../MaskedOcclusionCulling.cpp | 456 ++++ .../MaskedOcclusionCulling.h | 592 +++++ .../MaskedOcclusionCullingAVX2.cpp | 243 ++ .../MaskedOcclusionCullingAVX512.cpp | 309 +++ .../MaskedOcclusionCullingCommon.inl | 2053 +++++++++++++++++ .../MaskedOcclusionCulling/PackageInfo.json | 6 + 8 files changed, 3938 insertions(+) create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl create mode 100644 Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl new file mode 100644 index 0000000000..a6203ff939 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/CompilerSpecific.inl @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common shared include file to hide compiler/os specific functions from the rest of the code. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) + #define __MICROSOFT_COMPILER +#endif + +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__clang__)) // Windows: MSVC / Intel compiler / clang + #include + #include + + #define FORCE_INLINE __forceinline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + _BitScanForward(&idx, *mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return _aligned_malloc(size, alignment); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + _aligned_free(ptr); + } + +#elif defined(__GNUG__) || defined(__clang__) // G++ or clang + #include +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) + #include // memalign +#else + #include // memalign +#endif + #include + #include + #include + + #define FORCE_INLINE inline + + FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask) + { + unsigned long idx; + idx = __builtin_ctzl(*mask); + *mask &= *mask - 1; + return idx; + } + + FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) + { + return memalign(alignment, size); + } + + FORCE_INLINE void aligned_free(void *ptr) + { + free(ptr); + } + + FORCE_INLINE void __cpuidex(int* cpuinfo, int function, int subfunction) + { + __cpuid_count(function, subfunction, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); + } + + FORCE_INLINE unsigned long long _xgetbv(unsigned int index) + { + unsigned int eax, edx; + __asm__ __volatile__( + "xgetbv;" + : "=a" (eax), "=d"(edx) + : "c" (index) + ); + return ((unsigned long long)edx << 32) | eax; + } + +#else + #error Unsupported compiler +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt new file mode 100644 index 0000000000..f1b08a582c --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/LICENSE.txt @@ -0,0 +1,181 @@ + +Apache License + Version 2.0, January 2004 + + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + You must give any other recipients of the Work or Derivative Works a copy of + this License; and + + + You must cause any modified files to carry prominent notices stating that You + changed the files; and + + + You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from the + Source form of the Work, excluding those notices that do not pertain to any + part of the Derivative Works; and + + + If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those notices + that do not pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part of the + Derivative Works; within the Source form or documentation, if provided along + with the Derivative Works; or, within a display generated by the Derivative + Works, if and wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + +Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, +Version 2.0 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp new file mode 100644 index 0000000000..2844fbde00 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp @@ -0,0 +1,456 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__AVX__) || defined(__AVX2__) + // For performance reasons, the MaskedOcclusionCullingAVX2/512.cpp files should be compiled with VEX encoding for SSE instructions (to avoid + // AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, this file + // _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to use lowest supported target platform + // (/arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. + #error The MaskedOcclusionCulling.cpp should be compiled with lowest supported target platform, e.g. /arch:SSE2 +#endif + +static MaskedOcclusionCulling::Implementation DetectCPUFeatures(MaskedOcclusionCulling::pfnAlignedAlloc alignedAlloc, MaskedOcclusionCulling::pfnAlignedFree alignedFree) +{ + struct CpuInfo { int regs[4]; }; + + // Get regular CPUID values + int regs[4]; + __cpuidex(regs, 0, 0); + + // MOCVectorAllocator mocalloc( alignedAlloc, alignedFree ); + // std::vector> cpuId( mocalloc ), cpuIdEx( mocalloc ); + // cpuId.resize( regs[0] ); + size_t cpuIdCount = regs[0]; + CpuInfo * cpuId = (CpuInfo*)alignedAlloc( 64, sizeof(CpuInfo) * cpuIdCount ); + + for (size_t i = 0; i < cpuIdCount; ++i) + __cpuidex(cpuId[i].regs, (int)i, 0); + + // Get extended CPUID values + __cpuidex(regs, 0x80000000, 0); + + //cpuIdEx.resize(regs[0] - 0x80000000); + size_t cpuIdExCount = regs[0] - 0x80000000; + CpuInfo * cpuIdEx = (CpuInfo*)alignedAlloc( 64, sizeof( CpuInfo ) * cpuIdExCount ); + + for (size_t i = 0; i < cpuIdExCount; ++i) + __cpuidex(cpuIdEx[i].regs, 0x80000000 + (int)i, 0); + + #define TEST_BITS(A, B) (((A) & (B)) == (B)) + #define TEST_FMA_MOVE_OXSAVE (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 12) | (1 << 22) | (1 << 27))) + #define TEST_LZCNT (cpuIdExCount >= 1 && TEST_BITS(cpuIdEx[1].regs[2], 0x20)) + #define TEST_SSE41 (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 19))) + #define TEST_XMM_YMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 2) | (1 << 1))) + #define TEST_OPMASK_ZMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 7) | (1 << 6) | (1 << 5))) + #define TEST_BMI1_BMI2_AVX2 (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 3) | (1 << 5) | (1 << 8))) + #define TEST_AVX512_F_BW_DQ (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 16) | (1 << 17) | (1 << 30))) + + MaskedOcclusionCulling::Implementation retVal = MaskedOcclusionCulling::SSE2; + if (TEST_FMA_MOVE_OXSAVE && TEST_LZCNT && TEST_SSE41) + { + if (TEST_XMM_YMM && TEST_OPMASK_ZMM && TEST_BMI1_BMI2_AVX2 && TEST_AVX512_F_BW_DQ) + retVal = MaskedOcclusionCulling::AVX512; + else if (TEST_XMM_YMM && TEST_BMI1_BMI2_AVX2) + retVal = MaskedOcclusionCulling::AVX2; + } + else if (TEST_SSE41) + retVal = MaskedOcclusionCulling::SSE41; + alignedFree( cpuId ); + alignedFree( cpuIdEx ); + return retVal; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Utility functions (not directly related to the algorithm/rasterizer) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void MaskedOcclusionCulling::TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout) +{ + // This function pretty slow, about 10-20% slower than if the vertices are stored in aligned SOA form. + if (nVtx == 0) + return; + + // Load matrix and swizzle out the z component. For post-multiplication (OGL), the matrix is assumed to be column + // major, with one column per SSE register. For pre-multiplication (DX), the matrix is assumed to be row major. + __m128 mtxCol0 = _mm_loadu_ps(mtx); + __m128 mtxCol1 = _mm_loadu_ps(mtx + 4); + __m128 mtxCol2 = _mm_loadu_ps(mtx + 8); + __m128 mtxCol3 = _mm_loadu_ps(mtx + 12); + + int stride = vtxLayout.mStride; + const char *vPtr = (const char *)inVtx; + float *outPtr = xfVtx; + + // Iterate through all vertices and transform + for (unsigned int vtx = 0; vtx < nVtx; ++vtx) + { + __m128 xVal = _mm_load1_ps((float*)(vPtr)); + __m128 yVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetY)); + __m128 zVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetZ)); + + __m128 xform = _mm_add_ps(_mm_mul_ps(mtxCol0, xVal), _mm_add_ps(_mm_mul_ps(mtxCol1, yVal), _mm_add_ps(_mm_mul_ps(mtxCol2, zVal), mtxCol3))); + _mm_storeu_ps(outPtr, xform); + vPtr += stride; + outPtr += 4; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Typedefs +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; +typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 defines +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 4 +#define TILE_HEIGHT_SHIFT 2 + +#define SIMD_LANE_IDX _mm_setr_epi32(0, 1, 2, 3) + +#define SIMD_SUB_TILE_COL_OFFSET _mm_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm_setzero_si128() +#define SIMD_SUB_TILE_COL_OFFSET_F _mm_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm_setzero_ps() + +#define SIMD_LANE_YCOORD_I _mm_setr_epi32(128, 384, 640, 896) +#define SIMD_LANE_YCOORD_F _mm_setr_ps(128.0f, 384.0f, 640.0f, 896.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SSE2/SSE4.1 functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m128 __mw; +typedef __m128i __mwi; + +#define _mmw_set1_ps _mm_set1_ps +#define _mmw_setzero_ps _mm_setzero_ps +#define _mmw_and_ps _mm_and_ps +#define _mmw_or_ps _mm_or_ps +#define _mmw_xor_ps _mm_xor_ps +#define _mmw_not_ps(a) _mm_xor_ps((a), _mm_castsi128_ps(_mm_set1_epi32(~0))) +#define _mmw_andnot_ps _mm_andnot_ps +#define _mmw_neg_ps(a) _mm_xor_ps((a), _mm_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm_and_ps((a), _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm_add_ps +#define _mmw_sub_ps _mm_sub_ps +#define _mmw_mul_ps _mm_mul_ps +#define _mmw_div_ps _mm_div_ps +#define _mmw_min_ps _mm_min_ps +#define _mmw_max_ps _mm_max_ps +#define _mmw_movemask_ps _mm_movemask_ps +#define _mmw_cmpge_ps(a,b) _mm_cmpge_ps(a, b) +#define _mmw_cmpgt_ps(a,b) _mm_cmpgt_ps(a, b) +#define _mmw_cmpeq_ps(a,b) _mm_cmpeq_ps(a, b) +#define _mmw_fmadd_ps(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +#define _mmw_fmsub_ps(a,b,c) _mm_sub_ps(_mm_mul_ps(a,b), c) +#define _mmw_shuffle_ps _mm_shuffle_ps +#define _mmw_insertf32x4_ps(a,b,c) (b) +#define _mmw_cvtepi32_ps _mm_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm_set1_epi32 +#define _mmw_setzero_epi32 _mm_setzero_si128 +#define _mmw_and_epi32 _mm_and_si128 +#define _mmw_or_epi32 _mm_or_si128 +#define _mmw_xor_epi32 _mm_xor_si128 +#define _mmw_not_epi32(a) _mm_xor_si128((a), _mm_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm_andnot_si128 +#define _mmw_neg_epi32(a) _mm_sub_epi32(_mm_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm_add_epi32 +#define _mmw_sub_epi32 _mm_sub_epi32 +#define _mmw_subs_epu16 _mm_subs_epu16 +#define _mmw_cmpeq_epi32 _mm_cmpeq_epi32 +#define _mmw_cmpgt_epi32 _mm_cmpgt_epi32 +#define _mmw_srai_epi32 _mm_srai_epi32 +#define _mmw_srli_epi32 _mm_srli_epi32 +#define _mmw_slli_epi32 _mm_slli_epi32 +#define _mmw_cvtps_epi32 _mm_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm_cvttps_epi32 + +#define _mmx_fmadd_ps _mmw_fmadd_ps +#define _mmx_max_epi32 _mmw_max_epi32 +#define _mmx_min_epi32 _mmw_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized SSE input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +FORCE_INLINE void GatherVertices(__m128 *vtxX, __m128 *vtxY, __m128 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + for (int lane = 0; lane < numLanes; lane++) + { + for (int i = 0; i < 3; i++) + { + char *vPtrX = (char *)inVtx + inTrisPtr[lane * 3 + i] * vtxLayout.mStride; + char *vPtrY = vPtrX + vtxLayout.mOffsetY; + char *vPtrW = vPtrX + vtxLayout.mOffsetW; + + simd_f32(vtxX[i])[lane] = *((float*)vPtrX); + simd_f32(vtxY[i])[lane] = *((float*)vPtrY); + simd_f32(vtxW[i])[lane] = *((float*)vPtrW); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE4.1 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE41 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) { return _mm_mullo_epi32(a, b); } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) { return _mm_min_epi32(a, b); } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) { return _mm_max_epi32(a, b); } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) { return _mm_abs_epi32(a); } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) { return _mm_blendv_ps(a, b, c); } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) { return _mm_testz_si128(a, b); } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) { return _mm_dp_ps(a, b, 0xFF); } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC); } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + const __m128i shuff = _mm_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF); + return _mm_shuffle_epi8(a, shuff); + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mm_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses lookup tables and _mm_shuffle_epi8 to perform _mm_sllv_epi32(~0, shift) + const __m128i byteShiftLUT = _mm_setr_epi8((char)0xFF, (char)0xFE, (char)0xFC, (char)0xF8, (char)0xF0, (char)0xE0, (char)0xC0, (char)0x80, 0, 0, 0, 0, 0, 0, 0, 0); + const __m128i byteShiftOffset = _mm_setr_epi8(0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24); + const __m128i byteShiftShuffle = _mm_setr_epi8(0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0xC, 0xC, 0xC, 0xC); + + __m128i byteShift = _mm_shuffle_epi8(shift, byteShiftShuffle); + byteShift = _mm_min_epi8(_mm_subs_epu8(byteShift, byteShiftOffset), _mm_set1_epi8(8)); + __m128i retMask = _mm_shuffle_epi8(byteShiftLUT, byteShift); + + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE41; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SSE2 version +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace MaskedOcclusionCullingSSE2 +{ + FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) + { + // Do products for even / odd lanes & merge the result + __m128i even = _mm_and_si128(_mm_mul_epu32(a, b), _mm_setr_epi32(~0, 0, ~0, 0)); + __m128i odd = _mm_slli_epi64(_mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)), 32); + return _mm_or_si128(even, odd); + } + FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(a, b); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) + { + __m128i cond = _mm_cmpgt_epi32(b, a); + return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b)); + } + FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) + { + __m128i mask = _mm_cmplt_epi32(a, _mm_setzero_si128()); + return _mm_add_epi32(_mm_xor_si128(a, mask), _mm_srli_epi32(mask, 31)); + } + FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) + { + return _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(a, b), _mm_setzero_si128())) == 0xFFFF; + } + FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) + { + __m128 cond = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(c), 31)); + return _mm_or_ps(_mm_andnot_ps(cond, a), _mm_and_ps(cond, b)); + } + FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) + { + // Product and two shuffle/adds pairs (similar to hadd_ps) + __m128 prod = _mm_mul_ps(a, b); + __m128 dp = _mm_add_ps(prod, _mm_shuffle_ps(prod, prod, _MM_SHUFFLE(2, 3, 0, 1))); + dp = _mm_add_ps(dp, _mm_shuffle_ps(dp, dp, _MM_SHUFFLE(0, 1, 2, 3))); + return dp; + } + FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) + { + int originalMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_UP); + __m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a)); + _MM_SET_ROUNDING_MODE(originalMode); + return rounded; + } + FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a) + { + // Perform transpose through two 16->8 bit pack and byte shifts + __m128i res = a; + const __m128i mask = _mm_setr_epi8(~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8)); + return res; + } + FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift) + { + __m128i shift = _mmw_min_epi32(ishift, _mm_set1_epi32(32)); + + // Uses scalar approach to perform _mm_sllv_epi32(~0, shift) + static const unsigned int maskLUT[33] = { + ~0U << 0, ~0U << 1, ~0U << 2 , ~0U << 3, ~0U << 4, ~0U << 5, ~0U << 6 , ~0U << 7, ~0U << 8, ~0U << 9, ~0U << 10 , ~0U << 11, ~0U << 12, ~0U << 13, ~0U << 14 , ~0U << 15, + ~0U << 16, ~0U << 17, ~0U << 18 , ~0U << 19, ~0U << 20, ~0U << 21, ~0U << 22 , ~0U << 23, ~0U << 24, ~0U << 25, ~0U << 26 , ~0U << 27, ~0U << 28, ~0U << 29, ~0U << 30 , ~0U << 31, + 0U }; + + __m128i retMask; + simd_i32(retMask)[0] = (int)maskLUT[simd_i32(shift)[0]]; + simd_i32(retMask)[1] = (int)maskLUT[simd_i32(shift)[1]]; + simd_i32(retMask)[2] = (int)maskLUT[simd_i32(shift)[2]]; + simd_i32(retMask)[3] = (int)maskLUT[simd_i32(shift)[3]]; + return retMask; + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Object construction and allocation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace MaskedOcclusionCullingAVX512 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +namespace MaskedOcclusionCullingAVX2 +{ + extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD) +{ + return Create(RequestedSIMD, aligned_alloc, aligned_free); +} + +MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) +{ + MaskedOcclusionCulling *object = nullptr; + + MaskedOcclusionCulling::Implementation impl = DetectCPUFeatures(alignedAlloc, alignedFree); + + if (RequestedSIMD < impl) + impl = RequestedSIMD; + + // Return best supported version + if (object == nullptr && impl >= AVX512) + object = MaskedOcclusionCullingAVX512::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX512 version + if (object == nullptr && impl >= AVX2) + object = MaskedOcclusionCullingAVX2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX2 version + if (object == nullptr && impl >= SSE41) + object = MaskedOcclusionCullingSSE41::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE4.1 version + if (object == nullptr) + object = MaskedOcclusionCullingSSE2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE2 (slow) version + + return object; +} + +void MaskedOcclusionCulling::Destroy(MaskedOcclusionCulling *moc) +{ + pfnAlignedFree alignedFreeCallback = moc->mAlignedFreeCallback; + moc->~MaskedOcclusionCulling(); + alignedFreeCallback(moc); +} diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h new file mode 100644 index 0000000000..4ace525887 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCulling.h @@ -0,0 +1,592 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +/*! + * \file MaskedOcclusionCulling.h + * \brief Masked Occlusion Culling + * + * General information + * - Input to all API functions are (x,y,w) clip-space coordinates (x positive left, y positive up, w positive away from camera). + * We entirely skip the z component and instead compute it as 1 / w, see next bullet. For TestRect the input is NDC (x/w, y/w). + * - We use a simple z = 1 / w transform, which is a bit faster than OGL/DX depth transforms. Thus, depth is REVERSED and z = 0 at + * the far plane and z = inf at w = 0. We also have to use a GREATER depth function, which explains why all the conservative + * tests will be reversed compared to what you might be used to (for example zMaxTri >= zMinBuffer is a visibility test) + * - We support different layouts for vertex data (basic AoS and SoA), but note that it's beneficial to store the position data + * as tightly in memory as possible to reduce cache misses. Big strides are bad, so it's beneficial to keep position as a separate + * stream (rather than bundled with attributes) or to keep a copy of the position data for the occlusion culling system. + * - The resolution width must be a multiple of 8 and height a multiple of 4. + * - The hierarchical Z buffer is stored OpenGL-style with the y axis pointing up. This includes the scissor box. + * - This code is only tested with Visual Studio 2015, but should hopefully be easy to port to other compilers. + */ + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines used to configure the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef QUICK_MASK +/*! + * Configure the algorithm used for updating and merging hierarchical z buffer entries. If QUICK_MASK + * is defined to 1, use the algorithm from the paper "Masked Software Occlusion Culling", which has good + * balance between performance and low leakage. If QUICK_MASK is defined to 0, use the algorithm from + * "Masked Depth Culling for Graphics Hardware" which has less leakage, but also lower performance. + */ +#define QUICK_MASK 1 + +#endif + +#ifndef USE_D3D +/*! + * Configures the library for use with Direct3D (default) or OpenGL rendering. This changes whether the + * screen space Y axis points downwards (D3D) or upwards (OGL), and is primarily important in combination + * with the PRECISE_COVERAGE define, where this is important to ensure correct rounding and tie-breaker + * behaviour. It also affects the ScissorRect screen space coordinates. + */ +#define USE_D3D 1 + +#endif + +#ifndef PRECISE_COVERAGE +/*! + * Define PRECISE_COVERAGE to 1 to more closely match GPU rasterization rules. The increased precision comes + * at a cost of slightly lower performance. + */ +#define PRECISE_COVERAGE 1 + +#endif + +#ifndef USE_AVX512 +/*! + * Define USE_AVX512 to 1 to enable experimental AVX-512 support. It's currently mostly untested and only + * validated on simple examples using Intel SDE. Older compilers may not support AVX-512 intrinsics. + */ +#define USE_AVX512 0 + +#endif + +#ifndef CLIPPING_PRESERVES_ORDER +/*! + * Define CLIPPING_PRESERVES_ORDER to 1 to prevent clipping from reordering triangle rasterization + * order; This comes at a cost (approx 3-4%) but removes one source of temporal frame-to-frame instability. + */ +#define CLIPPING_PRESERVES_ORDER 1 + +#endif + +#ifndef ENABLE_STATS +/*! + * Define ENABLE_STATS to 1 to gather various statistics during occlusion culling. Can be used for profiling + * and debugging. Note that enabling this function will reduce performance significantly. + */ +#define ENABLE_STATS 0 + +#endif + +#ifndef MOC_RECORDER_ENABLE +/*! + * Define MOC_RECORDER_ENABLE to 1 to enable frame recorder (see FrameRecorder.h/cpp for details) + */ +#define MOC_RECORDER_ENABLE 0 + +#endif + +#if MOC_RECORDER_ENABLE +#ifndef MOC_RECORDER_ENABLE_PLAYBACK +/*! + * Define MOC_RECORDER_ENABLE_PLAYBACK to 1 to enable compilation of the playback code (not needed + for recording) + */ +#define MOC_RECORDER_ENABLE_PLAYBACK 0 +#endif +#endif + + +#if MOC_RECORDER_ENABLE + +#include + +class FrameRecorder; + +#endif // #if MOC_RECORDER_ENABLE + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Masked occlusion culling class +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCulling +{ +public: + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Memory management callback functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef void *(*pfnAlignedAlloc)(size_t alignment, size_t size); + typedef void (*pfnAlignedFree) (void *ptr); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Enums + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + enum Implementation + { + SSE2 = 0, + SSE41 = 1, + AVX2 = 2, + AVX512 = 3 + }; + + enum BackfaceWinding + { + BACKFACE_NONE = 0, + BACKFACE_CW = 1, + BACKFACE_CCW = 2, + }; + + enum CullingResult + { + VISIBLE = 0x0, + OCCLUDED = 0x1, + VIEW_CULLED = 0x3 + }; + + enum ClipPlanes + { + CLIP_PLANE_NONE = 0x00, + CLIP_PLANE_NEAR = 0x01, + CLIP_PLANE_LEFT = 0x02, + CLIP_PLANE_RIGHT = 0x04, + CLIP_PLANE_BOTTOM = 0x08, + CLIP_PLANE_TOP = 0x10, + CLIP_PLANE_SIDES = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP), + CLIP_PLANE_ALL = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP | CLIP_PLANE_NEAR) + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Structs + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * Used to specify custom vertex layout. Memory offsets to y and z coordinates are set through + * mOffsetY and mOffsetW, and vertex stride is given by mStride. It's possible to configure both + * AoS and SoA layouts. Note that large strides may cause more cache misses and decrease + * performance. It is advisable to store position data as compactly in memory as possible. + */ + struct VertexLayout + { + VertexLayout() {} + VertexLayout(int stride, int offsetY, int offsetZW) : + mStride(stride), mOffsetY(offsetY), mOffsetW(offsetZW) {} + + int mStride; //!< byte stride between vertices + int mOffsetY; //!< byte offset from X to Y coordinate + union { + int mOffsetZ; //!< byte offset from X to Z coordinate + int mOffsetW; //!< byte offset from X to W coordinate + }; + }; + + /*! + * Used to control scissoring during rasterization. Note that we only provide coarse scissor support. + * The scissor box x coordinates must be a multiple of 32, and the y coordinates a multiple of 8. + * Scissoring is mainly meant as a means of enabling binning (sort middle) rasterizers in case + * application developers want to use that approach for multithreading. + */ + struct ScissorRect + { + ScissorRect() {} + ScissorRect(int minX, int minY, int maxX, int maxY) : + mMinX(minX), mMinY(minY), mMaxX(maxX), mMaxY(maxY) {} + + int mMinX; //!< Screen space X coordinate for left side of scissor rect, inclusive and must be a multiple of 32 + int mMinY; //!< Screen space Y coordinate for bottom side of scissor rect, inclusive and must be a multiple of 8 + int mMaxX; //!< Screen space X coordinate for right side of scissor rect, non inclusive and must be a multiple of 32 + int mMaxY; //!< Screen space Y coordinate for top side of scissor rect, non inclusive and must be a multiple of 8 + }; + + /*! + * Used to specify storage area for a binlist, containing triangles. This struct is used for binning + * and multithreading. The host application is responsible for allocating memory for the binlists. + */ + struct TriList + { + unsigned int mNumTriangles; //!< Maximum number of triangles that may be stored in mPtr + unsigned int mTriIdx; //!< Index of next triangle to be written, clear before calling BinTriangles to start from the beginning of the list + float *mPtr; //!< Scratchpad buffer allocated by the host application + }; + + /*! + * Statistics that can be gathered during occluder rendering and visibility to aid debugging + * and profiling. Must be enabled by changing the ENABLE_STATS define. + */ + struct OcclusionCullingStatistics + { + struct + { + long long mNumProcessedTriangles; //!< Number of occluder triangles processed in total + long long mNumRasterizedTriangles; //!< Number of occluder triangles passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by the rasterizer + long long mNumTilesUpdated; //!< Number of tiles where the hierarchical z buffer was updated + long long mNumTilesMerged; //!< Number of tiles where the hierarchical z buffer was updated + } mOccluders; + + struct + { + long long mNumProcessedRectangles; //!< Number of rects processed (TestRect()) + long long mNumProcessedTriangles; //!< Number of ocludee triangles processed (TestTriangles()) + long long mNumRasterizedTriangles; //!< Number of ocludee triangle passing view frustum and backface culling + long long mNumTilesTraversed; //!< Number of tiles traversed by triangle & rect rasterizers + } mOccludees; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD = AVX512); + + /*! + * \brief Creates a new object with default state, no z buffer attached/allocated. + * \param alignedAlloc Pointer to a callback function used when allocating memory + * \param alignedFree Pointer to a callback function used when freeing memory + */ + static MaskedOcclusionCulling *Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree); + + /*! + * \brief Destroys an object and frees the z buffer memory. Note that you cannot + * use the delete operator, and should rather use this function to free up memory. + */ + static void Destroy(MaskedOcclusionCulling *moc); + + /*! + * \brief Sets the resolution of the hierarchical depth buffer. This function will + * re-allocate the current depth buffer (if present). The contents of the + * buffer is undefined until ClearBuffer() is called. + * + * \param witdh The width of the buffer in pixels, must be a multiple of 8 + * \param height The height of the buffer in pixels, must be a multiple of 4 + */ + virtual void SetResolution(unsigned int width, unsigned int height) = 0; + + /*! + * \brief Gets the resolution of the hierarchical depth buffer. + * + * \param witdh Output: The width of the buffer in pixels + * \param height Output: The height of the buffer in pixels + */ + virtual void GetResolution(unsigned int &width, unsigned int &height) const = 0; + + /*! + * \brief Returns the tile size for the current implementation. + * + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param outBinWidth Output: The width of the single bin in pixels (except for the + * rightmost bin width, which is extended to resolution width) + * \param outBinHeight Output: The height of the single bin in pixels (except for the + * bottommost bin height, which is extended to resolution height) + */ + virtual void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) = 0; + + /*! + * \brief Sets the distance for the near clipping plane. Default is nearDist = 0. + * + * \param nearDist The distance to the near clipping plane, given as clip space w + */ + virtual void SetNearClipPlane(float nearDist) = 0; + + /*! + * \brief Gets the distance for the near clipping plane. + */ + virtual float GetNearClipPlane() const = 0; + + /*! + * \brief Clears the hierarchical depth buffer. + */ + virtual void ClearBuffer() = 0; + + /*! + * \brief Merge a second hierarchical depth buffer into the main buffer. + */ + virtual void MergeBuffer(MaskedOcclusionCulling* BufferB) = 0; + + /*! + * \brief Renders a mesh of occluder triangles and updates the hierarchical z buffer + * with conservative depth values. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be rasterized. You may use BACKFACE_NONE to disable culling for + * double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return Will return VIEW_CULLED if all triangles are either outside the frustum or + * backface culled, returns VISIBLE otherwise. + */ + virtual CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Occlusion query for a rectangle with a given depth. The rectangle is given + * in normalized device coordinates where (x,y) coordinates between [-1,1] map + * to the visible screen area. The query uses a GREATER_EQUAL (reversed) depth + * test meaning that depth values equal to the contents of the depth buffer are + * counted as visible. + * + * \param xmin NDC coordinate of the left side of the rectangle. + * \param ymin NDC coordinate of the bottom side of the rectangle. + * \param xmax NDC coordinate of the right side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param ymax NDC coordinate of the top side of the rectangle. + * \param wmin Clip space W coordinate for the rectangle. + * \return The query will return VISIBLE if the rectangle may be visible, OCCLUDED + * if the rectangle is occluded by a previously rendered object, or VIEW_CULLED + * if the rectangle is outside the view frustum. + */ + virtual CullingResult TestRect(float xmin, float ymin, float xmax, float ymax, float wmin) const = 0; + + /*! + * \brief This function is similar to RenderTriangles(), but performs an occlusion + * query instead and does not update the hierarchical z buffer. The query uses + * a GREATER_EQUAL (reversed) depth test meaning that depth values equal to the + * contents of the depth buffer are counted as visible. + * + * This function is optimized for vertex layouts with stride 16 and y and w + * offsets of 4 and 12 bytes, respectively. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \return The query will return VISIBLE if the triangle mesh may be visible, OCCLUDED + * if the mesh is occluded by a previously rendered object, or VIEW_CULLED if all + * triangles are entirely outside the view frustum or backface culled. + */ + virtual CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Perform input assembly, clipping , projection, triangle setup, and write + * triangles to the screen space bins they overlap. This function can be used to + * distribute work for threading (See the CullingThreadpool class for an example) + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of vertex indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param triLists Pointer to an array of TriList objects with one TriList object per + * bin. If a triangle overlaps a bin, it will be written to the corresponding + * trilist. Note that this method appends the triangles to the current list, to + * start writing from the beginning of the list, set triList.mTriIdx = 0 + * \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH + * rectangular bins. + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be binned / rasterized. You may use BACKFACE_NONE to disable culling + * for double sided geometry + */ + virtual void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0; + + /*! + * \brief Renders all occluder triangles in a trilist. This function can be used in + * combination with BinTriangles() to create a threded (binning) rasterizer. The + * bins can be processed independently by different threads without risking writing + * to overlapping memory regions. + * + * \param triLists A triangle list, filled using the BinTriangles() function that is to + * be rendered. + * \param scissor A scissor box limiting the rendering region to the bin. The size of each + * bin must be a multiple of 32x8 pixels due to implementation constraints. For a + * render target with (width, height) resolution and (nBinsW, nBinsH) bins, the + * size of a bin is: + * binWidth = (width / nBinsW) - (width / nBinsW) % 32; + * binHeight = (height / nBinsH) - (height / nBinsH) % 8; + * The last row and column of tiles have a different size: + * lastColBinWidth = width - (nBinsW-1)*binWidth; + * lastRowBinHeight = height - (nBinsH-1)*binHeight; + */ + virtual void RenderTrilist(const TriList &triList, const ScissorRect *scissor) = 0; + + /*! + * \brief Creates a per-pixel depth buffer from the hierarchical z buffer representation. + * Intended for visualizing the hierarchical depth buffer for debugging. The + * buffer is written in scanline order, from the top to bottom (D3D) or bottom to + * top (OGL) of the surface. See the USE_D3D define. + * + * \param depthData Pointer to memory where the per-pixel depth data is written. Must + * hold storage for atleast width*height elements as set by setResolution. + */ + virtual void ComputePixelDepthBuffer(float *depthData, bool flipY) = 0; + + /*! + * \brief Fetch occlusion culling statistics, returns zeroes if ENABLE_STATS define is + * not defined. The statistics can be used for profiling or debugging. + */ + virtual OcclusionCullingStatistics GetStatistics() = 0; + + /*! + * \brief Returns the implementation (CPU instruction set) version of this object. + */ + virtual Implementation GetImplementation() = 0; + + /*! + * \brief Utility function for transforming vertices and outputting them to an (x,y,z,w) + * format suitable for the occluder rasterization and occludee testing functions. + * + * \param mtx Pointer to matrix data. The matrix should column major for post + * multiplication (OGL) and row major for pre-multiplication (DX). This is + * consistent with OpenGL / DirectX behavior. + * \param inVtx Pointer to an array of input vertices. The input vertices are given as + * (x,y,z) coordinates. The memory layout can be changed using vtxLayout. + * \param xfVtx Pointer to an array to store transformed vertices. The transformed + * vertices are always stored as array of structs (AoS) (x,y,z,w) packed in memory. + * \param nVtx Number of vertices to transform. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. Note that for this function, the + * w-component is assumed to be 1.0. + */ + static void TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout = VertexLayout(12, 4, 8)); + + /*! + * \brief Get used memory alloc/free callbacks. + */ + void GetAllocFreeCallback( pfnAlignedAlloc & allocCallback, pfnAlignedFree & freeCallback ) { allocCallback = mAlignedAllocCallback, freeCallback = mAlignedFreeCallback; } + +#if MOC_RECORDER_ENABLE + /*! + * \brief Start recording subsequent rasterization and testing calls using the FrameRecorder. + * The function calls that are recorded are: + * - ClearBuffer + * - RenderTriangles + * - TestTriangles + * - TestRect + * All inputs and outputs are recorded, which can be used for correctness validation + * and performance testing. + * + * \param outputFilePath Pointer to name of the output file. + * \return 'true' if recording was started successfully, 'false' otherwise (file access error). + */ + bool RecorderStart( const char * outputFilePath ) const; + + /*! + * \brief Stop recording, flush output and release used memory. + */ + void RecorderStop( ) const; + + /*! + * \brief Manually record triangles. This is called automatically from MaskedOcclusionCulling::RenderTriangles + * if the recording is started, but not from BinTriangles/RenderTrilist (used in multithreaded codepath), in + * which case it has to be called manually. + * + * \param inVtx Pointer to an array of input vertices, should point to the x component + * of the first vertex. The input vertices are given as (x,y,w) coordinates + * in clip space. The memory layout can be changed using vtxLayout. + * \param inTris Pointer to an array of triangle indices. Each triangle is created + * from three indices consecutively fetched from the array. + * \param nTris The number of triangles to render (inTris must contain atleast 3*nTris + * entries) + * \param modelToClipMatrix all vertices will be transformed by this matrix before + * performing projection. If nullptr is passed the transform step will be skipped + * \param bfWinding Sets triangle winding order to consider backfacing, must be one one + * of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled + * and will not be occlusion tested. You may use BACKFACE_NONE to disable culling + * for double sided geometry + * \param clipPlaneMask A mask indicating which clip planes should be considered by the + * triangle clipper. Can be used as an optimization if your application can + * determine (for example during culling) that a group of triangles does not + * intersect a certain frustum plane. However, setting an incorrect mask may + * cause out of bounds memory accesses. + * \param vtxLayout A struct specifying the vertex layout (see struct for detailed + * description). For best performance, it is advisable to store position data + * as compactly in memory as possible. + * \param cullingResult cull result value expected to be returned by executing the + * RenderTriangles call with recorded parameters. + */ + // + // merge the binned data back into original layout; in this case, call it manually from your Threadpool implementation (already added to CullingThreadpool). + // If recording is not enabled, calling this function will do nothing. + void RecordRenderTriangles( const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, BackfaceWinding bfWinding = BACKFACE_CW, const VertexLayout &vtxLayout = VertexLayout( 16, 4, 12 ), CullingResult cullingResult = (CullingResult)-1 ); +#endif // #if MOC_RECORDER_ENABLE + +protected: + pfnAlignedAlloc mAlignedAllocCallback; + pfnAlignedFree mAlignedFreeCallback; + + mutable OcclusionCullingStatistics mStats; + +#if MOC_RECORDER_ENABLE + mutable FrameRecorder * mRecorder; + mutable std::mutex mRecorderMutex; +#endif // #if MOC_RECORDER_ENABLE + + virtual ~MaskedOcclusionCulling() {} +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp new file mode 100644 index 0000000000..b129f12943 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +#if defined(__MICROSOFT_COMPILER) && _MSC_VER < 1900 + // If you remove/comment this error, the code will compile & use the SSE41 version instead. + #error Older versions than visual studio 2015 not supported due to compiler bug(s) +#endif + +#if !defined(__MICROSOFT_COMPILER) || _MSC_VER >= 1900 + +// For performance reasons, the MaskedOcclusionCullingAVX2.cpp file should be compiled with VEX encoding for SSE instructions (to avoid +// AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX2.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 8 +#define TILE_HEIGHT_SHIFT 3 + +#define SIMD_LANE_IDX _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7) + +#define SIMD_SUB_TILE_COL_OFFSET _mm256_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm256_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm256_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm256_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm256_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF, 0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF) + +#define SIMD_LANE_YCOORD_I _mm256_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920) +#define SIMD_LANE_YCOORD_F _mm256_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m256 __mw; +typedef __m256i __mwi; + +#define _mmw_set1_ps _mm256_set1_ps +#define _mmw_setzero_ps _mm256_setzero_ps +#define _mmw_and_ps _mm256_and_ps +#define _mmw_or_ps _mm256_or_ps +#define _mmw_xor_ps _mm256_xor_ps +#define _mmw_not_ps(a) _mm256_xor_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(~0))) +#define _mmw_andnot_ps _mm256_andnot_ps +#define _mmw_neg_ps(a) _mm256_xor_ps((a), _mm256_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm256_and_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm256_add_ps +#define _mmw_sub_ps _mm256_sub_ps +#define _mmw_mul_ps _mm256_mul_ps +#define _mmw_div_ps _mm256_div_ps +#define _mmw_min_ps _mm256_min_ps +#define _mmw_max_ps _mm256_max_ps +#define _mmw_fmadd_ps _mm256_fmadd_ps +#define _mmw_fmsub_ps _mm256_fmsub_ps +#define _mmw_movemask_ps _mm256_movemask_ps +#define _mmw_blendv_ps _mm256_blendv_ps +#define _mmw_cmpge_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GE_OQ) +#define _mmw_cmpgt_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GT_OQ) +#define _mmw_cmpeq_ps(a,b) _mm256_cmp_ps(a, b, _CMP_EQ_OQ) +#define _mmw_floor_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) +#define _mmw_ceil_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) +#define _mmw_shuffle_ps _mm256_shuffle_ps +#define _mmw_insertf32x4_ps _mm256_insertf128_ps +#define _mmw_cvtepi32_ps _mm256_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm256_set1_epi32 +#define _mmw_setzero_epi32 _mm256_setzero_si256 +#define _mmw_and_epi32 _mm256_and_si256 +#define _mmw_or_epi32 _mm256_or_si256 +#define _mmw_xor_epi32 _mm256_xor_si256 +#define _mmw_not_epi32(a) _mm256_xor_si256((a), _mm256_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm256_andnot_si256 +#define _mmw_neg_epi32(a) _mm256_sub_epi32(_mm256_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm256_add_epi32 +#define _mmw_sub_epi32 _mm256_sub_epi32 +#define _mmw_min_epi32 _mm256_min_epi32 +#define _mmw_max_epi32 _mm256_max_epi32 +#define _mmw_subs_epu16 _mm256_subs_epu16 +#define _mmw_mullo_epi32 _mm256_mullo_epi32 +#define _mmw_cmpeq_epi32 _mm256_cmpeq_epi32 +#define _mmw_testz_epi32 _mm256_testz_si256 +#define _mmw_cmpgt_epi32 _mm256_cmpgt_epi32 +#define _mmw_srai_epi32 _mm256_srai_epi32 +#define _mmw_srli_epi32 _mm256_srli_epi32 +#define _mmw_slli_epi32 _mm256_slli_epi32 +#define _mmw_sllv_ones(x) _mm256_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm256_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm256_abs_epi32 +#define _mmw_cvtps_epi32 _mm256_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm256_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m256 *vtxX, __m256 *vtxY, __m256 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m256i SIMD_TRI_IDX_OFFSET = _mm256_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21); + static const __m256i SIMD_LANE_MASK[9] = { + _mm256_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m256i safeTriIdxOffset = _mm256_and_si256(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m256i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 0, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 1, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 2, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm256_i32gather_ps((float *)vPtr, vtxIdx[i], 1); + vtxY[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetY), vtxIdx[i], 1); + vtxW[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetW), vtxIdx[i], 1); + } +} + +namespace MaskedOcclusionCullingAVX2 +{ + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX2; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX2 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp new file mode 100644 index 0000000000..1dccccd83e --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp @@ -0,0 +1,309 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include "MaskedOcclusionCulling.h" +#include "CompilerSpecific.inl" + +#if MOC_RECORDER_ENABLE +#include "FrameRecorder.h" +#endif + +// Make sure compiler supports AVX-512 intrinsics: Visual Studio 2017 (Update 3) || Intel C++ Compiler 16.0 || Clang 4.0 || GCC 5.0 +#if USE_AVX512 != 0 && ((defined(_MSC_VER) && _MSC_VER >= 1911) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1600) || (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && __GNUC__ >= 5)) + +// The MaskedOcclusionCullingAVX512.cpp file should be compiled avx2/avx512 architecture options turned on in the compiler. However, the SSE +// version in MaskedOcclusionCulling.cpp _must_ be compiled with SSE2 architecture allow backwards compatibility. Best practice is to +// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files. +#ifndef __AVX2__ + #error For best performance, MaskedOcclusionCullingAVX512.cpp should be compiled with /arch:AVX2 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_LANES 16 +#define TILE_HEIGHT_SHIFT 4 + +#define SIMD_LANE_IDX _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + +#define SIMD_SUB_TILE_COL_OFFSET _mm512_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET _mm512_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) +#define SIMD_SUB_TILE_COL_OFFSET_F _mm512_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3) +#define SIMD_SUB_TILE_ROW_OFFSET_F _mm512_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3) + +#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm512_set_epi32(0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400) + +#define SIMD_LANE_YCOORD_I _mm512_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920, 2176, 2432, 2688, 2944, 3200, 3456, 3712, 3968) +#define SIMD_LANE_YCOORD_F _mm512_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f, 2176.0f, 2432.0f, 2688.0f, 2944.0f, 3200.0f, 3456.0f, 3712.0f, 3968.0f) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AVX specific typedefs and functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef __m512 __mw; +typedef __m512i __mwi; + +#define _mmw_set1_ps _mm512_set1_ps +#define _mmw_setzero_ps _mm512_setzero_ps +#define _mmw_and_ps _mm512_and_ps +#define _mmw_or_ps _mm512_or_ps +#define _mmw_xor_ps _mm512_xor_ps +#define _mmw_not_ps(a) _mm512_xor_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(~0))) +#define _mmw_andnot_ps _mm512_andnot_ps +#define _mmw_neg_ps(a) _mm512_xor_ps((a), _mm512_set1_ps(-0.0f)) +#define _mmw_abs_ps(a) _mm512_and_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(0x7FFFFFFF))) +#define _mmw_add_ps _mm512_add_ps +#define _mmw_sub_ps _mm512_sub_ps +#define _mmw_mul_ps _mm512_mul_ps +#define _mmw_div_ps _mm512_div_ps +#define _mmw_min_ps _mm512_min_ps +#define _mmw_max_ps _mm512_max_ps +#define _mmw_fmadd_ps _mm512_fmadd_ps +#define _mmw_fmsub_ps _mm512_fmsub_ps +#define _mmw_shuffle_ps _mm512_shuffle_ps +#define _mmw_insertf32x4_ps _mm512_insertf32x4 +#define _mmw_cvtepi32_ps _mm512_cvtepi32_ps +#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c))) + +#define _mmw_set1_epi32 _mm512_set1_epi32 +#define _mmw_setzero_epi32 _mm512_setzero_si512 +#define _mmw_and_epi32 _mm512_and_si512 +#define _mmw_or_epi32 _mm512_or_si512 +#define _mmw_xor_epi32 _mm512_xor_si512 +#define _mmw_not_epi32(a) _mm512_xor_si512((a), _mm512_set1_epi32(~0)) +#define _mmw_andnot_epi32 _mm512_andnot_si512 +#define _mmw_neg_epi32(a) _mm512_sub_epi32(_mm512_set1_epi32(0), (a)) +#define _mmw_add_epi32 _mm512_add_epi32 +#define _mmw_sub_epi32 _mm512_sub_epi32 +#define _mmw_min_epi32 _mm512_min_epi32 +#define _mmw_max_epi32 _mm512_max_epi32 +#define _mmw_subs_epu16 _mm512_subs_epu16 +#define _mmw_mullo_epi32 _mm512_mullo_epi32 +#define _mmw_srai_epi32 _mm512_srai_epi32 +#define _mmw_srli_epi32 _mm512_srli_epi32 +#define _mmw_slli_epi32 _mm512_slli_epi32 +#define _mmw_sllv_ones(x) _mm512_sllv_epi32(SIMD_BITS_ONE, x) +#define _mmw_transpose_epi8(x) _mm512_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES) +#define _mmw_abs_epi32 _mm512_abs_epi32 +#define _mmw_cvtps_epi32 _mm512_cvtps_epi32 +#define _mmw_cvttps_epi32 _mm512_cvttps_epi32 + +#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF) +#define _mmx_fmadd_ps _mm_fmadd_ps +#define _mmx_max_epi32 _mm_max_epi32 +#define _mmx_min_epi32 _mm_min_epi32 + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD casting functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T simd_cast(Y A); +template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); } +template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); } +template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; } +template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); } +template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); } +template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; } +template<> FORCE_INLINE __m512 simd_cast<__m512>(float A) { return _mm512_set1_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512i A) { return _mm512_castsi512_ps(A); } +template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512 A) { return A; } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(int A) { return _mm512_set1_epi32(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512 A) { return _mm512_castps_si512(A); } +template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512i A) { return A; } + +#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \ + FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \ + union accessor { simd_type m_native; base_type m_array[elements]; }; \ + is_const accessor *acs = reinterpret_cast(&a); \ + return acs->m_array; \ + } + +MAKE_ACCESSOR(simd_f32, __m128, float, , 4) +MAKE_ACCESSOR(simd_f32, __m128, float, const, 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, , 4) +MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4) + +MAKE_ACCESSOR(simd_f32, __m256, float, , 8) +MAKE_ACCESSOR(simd_f32, __m256, float, const, 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, , 8) +MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8) + +MAKE_ACCESSOR(simd_f32, __m512, float, , 16) +MAKE_ACCESSOR(simd_f32, __m512, float, const, 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, , 16) +MAKE_ACCESSOR(simd_i32, __m512i, int, const, 16) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Specialized AVX input assembly function for general vertex gather +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef MaskedOcclusionCulling::VertexLayout VertexLayout; + +FORCE_INLINE void GatherVertices(__m512 *vtxX, __m512 *vtxY, __m512 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout) +{ + assert(numLanes >= 1); + + const __m512i SIMD_TRI_IDX_OFFSET = _mm512_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45); + static const __m512i SIMD_LANE_MASK[17] = { + _mm512_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0), + _mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0) + }; + + // Compute per-lane index list offset that guards against out of bounds memory accesses + __m512i safeTriIdxOffset = _mm512_and_si512(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]); + + // Fetch triangle indices. + __m512i vtxIdx[3]; + vtxIdx[0] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 0, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[1] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 1, 4), _mmw_set1_epi32(vtxLayout.mStride)); + vtxIdx[2] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 2, 4), _mmw_set1_epi32(vtxLayout.mStride)); + + char *vPtr = (char *)inVtx; + + // Fetch triangle vertices + for (int i = 0; i < 3; i++) + { + vtxX[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)vPtr, 1); + vtxY[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetY), 1); + vtxW[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetW), 1); + } +} + +namespace MaskedOcclusionCullingAVX512 +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Poorly implemented functions. TODO: fix common (maskedOcclusionCullingCommon.inl) code to improve perf + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE __m512 _mmw_floor_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 1); // 1 = floor + } + + FORCE_INLINE __m512 _mmw_ceil_ps(__m512 x) + { + return _mm512_roundscale_ps(x, 2); // 2 = ceil + } + + FORCE_INLINE __m512i _mmw_cmpeq_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE __m512i _mmw_cmpgt_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpgt_epi32_mask(a, b); + return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)); + } + + FORCE_INLINE bool _mmw_testz_epi32(__m512i a, __m512i b) + { + __mmask16 mask = _mm512_cmpeq_epi32_mask(_mm512_and_si512(a, b), _mm512_set1_epi32(0)); + return mask == 0xFFFF; + } + + FORCE_INLINE __m512 _mmw_cmpge_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GE_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpgt_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GT_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __m512 _mmw_cmpeq_ps(__m512 a, __m512 b) + { + __mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ); + return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0))); + } + + FORCE_INLINE __mmask16 _mmw_movemask_ps(const __m512 &a) + { + __mmask16 mask = _mm512_cmp_epi32_mask(_mm512_and_si512(_mm512_castps_si512(a), _mm512_set1_epi32(0x80000000)), _mm512_set1_epi32(0), 4); // a & 0x8000000 != 0 + return mask; + } + + FORCE_INLINE __m512 _mmw_blendv_ps(const __m512 &a, const __m512 &b, const __m512 &c) + { + __mmask16 mask = _mmw_movemask_ps(c); + return _mm512_mask_mov_ps(a, mask, b); + } + + static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX512; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Include common algorithm implementation (general, SIMD independent code) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #include "MaskedOcclusionCullingCommon.inl" + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Utility function to create a new object using the allocator callbacks + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate)); + new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree); + return object; + } +}; + +#else + +namespace MaskedOcclusionCullingAVX512 +{ + typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc; + typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree; + + MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) + { + return nullptr; + } +}; + +#endif diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl new file mode 100644 index 0000000000..331ca69964 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl @@ -0,0 +1,2053 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +//////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common SIMD math utility functions +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE T max(const T &a, const T &b) { return a > b ? a : b; } +template FORCE_INLINE T min(const T &a, const T &b) { return a < b ? a : b; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common defines and constants +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_ALL_LANES_MASK ((1 << SIMD_LANES) - 1) + +// Tile dimensions are 32xN pixels. These values are not tweakable and the code must also be modified +// to support different tile sizes as it is tightly coupled with the SSE/AVX register size +#define TILE_WIDTH_SHIFT 5 +#define TILE_WIDTH (1 << TILE_WIDTH_SHIFT) +#define TILE_HEIGHT (1 << TILE_HEIGHT_SHIFT) + +// Sub-tiles (used for updating the masked HiZ buffer) are 8x4 tiles, so there are 4x2 sub-tiles in a tile +#define SUB_TILE_WIDTH 8 +#define SUB_TILE_HEIGHT 4 + +// The number of fixed point bits used to represent vertex coordinates / edge slopes. +#if PRECISE_COVERAGE != 0 + #define FP_BITS 8 + #define FP_HALF_PIXEL (1 << (FP_BITS - 1)) + #define FP_INV (1.0f / (float)(1 << FP_BITS)) +#else + // Note that too low precision, without precise coverage, may cause overshoots / false coverage during rasterization. + // This is configured for 14 bits for AVX512 and 16 bits for SSE. Max tile slope delta is roughly + // (screenWidth + 2*(GUARD_BAND_PIXEL_SIZE + 1)) * (2^FP_BITS * (TILE_HEIGHT + GUARD_BAND_PIXEL_SIZE + 1)) + // and must fit in 31 bits. With this config, max image resolution (width) is ~3272, so stay well clear of this limit. + #define FP_BITS (19 - TILE_HEIGHT_SHIFT) +#endif + +// Tile dimensions in fixed point coordinates +#define FP_TILE_HEIGHT_SHIFT (FP_BITS + TILE_HEIGHT_SHIFT) +#define FP_TILE_HEIGHT (1 << FP_TILE_HEIGHT_SHIFT) + +// Maximum number of triangles that may be generated during clipping. We process SIMD_LANES triangles at a time and +// clip against 5 planes, so the max should be 5*8 = 40 (we immediately draw the first clipped triangle). +// This number must be a power of two. +#define MAX_CLIPPED (8*SIMD_LANES) +#define MAX_CLIPPED_WRAP (MAX_CLIPPED - 1) + +// Size of guard band in pixels. Clipping doesn't seem to be very expensive so we use a small guard band +// to improve rasterization performance. It's not recommended to set the guard band to zero, as this may +// cause leakage along the screen border due to precision/rounding. +#define GUARD_BAND_PIXEL_SIZE 1.0f + +// We classify triangles as big if the bounding box is wider than this given threshold and use a tighter +// but slightly more expensive traversal algorithm. This improves performance greatly for sliver triangles +#define BIG_TRIANGLE 3 + +// Only gather statistics if enabled. +#if ENABLE_STATS != 0 + #define STATS_ADD(var, val) _InterlockedExchangeAdd64( &var, val ) +#else + #define STATS_ADD(var, val) +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SIMD common defines (constant values) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SIMD_BITS_ONE _mmw_set1_epi32(~0) +#define SIMD_BITS_ZERO _mmw_setzero_epi32() +#define SIMD_TILE_WIDTH _mmw_set1_epi32(TILE_WIDTH) + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vertex fetch utility function, need to be in global namespace due to template specialization +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template FORCE_INLINE void VtxFetch4(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Fetch 4 vectors (matching 1 sse part of the SIMD register), and continue to the next + const int ssePart = (SIMD_LANES / 4) - N; + for (int k = 0; k < 4; k++) + { + int lane = 4 * ssePart + k; + if (numLanes > lane) + v[k] = _mmw_insertf32x4_ps(v[k], _mm_loadu_ps(&inVtx[inTrisPtr[lane * 3 + triVtx] << 2]), ssePart); + } + VtxFetch4(v, inTrisPtr, triVtx, inVtx, numLanes); +} + +template<> FORCE_INLINE void VtxFetch4<0>(__mw *v, const unsigned int *inTrisPtr, int triVtx, const float *inVtx, int numLanes) +{ + // Workaround for unused parameter warning + (void)v; (void)inTrisPtr; (void)triVtx; (void)inVtx; (void)numLanes; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Private class containing the implementation +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MaskedOcclusionCullingPrivate : public MaskedOcclusionCulling +{ +public: + struct ZTile + { + __mw mZMin[2]; + __mwi mMask; + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Member variables + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + __mw mHalfWidth; + __mw mHalfHeight; + __mw mCenterX; + __mw mCenterY; + __m128 mCSFrustumPlanes[5]; + __m128 mIHalfSize; + __m128 mICenter; + __m128i mIScreenSize; + + float mNearDist; + int mWidth; + int mHeight; + int mTilesWidth; + int mTilesHeight; + + ZTile *mMaskedHiZBuffer; + ScissorRect mFullscreenScissor; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors and state handling + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCullingPrivate(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree) : mFullscreenScissor(0, 0, 0, 0) + { + mMaskedHiZBuffer = nullptr; + mAlignedAllocCallback = alignedAlloc; + mAlignedFreeCallback = alignedFree; +#if MOC_RECORDER_ENABLE + mRecorder = nullptr; +#endif + + SetNearClipPlane(0.0f); + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f, 1.0f, 0.0f); + + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); + + SetResolution(0, 0); + } + + ~MaskedOcclusionCullingPrivate() override + { + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + +#if MOC_RECORDER_ENABLE + assert( mRecorder == nullptr ); // forgot to call StopRecording()? +#endif + } + + void SetResolution(unsigned int width, unsigned int height) override + { + // Resolution must be a multiple of the subtile size + assert(width % SUB_TILE_WIDTH == 0 && height % SUB_TILE_HEIGHT == 0); +#if PRECISE_COVERAGE == 0 + // Test if combination of resolution & SLOPE_FP_BITS bits may cause 32-bit overflow. Note that the maximum resolution estimate + // is only an estimate (not conservative). It's advicable to stay well below the limit. + assert(width < ((1U << 31) - 1U) / ((1U << FP_BITS) * (TILE_HEIGHT + (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))) - (2U * (unsigned int)(GUARD_BAND_PIXEL_SIZE + 1.0f))); +#endif + + // Delete current masked hierarchical Z buffer + if (mMaskedHiZBuffer != nullptr) + mAlignedFreeCallback(mMaskedHiZBuffer); + mMaskedHiZBuffer = nullptr; + + // Setup various resolution dependent constant values + mWidth = (int)width; + mHeight = (int)height; + mTilesWidth = (int)(width + TILE_WIDTH - 1) >> TILE_WIDTH_SHIFT; + mTilesHeight = (int)(height + TILE_HEIGHT - 1) >> TILE_HEIGHT_SHIFT; + mCenterX = _mmw_set1_ps((float)mWidth * 0.5f); + mCenterY = _mmw_set1_ps((float)mHeight * 0.5f); + mICenter = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); + mHalfWidth = _mmw_set1_ps((float)mWidth * 0.5f); +#if USE_D3D != 0 + mHalfHeight = _mmw_set1_ps((float)-mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)-mHeight * 0.5f, (float)-mHeight * 0.5f); +#else + mHalfHeight = _mmw_set1_ps((float)mHeight * 0.5f); + mIHalfSize = _mm_setr_ps((float)mWidth * 0.5f, (float)mWidth * 0.5f, (float)mHeight * 0.5f, (float)mHeight * 0.5f); +#endif + mIScreenSize = _mm_setr_epi32(mWidth - 1, mWidth - 1, mHeight - 1, mHeight - 1); + + // Setup a full screen scissor rectangle + mFullscreenScissor.mMinX = 0; + mFullscreenScissor.mMinY = 0; + mFullscreenScissor.mMaxX = mTilesWidth << TILE_WIDTH_SHIFT; + mFullscreenScissor.mMaxY = mTilesHeight << TILE_HEIGHT_SHIFT; + + // Adjust clip planes to include a small guard band to avoid clipping leaks + if (mWidth > 0.0f && mHeight > 0.0f) + { + float guardBandWidth = (2.0f / (float)mWidth) * GUARD_BAND_PIXEL_SIZE; + float guardBandHeight = (2.0f / (float)mHeight) * GUARD_BAND_PIXEL_SIZE; + mCSFrustumPlanes[1] = _mm_setr_ps(1.0f - guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[2] = _mm_setr_ps(-1.0f + guardBandWidth, 0.0f, 1.0f, 0.0f); + mCSFrustumPlanes[3] = _mm_setr_ps(0.0f, 1.0f - guardBandHeight, 1.0f, 0.0f); + mCSFrustumPlanes[4] = _mm_setr_ps(0.0f, -1.0f + guardBandHeight, 1.0f, 0.0f); + } + + // Allocate masked hierarchical Z buffer (if zero size leave at nullptr) + if(mTilesWidth * mTilesHeight > 0) + mMaskedHiZBuffer = (ZTile *)mAlignedAllocCallback(64, sizeof(ZTile) * mTilesWidth * mTilesHeight); + } + + void GetResolution(unsigned int &width, unsigned int &height) const override + { + width = mWidth; + height = mHeight; + } + + void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) override + { + outBinWidth = (mWidth / nBinsW) - ((mWidth / nBinsW) % TILE_WIDTH); + outBinHeight = (mHeight / nBinsH) - ((mHeight / nBinsH) % TILE_HEIGHT); + } + + void SetNearClipPlane(float nearDist) override + { + // Setup the near frustum plane + mNearDist = nearDist; + mCSFrustumPlanes[0] = _mm_setr_ps(0.0f, 0.0f, 1.0f, -nearDist); + } + + float GetNearClipPlane() const override + { + return mNearDist; + } + + void ClearBuffer() override + { + assert(mMaskedHiZBuffer != nullptr); + + // Iterate through all depth tiles and clear to default values + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + mMaskedHiZBuffer[i].mMask = _mmw_setzero_epi32(); + + // Clear z0 to beyond infinity to ensure we never merge with clear data + mMaskedHiZBuffer[i].mZMin[0] = _mmw_set1_ps(-1.0f); +#if QUICK_MASK != 0 + // Clear z1 to nearest depth value as it is pushed back on each update + mMaskedHiZBuffer[i].mZMin[1] = _mmw_set1_ps(FLT_MAX); +#else + mMaskedHiZBuffer[i].mZMin[1] = _mmw_setzero_ps(); +#endif + } + +#if ENABLE_STATS != 0 + memset(&mStats, 0, sizeof(OcclusionCullingStatistics)); +#endif + +#if MOC_RECORDER_ENABLE != 0 + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordClearBuffer(); + } +#endif + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MergeBuffer + // Utility Function merges another MOC buffer into the existing one + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void MergeBuffer(MaskedOcclusionCulling* BufferB) override + { + assert(mMaskedHiZBuffer != nullptr); + + //// Iterate through all depth tiles and merge the 2 tiles + for (int i = 0; i < mTilesWidth * mTilesHeight; i++) + { + __mw *zMinB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mZMin; + __mw *zMinA = mMaskedHiZBuffer[i].mZMin; + __mwi RastMaskB = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + +#if QUICK_MASK != 0 + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + if (!_mmw_testz_epi32(sign0, sign0)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + zMinA[0] = _mmw_max_ps(zMinA[0], zMinB[0]); + + __mwi rastMask = mMaskedHiZBuffer[i].mMask; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zMinA[1], zMinA[0])), 31)); + mMaskedHiZBuffer[i].mMask = _mmw_andnot_epi32(deadLane, rastMask); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(RastMaskB, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + UpdateTileQuick(i, RastMaskB, zMinB[1]); + } +#else + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(mMaskedHiZBuffer[i].mZMin[0]), 31); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + sign1 = _mmw_cmpeq_epi32(sign1, SIMD_BITS_ZERO); + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile1 = _mmw_cmpeq_epi32(mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t1inv = _mmw_not_epi32(LiveTile1); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (_mmw_testz_epi32(sign1, sign1) && _mmw_testz_epi32(t1inv, t1inv)) + { + mMaskedHiZBuffer[i].mMask = ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask; + mMaskedHiZBuffer[i].mZMin[0] = zMinB[0]; + mMaskedHiZBuffer[i].mZMin[1] = zMinB[1]; + } + else + { + // Clear z0 to beyond infinity to ensure we never merge with clear data + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[0]), 31); + sign0 = _mmw_cmpeq_epi32(sign0, SIMD_BITS_ZERO); + // Only merge tiles that have data in zMinB[0], use the sign bit to determine if they are still in a clear state + if (!_mmw_testz_epi32(sign0, sign0)) + { + // build a mask for Zmin[0], full if the layer has been completed, or partial if tile is still partly filled. + // cant just use the completement of the mask, as tiles might not get updated by merge + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(zMinB[1]), 31); + __mwi LayerMask0 = _mmw_not_epi32(sign1); + __mwi LayerMask1 = _mmw_not_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask); + __mwi rastMask = _mmw_or_epi32(LayerMask0, LayerMask1); + + UpdateTileAccurate(i, rastMask, zMinB[0]); + } + + // Set 32bit value to -1 if any pixels are set incide the coverage mask for a subtile + __mwi LiveTile = _mmw_cmpeq_epi32(((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, SIMD_BITS_ZERO); + // invert to have bits set for clear subtiles + __mwi t0inv = _mmw_not_epi32(LiveTile); + // VPTEST sets the ZF flag if all the resulting bits are 0 (ie if all tiles are clear) + if (!_mmw_testz_epi32(t0inv, t0inv)) + { + UpdateTileAccurate(i, ((MaskedOcclusionCullingPrivate*)BufferB)->mMaskedHiZBuffer[i].mMask, zMinB[1]); + } + + //if (_mmw_testz_epi32(sign0, sign0) && _mmw_testz_epi32(t0inv, t0inv)) + // STATS_ADD(mStats.mOccluders.mNumTilesMerged, 1); + + } + +#endif + } + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Polygon clipping functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE int ClipPolygon(__m128 *outVtx, __m128 *inVtx, const __m128 &plane, int n) const + { + __m128 p0 = inVtx[n - 1]; + __m128 dist0 = _mmx_dp4_ps(p0, plane); + + // Loop over all polygon edges and compute intersection with clip plane (if any) + int nout = 0; + for (int k = 0; k < n; k++) + { + __m128 p1 = inVtx[k]; + __m128 dist1 = _mmx_dp4_ps(p1, plane); + int dist0Neg = _mm_movemask_ps(dist0); + if (!dist0Neg) // dist0 > 0.0f + outVtx[nout++] = p0; + + // Edge intersects the clip plane if dist0 and dist1 have opposing signs + if (_mm_movemask_ps(_mm_xor_ps(dist0, dist1))) + { + // Always clip from the positive side to avoid T-junctions + if (!dist0Neg) + { + __m128 t = _mm_div_ps(dist0, _mm_sub_ps(dist0, dist1)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p1, p0), t, p0); + } + else + { + __m128 t = _mm_div_ps(dist1, _mm_sub_ps(dist1, dist0)); + outVtx[nout++] = _mmx_fmadd_ps(_mm_sub_ps(p0, p1), t, p1); + } + } + + dist0 = dist1; + p0 = p1; + } + return nout; + } + + template void TestClipPlane(__mw *vtxX, __mw *vtxY, __mw *vtxW, unsigned int &straddleMask, unsigned int &triMask, ClipPlanes clipPlaneMask) + { + straddleMask = 0; + // Skip masked clip planes + if (!(clipPlaneMask & CLIP_PLANE)) + return; + + // Evaluate all 3 vertices against the frustum plane + __mw planeDp[3]; + for (int i = 0; i < 3; ++i) + { + switch (CLIP_PLANE) + { + case ClipPlanes::CLIP_PLANE_LEFT: planeDp[i] = _mmw_add_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_RIGHT: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxX[i]); break; + case ClipPlanes::CLIP_PLANE_BOTTOM: planeDp[i] = _mmw_add_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_TOP: planeDp[i] = _mmw_sub_ps(vtxW[i], vtxY[i]); break; + case ClipPlanes::CLIP_PLANE_NEAR: planeDp[i] = _mmw_sub_ps(vtxW[i], _mmw_set1_ps(mNearDist)); break; + } + } + + // Look at FP sign and determine if tri is inside, outside or straddles the frustum plane + __mw inside = _mmw_andnot_ps(planeDp[0], _mmw_andnot_ps(planeDp[1], _mmw_not_ps(planeDp[2]))); + __mw outside = _mmw_and_ps(planeDp[0], _mmw_and_ps(planeDp[1], planeDp[2])); + unsigned int inMask = (unsigned int)_mmw_movemask_ps(inside); + unsigned int outMask = (unsigned int)_mmw_movemask_ps(outside); + straddleMask = (~outMask) & (~inMask); + triMask &= ~outMask; + } + + FORCE_INLINE void ClipTriangleAndAddToBuffer(__mw *vtxX, __mw *vtxY, __mw *vtxW, __m128 *clippedTrisBuffer, int &clipWriteIdx, unsigned int &triMask, unsigned int triClipMask, ClipPlanes clipPlaneMask) + { + if (!triClipMask) + return; + + // Inside test all 3 triangle vertices against all active frustum planes + unsigned int straddleMask[5]; + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[0], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[1], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[2], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[3], triMask, clipPlaneMask); + TestClipPlane(vtxX, vtxY, vtxW, straddleMask[4], triMask, clipPlaneMask); + + // Clip triangle against straddling planes and add to the clipped triangle buffer + __m128 vtxBuf[2][8]; + +#if CLIPPING_PRESERVES_ORDER != 0 + unsigned int clipMask = triClipMask & triMask; + unsigned int clipAndStraddleMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & clipMask; + // no clipping needed after all - early out + if (clipAndStraddleMask == 0) + return; + while( clipMask ) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) // <- second part maybe not needed? + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write all triangles into the clip buffer and process them next loop iteration + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][1]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][2]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + } + // since all triangles were copied to clip buffer for next iteration, skip further processing + triMask = 0; +#else + unsigned int clipMask = (straddleMask[0] | straddleMask[1] | straddleMask[2] | straddleMask[3] | straddleMask[4]) & (triClipMask & triMask); + while (clipMask) + { + // Find and setup next triangle to clip + unsigned int triIdx = find_clear_lsb(&clipMask); + unsigned int triBit = (1U << triIdx); + assert(triIdx < SIMD_LANES); + + int bufIdx = 0; + int nClippedVerts = 3; + for (int i = 0; i < 3; i++) + vtxBuf[0][i] = _mm_setr_ps(simd_f32(vtxX[i])[triIdx], simd_f32(vtxY[i])[triIdx], simd_f32(vtxW[i])[triIdx], 1.0f); + + // Clip triangle with straddling planes. + for (int i = 0; i < 5; ++i) + { + if ((straddleMask[i] & triBit) && (clipPlaneMask & (1 << i))) + { + nClippedVerts = ClipPolygon(vtxBuf[bufIdx ^ 1], vtxBuf[bufIdx], mCSFrustumPlanes[i], nClippedVerts); + bufIdx ^= 1; + } + } + + if (nClippedVerts >= 3) + { + // Write the first triangle back into the list of currently processed triangles + for (int i = 0; i < 3; i++) + { + simd_f32(vtxX[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[0]; + simd_f32(vtxY[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[1]; + simd_f32(vtxW[i])[triIdx] = simd_f32(vtxBuf[bufIdx][i])[2]; + } + // Write the remaining triangles into the clip buffer and process them next loop iteration + for (int i = 2; i < nClippedVerts - 1; i++) + { + clippedTrisBuffer[clipWriteIdx * 3 + 0] = vtxBuf[bufIdx][0]; + clippedTrisBuffer[clipWriteIdx * 3 + 1] = vtxBuf[bufIdx][i]; + clippedTrisBuffer[clipWriteIdx * 3 + 2] = vtxBuf[bufIdx][i + 1]; + clipWriteIdx = (clipWriteIdx + 1) & (MAX_CLIPPED - 1); + } + } + else // Kill triangles that was removed by clipping + triMask &= ~triBit; + } +#endif + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Vertex transform & projection + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void TransformVerts(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *modelToClipMatrix) + { + if (modelToClipMatrix != nullptr) + { + for (int i = 0; i < 3; ++i) + { + __mw tmpX, tmpY, tmpW; + tmpX = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[0]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[4]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[8]), _mmw_set1_ps(modelToClipMatrix[12])))); + tmpY = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[1]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[5]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[9]), _mmw_set1_ps(modelToClipMatrix[13])))); + tmpW = _mmw_fmadd_ps(vtxX[i], _mmw_set1_ps(modelToClipMatrix[3]), _mmw_fmadd_ps(vtxY[i], _mmw_set1_ps(modelToClipMatrix[7]), _mmw_fmadd_ps(vtxW[i], _mmw_set1_ps(modelToClipMatrix[11]), _mmw_set1_ps(modelToClipMatrix[15])))); + vtxX[i] = tmpX; vtxY[i] = tmpY; vtxW[i] = tmpW; + } + } + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void ProjectVertices(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + + // Project vertices and transform to screen space. Snap to sub-pixel coordinates with FP_BITS precision. + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + __mw screenX = _mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX); + __mw screenY = _mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY); + ipVtxX[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenX, _mmw_set1_ps(float(1 << FP_BITS)))); + ipVtxY[idx] = _mmw_cvtps_epi32(_mmw_mul_ps(screenY, _mmw_set1_ps(float(1 << FP_BITS)))); + pVtxX[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[idx]), _mmw_set1_ps(FP_INV)); + pVtxY[idx] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[idx]), _mmw_set1_ps(FP_INV)); + pVtxZ[idx] = rcpW; + } + } +#else + FORCE_INLINE void ProjectVertices(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw *vtxX, const __mw *vtxY, const __mw *vtxW) + { +#if USE_D3D != 0 + static const int vertexOrder[] = {2, 1, 0}; +#else + static const int vertexOrder[] = {0, 1, 2}; +#endif + // Project vertices and transform to screen space. Round to nearest integer pixel coordinate + for (int i = 0; i < 3; i++) + { + int idx = vertexOrder[i]; + __mw rcpW = _mmw_div_ps(_mmw_set1_ps(1.0f), vtxW[i]); + + // The rounding modes are set to match HW rasterization with OpenGL. In practice our samples are placed + // in the (1,0) corner of each pixel, while HW rasterizer uses (0.5, 0.5). We get (1,0) because of the + // floor used when interpolating along triangle edges. The rounding modes match an offset of (0.5, -0.5) + pVtxX[idx] = _mmw_ceil_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxX[i], mHalfWidth), rcpW, mCenterX)); + pVtxY[idx] = _mmw_floor_ps(_mmw_fmadd_ps(_mmw_mul_ps(vtxY[i], mHalfHeight), rcpW, mCenterY)); + pVtxZ[idx] = rcpW; + } + } +#endif + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Common SSE/AVX input assembly functions, note that there are specialized gathers for the general case in the SSE/AVX specific files + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void GatherVerticesFast(__mw *vtxX, __mw *vtxY, __mw *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes) + { + // This function assumes that the vertex layout is four packed x, y, z, w-values. + // Since the layout is known we can get some additional performance by using a + // more optimized gather strategy. + assert(numLanes >= 1); + + // Gather vertices + __mw v[4], swz[4]; + for (int i = 0; i < 3; i++) + { + // Load 4 (x,y,z,w) vectors per SSE part of the SIMD register (so 4 vectors for SSE, 8 vectors for AVX) + // this fetch uses templates to unroll the loop + VtxFetch4(v, inTrisPtr, i, inVtx, numLanes); + + // Transpose each individual SSE part of the SSE/AVX register (similar to _MM_TRANSPOSE4_PS) + swz[0] = _mmw_shuffle_ps(v[0], v[1], 0x44); + swz[2] = _mmw_shuffle_ps(v[0], v[1], 0xEE); + swz[1] = _mmw_shuffle_ps(v[2], v[3], 0x44); + swz[3] = _mmw_shuffle_ps(v[2], v[3], 0xEE); + + vtxX[i] = _mmw_shuffle_ps(swz[0], swz[1], 0x88); + vtxY[i] = _mmw_shuffle_ps(swz[0], swz[1], 0xDD); + vtxW[i] = _mmw_shuffle_ps(swz[2], swz[3], 0xDD); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Rasterization functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + FORCE_INLINE void ComputeBoundingBox(__mwi &bbminX, __mwi &bbminY, __mwi &bbmaxX, __mwi &bbmaxY, const __mw *vX, const __mw *vY, const ScissorRect *scissor) + { + static const __mwi SIMD_PAD_W_MASK = _mmw_set1_epi32(~(TILE_WIDTH - 1)); + static const __mwi SIMD_PAD_H_MASK = _mmw_set1_epi32(~(TILE_HEIGHT - 1)); + + // Find Min/Max vertices + bbminX = _mmw_cvttps_epi32(_mmw_min_ps(vX[0], _mmw_min_ps(vX[1], vX[2]))); + bbminY = _mmw_cvttps_epi32(_mmw_min_ps(vY[0], _mmw_min_ps(vY[1], vY[2]))); + bbmaxX = _mmw_cvttps_epi32(_mmw_max_ps(vX[0], _mmw_max_ps(vX[1], vX[2]))); + bbmaxY = _mmw_cvttps_epi32(_mmw_max_ps(vY[0], _mmw_max_ps(vY[1], vY[2]))); + + // Clamp to tile boundaries + bbminX = _mmw_and_epi32(bbminX, SIMD_PAD_W_MASK); + bbmaxX = _mmw_and_epi32(_mmw_add_epi32(bbmaxX, _mmw_set1_epi32(TILE_WIDTH)), SIMD_PAD_W_MASK); + bbminY = _mmw_and_epi32(bbminY, SIMD_PAD_H_MASK); + bbmaxY = _mmw_and_epi32(_mmw_add_epi32(bbmaxY, _mmw_set1_epi32(TILE_HEIGHT)), SIMD_PAD_H_MASK); + + // Clip to scissor + bbminX = _mmw_max_epi32(bbminX, _mmw_set1_epi32(scissor->mMinX)); + bbmaxX = _mmw_min_epi32(bbmaxX, _mmw_set1_epi32(scissor->mMaxX)); + bbminY = _mmw_max_epi32(bbminY, _mmw_set1_epi32(scissor->mMinY)); + bbmaxY = _mmw_min_epi32(bbmaxY, _mmw_set1_epi32(scissor->mMaxY)); + } + +#if PRECISE_COVERAGE != 0 + FORCE_INLINE void SortVertices(__mwi *vX, __mwi *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mwi ey1 = _mmw_sub_epi32(vY[1], vY[0]); + __mwi ey2 = _mmw_sub_epi32(vY[2], vY[0]); + __mwi swapMask = _mmw_or_epi32(_mmw_or_epi32(ey1, ey2), _mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO)); + __mwi sX, sY; + sX = _mmw_blendv_epi32(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_epi32(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_epi32(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_epi32(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_epi32(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_epi32(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mwi *ipVtxX, __mwi *ipVtxY, __mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + __mwi itmpX, itmpY; + itmpX = _mmw_blendv_epi32(ipVtxX[2], ipVtxX[0], simd_cast<__mwi>(ccwMask)); + itmpY = _mmw_blendv_epi32(ipVtxY[2], ipVtxY[0], simd_cast<__mwi>(ccwMask)); + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + ipVtxX[2] = _mmw_blendv_epi32(ipVtxX[0], ipVtxX[2], simd_cast<__mwi>(ccwMask)); + ipVtxY[2] = _mmw_blendv_epi32(ipVtxY[0], ipVtxY[2], simd_cast<__mwi>(ccwMask)); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + ipVtxX[0] = itmpX; + ipVtxY[0] = itmpY; + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#else + FORCE_INLINE void SortVertices(__mw *vX, __mw *vY) + { + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + for (int i = 0; i < 2; i++) + { + __mw ey1 = _mmw_sub_ps(vY[1], vY[0]); + __mw ey2 = _mmw_sub_ps(vY[2], vY[0]); + __mw swapMask = _mmw_or_ps(_mmw_or_ps(ey1, ey2), simd_cast<__mw>(_mmw_cmpeq_epi32(simd_cast<__mwi>(ey2), SIMD_BITS_ZERO))); + __mw sX, sY; + sX = _mmw_blendv_ps(vX[2], vX[0], swapMask); + vX[0] = _mmw_blendv_ps(vX[0], vX[1], swapMask); + vX[1] = _mmw_blendv_ps(vX[1], vX[2], swapMask); + vX[2] = sX; + sY = _mmw_blendv_ps(vY[2], vY[0], swapMask); + vY[0] = _mmw_blendv_ps(vY[0], vY[1], swapMask); + vY[1] = _mmw_blendv_ps(vY[1], vY[2], swapMask); + vY[2] = sY; + } + } + + FORCE_INLINE int CullBackfaces(__mw *pVtxX, __mw *pVtxY, __mw *pVtxZ, const __mw &ccwMask, BackfaceWinding bfWinding) + { + // Reverse vertex order if non cw faces are considered front facing (rasterizer code requires CCW order) + if (!(bfWinding & BACKFACE_CW)) + { + __mw tmpX, tmpY, tmpZ; + tmpX = _mmw_blendv_ps(pVtxX[2], pVtxX[0], ccwMask); + tmpY = _mmw_blendv_ps(pVtxY[2], pVtxY[0], ccwMask); + tmpZ = _mmw_blendv_ps(pVtxZ[2], pVtxZ[0], ccwMask); + pVtxX[2] = _mmw_blendv_ps(pVtxX[0], pVtxX[2], ccwMask); + pVtxY[2] = _mmw_blendv_ps(pVtxY[0], pVtxY[2], ccwMask); + pVtxZ[2] = _mmw_blendv_ps(pVtxZ[0], pVtxZ[2], ccwMask); + pVtxX[0] = tmpX; + pVtxY[0] = tmpY; + pVtxZ[0] = tmpZ; + } + + // Return a lane mask with all front faces set + return ((bfWinding & BACKFACE_CCW) ? 0 : _mmw_movemask_ps(ccwMask)) | ((bfWinding & BACKFACE_CW) ? 0 : ~_mmw_movemask_ps(ccwMask)); + } +#endif + + FORCE_INLINE void ComputeDepthPlane(const __mw *pVtxX, const __mw *pVtxY, const __mw *pVtxZ, __mw &zPixelDx, __mw &zPixelDy) const + { + // Setup z(x,y) = z0 + dx*x + dy*y screen space depth plane equation + __mw x2 = _mmw_sub_ps(pVtxX[2], pVtxX[0]); + __mw x1 = _mmw_sub_ps(pVtxX[1], pVtxX[0]); + __mw y1 = _mmw_sub_ps(pVtxY[1], pVtxY[0]); + __mw y2 = _mmw_sub_ps(pVtxY[2], pVtxY[0]); + __mw z1 = _mmw_sub_ps(pVtxZ[1], pVtxZ[0]); + __mw z2 = _mmw_sub_ps(pVtxZ[2], pVtxZ[0]); + __mw d = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_fmsub_ps(x1, y2, _mmw_mul_ps(y1, x2))); + zPixelDx = _mmw_mul_ps(_mmw_fmsub_ps(z1, y2, _mmw_mul_ps(y1, z2)), d); + zPixelDy = _mmw_mul_ps(_mmw_fmsub_ps(x1, z2, _mmw_mul_ps(z1, x2)), d); + } + + FORCE_INLINE void UpdateTileQuick(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + // Update heuristic used in the paper "Masked Software Occlusion Culling", + // good balance between performance and accuracy + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + + // Swizzle coverage mask to 8x4 subtiles and test if any subtiles are not covered at all + __mwi rastMask = coverage; + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + + // Mask out all subtiles failing the depth test (don't update these subtiles) + deadLane = _mmw_or_epi32(deadLane, _mmw_srai_epi32(simd_cast<__mwi>(_mmw_sub_ps(zTriv, zMin[0])), 31)); + rastMask = _mmw_andnot_epi32(deadLane, rastMask); + + // Use distance heuristic to discard layer 1 if incoming triangle is significantly nearer to observer + // than the buffer contents. See Section 3.2 in "Masked Software Occlusion Culling" + __mwi coveredLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ONE); + __mw diff = _mmw_fmsub_ps(zMin[1], _mmw_set1_ps(2.0f), _mmw_add_ps(zTriv, zMin[0])); + __mwi discardLayerMask = _mmw_andnot_epi32(deadLane, _mmw_or_epi32(_mmw_srai_epi32(simd_cast<__mwi>(diff), 31), coveredLane)); + + // Update the mask with incoming triangle coverage + mask = _mmw_or_epi32(_mmw_andnot_epi32(discardLayerMask, mask), rastMask); + + __mwi maskFull = _mmw_cmpeq_epi32(mask, SIMD_BITS_ONE); + + // Compute new value for zMin[1]. This has one of four outcomes: zMin[1] = min(zMin[1], zTriv), zMin[1] = zTriv, + // zMin[1] = FLT_MAX or unchanged, depending on if the layer is updated, discarded, fully covered, or not updated + __mw opA = _mmw_blendv_ps(zTriv, zMin[1], simd_cast<__mw>(deadLane)); + __mw opB = _mmw_blendv_ps(zMin[1], zTriv, simd_cast<__mw>(discardLayerMask)); + __mw z1min = _mmw_min_ps(opA, opB); + zMin[1] = _mmw_blendv_ps(z1min, _mmw_set1_ps(FLT_MAX), simd_cast<__mw>(maskFull)); + + // Propagate zMin[1] back to zMin[0] if tile was fully covered, and update the mask + zMin[0] = _mmw_blendv_ps(zMin[0], z1min, simd_cast<__mw>(maskFull)); + mMaskedHiZBuffer[tileIdx].mMask = _mmw_andnot_epi32(maskFull, mask); + } + + FORCE_INLINE void UpdateTileAccurate(int tileIdx, const __mwi &coverage, const __mw &zTriv) + { + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + __mw *zMin = mMaskedHiZBuffer[tileIdx].mZMin; + __mwi &mask = mMaskedHiZBuffer[tileIdx].mMask; + + // Swizzle coverage mask to 8x4 subtiles + __mwi rastMask = coverage; + + // Perform individual depth tests with layer 0 & 1 and mask out all failing pixels + __mw sdist0 = _mmw_sub_ps(zMin[0], zTriv); + __mw sdist1 = _mmw_sub_ps(zMin[1], zTriv); + __mwi sign0 = _mmw_srai_epi32(simd_cast<__mwi>(sdist0), 31); + __mwi sign1 = _mmw_srai_epi32(simd_cast<__mwi>(sdist1), 31); + __mwi triMask = _mmw_and_epi32(rastMask, _mmw_or_epi32(_mmw_andnot_epi32(mask, sign0), _mmw_and_epi32(mask, sign1))); + + // Early out if no pixels survived the depth test (this test is more accurate than + // the early culling test in TraverseScanline()) + __mwi t0 = _mmw_cmpeq_epi32(triMask, SIMD_BITS_ZERO); + __mwi t0inv = _mmw_not_epi32(t0); + if (_mmw_testz_epi32(t0inv, t0inv)) + return; + + STATS_ADD(mStats.mOccluders.mNumTilesUpdated, 1); + + __mw zTri = _mmw_blendv_ps(zTriv, zMin[0], simd_cast<__mw>(t0)); + + // Test if incoming triangle completely overwrites layer 0 or 1 + __mwi layerMask0 = _mmw_andnot_epi32(triMask, _mmw_not_epi32(mask)); + __mwi layerMask1 = _mmw_andnot_epi32(triMask, mask); + __mwi lm0 = _mmw_cmpeq_epi32(layerMask0, SIMD_BITS_ZERO); + __mwi lm1 = _mmw_cmpeq_epi32(layerMask1, SIMD_BITS_ZERO); + __mw z0 = _mmw_blendv_ps(zMin[0], zTri, simd_cast<__mw>(lm0)); + __mw z1 = _mmw_blendv_ps(zMin[1], zTri, simd_cast<__mw>(lm1)); + + // Compute distances used for merging heuristic + __mw d0 = _mmw_abs_ps(sdist0); + __mw d1 = _mmw_abs_ps(sdist1); + __mw d2 = _mmw_abs_ps(_mmw_sub_ps(z0, z1)); + + // Find minimum distance + __mwi c01 = simd_cast<__mwi>(_mmw_sub_ps(d0, d1)); + __mwi c02 = simd_cast<__mwi>(_mmw_sub_ps(d0, d2)); + __mwi c12 = simd_cast<__mwi>(_mmw_sub_ps(d1, d2)); + // Two tests indicating which layer the incoming triangle will merge with or + // overwrite. d0min indicates that the triangle will overwrite layer 0, and + // d1min flags that the triangle will overwrite layer 1. + __mwi d0min = _mmw_or_epi32(_mmw_and_epi32(c01, c02), _mmw_or_epi32(lm0, t0)); + __mwi d1min = _mmw_andnot_epi32(d0min, _mmw_or_epi32(c12, lm1)); + + /////////////////////////////////////////////////////////////////////////////// + // Update depth buffer entry. NOTE: we always merge into layer 0, so if the + // triangle should be merged with layer 1, we first swap layer 0 & 1 and then + // merge into layer 0. + /////////////////////////////////////////////////////////////////////////////// + + // Update mask based on which layer the triangle overwrites or was merged into + __mw inner = _mmw_blendv_ps(simd_cast<__mw>(triMask), simd_cast<__mw>(layerMask1), simd_cast<__mw>(d0min)); + mask = simd_cast<__mwi>(_mmw_blendv_ps(inner, simd_cast<__mw>(layerMask0), simd_cast<__mw>(d1min))); + + // Update the zMin[0] value. There are four outcomes: overwrite with layer 1, + // merge with layer 1, merge with zTri or overwrite with layer 1 and then merge + // with zTri. + __mw e0 = _mmw_blendv_ps(z0, z1, simd_cast<__mw>(d1min)); + __mw e1 = _mmw_blendv_ps(z1, zTri, simd_cast<__mw>(_mmw_or_epi32(d1min, d0min))); + zMin[0] = _mmw_min_ps(e0, e1); + + // Update the zMin[1] value. There are three outcomes: keep current value, + // overwrite with zTri, or overwrite with z1 + __mw z1t = _mmw_blendv_ps(zTri, z1, simd_cast<__mw>(d0min)); + zMin[1] = _mmw_blendv_ps(z1t, z0, simd_cast<__mw>(d1min)); + } + + template + FORCE_INLINE int TraverseScanline(int leftOffset, int rightOffset, int tileIdx, int rightEvent, int leftEvent, const __mwi *events, const __mw &zTriMin, const __mw &zTriMax, const __mw &iz0, float zx) + { + // Floor edge events to integer pixel coordinates (shift out fixed point bits) + int eventOffset = leftOffset << TILE_WIDTH_SHIFT; + __mwi right[NRIGHT], left[NLEFT]; + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[rightEvent + i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_max_epi32(_mmw_sub_epi32(_mmw_srai_epi32(events[leftEvent - i], FP_BITS), _mmw_set1_epi32(eventOffset)), SIMD_BITS_ZERO); + + __mw z0 = _mmw_add_ps(iz0, _mmw_set1_ps(zx*leftOffset)); + int tileIdxEnd = tileIdx + rightOffset; + tileIdx += leftOffset; + for (;;) + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + else + STATS_ADD(mStats.mOccluders.mNumTilesTraversed, 1); + + // Perform a coarse test to quickly discard occluded tiles +#if QUICK_MASK != 0 + // Only use the reference layer (layer 0) to cull as it is always conservative + __mw zMinBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + // Compute zMin for the overlapped layers + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zMinBuf = _mmw_min_ps(zMin0, zMin1); +#endif + __mw dist0 = _mmw_sub_ps(zTriMax, zMinBuf); + if (_mmw_movemask_ps(dist0) != SIMD_ALL_LANES_MASK) + { + // Compute coverage mask for entire 32xN using shift operations + __mwi accumulatedMask = _mmw_sllv_ones(left[0]); + for (int i = 1; i < NLEFT; ++i) + accumulatedMask = _mmw_and_epi32(accumulatedMask, _mmw_sllv_ones(left[i])); + for (int i = 0; i < NRIGHT; ++i) + accumulatedMask = _mmw_andnot_epi32(_mmw_sllv_ones(right[i]), accumulatedMask); + + if (TEST_Z) + { + // Perform a conservative visibility test (test zMax against buffer for each covered 8x4 subtile) + __mw zSubTileMax = _mmw_min_ps(z0, zTriMax); + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zSubTileMax, zMinBuf)); + + __mwi rastMask = _mmw_transpose_epi8(accumulatedMask); + __mwi deadLane = _mmw_cmpeq_epi32(rastMask, SIMD_BITS_ZERO); + zPass = _mmw_andnot_epi32(deadLane, zPass); + + if (!_mmw_testz_epi32(zPass, zPass)) + return CullingResult::VISIBLE; + } + else + { + // Compute interpolated min for each 8x4 subtile and update the masked hierarchical z buffer entry + __mw zSubTileMin = _mmw_max_ps(z0, zTriMin); +#if QUICK_MASK != 0 + UpdateTileQuick(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#else + UpdateTileAccurate(tileIdx, _mmw_transpose_epi8(accumulatedMask), zSubTileMin); +#endif + } + } + + // Update buffer address, interpolate z and edge events + tileIdx++; + if (tileIdx >= tileIdxEnd) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zx)); + for (int i = 0; i < NRIGHT; ++i) + right[i] = _mmw_subs_epu16(right[i], SIMD_TILE_WIDTH); // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) + for (int i = 0; i < NLEFT; ++i) + left[i] = _mmw_subs_epu16(left[i], SIMD_TILE_WIDTH); + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mw *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy, const __mwi *edgeY, const __mwi *absEdgeX, const __mwi *slopeSign, const __mwi *eventStartRemainder, const __mwi *slopeTileRemainder) +#else + FORCE_INLINE int RasterizeTriangle(unsigned int triIdx, int bbWidth, int tileRowIdx, int tileMidRowIdx, int tileEndRowIdx, const __mwi *eventStart, const __mwi *slope, const __mwi *slopeTileDelta, const __mw &zTriMin, const __mw &zTriMax, __mw &z0, float zx, float zy) +#endif + { + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumRasterizedTriangles, 1); + else + STATS_ADD(mStats.mOccluders.mNumRasterizedTriangles, 1); + + int cullResult; + +#if PRECISE_COVERAGE != 0 + #define LEFT_EDGE_BIAS -1 + #define RIGHT_EDGE_BIAS 1 + #define UPDATE_TILE_EVENTS_Y(i) \ + triEventRemainder[i] = _mmw_sub_epi32(triEventRemainder[i], triSlopeTileRemainder[i]); \ + __mwi overflow##i = _mmw_srai_epi32(triEventRemainder[i], 31); \ + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow##i, triEdgeY[i])); \ + triEvent[i] = _mmw_add_epi32(triEvent[i], _mmw_add_epi32(triSlopeTileDelta[i], _mmw_and_epi32(overflow##i, triSlopeSign[i]))) + + __mwi triEvent[3], triSlopeSign[3], triSlopeTileDelta[3], triEdgeY[3], triSlopeTileRemainder[3], triEventRemainder[3]; + for (int i = 0; i < 3; ++i) + { + triSlopeSign[i] = _mmw_set1_epi32(simd_i32(slopeSign[i])[triIdx]); + triSlopeTileDelta[i] = _mmw_set1_epi32(simd_i32(slopeTileDelta[i])[triIdx]); + triEdgeY[i] = _mmw_set1_epi32(simd_i32(edgeY[i])[triIdx]); + triSlopeTileRemainder[i] = _mmw_set1_epi32(simd_i32(slopeTileRemainder[i])[triIdx]); + + __mw triSlope = _mmw_set1_ps(simd_f32(slope[i])[triIdx]); + __mwi triAbsEdgeX = _mmw_set1_epi32(simd_i32(absEdgeX[i])[triIdx]); + __mwi triStartRemainder = _mmw_set1_epi32(simd_i32(eventStartRemainder[i])[triIdx]); + __mwi triEventStart = _mmw_set1_epi32(simd_i32(eventStart[i])[triIdx]); + + __mwi scanlineDelta = _mmw_cvttps_epi32(_mmw_mul_ps(triSlope, SIMD_LANE_YCOORD_F)); + __mwi scanlineSlopeRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(triAbsEdgeX, SIMD_LANE_YCOORD_I), _mmw_mullo_epi32(_mmw_abs_epi32(scanlineDelta), triEdgeY[i])); + + triEventRemainder[i] = _mmw_sub_epi32(triStartRemainder, scanlineSlopeRemainder); + __mwi overflow = _mmw_srai_epi32(triEventRemainder[i], 31); + triEventRemainder[i] = _mmw_add_epi32(triEventRemainder[i], _mmw_and_epi32(overflow, triEdgeY[i])); + triEvent[i] = _mmw_add_epi32(_mmw_add_epi32(triEventStart, scanlineDelta), _mmw_and_epi32(overflow, triSlopeSign[i])); + } + +#else + #define LEFT_EDGE_BIAS 0 + #define RIGHT_EDGE_BIAS 0 + #define UPDATE_TILE_EVENTS_Y(i) triEvent[i] = _mmw_add_epi32(triEvent[i], triSlopeTileDelta[i]); + + // Get deltas used to increment edge events each time we traverse one scanline of tiles + __mwi triSlopeTileDelta[3]; + triSlopeTileDelta[0] = _mmw_set1_epi32(simd_i32(slopeTileDelta[0])[triIdx]); + triSlopeTileDelta[1] = _mmw_set1_epi32(simd_i32(slopeTileDelta[1])[triIdx]); + triSlopeTileDelta[2] = _mmw_set1_epi32(simd_i32(slopeTileDelta[2])[triIdx]); + + // Setup edge events for first batch of SIMD_LANES scanlines + __mwi triEvent[3]; + triEvent[0] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[0])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[0])[triIdx]))); + triEvent[1] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[1])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[1])[triIdx]))); + triEvent[2] = _mmw_add_epi32(_mmw_set1_epi32(simd_i32(eventStart[2])[triIdx]), _mmw_mullo_epi32(SIMD_LANE_IDX, _mmw_set1_epi32(simd_i32(slope[2])[triIdx]))); +#endif + + // For big triangles track start & end tile for each scanline and only traverse the valid region + int startDelta, endDelta, topDelta, startEvent, endEvent, topEvent; + if (TIGHT_TRAVERSAL) + { + startDelta = simd_i32(slopeTileDelta[2])[triIdx] + LEFT_EDGE_BIAS; + endDelta = simd_i32(slopeTileDelta[0])[triIdx] + RIGHT_EDGE_BIAS; + topDelta = simd_i32(slopeTileDelta[1])[triIdx] + (MID_VTX_RIGHT ? RIGHT_EDGE_BIAS : LEFT_EDGE_BIAS); + + // Compute conservative bounds for the edge events over a 32xN tile + startEvent = simd_i32(eventStart[2])[triIdx] + min(0, startDelta); + endEvent = simd_i32(eventStart[0])[triIdx] + max(0, endDelta) + (TILE_WIDTH << FP_BITS); + if (MID_VTX_RIGHT) + topEvent = simd_i32(eventStart[1])[triIdx] + max(0, topDelta) + (TILE_WIDTH << FP_BITS); + else + topEvent = simd_i32(eventStart[1])[triIdx] + min(0, topDelta); + } + + if (tileRowIdx <= tileMidRowIdx) + { + int tileStopIdx = min(tileEndRowIdx, tileMidRowIdx); + // Traverse the bottom half of the triangle + while (tileRowIdx < tileStopIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(0); + UPDATE_TILE_EVENTS_Y(2); + } + + // Traverse the middle scanline of tiles. We must consider all three edges only in this region + if (tileRowIdx < tileEndRowIdx) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + + // Switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer. + if (MID_VTX_RIGHT) + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + else + cullResult = TraverseScanline(start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + tileRowIdx += mTilesWidth; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + // move to the next scanline of tiles, update edge events and interpolate z + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + else + { + if (TIGHT_TRAVERSAL) + { + // For large triangles, switch the traversal start / end to account for the upper side edge + endEvent = MID_VTX_RIGHT ? topEvent : endEvent; + endDelta = MID_VTX_RIGHT ? topDelta : endDelta; + startEvent = MID_VTX_RIGHT ? startEvent : topEvent; + startDelta = MID_VTX_RIGHT ? startDelta : topDelta; + } + + // Traverse the top half of the triangle + if (tileRowIdx < tileEndRowIdx) + { + int i0 = MID_VTX_RIGHT + 0; + int i1 = MID_VTX_RIGHT + 1; + for (;;) + { + int start = 0, end = bbWidth; + if (TIGHT_TRAVERSAL) + { + // Compute tighter start and endpoints to avoid traversing lots of empty space + start = max(0, min(bbWidth - 1, startEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + end = min(bbWidth, ((int)endEvent >> (TILE_WIDTH_SHIFT + FP_BITS))); + startEvent += startDelta; + endEvent += endDelta; + } + + // Traverse the scanline and update the masked hierarchical z buffer + cullResult = TraverseScanline(start, end, tileRowIdx, MID_VTX_RIGHT + 0, MID_VTX_RIGHT + 1, triEvent, zTriMin, zTriMax, z0, zx); + + if (TEST_Z && cullResult == CullingResult::VISIBLE) // Early out if performing occlusion query + return CullingResult::VISIBLE; + + // move to the next scanline of tiles, update edge events and interpolate z + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileEndRowIdx) + break; + z0 = _mmw_add_ps(z0, _mmw_set1_ps(zy)); + UPDATE_TILE_EVENTS_Y(i0); + UPDATE_TILE_EVENTS_Y(i1); + } + } + } + + return TEST_Z ? CullingResult::OCCLUDED : CullingResult::VISIBLE; + } + + template +#if PRECISE_COVERAGE != 0 + FORCE_INLINE int RasterizeTriangleBatch(__mwi ipVtxX[3], __mwi ipVtxY[3], __mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#else + FORCE_INLINE int RasterizeTriangleBatch(__mw pVtxX[3], __mw pVtxY[3], __mw pVtxZ[3], unsigned int triMask, const ScissorRect *scissor) +#endif + { + int cullResult = CullingResult::VIEW_CULLED; + + ////////////////////////////////////////////////////////////////////////////// + // Compute bounding box and clamp to tile coordinates + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, scissor); + + // Clamp bounding box to tiles (it's already padded in computeBoundingBox) + __mwi bbTileMinX = _mmw_srai_epi32(bbPixelMinX, TILE_WIDTH_SHIFT); + __mwi bbTileMinY = _mmw_srai_epi32(bbPixelMinY, TILE_HEIGHT_SHIFT); + __mwi bbTileMaxX = _mmw_srai_epi32(bbPixelMaxX, TILE_WIDTH_SHIFT); + __mwi bbTileMaxY = _mmw_srai_epi32(bbPixelMaxY, TILE_HEIGHT_SHIFT); + __mwi bbTileSizeX = _mmw_sub_epi32(bbTileMaxX, bbTileMinX); + __mwi bbTileSizeY = _mmw_sub_epi32(bbTileMaxY, bbTileMinY); + + // Cull triangles with zero bounding box + __mwi bboxSign = _mmw_or_epi32(_mmw_sub_epi32(bbTileSizeX, _mmw_set1_epi32(1)), _mmw_sub_epi32(bbTileSizeY, _mmw_set1_epi32(1))); + triMask &= ~_mmw_movemask_ps(simd_cast<__mw>(bboxSign)) & SIMD_ALL_LANES_MASK; + if (triMask == 0x0) + return cullResult; + + if (!TEST_Z) + cullResult = CullingResult::VISIBLE; + + ////////////////////////////////////////////////////////////////////////////// + // Set up screen space depth plane + ////////////////////////////////////////////////////////////////////////////// + + __mw zPixelDx, zPixelDy; + ComputeDepthPlane(pVtxX, pVtxY, pVtxZ, zPixelDx, zPixelDy); + + // Compute z value at min corner of bounding box. Offset to make sure z is conservative for all 8x4 subtiles + __mw bbMinXV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinX), pVtxX[0]); + __mw bbMinYV0 = _mmw_sub_ps(_mmw_cvtepi32_ps(bbPixelMinY), pVtxY[0]); + __mw zPlaneOffset = _mmw_fmadd_ps(zPixelDx, bbMinXV0, _mmw_fmadd_ps(zPixelDy, bbMinYV0, pVtxZ[0])); + __mw zTileDx = _mmw_mul_ps(zPixelDx, _mmw_set1_ps((float)TILE_WIDTH)); + __mw zTileDy = _mmw_mul_ps(zPixelDy, _mmw_set1_ps((float)TILE_HEIGHT)); + if (TEST_Z) + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_max_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + else + { + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDx, _mmw_set1_ps(SUB_TILE_WIDTH)))); + zPlaneOffset = _mmw_add_ps(zPlaneOffset, _mmw_min_ps(_mmw_setzero_ps(), _mmw_mul_ps(zPixelDy, _mmw_set1_ps(SUB_TILE_HEIGHT)))); + } + + // Compute Zmin and Zmax for the triangle (used to narrow the range for difficult tiles) + __mw zMin = _mmw_min_ps(pVtxZ[0], _mmw_min_ps(pVtxZ[1], pVtxZ[2])); + __mw zMax = _mmw_max_ps(pVtxZ[0], _mmw_max_ps(pVtxZ[1], pVtxZ[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Sort vertices (v0 has lowest Y, and the rest is in winding order) and + // compute edges. Also find the middle vertex and compute tile + ////////////////////////////////////////////////////////////////////////////// + +#if PRECISE_COVERAGE != 0 + + // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value + SortVertices(ipVtxX, ipVtxY); + + // Compute edges + __mwi edgeX[3] = { _mmw_sub_epi32(ipVtxX[1], ipVtxX[0]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[1]), _mmw_sub_epi32(ipVtxX[2], ipVtxX[0]) }; + __mwi edgeY[3] = { _mmw_sub_epi32(ipVtxY[1], ipVtxY[0]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[1]), _mmw_sub_epi32(ipVtxY[2], ipVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(simd_cast<__mw>(edgeY[1])); + __mwi midPixelX = _mmw_blendv_epi32(ipVtxX[1], ipVtxX[2], edgeY[1]); + __mwi midPixelY = _mmw_blendv_epi32(ipVtxY[1], ipVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(midPixelY, SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT + FP_BITS); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_sub_epi32(ipVtxX[0], _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + xDiffi[1] = _mmw_sub_epi32(midPixelX, _mmw_slli_epi32(bbPixelMinX, FP_BITS)); + yDiffi[0] = _mmw_sub_epi32(ipVtxY[0], _mmw_slli_epi32(bbPixelMinY, FP_BITS)); + yDiffi[1] = _mmw_sub_epi32(midPixelY, _mmw_slli_epi32(bbMidTileY, FP_BITS + TILE_HEIGHT_SHIFT)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Potentially flip edge to ensure that all edges have positive Y slope. + edgeX[1] = _mmw_blendv_epi32(edgeX[1], _mmw_neg_epi32(edgeX[1]), edgeY[1]); + edgeY[1] = _mmw_abs_epi32(edgeY[1]); + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[0]), _mmw_cvtepi32_ps(edgeY[0])); + slope[1] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[1]), _mmw_cvtepi32_ps(edgeY[1])); + slope[2] = _mmw_div_ps(_mmw_cvtepi32_ps(edgeX[2]), _mmw_cvtepi32_ps(edgeY[2])); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps(2.0f * ((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f))); + __mwi horizontalSlope0 = _mmw_cmpeq_epi32(edgeY[0], _mmw_setzero_epi32()); + __mwi horizontalSlope1 = _mmw_cmpeq_epi32(edgeY[1], _mmw_setzero_epi32()); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, simd_cast<__mw>(horizontalSlope0)); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), simd_cast<__mw>(horizontalSlope1)); + + __mwi vy[3] = { yDiffi[0], yDiffi[1], yDiffi[0] }; + __mwi offset0 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[0], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + __mwi offset1 = _mmw_and_epi32(_mmw_add_epi32(yDiffi[1], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), _mmw_set1_epi32((int)((~0u) << FP_BITS))); + vy[0] = _mmw_blendv_epi32(yDiffi[0], offset0, horizontalSlope0); + vy[1] = _mmw_blendv_epi32(yDiffi[1], offset1, horizontalSlope1); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi slopeSign[3], absEdgeX[3]; + __mwi slopeTileDelta[3], eventStartRemainder[3], slopeTileRemainder[3], eventStart[3]; + for (int i = 0; i < 3; i++) + { + // Common, compute slope sign (used to propagate the remainder term when overflowing) is postive or negative x-direction + slopeSign[i] = _mmw_blendv_epi32(_mmw_set1_epi32(1), _mmw_set1_epi32(-1), edgeX[i]); + absEdgeX[i] = _mmw_abs_epi32(edgeX[i]); + + // Delta and error term for one vertical tile step. The exact delta is exactDelta = edgeX / edgeY, due to limited precision we + // repersent the delta as delta = qoutient + remainder / edgeY, where quotient = int(edgeX / edgeY). In this case, since we step + // one tile of scanlines at a time, the slope is computed for a tile-sized step. + slopeTileDelta[i] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_set1_ps(FP_TILE_HEIGHT))); + slopeTileRemainder[i] = _mmw_sub_epi32(_mmw_slli_epi32(absEdgeX[i], FP_TILE_HEIGHT_SHIFT), _mmw_mullo_epi32(_mmw_abs_epi32(slopeTileDelta[i]), edgeY[i])); + + // Jump to bottom scanline of tile row, this is the bottom of the bounding box, or the middle vertex of the triangle. + // The jump can be in both positive and negative y-direction due to clipping / offscreen vertices. + __mwi tileStartDir = _mmw_blendv_epi32(slopeSign[i], _mmw_neg_epi32(slopeSign[i]), vy[i]); + __mwi tieBreaker = _mmw_blendv_epi32(_mmw_set1_epi32(0), _mmw_set1_epi32(1), tileStartDir); + __mwi tileStartSlope = _mmw_cvttps_epi32(_mmw_mul_ps(slope[i], _mmw_cvtepi32_ps(_mmw_neg_epi32(vy[i])))); + __mwi tileStartRemainder = _mmw_sub_epi32(_mmw_mullo_epi32(absEdgeX[i], _mmw_abs_epi32(vy[i])), _mmw_mullo_epi32(_mmw_abs_epi32(tileStartSlope), edgeY[i])); + + eventStartRemainder[i] = _mmw_sub_epi32(tileStartRemainder, tieBreaker); + __mwi overflow = _mmw_srai_epi32(eventStartRemainder[i], 31); + eventStartRemainder[i] = _mmw_add_epi32(eventStartRemainder[i], _mmw_and_epi32(overflow, edgeY[i])); + eventStartRemainder[i] = _mmw_blendv_epi32(eventStartRemainder[i], _mmw_sub_epi32(_mmw_sub_epi32(edgeY[i], eventStartRemainder[i]), _mmw_set1_epi32(1)), vy[i]); + + //eventStart[i] = xDiffi[i & 1] + tileStartSlope + (overflow & tileStartDir) + _mmw_set1_epi32(FP_HALF_PIXEL - 1) + tieBreaker; + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(xDiffi[i & 1], tileStartSlope), _mmw_and_epi32(overflow, tileStartDir)); + eventStart[i] = _mmw_add_epi32(_mmw_add_epi32(eventStart[i], _mmw_set1_epi32(FP_HALF_PIXEL - 1)), tieBreaker); + } + +#else // PRECISE_COVERAGE + + SortVertices(pVtxX, pVtxY); + + // Compute edges + __mw edgeX[3] = { _mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxX[2], pVtxX[1]), _mmw_sub_ps(pVtxX[2], pVtxX[0]) }; + __mw edgeY[3] = { _mmw_sub_ps(pVtxY[1], pVtxY[0]), _mmw_sub_ps(pVtxY[2], pVtxY[1]), _mmw_sub_ps(pVtxY[2], pVtxY[0]) }; + + // Classify if the middle vertex is on the left or right and compute its position + int midVtxRight = ~_mmw_movemask_ps(edgeY[1]); + __mw midPixelX = _mmw_blendv_ps(pVtxX[1], pVtxX[2], edgeY[1]); + __mw midPixelY = _mmw_blendv_ps(pVtxY[1], pVtxY[2], edgeY[1]); + __mwi midTileY = _mmw_srai_epi32(_mmw_max_epi32(_mmw_cvttps_epi32(midPixelY), SIMD_BITS_ZERO), TILE_HEIGHT_SHIFT); + __mwi bbMidTileY = _mmw_max_epi32(bbTileMinY, _mmw_min_epi32(bbTileMaxY, midTileY)); + + ////////////////////////////////////////////////////////////////////////////// + // Edge slope setup - Note we do not conform to DX/GL rasterization rules + ////////////////////////////////////////////////////////////////////////////// + + // Compute floating point slopes + __mw slope[3]; + slope[0] = _mmw_div_ps(edgeX[0], edgeY[0]); + slope[1] = _mmw_div_ps(edgeX[1], edgeY[1]); + slope[2] = _mmw_div_ps(edgeX[2], edgeY[2]); + + // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen + // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that + // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 + __mw horizontalSlopeDelta = _mmw_set1_ps((float)mWidth + 2.0f*(GUARD_BAND_PIXEL_SIZE + 1.0f)); + slope[0] = _mmw_blendv_ps(slope[0], horizontalSlopeDelta, _mmw_cmpeq_ps(edgeY[0], _mmw_setzero_ps())); + slope[1] = _mmw_blendv_ps(slope[1], _mmw_neg_ps(horizontalSlopeDelta), _mmw_cmpeq_ps(edgeY[1], _mmw_setzero_ps())); + + // Convert floaing point slopes to fixed point + __mwi slopeFP[3]; + slopeFP[0] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[0], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[1] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[1], _mmw_set1_ps(1 << FP_BITS))); + slopeFP[2] = _mmw_cvttps_epi32(_mmw_mul_ps(slope[2], _mmw_set1_ps(1 << FP_BITS))); + + // Fan out edge slopes to avoid (rare) cracks at vertices. We increase right facing slopes + // by 1 LSB, which results in overshooting vertices slightly, increasing triangle coverage. + // e0 is always right facing, e1 depends on if the middle vertex is on the left or right + slopeFP[0] = _mmw_add_epi32(slopeFP[0], _mmw_set1_epi32(1)); + slopeFP[1] = _mmw_add_epi32(slopeFP[1], _mmw_srli_epi32(_mmw_not_epi32(simd_cast<__mwi>(edgeY[1])), 31)); + + // Compute slope deltas for an SIMD_LANES scanline step (tile height) + __mwi slopeTileDelta[3]; + slopeTileDelta[0] = _mmw_slli_epi32(slopeFP[0], TILE_HEIGHT_SHIFT); + slopeTileDelta[1] = _mmw_slli_epi32(slopeFP[1], TILE_HEIGHT_SHIFT); + slopeTileDelta[2] = _mmw_slli_epi32(slopeFP[2], TILE_HEIGHT_SHIFT); + + // Compute edge events for the bottom of the bounding box, or for the middle tile in case of + // the edge originating from the middle vertex. + __mwi xDiffi[2], yDiffi[2]; + xDiffi[0] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(pVtxX[0]), bbPixelMinX), FP_BITS); + xDiffi[1] = _mmw_slli_epi32(_mmw_sub_epi32(_mmw_cvttps_epi32(midPixelX), bbPixelMinX), FP_BITS); + yDiffi[0] = _mmw_sub_epi32(_mmw_cvttps_epi32(pVtxY[0]), bbPixelMinY); + yDiffi[1] = _mmw_sub_epi32(_mmw_cvttps_epi32(midPixelY), _mmw_slli_epi32(bbMidTileY, TILE_HEIGHT_SHIFT)); + + __mwi eventStart[3]; + eventStart[0] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[0], yDiffi[0])); + eventStart[1] = _mmw_sub_epi32(xDiffi[1], _mmw_mullo_epi32(slopeFP[1], yDiffi[1])); + eventStart[2] = _mmw_sub_epi32(xDiffi[0], _mmw_mullo_epi32(slopeFP[2], yDiffi[0])); +#endif + + ////////////////////////////////////////////////////////////////////////////// + // Split bounding box into bottom - middle - top region. + ////////////////////////////////////////////////////////////////////////////// + + __mwi bbBottomIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(bbTileMinY, _mmw_set1_epi32(mTilesWidth))); + __mwi bbTopIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(_mmw_add_epi32(bbTileMinY, bbTileSizeY), _mmw_set1_epi32(mTilesWidth))); + __mwi bbMidIdx = _mmw_add_epi32(bbTileMinX, _mmw_mullo_epi32(midTileY, _mmw_set1_epi32(mTilesWidth))); + + ////////////////////////////////////////////////////////////////////////////// + // Loop over non-culled triangle and change SIMD axis to per-pixel + ////////////////////////////////////////////////////////////////////////////// + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + int triMidVtxRight = (midVtxRight >> triIdx) & 1; + + // Get Triangle Zmin zMax + __mw zTriMax = _mmw_set1_ps(simd_f32(zMax)[triIdx]); + __mw zTriMin = _mmw_set1_ps(simd_f32(zMin)[triIdx]); + + // Setup Zmin value for first set of 8x4 subtiles + __mw z0 = _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDx)[triIdx]), SIMD_SUB_TILE_COL_OFFSET_F, + _mmw_fmadd_ps(_mmw_set1_ps(simd_f32(zPixelDy)[triIdx]), SIMD_SUB_TILE_ROW_OFFSET_F, _mmw_set1_ps(simd_f32(zPlaneOffset)[triIdx]))); + float zx = simd_f32(zTileDx)[triIdx]; + float zy = simd_f32(zTileDy)[triIdx]; + + // Get dimension of bounding box bottom, mid & top segments + int bbWidth = simd_i32(bbTileSizeX)[triIdx]; + int bbHeight = simd_i32(bbTileSizeY)[triIdx]; + int tileRowIdx = simd_i32(bbBottomIdx)[triIdx]; + int tileMidRowIdx = simd_i32(bbMidIdx)[triIdx]; + int tileEndRowIdx = simd_i32(bbTopIdx)[triIdx]; + + if (bbWidth > BIG_TRIANGLE && bbHeight > BIG_TRIANGLE) // For big triangles we use a more expensive but tighter traversal algorithm + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + else + { +#if PRECISE_COVERAGE != 0 + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); +#else + if (triMidVtxRight) + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); + else + cullResult &= RasterizeTriangle(triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slopeFP, slopeTileDelta, zTriMin, zTriMax, z0, zx, zy); +#endif + } + + if (TEST_Z && cullResult == CullingResult::VISIBLE) + return CullingResult::VISIBLE; + } + + return cullResult; + } + + template + FORCE_INLINE CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + + if (TEST_Z) + STATS_ADD(mStats.mOccludees.mNumProcessedTriangles, nTris); + else + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + int cullResult = CullingResult::VIEW_CULLED; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + __mw vtxX[3], vtxY[3], vtxW[3]; + unsigned int triMask = SIMD_ALL_LANES_MASK; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// +#if PRECISE_COVERAGE != 0 + cullResult &= RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#else + cullResult &= RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, &mFullscreenScissor); +#endif + + if (TEST_Z && cullResult == CullingResult::VISIBLE) { +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return CullingResult::VISIBLE; + } + } + +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + return (CullingResult)cullResult; + } + + CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<0, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<0, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + RecordRenderTriangles( inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout, retVal ); +#endif + return retVal; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Occlusion query functions + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + CullingResult retVal; + + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + retVal = (CullingResult)RenderTriangles<1, 1>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + retVal = (CullingResult)RenderTriangles<1, 0>(inVtx, inTris, nTris, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestTriangles( retVal, inVtx, inTris, nTris, modelToClipMatrix, clipPlaneMask, bfWinding, vtxLayout ); + } +#endif + return retVal; + } + + CullingResult TestRect( float xmin, float ymin, float xmax, float ymax, float wmin ) const override + { + STATS_ADD(mStats.mOccludees.mNumProcessedRectangles, 1); + assert(mMaskedHiZBuffer != nullptr); + + static const __m128i SIMD_TILE_PAD = _mm_setr_epi32(0, TILE_WIDTH, 0, TILE_HEIGHT); + static const __m128i SIMD_TILE_PAD_MASK = _mm_setr_epi32(~(TILE_WIDTH - 1), ~(TILE_WIDTH - 1), ~(TILE_HEIGHT - 1), ~(TILE_HEIGHT - 1)); + static const __m128i SIMD_SUB_TILE_PAD = _mm_setr_epi32(0, SUB_TILE_WIDTH, 0, SUB_TILE_HEIGHT); + static const __m128i SIMD_SUB_TILE_PAD_MASK = _mm_setr_epi32(~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_WIDTH - 1), ~(SUB_TILE_HEIGHT - 1), ~(SUB_TILE_HEIGHT - 1)); + + ////////////////////////////////////////////////////////////////////////////// + // Compute screen space bounding box and guard for out of bounds + ////////////////////////////////////////////////////////////////////////////// +#if USE_D3D != 0 + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymax, ymin), mIHalfSize, mICenter); +#else + __m128 pixelBBox = _mmx_fmadd_ps(_mm_setr_ps(xmin, xmax, ymin, ymax), mIHalfSize, mICenter); +#endif + __m128i pixelBBoxi = _mm_cvttps_epi32(pixelBBox); + pixelBBoxi = _mmx_max_epi32(_mm_setzero_si128(), _mmx_min_epi32(mIScreenSize, pixelBBoxi)); + + ////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (32xN) tiles. Tile BB is used for looping / traversal + ////////////////////////////////////////////////////////////////////////////// + __m128i tileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_TILE_PAD), SIMD_TILE_PAD_MASK); + int txMin = simd_i32(tileBBoxi)[0] >> TILE_WIDTH_SHIFT; + int txMax = simd_i32(tileBBoxi)[1] >> TILE_WIDTH_SHIFT; + int tileRowIdx = (simd_i32(tileBBoxi)[2] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + int tileRowIdxEnd = (simd_i32(tileBBoxi)[3] >> TILE_HEIGHT_SHIFT)*mTilesWidth; + + if (simd_i32(tileBBoxi)[0] == simd_i32(tileBBoxi)[1] || simd_i32(tileBBoxi)[2] == simd_i32(tileBBoxi)[3]) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VIEW_CULLED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VIEW_CULLED; + } + + /////////////////////////////////////////////////////////////////////////////// + // Pad bounding box to (8x4) subtiles. Skip SIMD lanes outside the subtile BB + /////////////////////////////////////////////////////////////////////////////// + __m128i subTileBBoxi = _mm_and_si128(_mm_add_epi32(pixelBBoxi, SIMD_SUB_TILE_PAD), SIMD_SUB_TILE_PAD_MASK); + __mwi stxmin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[0] - 1); // - 1 to be able to use GT test + __mwi stymin = _mmw_set1_epi32(simd_i32(subTileBBoxi)[2] - 1); // - 1 to be able to use GT test + __mwi stxmax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[1]); + __mwi stymax = _mmw_set1_epi32(simd_i32(subTileBBoxi)[3]); + + // Setup pixel coordinates used to discard lanes outside subtile BB + __mwi startPixelX = _mmw_add_epi32(SIMD_SUB_TILE_COL_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[0])); + __mwi pixelY = _mmw_add_epi32(SIMD_SUB_TILE_ROW_OFFSET, _mmw_set1_epi32(simd_i32(tileBBoxi)[2])); + + ////////////////////////////////////////////////////////////////////////////// + // Compute z from w. Note that z is reversed order, 0 = far, 1 = near, which + // means we use a greater than test, so zMax is used to test for visibility. + ////////////////////////////////////////////////////////////////////////////// + __mw zMax = _mmw_div_ps(_mmw_set1_ps(1.0f), _mmw_set1_ps(wmin)); + + for (;;) + { + __mwi pixelX = startPixelX; + for (int tx = txMin;;) + { + STATS_ADD(mStats.mOccludees.mNumTilesTraversed, 1); + + int tileIdx = tileRowIdx + tx; + assert(tileIdx >= 0 && tileIdx < mTilesWidth*mTilesHeight); + + // Fetch zMin from masked hierarchical Z buffer +#if QUICK_MASK != 0 + __mw zBuf = mMaskedHiZBuffer[tileIdx].mZMin[0]; +#else + __mwi mask = mMaskedHiZBuffer[tileIdx].mMask; + __mw zMin0 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[0], mMaskedHiZBuffer[tileIdx].mZMin[1], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_set1_epi32(~0)))); + __mw zMin1 = _mmw_blendv_ps(mMaskedHiZBuffer[tileIdx].mZMin[1], mMaskedHiZBuffer[tileIdx].mZMin[0], simd_cast<__mw>(_mmw_cmpeq_epi32(mask, _mmw_setzero_epi32()))); + __mw zBuf = _mmw_min_ps(zMin0, zMin1); +#endif + // Perform conservative greater than test against hierarchical Z buffer (zMax >= zBuf means the subtile is visible) + __mwi zPass = simd_cast<__mwi>(_mmw_cmpge_ps(zMax, zBuf)); //zPass = zMax >= zBuf ? ~0 : 0 + + // Mask out lanes corresponding to subtiles outside the bounding box + __mwi bboxTestMin = _mmw_and_epi32(_mmw_cmpgt_epi32(pixelX, stxmin), _mmw_cmpgt_epi32(pixelY, stymin)); + __mwi bboxTestMax = _mmw_and_epi32(_mmw_cmpgt_epi32(stxmax, pixelX), _mmw_cmpgt_epi32(stymax, pixelY)); + __mwi boxMask = _mmw_and_epi32(bboxTestMin, bboxTestMax); + zPass = _mmw_and_epi32(zPass, boxMask); + + // If not all tiles failed the conservative z test we can immediately terminate the test + if (!_mmw_testz_epi32(zPass, zPass)) + { +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::VISIBLE, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::VISIBLE; + } + + if (++tx >= txMax) + break; + pixelX = _mmw_add_epi32(pixelX, _mmw_set1_epi32(TILE_WIDTH)); + } + + tileRowIdx += mTilesWidth; + if (tileRowIdx >= tileRowIdxEnd) + break; + pixelY = _mmw_add_epi32(pixelY, _mmw_set1_epi32(TILE_HEIGHT)); + } +#if MOC_RECORDER_ENABLE + { + std::lock_guard lock( mRecorderMutex ); + if( mRecorder != nullptr ) mRecorder->RecordTestRect( CullingResult::OCCLUDED, xmin, ymin, xmax, ymax, wmin ); + } +#endif + return CullingResult::OCCLUDED; + } + + template + FORCE_INLINE void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) + { + assert(mMaskedHiZBuffer != nullptr); + +#if PRECISE_COVERAGE != 0 + int originalRoundingMode = _MM_GET_ROUNDING_MODE(); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + + STATS_ADD(mStats.mOccluders.mNumProcessedTriangles, nTris); + + int clipHead = 0; + int clipTail = 0; + __m128 clipTriBuffer[MAX_CLIPPED * 3]; + + const unsigned int *inTrisPtr = inTris; + int numLanes = SIMD_LANES; + int triIndex = 0; + while (triIndex < nTris || clipHead != clipTail) + { + unsigned int triMask = SIMD_ALL_LANES_MASK; + __mw vtxX[3], vtxY[3], vtxW[3]; + + GatherTransformClip( clipHead, clipTail, numLanes, nTris, triIndex, vtxX, vtxY, vtxW, inVtx, inTrisPtr, vtxLayout, modelToClipMatrix, clipTriBuffer, triMask, clipPlaneMask ); + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Project, transform to screen space and perform backface culling. Note + // that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and + // z = 1 is near. We must also use a greater than depth test, and in effect + // everything is reversed compared to regular z implementations. + ////////////////////////////////////////////////////////////////////////////// + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; + +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + ProjectVertices(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#else + ProjectVertices(pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); +#endif + + // Perform backface test. + __mw triArea1 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[1], pVtxX[0]), _mmw_sub_ps(pVtxY[2], pVtxY[0])); + __mw triArea2 = _mmw_mul_ps(_mmw_sub_ps(pVtxX[0], pVtxX[2]), _mmw_sub_ps(pVtxY[0], pVtxY[1])); + __mw triArea = _mmw_sub_ps(triArea1, triArea2); + __mw ccwMask = _mmw_cmpgt_ps(triArea, _mmw_setzero_ps()); + +#if PRECISE_COVERAGE != 0 + triMask &= CullBackfaces(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#else + triMask &= CullBackfaces(pVtxX, pVtxY, pVtxZ, ccwMask, bfWinding); +#endif + + if (triMask == 0x0) + continue; + + ////////////////////////////////////////////////////////////////////////////// + // Bin triangles + ////////////////////////////////////////////////////////////////////////////// + + unsigned int binWidth; + unsigned int binHeight; + ComputeBinWidthHeight(nBinsW, nBinsH, binWidth, binHeight); + + // Compute pixel bounding box + __mwi bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY; + ComputeBoundingBox(bbPixelMinX, bbPixelMinY, bbPixelMaxX, bbPixelMaxY, pVtxX, pVtxY, &mFullscreenScissor); + + while (triMask) + { + unsigned int triIdx = find_clear_lsb(&triMask); + + // Clamp bounding box to bins + int startX = min(nBinsW-1, simd_i32(bbPixelMinX)[triIdx] / binWidth); + int startY = min(nBinsH-1, simd_i32(bbPixelMinY)[triIdx] / binHeight); + int endX = min(nBinsW, (simd_i32(bbPixelMaxX)[triIdx] + binWidth - 1) / binWidth); + int endY = min(nBinsH, (simd_i32(bbPixelMaxY)[triIdx] + binHeight - 1) / binHeight); + + for (int y = startY; y < endY; ++y) + { + for (int x = startX; x < endX; ++x) + { + int binIdx = x + y * nBinsW; + unsigned int writeTriIdx = triLists[binIdx].mTriIdx; + for (int i = 0; i < 3; ++i) + { +#if PRECISE_COVERAGE != 0 + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 0] = simd_i32(ipVtxX[i])[triIdx]; + ((int*)triLists[binIdx].mPtr)[i * 3 + writeTriIdx * 9 + 1] = simd_i32(ipVtxY[i])[triIdx]; +#else + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 0] = simd_f32(pVtxX[i])[triIdx]; + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 1] = simd_f32(pVtxY[i])[triIdx]; +#endif + triLists[binIdx].mPtr[i * 3 + writeTriIdx * 9 + 2] = simd_f32(pVtxZ[i])[triIdx]; + } + triLists[binIdx].mTriIdx++; + } + } + } + } +#if PRECISE_COVERAGE != 0 + _MM_SET_ROUNDING_MODE(originalRoundingMode); +#endif + } + + void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix, BackfaceWinding bfWinding, ClipPlanes clipPlaneMask, const VertexLayout &vtxLayout) override + { + if (vtxLayout.mStride == 16 && vtxLayout.mOffsetY == 4 && vtxLayout.mOffsetW == 12) + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + else + BinTriangles(inVtx, inTris, nTris, triLists, nBinsW, nBinsH, modelToClipMatrix, bfWinding, clipPlaneMask, vtxLayout); + } + + template + void GatherTransformClip( int & clipHead, int & clipTail, int & numLanes, int nTris, int & triIndex, __mw * vtxX, __mw * vtxY, __mw * vtxW, const float * inVtx, const unsigned int * &inTrisPtr, const VertexLayout & vtxLayout, const float * modelToClipMatrix, __m128 * clipTriBuffer, unsigned int &triMask, ClipPlanes clipPlaneMask ) + { + ////////////////////////////////////////////////////////////////////////////// + // Assemble triangles from the index list + ////////////////////////////////////////////////////////////////////////////// + unsigned int triClipMask = SIMD_ALL_LANES_MASK; + + if( clipHead != clipTail ) + { + int clippedTris = clipHead > clipTail ? clipHead - clipTail : MAX_CLIPPED + clipHead - clipTail; + clippedTris = min( clippedTris, SIMD_LANES ); + +#if CLIPPING_PRESERVES_ORDER != 0 + // if preserving order, don't mix clipped and new triangles, handle the clip buffer fully + // and then continue gathering; this is not as efficient - ideally we want to gather + // at the end (if clip buffer has less than SIMD_LANES triangles) but that requires + // more modifications below - something to do in the future. + numLanes = 0; +#else + // Fill out SIMD registers by fetching more triangles. + numLanes = max( 0, min( SIMD_LANES - clippedTris, nTris - triIndex ) ); +#endif + + if( numLanes > 0 ) { + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + } + + for( int clipTri = numLanes; clipTri < numLanes + clippedTris; clipTri++ ) + { + int triIdx = clipTail * 3; + for( int i = 0; i < 3; i++ ) + { + simd_f32( vtxX[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[0]; + simd_f32( vtxY[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[1]; + simd_f32( vtxW[i] )[clipTri] = simd_f32( clipTriBuffer[triIdx + i] )[2]; + } + clipTail = ( clipTail + 1 ) & ( MAX_CLIPPED - 1 ); + } + + triIndex += numLanes; + inTrisPtr += numLanes * 3; + + triMask = ( 1U << ( clippedTris + numLanes ) ) - 1; + triClipMask = ( 1U << numLanes ) - 1; // Don't re-clip already clipped triangles + } + else + { + numLanes = min( SIMD_LANES, nTris - triIndex ); + triMask = ( 1U << numLanes ) - 1; + triClipMask = triMask; + + if( FAST_GATHER ) + GatherVerticesFast( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes ); + else + GatherVertices( vtxX, vtxY, vtxW, inVtx, inTrisPtr, numLanes, vtxLayout ); + + TransformVerts( vtxX, vtxY, vtxW, modelToClipMatrix ); + + triIndex += SIMD_LANES; + inTrisPtr += SIMD_LANES * 3; + } + + ////////////////////////////////////////////////////////////////////////////// + // Clip transformed triangles + ////////////////////////////////////////////////////////////////////////////// + + if( clipPlaneMask != ClipPlanes::CLIP_PLANE_NONE ) + ClipTriangleAndAddToBuffer( vtxX, vtxY, vtxW, clipTriBuffer, clipHead, triMask, triClipMask, clipPlaneMask ); + } + + void RenderTrilist(const TriList &triList, const ScissorRect *scissor) override + { + assert(mMaskedHiZBuffer != nullptr); + + // Setup fullscreen scissor rect as default + scissor = scissor == nullptr ? &mFullscreenScissor : scissor; + + for (unsigned int i = 0; i < triList.mTriIdx; i += SIMD_LANES) + { + ////////////////////////////////////////////////////////////////////////////// + // Fetch triangle vertices + ////////////////////////////////////////////////////////////////////////////// + + unsigned int numLanes = min((unsigned int)SIMD_LANES, triList.mTriIdx - i); + unsigned int triMask = (1U << numLanes) - 1; + + __mw pVtxX[3], pVtxY[3], pVtxZ[3]; +#if PRECISE_COVERAGE != 0 + __mwi ipVtxX[3], ipVtxY[3]; + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_i32(ipVtxX[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 0]; + simd_i32(ipVtxY[v])[l] = ((int*)triList.mPtr)[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + for (int v = 0; v < 3; ++v) + { + pVtxX[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxX[v]), _mmw_set1_ps(FP_INV)); + pVtxY[v] = _mmw_mul_ps(_mmw_cvtepi32_ps(ipVtxY[v]), _mmw_set1_ps(FP_INV)); + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, scissor); +#else + for (unsigned int l = 0; l < numLanes; ++l) + { + unsigned int triIdx = i + l; + for (int v = 0; v < 3; ++v) + { + simd_f32(pVtxX[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 0]; + simd_f32(pVtxY[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 1]; + simd_f32(pVtxZ[v])[l] = triList.mPtr[v * 3 + triIdx * 9 + 2]; + } + } + + ////////////////////////////////////////////////////////////////////////////// + // Setup and rasterize a SIMD batch of triangles + ////////////////////////////////////////////////////////////////////////////// + + RasterizeTriangleBatch(pVtxX, pVtxY, pVtxZ, triMask, scissor); +#endif + + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Debugging and statistics + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + MaskedOcclusionCulling::Implementation GetImplementation() override + { + return gInstructionSet; + } + + void ComputePixelDepthBuffer(float *depthData, bool flipY) override + { + assert(mMaskedHiZBuffer != nullptr); + for (int y = 0; y < mHeight; y++) + { + for (int x = 0; x < mWidth; x++) + { + // Compute 32xN tile index (SIMD value offset) + int tx = x / TILE_WIDTH; + int ty = y / TILE_HEIGHT; + int tileIdx = ty * mTilesWidth + tx; + + // Compute 8x4 subtile index (SIMD lane offset) + int stx = (x % TILE_WIDTH) / SUB_TILE_WIDTH; + int sty = (y % TILE_HEIGHT) / SUB_TILE_HEIGHT; + int subTileIdx = sty * 4 + stx; + + // Compute pixel index in subtile (bit index in 32-bit word) + int px = (x % SUB_TILE_WIDTH); + int py = (y % SUB_TILE_HEIGHT); + int bitIdx = py * 8 + px; + + int pixelLayer = (simd_i32(mMaskedHiZBuffer[tileIdx].mMask)[subTileIdx] >> bitIdx) & 1; + float pixelDepth = simd_f32(mMaskedHiZBuffer[tileIdx].mZMin[pixelLayer])[subTileIdx]; + + if( flipY ) + depthData[( mHeight - y - 1 ) * mWidth + x] = pixelDepth; + else + depthData[y * mWidth + x] = pixelDepth; + } + } + } + + OcclusionCullingStatistics GetStatistics() override + { + return mStats; + } + +}; diff --git a/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json new file mode 100644 index 0000000000..08b29fd566 --- /dev/null +++ b/Gems/Atom/RPI/Code/External/MaskedOcclusionCulling/PackageInfo.json @@ -0,0 +1,6 @@ +{ + "PackageName": "Masked Occlusion Culling", + "URL": "https://software.intel.com/content/www/us/en/develop/articles/masked-software-occlusion-culling.html", + "License": "Apache 2.0", + "LicenseFile": "LICENSE.txt" +} From 89b1afc50e00e7dd78488feb9ba6770544412d9d Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Wed, 2 Jun 2021 19:37:35 -0700 Subject: [PATCH 029/105] Adding Multiplayer:: namespace to RpcIndex so components outside the Multiplayer gem can compile --- Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 5cfd0bfc4d..917b1058a7 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -311,7 +311,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop {{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }}) { - constexpr RpcIndex rpcId = static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }}); + constexpr Multiplayer::RpcIndex rpcId = static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }}); {% if Property.attrib['IsReliable']|booleanTrue %} constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable; {% else %} From 69e2d6bba1022f9e439a8c55edf1bbabf89887da Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:44:55 -0700 Subject: [PATCH 030/105] Minor comment update --- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h index 7d192ea62a..e611ecf0d6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/View.h @@ -202,7 +202,7 @@ namespace AZ MatrixChangedEvent m_onWorldToClipMatrixChange; MatrixChangedEvent m_onWorldToViewMatrixChange; - // Software occlusion culling + // Masked Occlusion Culling interface MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr; }; From d737fcd3d3acd03edd184086e0e4b3754f2c6ed2 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:52:28 -0700 Subject: [PATCH 031/105] Removed extra newline --- Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 85b6bf07b8..e97805ceb2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -617,7 +617,6 @@ namespace AZ jobData->m_maskedOcclusionCulling = maskedOcclusionCulling; #endif - auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void { AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()"); From 113073ca31fcf10b9d2d1ff8e02962c5264c9ff9 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 19:54:22 -0700 Subject: [PATCH 032/105] Removed unnecessary include --- Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index c02ac0713c..7e33750eb5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include From b09f73378f3efc08f7fa775e1f49bd38b30f63f3 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 20:49:39 -0700 Subject: [PATCH 033/105] [cpack_installer] replaced LY_DEFAULT_INSTALL_COMPONENT with built-in CMAKE_INSTALL_DEFAULT_COMPONENT_NAME. updated stale references to ly_install_target_COMPONENT with a get_prop call --- cmake/3rdParty.cmake | 2 - cmake/Packaging.cmake | 4 +- cmake/Platform/Common/Install_common.cmake | 46 ++++++++-------------- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index eb11404237..ebf640af23 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -291,7 +291,6 @@ function(ly_install_external_target 3RDPARTY_ROOT_DIRECTORY) # Install the Find file to our /cmake directory install(FILES ${CMAKE_CURRENT_LIST_FILE} DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # We only want to install external targets that are part of our source tree @@ -302,7 +301,6 @@ function(ly_install_external_target 3RDPARTY_ROOT_DIRECTORY) get_filename_component(rel_path ${rel_path} DIRECTORY) install(DIRECTORY ${3RDPARTY_ROOT_DIRECTORY} DESTINATION ${rel_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endif() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e7136eab12..1bcb251271 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -101,7 +101,7 @@ endif() install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # IMPORTANT: required to be included AFTER setting all property overrides @@ -141,7 +141,7 @@ endfunction() # configure ALL components here ly_configure_cpack_component( - ${LY_DEFAULT_INSTALL_COMPONENT} REQUIRED + ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} REQUIRED DISPLAY_NAME "${PROJECT_NAME} Core" DESCRIPTION "${PROJECT_NAME} Headers, Libraries and Tools" ) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 710a8b266f..e0301bb914 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -11,7 +11,7 @@ set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise -ly_set(LY_DEFAULT_INSTALL_COMPONENT Core) +ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) @@ -27,6 +27,12 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) # De-alias target name ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) + # get the component ID. if the property isn't set for the target, fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_target_property(install_componet ${TARGET_NAME} INSTALL_COMPONENT) + if("${install_componet}" STREQUAL "install_componet-NOTFOUND") + unset(install_componet) + endif() + # All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that # we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the # headers end up in one folder instead of duplicating the folder structure of the public/interface include directory. @@ -41,7 +47,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) unset(current_public_headers) install(DIRECTORY ${include_directory} DESTINATION ${include_location}/${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} FILES_MATCHING PATTERN *.h PATTERN *.hpp @@ -68,13 +74,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} LIBRARY DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} RUNTIME DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) # CMakeLists.txt file @@ -182,7 +188,7 @@ set_property(TARGET ${TARGET_NAME} file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target @@ -239,9 +245,13 @@ function(ly_setup_subdirectory absolute_target_source_dir) "\n" "${CREATE_ALIASES_PLACEHOLDER}" ) + + # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_property(install_componet DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} - COMPONENT ${ly_install_target_COMPONENT} + COMPONENT ${install_componet} ) endfunction() @@ -262,7 +272,6 @@ function(ly_setup_cmake_install) install(DIRECTORY "${LY_ROOT_FOLDER}/cmake" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE REGEX "Findo3de.cmake" EXCLUDE REGEX "Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE @@ -290,7 +299,6 @@ function(ly_setup_cmake_install) "${LY_ROOT_FOLDER}/CMakeLists.txt" "${CMAKE_CURRENT_BINARY_DIR}/cmake/engine.json" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Collect all Find files that were added with ly_add_external_target_path @@ -303,7 +311,6 @@ function(ly_setup_cmake_install) endforeach() install(FILES ${additional_find_files} DESTINATION cmake/3rdParty - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all @@ -320,7 +327,6 @@ function(ly_setup_cmake_install) configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect @@ -340,7 +346,6 @@ function(ly_setup_cmake_install) ) install(FILES "${pal_builtin_file}" DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -362,7 +367,6 @@ endfunction() function(ly_copy source_file target_directory) file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) endfunction()" - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) unset(runtime_commands) @@ -408,7 +412,6 @@ endfunction()" list(REMOVE_DUPLICATES runtime_commands) list(JOIN runtime_commands " " runtime_commands_str) # the spaces are just to see the right identation in the cmake_install.cmake file install(CODE "${runtime_commands_str}" - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -427,7 +430,6 @@ function(ly_setup_others) install(DIRECTORY "${LY_ROOT_FOLDER}/${dir}" DESTINATION ${install_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE ) @@ -438,7 +440,6 @@ function(ly_setup_others) install(FILES ${o3de_scripts} DESTINATION ./scripts - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY @@ -446,7 +447,6 @@ function(ly_setup_others) ${LY_ROOT_FOLDER}/scripts/project_manager ${LY_ROOT_FOLDER}/scripts/o3de DESTINATION ./scripts - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} PATTERN "__pycache__" EXCLUDE PATTERN "CMakeLists.txt" EXCLUDE PATTERN "tests" EXCLUDE @@ -454,7 +454,6 @@ function(ly_setup_others) install(DIRECTORY "${LY_ROOT_FOLDER}/python" DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} REGEX "downloaded_packages" EXCLUDE REGEX "runtime" EXCLUDE ) @@ -463,19 +462,16 @@ function(ly_setup_others) install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/Registry DESTINATION ./${runtime_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY ${LY_ROOT_FOLDER}/Registry DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Engine Source Assets install(DIRECTORY ${LY_ROOT_FOLDER}/Assets DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Gem Source Assets and Registry @@ -495,7 +491,6 @@ function(ly_setup_others) # the "Assets" folder from being copied underneath the /Assets folder install(DIRECTORY ${gem_abs_assets_path} DESTINATION ${gem_assets_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endif() endforeach() @@ -511,7 +506,6 @@ function(ly_setup_others) get_filename_component(gem_relative_path ${gem_json_path} DIRECTORY) install(FILES ${gem_json_path} DESTINATION ${gem_relative_path} - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endforeach() @@ -519,14 +513,12 @@ function(ly_setup_others) install(DIRECTORY ${LY_ROOT_FOLDER}/Gems/Atom/Asset/ImageProcessingAtom/Config DESTINATION Gems/Atom/Asset/ImageProcessingAtom - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Templates install(DIRECTORY ${LY_ROOT_FOLDER}/Templates DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) # Misc @@ -535,7 +527,6 @@ function(ly_setup_others) ${LY_ROOT_FOLDER}/LICENSE.txt ${LY_ROOT_FOLDER}/README.md DESTINATION . - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() @@ -549,15 +540,12 @@ function(ly_setup_target_generator) ${LY_ROOT_FOLDER}/Code/LauncherUnified/LauncherProject.cpp ${LY_ROOT_FOLDER}/Code/LauncherUnified/StaticModules.in DESTINATION LauncherGenerator - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(DIRECTORY ${LY_ROOT_FOLDER}/Code/LauncherUnified/Platform DESTINATION LauncherGenerator - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) install(FILES ${LY_ROOT_FOLDER}/Code/LauncherUnified/FindLauncherGenerator.cmake DESTINATION cmake - COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} ) endfunction() From 99497672f4188479a92f45411ee66a09012c23ec Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 20:51:07 -0700 Subject: [PATCH 034/105] Fixed cmake platform files for non-Windows platforms --- .../Code/Source/Platform/Android/platform_android_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/Linux/platform_linux_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/Mac/platform_mac_files.cmake | 4 ++-- .../RPI/Code/Source/Platform/iOS/platform_ios_files.cmake | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake index 357d8f0381..83ddc410d6 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/platform_android_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Android.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Android.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake index 19be7951f6..99df861f9c 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Linux.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Linux.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake index bde67ff340..b1baca036e 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_Mac.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_Mac.h ) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake index 7f603e4bfd..3eae72e612 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/platform_ios_files.cmake @@ -10,6 +10,6 @@ # set(FILES - Atom_Feature_Traits_Platform.h - Atom_Feature_Traits_iOS.h + Atom_RPI_Traits_Platform.h + Atom_RPI_Traits_iOS.h ) From 9afd9b0992befcd19390cea11ec74bdb0080103a Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 20:58:22 -0700 Subject: [PATCH 035/105] Fixed PAL cmake files for non-Windows builds --- .../Code/Source/Platform/Android/PAL_android.cmake | 13 +++++++++++++ .../RPI/Code/Source/Platform/Linux/PAL_linux.cmake | 1 + .../Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake | 1 + .../Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake | 13 +++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake create mode 100644 Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/PAL_android.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake index e9a14ba928..4542e7c707 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/PAL_linux.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake index c060b8bbaa..f177b9dfb9 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/PAL_mac.cmake @@ -10,3 +10,4 @@ # set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..4542e7c707 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,13 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE) +set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE) From 5695681ed3518d3e7c4ab7ed3aad92c4bb01d422 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 21:03:41 -0700 Subject: [PATCH 036/105] [cpack_installer] fifth attempt to fix cpack selection --- scripts/build/Platform/Windows/installer_windows.cmd | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index 73d1a3d88c..c71b091de9 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -29,22 +29,25 @@ IF NOT EXIST "%WIX_TEMP%" ( REM Make sure we are using the CMake version of CPack and not the one that comes with chocolatey SET CPACK_PATH= IF "%LY_CMAKE_PATH%"=="" ( - FOR /F %%i in ('where cpack') DO ( + REM quote the paths from 'where' so we can properly tokenize ones in the list with spaces + FOR /F delims^=^"^ tokens^=1 %%i in ('where /F cpack') DO ( REM The cpack in chocolatey expects a number supplied with --version so it will error "%%i" --version > NUL IF !ERRORLEVEL!==0 ( SET "CPACK_PATH=%%i" + GOTO :run_cpack ) ) ) ELSE ( SET "CPACK_PATH=%LY_CMAKE_PATH%\cpack.exe" ) +:run_cpack ECHO [ci_build] "!CPACK_PATH!" --version "!CPACK_PATH!" --version IF ERRORLEVEL 1 ( ECHO [ci_build] CPack not found! - exit /b 1 + GOTO :popd_error ) ECHO [ci_build] "!CPACK_PATH!" -C %CONFIGURATION% From 091d6894cb928d51d61a6d026b6854ca304da47b Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Wed, 2 Jun 2021 22:02:10 -0700 Subject: [PATCH 037/105] Fixed non-Windows platform include --- .../RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h | 2 +- .../RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h index 27e0af7f35..60835c7025 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Android/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Android.h" +#include "Atom_RPI_Traits_Android.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h index 39c6a3e572..f7a51ddbf7 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Linux/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Linux.h" +#include "Atom_RPI_Traits_Linux.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h index 19816f2bd1..87bc2190f3 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/Mac/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_Mac.h" +#include "Atom_RPI_Traits_Mac.h" diff --git a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h index 4403d741dc..48fe26cc61 100644 --- a/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h +++ b/Gems/Atom/RPI/Code/Source/Platform/iOS/Atom_RPI_Traits_Platform.h @@ -11,4 +11,4 @@ */ #pragma once -#include "Atom_Feature_Traits_iOS.h" +#include "Atom_RPI_Traits_iOS.h" From 602dd01434848ad04e32123e1dbd6d0aeb717824 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 22:02:11 -0700 Subject: [PATCH 038/105] [cpack_installer] fixed typo in error message --- cmake/Packaging.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 1bcb251271..e2aa0f9348 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -54,7 +54,7 @@ endif() if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) message(FATAL_ERROR "The desired version of CMake to be included in the package is " - "is below the minium required version of CMake to run") + "is below the minimum required version of CMake to run") endif() # pull down the desired copy of CMake so it can be included in the package From 881c51dc9c9ff3caa1f4b404c91e9cbaf74bd6ab Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 2 Jun 2021 22:05:20 -0700 Subject: [PATCH 039/105] [cpack_installer] removed unnecessary explicit use of CMAKE_INSTALL_DEFAULT_COMPONENT_NAME --- cmake/Packaging.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e2aa0f9348..e5799c8ff1 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -101,7 +101,6 @@ endif() install(FILES ${_cmake_package_dest} DESTINATION ./Tools/Redistributables/CMake - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) # IMPORTANT: required to be included AFTER setting all property overrides From 328ced0059f90f66d740d97e2f50721c9724995f Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 07:24:30 -0700 Subject: [PATCH 040/105] [cpack_installer] replaced missing get_target_property hack and fixed a typo --- cmake/Platform/Common/Install_common.cmake | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index e0301bb914..7e6917287f 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -27,11 +27,8 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) # De-alias target name ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) - # get the component ID. if the property isn't set for the target, fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME - get_target_property(install_componet ${TARGET_NAME} INSTALL_COMPONENT) - if("${install_componet}" STREQUAL "install_componet-NOTFOUND") - unset(install_componet) - endif() + # get the component ID. if the property isn't set for the target, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + get_property(install_component TARGET ${TARGET_NAME} PROPERTY INSTALL_COMPONENT) # All include directories marked PUBLIC or INTERFACE will be installed. We dont use PUBLIC_HEADER because in order to do that # we need to set the PUBLIC_HEADER property of the target for all the headers we are exporting. After doing that, installing the @@ -47,7 +44,7 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) unset(current_public_headers) install(DIRECTORY ${include_directory} DESTINATION ${include_location}/${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} FILES_MATCHING PATTERN *.h PATTERN *.hpp @@ -74,13 +71,13 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) TARGETS ${TARGET_NAME} ARCHIVE DESTINATION ${archive_output_directory}/${PAL_PLATFORM_NAME}/$ - COMPONENT ${install_componet} + COMPONENT ${install_component} LIBRARY DESTINATION ${library_output_directory}/${PAL_PLATFORM_NAME}/$/${target_library_output_subdirectory} - COMPONENT ${install_componet} + COMPONENT ${install_component} RUNTIME DESTINATION ${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${target_runtime_output_subdirectory} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) # CMakeLists.txt file @@ -188,7 +185,7 @@ set_property(TARGET ${TARGET_NAME} file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) # Since a CMakeLists.txt could contain multiple targets, we generate it in a folder per target @@ -247,11 +244,11 @@ function(ly_setup_subdirectory absolute_target_source_dir) ) # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME - get_property(install_componet DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) + get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} - COMPONENT ${install_componet} + COMPONENT ${install_component} ) endfunction() From 8214706ff9ab2c49cfafc7164bb6dbe0d296112c Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 08:24:01 -0700 Subject: [PATCH 041/105] [cpack_installer] reworked how packaging is enabled for windows --- cmake/Platform/Windows/Packaging_windows.cmake | 10 ++-------- scripts/build/Platform/Windows/build_config.json | 3 +-- scripts/build/Platform/Windows/build_windows.cmd | 8 -------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 5210c24e7b..2fd281ad51 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -9,17 +9,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(LY_WIX_PATH "" CACHE PATH "Path to the WiX install path") - -if(LY_WIX_PATH) - file(TO_CMAKE_PATH ${LY_WIX_PATH} CPACK_WIX_ROOT) -elseif(DEFINED ENV{WIX}) - file(TO_CMAKE_PATH $ENV{WIX} CPACK_WIX_ROOT) -endif() +set(CPACK_WIX_ROOT "" CACHE PATH "Path to the WiX install path") if(CPACK_WIX_ROOT) if(NOT EXISTS ${CPACK_WIX_ROOT}) - message(FATAL_ERROR "Invalid path supplied for LY_WIX_PATH argument or WIX environment variable") + message(FATAL_ERROR "Invalid path supplied for CPACK_WIX_ROOT argument") endif() else() # early out as no path to WiX has been supplied effectively disabling support diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index b0d16f1fb6..fc4668de0a 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -314,8 +314,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE", - "CMAKE_INCLUDE_WIX": "True", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DCPACK_WIX_ROOT=\"!WIX!\"", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 799e6828b2..3e995e1905 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -38,14 +38,6 @@ IF NOT EXIST %TMP% ( REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 -IF "%CMAKE_INCLUDE_WIX%"=="True" ( - REM Explicitly enable wix via command line arg for forensic logging - SET CMAKE_OPTIONS=%CMAKE_OPTIONS% -DLY_WIX_PATH="%WIX%" -) ELSE ( - REM Disable implicit enabling of windows packing by clearing out the wix variable - SET WIX= -) - SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS% IF NOT EXIST CMakeCache.txt ( From cd619e14dc9b74f845cb4897780ea8aa6004f10b Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Thu, 3 Jun 2021 08:31:38 -0700 Subject: [PATCH 042/105] Allow script canvas users to send RPCs via entityId --- .../Source/AutoGen/AutoComponent_Common.jinja | 2 +- .../Source/AutoGen/AutoComponent_Source.jinja | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 61dcacaa94..05403a00ef 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -175,7 +175,7 @@ void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ //! {{ PropertyName }} Handler //! {{ Property.attrib['Description'] }} //! HandleOn {{ HandleOn }} -virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) = 0; +virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection, [[maybe_unused]] {{ ', [[maybe_unused]] '.join(paramDefines) }}) {} {% endif %} {% endmacro %} {# diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 917b1058a7..259f469020 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -368,6 +368,31 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo ->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) { self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); }) + ->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntity", [](AZ::EntityId id, {{ ', '.join(paramDefines) }}) { + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return; + } + + {{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>(); + if (!networkComponent) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) + return; + } + + {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); + if (!controller) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) + return; + } + + controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); + }) {% endif %} {% endcall %} {% endmacro %} From 5fbf587b9e34a88a7b26d8ccbde77f6ae9fa15a4 Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 10:50:27 -0500 Subject: [PATCH 043/105] Updating File Menu actions in test_Menus_FileMenuOptions_Work. Temporarily marking test as xfail due to LYN-4208 --- .../EditorScripts/Menus_FileMenuOptions.py | 10 ++++----- .../Gem/PythonTests/editor/test_Menus.py | 21 ++++++++++--------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index 3e174af2bd..734226a9d1 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -9,11 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ -""" -C24064528: The File menu options function normally -C16780778: The File menu options function normally-New view interaction Model enabled -""" - import os import sys @@ -54,7 +49,10 @@ class TestFileMenuOptions(EditorTestHelper): ("Save",), ("Save As",), ("Save Level Statistics",), - ("Project Settings", "Project Settings Tool"), + ("Edit Project Settings",), + ("Edit Platform Settings",), + ("New Project",), + ("Open Project",), ("Show Log File",), ("Resave All Slices",), ("Exit",), diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index c2da1343de..2b3fdcbdf3 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -7,8 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens or, if provided, by the license below or the license accompanying this file. Do not remove or modify any license notices. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -C16780783: Base Edit Menu Options (New Viewport Interaction Model) """ import os @@ -17,6 +15,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools.environment.process_utils as process_utils import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") @@ -33,6 +32,7 @@ class TestMenus(object): def setup_teardown(self, request, workspace, project, level): def teardown(): file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) + process_utils.kill_processes_named("o3de", ignore_extensions=True) # Kill ProjectManager windows request.addfinalizer(teardown) @@ -80,8 +80,7 @@ class TestMenus(object): expected_lines, cfg_args=[level], run_python="--runpython", - auto_test_mode=True, - timeout=log_monitor_timeout, + timeout=log_monitor_timeout ) @pytest.mark.test_case_id("C16780807") @@ -107,13 +106,13 @@ class TestMenus(object): "Menus_ViewMenuOptions.py", expected_lines, cfg_args=[level], - auto_test_mode=True, run_python="--runpython", - timeout=log_monitor_timeout, + timeout=log_monitor_timeout ) @pytest.mark.test_case_id("C16780778") @pytest.mark.SUITE_sandbox + @pytest.mark.xfail # LYN-4208 def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform): expected_lines = [ "New Level Action triggered", @@ -122,7 +121,10 @@ class TestMenus(object): "Save Action triggered", "Save As Action triggered", "Save Level Statistics Action triggered", - "Project Settings Tool Action triggered", + "Edit Project Settings Action triggered", + "Edit Platform Settings Action triggered", + "New Project Action triggered", + "Open Project Action triggered", "Show Log File Action triggered", "Resave All Slices Action triggered", "Exit Action triggered", @@ -135,7 +137,6 @@ class TestMenus(object): "Menus_FileMenuOptions.py", expected_lines, cfg_args=[level], - auto_test_mode=True, run_python="--runpython", - timeout=log_monitor_timeout, - ) \ No newline at end of file + timeout=log_monitor_timeout + ) From d56688e6cd9e620e384f8233431fa8180294da5a Mon Sep 17 00:00:00 2001 From: Tommy Walton <82672795+amzn-tommy@users.noreply.github.com> Date: Thu, 3 Jun 2021 09:41:31 -0700 Subject: [PATCH 044/105] Adding a file that was missed when merging PR 481 from 1.0->main. Also, updated the comment a bit (#912) --- .../RPI/Code/Source/RPI.Public/Culling.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index c64f08e4f8..c5e9d949aa 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -239,16 +239,26 @@ namespace AZ void CullingScene::RegisterOrUpdateCullable(Cullable& cullable) { - m_cullDataConcurrencyCheck.soft_lock(); + // Multiple threads can call RegisterOrUpdateCullable at the same time + // since the underlying visScene is thread safe, but if you're inserting or + // updating between BeginCulling and EndCulling, you'll get non-deterministic + // results depending on a race condition if you happen to update before or after + // the culling system starts Enumerating, so use soft_lock_shared here + m_cullDataConcurrencyCheck.soft_lock_shared(); m_visScene->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry); - m_cullDataConcurrencyCheck.soft_unlock(); + m_cullDataConcurrencyCheck.soft_unlock_shared(); } void CullingScene::UnregisterCullable(Cullable& cullable) { - m_cullDataConcurrencyCheck.soft_lock(); + // Multiple threads can call RegisterOrUpdateCullable at the same time + // since the underlying visScene is thread safe, but if you're inserting or + // updating between BeginCulling and EndCulling, you'll get non-deterministic + // results depending on a race condition if you happen to update before or after + // the culling system starts Enumerating, so use soft_lock_shared here + m_cullDataConcurrencyCheck.soft_lock_shared(); m_visScene->RemoveEntry(cullable.m_cullData.m_visibilityEntry); - m_cullDataConcurrencyCheck.soft_unlock(); + m_cullDataConcurrencyCheck.soft_unlock_shared(); } uint32_t CullingScene::GetNumCullables() const From eef5122ce54dc44f97d852b4bf11b9a6a6cfc51b Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Thu, 3 Jun 2021 11:56:20 -0500 Subject: [PATCH 045/105] [LYN-3008] Only register the Slice Relationship View when prefabs are disabled. --- .../ComponentEntityEditorPlugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp index 54ac71db16..ed21b5b6e9 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp @@ -179,11 +179,11 @@ ComponentEntityEditorPlugin::ComponentEntityEditorPlugin([[maybe_unused]] IEdito LyViewPane::EntityOutliner, LyViewPane::CategoryTools, outlinerOptions); - } - AzToolsFramework::ViewPaneOptions options; - options.preferedDockingArea = Qt::NoDockWidgetArea; - RegisterViewPane(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options); + AzToolsFramework::ViewPaneOptions options; + options.preferedDockingArea = Qt::NoDockWidgetArea; + RegisterViewPane(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options); + } RegisterModuleResourceSelectors(GetIEditor()->GetResourceSelectorHost()); From 5838975d626fc632d74f6069bfa329b5092ab062 Mon Sep 17 00:00:00 2001 From: mgwynn Date: Thu, 3 Jun 2021 13:29:51 -0400 Subject: [PATCH 046/105] Incorporating review comments. Minor formatting changes and changes to parameter descriptions. Removed try-catch for property modification and added creation of user_tags element if it does not exist but is modified through CLI --- scripts/o3de/o3de/project_properties.py | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py index 10153ff833..d8453a2c4f 100644 --- a/scripts/o3de/o3de/project_properties.py +++ b/scripts/o3de/o3de/project_properties.py @@ -21,22 +21,23 @@ def edit_project_props(proj_path, proj_name, new_origin, new_display, new_summary, new_icon, new_tag) -> int: proj_json = get_project_props(proj_name, proj_path) - try: - if new_origin and 'origin' in proj_json: - proj_json['origin'] = new_origin - if new_display and 'display_name' in proj_json: - proj_json['display_name'] = new_display - if new_summary and 'summary' in proj_json: - proj_json['summary'] = new_summary - if new_icon and 'icon_path' in proj_json: - proj_json['icon_path'] = new_icon - if new_tag and 'user_tags' in proj_json: - proj_json['user_tags'].append(new_tag) - except Exception as e: - logger.error(e) + if not proj_json: return 1 - manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path)/'project.json') + if new_origin: + proj_json['origin'] = new_origin + if new_display: + proj_json['display_name'] = new_display + if new_summary: + proj_json['summary'] = new_summary + if new_icon: + proj_json['icon_path'] = new_icon + if new_tag: + if 'user_tags' not in proj_json: + proj_json['user_tags'] = [] + proj_json['user_tags'].append(new_tag) + + manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json') return 0 def _edit_project_props(args: argparse) -> int: @@ -56,7 +57,7 @@ def add_parser_args(parser): help='The name of the project.') group = parser.add_argument_group('properties', 'arguments for modifying individual project properties.') group.add_argument('-po', '--project-origin', type=str, required=False, - help='Sets description or url for project origin.') + help='Sets description or url for project origin (such as project host, repository, owner...etc).') group.add_argument('-pd', '--project-display', type=str, required=False, help='Sets the project display name.') group.add_argument('-ps', '--project-summary', type=str, required=False, @@ -64,11 +65,11 @@ def add_parser_args(parser): group.add_argument('-pi', '--project-icon', type=str, required=False, help='Sets the path to the projects icon resource.') group.add_argument('-pt', '--project-tag', type=str, required=False, - help='Adds a tag to canonical user tags.') + help='Adds a tag to canonical user tags. These tags are intended for documentation and filtering.') parser.set_defaults(func=_edit_project_props) def add_args(subparsers) -> None: - enable_project_props_subparser = subparsers.add_parser('edit-project-props') + enable_project_props_subparser = subparsers.add_parser('edit-project-properties') add_parser_args(enable_project_props_subparser) def main(): @@ -79,4 +80,4 @@ def main(): sys.exit(ret) if __name__ == "__main__": - main() \ No newline at end of file + main() From c009e7d50bc47daa1a3a19c775d66c031c5b8a5f Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:42:20 -0700 Subject: [PATCH 047/105] ATOM-4782 [Material] Transparent pass is using StandardPBR_Forwardpass shader with incorrect SRG (#1103) Removed TransparentPassSrg until we have pbr shaders TransparentPassSrg --- .../Materials/Special/ShadowCatcher.azsl | 2 +- .../Assets/Passes/TransparentParent.pass | 2 +- .../Atom/Features/PBR/ForwardPassSrg.azsli | 1 + .../Features/PBR/TransparentPassSrg.azsli | 39 ------------------- .../atom_feature_common_asset_files.cmake | 1 - 5 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl index 942db3ed5f..4a228f076c 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl @@ -37,7 +37,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial } #include -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass index b278f2bcb4..a9db59c646 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass @@ -124,7 +124,7 @@ "DrawListSortType": "KeyThenReverseDepth", "PipelineViewTag": "MainCamera", "PassSrgAsset": { - "FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg" + "FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg" } } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli index 14cff21739..d9367f9d03 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli @@ -35,4 +35,5 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_tileLightData; StructuredBuffer m_lightListRemapped; + Texture2D m_linearDepthTexture; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli deleted file mode 100644 index d9367f9d03..0000000000 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli +++ /dev/null @@ -1,39 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include - -ShaderResourceGroup PassSrg : SRG_PerPass -{ - // [GFX TODO][ATOM-2012] adapt to multiple shadowmaps - Texture2DArray m_directionalLightShadowmap; - Texture2DArray m_directionalLightExponentialShadowmap; - Texture2DArray m_projectedShadowmaps; - Texture2DArray m_projectedExponentialShadowmap; - Texture2D m_brdfMap; - - Sampler LinearSampler - { - MinFilter = Linear; - MagFilter = Linear; - MipFilter = Linear; - AddressU = Clamp; - AddressV = Clamp; - AddressW = Clamp; - }; - - Texture2D m_tileLightData; - StructuredBuffer m_lightListRemapped; - Texture2D m_linearDepthTexture; -} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 359c0b9b20..a9ba765329 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -246,7 +246,6 @@ set(FILES ShaderLib/Atom/Features/PBR/Hammersley.azsli ShaderLib/Atom/Features/PBR/LightingOptions.azsli ShaderLib/Atom/Features/PBR/LightingUtils.azsli - ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli ShaderLib/Atom/Features/PBR/Lighting/EnhancedLighting.azsli ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli From bcef8856ff2143fa137078525d98d47ddc6fe348 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 10:51:13 -0700 Subject: [PATCH 048/105] [cpack_installer] minor wording fixes --- cmake/Packaging.cmake | 2 +- scripts/build/Platform/Windows/installer_windows.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index e5799c8ff1..3e23511fa1 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -54,7 +54,7 @@ endif() if(${CPACK_DESIRED_CMAKE_VERSION} VERSION_LESS ${CMAKE_MINIMUM_REQUIRED_VERSION}) message(FATAL_ERROR "The desired version of CMake to be included in the package is " - "is below the minimum required version of CMake to run") + "below the minimum required version of CMake to run") endif() # pull down the desired copy of CMake so it can be included in the package diff --git a/scripts/build/Platform/Windows/installer_windows.cmd b/scripts/build/Platform/Windows/installer_windows.cmd index c71b091de9..a6ce15d59e 100644 --- a/scripts/build/Platform/Windows/installer_windows.cmd +++ b/scripts/build/Platform/Windows/installer_windows.cmd @@ -20,7 +20,7 @@ IF NOT EXIST %OUTPUT_DIRECTORY% ( ) PUSHD %OUTPUT_DIRECTORY% -REM Override the temporary directory used by wix to the EBS volume +REM Override the temporary directory used by wix to the workspace SET "WIX_TEMP=!WORKSPACE!/temp/wix" IF NOT EXIST "%WIX_TEMP%" ( MKDIR "%WIX_TEMP%" From 8704b9233a699af223cfd0964bedd5271b79db45 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Thu, 3 Jun 2021 10:53:00 -0700 Subject: [PATCH 049/105] build fix --- .../Rendering/ThumbnailRendererSteps/InitializeStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index c35c33017a..8bf157e5f3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -12,7 +12,7 @@ #include -#include +#include #include From 54fdca353b9d3a1108e4a5dd2f63a23b171cdb3a Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:54:14 -0500 Subject: [PATCH 050/105] Fix editor axis gizmo text rendering above gizmo Add AzToolsFramework utility function to query the display scale for a viewport. Use new function to fix the text location on the axis gizmo. --- .../ViewportSelection/EditorSelectionUtil.cpp | 11 +++++++++++ .../ViewportSelection/EditorSelectionUtil.h | 3 +++ .../EditorTransformComponentSelection.cpp | 7 ++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp index 88b92e8c41..d0143c5517 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp @@ -124,4 +124,15 @@ namespace AzToolsFramework return cameraState; } + + float GetScreenDisplayScaling(const int viewportId) + { + float scaling = 1.0f; + ViewportInteraction::ViewportInteractionRequestBus::EventResult( + scaling, viewportId, + &ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor); + + return scaling; + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h index 9936fb9afd..e904277078 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h @@ -60,6 +60,9 @@ namespace AzToolsFramework /// Wrapper for EBus call to return the CameraState for a given viewport. AzFramework::CameraState GetCameraState(int viewportId); + /// Wrapper for EBus call to return the DPI scaling for a given viewport. + float GetScreenDisplayScaling(const int viewportId); + /// A utility to return the center of several points. /// Take several positions and store the min and max of each in /// turn - when all points have been added return the center/midpoint. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 91ac495e0e..5acfa5df59 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -3573,9 +3573,10 @@ namespace AzToolsFramework debugDisplay.SetLineWidth(1.0f); const float labelOffset = cl_viewportGizmoAxisLabelOffset; - const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize; - const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize; - const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize; + const float screenScale = GetScreenDisplayScaling(viewportId); + const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale; + const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale; + const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale; // draw the label of of each axis for the gizmo const float labelSize = cl_viewportGizmoAxisLabelSize; From 1bf8c599e3789999e2c33da5a2f82d77e48ea9df Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 27 May 2021 09:56:11 -0700 Subject: [PATCH 051/105] External Serialize Context for spawning This change makes it possible to provide a Serialize Context for spawning entities from spawnables. This also removes the need for the Serialize Context to be retrieved multiple times per frame. --- .../Spawnable/SpawnableEntitiesInterface.h | 54 +++++--- .../Spawnable/SpawnableEntitiesManager.cpp | 126 +++++++++--------- .../Spawnable/SpawnableEntitiesManager.h | 33 ++--- .../SpawnableEntitiesManagerTests.cpp | 24 +++- 4 files changed, 139 insertions(+), 98 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 27f45064b6..cfa1f3b464 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -21,6 +21,7 @@ namespace AZ { class Entity; + class SerializeContext; } namespace AzFramework @@ -171,6 +172,34 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; + struct SpawnEntitiesOptionalArgs + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + }; + + struct DespawnAllEntitiesOptionalArgs + { + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntityDespawnCallback m_completionCallback; + }; + + struct ReloadSpawnableOptionalArgs + { + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + ReloadSpawnableCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext { nullptr }; + }; + //! Interface definition to (de)spawn entities from a spawnable into the game world. //! //! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be @@ -197,40 +226,31 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made the function call. The returned list of entities contains all the newly - //! created entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) = 0; + EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. - //! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the newly - //! created entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnEntities( EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0; + SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. - //! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from - //! a different thread than the one that made this function call. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) = 0; - + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. - //! @param completionCallback Optional callback that's called when the entities have been reloaded. This can be called from - //! a different thread than the one that made this function call. The returned list of entities contains all the replacement - //! entities. + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) = 0; + ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 7b767d2a72..77106172f6 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -38,6 +38,10 @@ namespace AzFramework SpawnableEntitiesManager::SpawnableEntitiesManager() { + AZ::ComponentApplicationBus::BroadcastResult(m_defaultSerializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert( + m_defaultSerializeContext, "Failed to retrieve serialization context during construction of the Spawnable Entities Manager."); + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { AZ::u64 value = aznumeric_caster(m_highPriorityThreshold); @@ -47,53 +51,57 @@ namespace AzFramework } void SpawnableEntitiesManager::SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback, - EntitySpawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); SpawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); SpawnEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_entityIndices = AZStd::move(entityIndices); - queueEntry.m_completionCallback = AZStd::move(completionCallback); - queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback) + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); - queueEntry.m_completionCallback = AZStd::move(completionCallback); + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback) + ReloadSpawnableOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); ReloadSpawnableCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_spawnable = AZStd::move(spawnable); - queueEntry.m_completionCallback = AZStd::move(completionCallback); + queueEntry.m_serializeContext = + optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); QueueRequest(ticket, priority, AZStd::move(queueEntry)); } @@ -174,58 +182,57 @@ namespace AzFramework auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus { - AZStd::queue pendingRequestQueue; + // Process delayed requests first. + // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. + size_t delayedSize = queue.m_delayed.size(); + for (size_t i = 0; i < delayedSize; ++i) { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); + Requests& request = queue.m_delayed.front(); + bool result = AZStd::visit( + [this](auto&& args) -> bool + { + return ProcessRequest(args); + }, + request); + if (!result) + { + queue.m_delayed.emplace_back(AZStd::move(request)); + } + queue.m_delayed.pop_front(); } - if (!pendingRequestQueue.empty() || !queue.m_delayed.empty()) + // Process newly added requests. + while (true) { - AZ::SerializeContext* serializeContext = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); - AZ_Assert(serializeContext, "Failed to retrieve serialization context."); - - // Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete. - size_t delayedSize = queue.m_delayed.size(); - for (size_t i = 0; i < delayedSize; ++i) + AZStd::queue pendingRequestQueue; { - Requests& request = queue.m_delayed.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool - { - return ProcessRequest(args, *serializeContext); - }, request); - if (!result) - { - queue.m_delayed.emplace_back(AZStd::move(request)); - } - queue.m_delayed.pop_front(); + AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); + queue.m_pendingRequest.swap(pendingRequestQueue); } - do + if (!pendingRequestQueue.empty()) { while (!pendingRequestQueue.empty()) { Requests& request = pendingRequestQueue.front(); - bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool + bool result = AZStd::visit( + [this](auto&& args) -> bool { - return ProcessRequest(args, *serializeContext); - }, request); + return ProcessRequest(args); + }, + request); if (!result) { queue.m_delayed.emplace_back(AZStd::move(request)); } pendingRequestQueue.pop(); } - - // Spawning entities can result in more entities being queued to spawn. Repeat spawning until the queue is - // empty to avoid a chain of entity spawning getting dragged out over multiple frames. - { - AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex); - queue.m_pendingRequest.swap(pendingRequestQueue); - } - } while (!pendingRequestQueue.empty()); - } + } + else + { + break; + } + }; return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft; } @@ -267,7 +274,7 @@ namespace AzFramework &entityTemplate, templateToCloneEntityIdMap, &serializeContext); } - bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) @@ -296,7 +303,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); @@ -347,7 +354,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) @@ -371,7 +378,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entitiesToSpawn[index]; - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); + AZ::Entity* clone = request.m_serializeContext->CloneObject(&entityTemplate); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); clone->SetId(AZ::Entity::MakeId()); @@ -413,8 +420,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request, - [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -447,7 +453,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) { Ticket& ticket = *request.m_ticket; AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(), @@ -488,7 +494,7 @@ namespace AzFramework { const AZ::Entity& entityTemplate = *entities[i]; - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); @@ -502,7 +508,7 @@ namespace AzFramework for (size_t index : ticket.m_spawnedEntityIndices) { ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], serializeContext) : nullptr); + index < entitiesSize ? SpawnSingleEntity(*entities[index], *request.m_serializeContext) : nullptr); } } ticket.m_spawnable = AZStd::move(request.m_spawnable); @@ -525,7 +531,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -541,7 +547,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -560,7 +566,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -580,7 +586,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request) { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -599,7 +605,7 @@ namespace AzFramework } } - bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext) + bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request) { if (request.m_requestId == request.m_ticket->m_currentRequestId) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index afffdab8b5..b40ec20aa3 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -57,19 +57,16 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - EntityPreInsertionCallback preInsertionCallback = {}, - EntitySpawnCallback completionCallback = {}) override; + SpawnEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) override; + EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void ReloadSpawnable( EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableCallback completionCallback = {}) override; + ReloadSpawnableOptionalArgs optionalArgs = {}) override; void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; void ListIndicesAndEntities( @@ -105,6 +102,7 @@ namespace AzFramework { EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -114,6 +112,7 @@ namespace AzFramework AZStd::vector m_entityIndices; EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -129,6 +128,7 @@ namespace AzFramework { AZ::Data::Asset m_spawnable; ReloadSpawnableCallback m_completionCallback; + AZ::SerializeContext* m_serializeContext; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; @@ -191,15 +191,15 @@ namespace AzFramework AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext); - bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext); + bool ProcessRequest(SpawnAllEntitiesCommand& request); + bool ProcessRequest(SpawnEntitiesCommand& request); + bool ProcessRequest(DespawnAllEntitiesCommand& request); + bool ProcessRequest(ReloadSpawnableCommand& request); + bool ProcessRequest(ListEntitiesCommand& request); + bool ProcessRequest(ListIndicesEntitiesCommand& request); + bool ProcessRequest(ClaimEntitiesCommand& request); + bool ProcessRequest(BarrierCommand& request); + bool ProcessRequest(DestroyTicketCommand& request); Queue m_highPriorityQueue; Queue m_regularPriorityQueue; @@ -207,6 +207,7 @@ namespace AzFramework AZ::Event> m_onSpawnedEvent; AZ::Event> m_onDespawnedEvent; + AZ::SerializeContext* m_defaultSerializeContext { nullptr }; //! The threshold used to determine if a request goes in the regular (if bigger than the value) or high priority queue (if smaller //! or equal to this value). The starting value of 64 is chosen as it's between default values SpawnablePriority_High and //! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 484b7f46d7..7a2e7f614b 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -104,7 +104,9 @@ namespace UnitTest { spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(NumEntities, spawnedEntitiesCount); @@ -305,8 +307,14 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + + AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -333,8 +341,14 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback)); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback)); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(defaultCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + + AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); + m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); From 8c541b5205dd7605965e9719df6bfcfc1470f52a Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 1 Jun 2021 16:11:55 -0700 Subject: [PATCH 052/105] Moved default values to object in the Spawnable Entities Interface --- .../Spawnable/SpawnableEntitiesContainer.cpp | 11 ++-- .../Spawnable/SpawnableEntitiesInterface.h | 53 +++++++++++++------ .../Spawnable/SpawnableEntitiesManager.cpp | 37 +++++++------ .../Spawnable/SpawnableEntitiesManager.h | 22 ++++---- .../SpawnableEntitiesManagerTests.cpp | 26 +++++---- .../Libraries/Spawning/SpawnNodeable.cpp | 6 ++- 6 files changed, 88 insertions(+), 67 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp index 808de74e71..a945ea4edc 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp @@ -38,20 +38,20 @@ namespace AzFramework void SpawnableEntitiesContainer::SpawnAllEntities() { AZ_Assert(m_threadData, "Calling SpawnAllEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) { AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->SpawnEntities( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(entityIndices)); + m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices)); } void SpawnableEntitiesContainer::DespawnAllEntities() { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default); + SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } void SpawnableEntitiesContainer::Reset(AZ::Data::Asset spawnable) @@ -69,7 +69,6 @@ namespace AzFramework SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [threadData = m_threadData](EntitySpawnTicket::Id) mutable { threadData.reset(); @@ -88,7 +87,6 @@ namespace AzFramework AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->Barrier( m_threadData->m_spawnedEntitiesTicket, - SpawnablePriority_Default, [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) { callback(generation); @@ -115,7 +113,6 @@ namespace AzFramework AZ_Assert(m_threadData, "SpawnableEntitiesContainer is monitoring a spawnable, but doesn't have the associated data."); AZ_TracePrintf("Spawnables", "Reloading spawnable '%s'.\n", replacementAsset.GetHint().c_str()); - SpawnableEntitiesInterface::Get()->ReloadSpawnable( - m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(replacementAsset)); + SpawnableEntitiesInterface::Get()->ReloadSpawnable(m_threadData->m_spawnedEntitiesTicket, AZStd::move(replacementAsset)); } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index cfa1f3b464..5d7ce79641 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -172,7 +172,7 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; - struct SpawnEntitiesOptionalArgs + struct SpawnEntitiesOptionalArgs final { //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. @@ -182,22 +182,46 @@ namespace AzFramework EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; }; - struct DespawnAllEntitiesOptionalArgs + struct DespawnAllEntitiesOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that //! made the function call. The returned list of entities contains all the newly created entities. EntityDespawnCallback m_completionCallback; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; }; - struct ReloadSpawnableOptionalArgs + struct ReloadSpawnableOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that //! made the function call. The returned list of entities contains all the newly created entities. ReloadSpawnableCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority { SpawnablePriority_Default }; + }; + + struct ListEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct ClaimEntitiesOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + + struct BarrierOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; }; //! Interface definition to (de)spawn entities from a spawnable into the game world. @@ -225,38 +249,34 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. - //! @param priority The priority at which this call will be executed. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs virtual void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. - virtual void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) = 0; + virtual void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket with their spawnable index. //! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity //! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return @@ -267,20 +287,21 @@ namespace AzFramework //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities and indices on. virtual void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) = 0; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. - virtual void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) = 0; + virtual void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. //! @param ticket The ticket to monitor. //! @param priority The priority at which this call will be executed. //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. - virtual void Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) = 0; + virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; //! Register a handler for OnSpawned events. virtual void AddOnSpawnedHandler(AZ::Event>::Handler& handler) = 0; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 77106172f6..c11ce94bb2 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -50,8 +50,7 @@ namespace AzFramework } } - void SpawnableEntitiesManager::SpawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); @@ -61,11 +60,11 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); @@ -76,23 +75,21 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized."); DespawnAllEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs) + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); @@ -102,10 +99,11 @@ namespace AzFramework queueEntry.m_serializeContext = optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) + void SpawnableEntitiesManager::ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -113,11 +111,11 @@ namespace AzFramework ListEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized."); @@ -125,10 +123,11 @@ namespace AzFramework ListIndicesEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) + void SpawnableEntitiesManager::ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs) { AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized."); @@ -136,10 +135,10 @@ namespace AzFramework ClaimEntitiesCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_listCallback = AZStd::move(listCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } - void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) + void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs) { AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use."); AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized."); @@ -147,7 +146,7 @@ namespace AzFramework BarrierCommand queueEntry; queueEntry.m_ticketId = ticket.GetId(); queueEntry.m_completionCallback = AZStd::move(completionCallback); - QueueRequest(ticket, priority, AZStd::move(queueEntry)); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event>::Handler& handler) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index b40ec20aa3..e6db19557f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -57,23 +57,21 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector entityIndices, - SpawnEntitiesOptionalArgs optionalArgs = {}) override; - void DespawnAllEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; - + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void ReloadSpawnable( - EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset spawnable, - ReloadSpawnableOptionalArgs optionalArgs = {}) override; + EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override; - void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override; + void ListEntities( + EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; void ListIndicesAndEntities( - EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) override; - void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) override; + EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; + void ClaimEntities( + EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override; - void Barrier(EntitySpawnTicket& spawnInfo, SpawnablePriority priority, BarrierCallback completionCallback) override; + void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; void AddOnSpawnedHandler(AZ::Event>::Handler& handler) override; void AddOnDespawnedHandler(AZ::Event>::Handler& handler) override; diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 7a2e7f614b..8637684b3c 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -106,7 +106,7 @@ namespace UnitTest }; AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(NumEntities, spawnedEntitiesCount); @@ -116,7 +116,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -130,7 +130,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {}); + m_manager->SpawnEntities(ticket, {/* Deliberate empty list of indices. */}); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -144,7 +144,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default); + m_manager->DespawnAllEntities(ticket); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -158,7 +158,7 @@ namespace UnitTest { { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset); + m_manager->ReloadSpawnable(ticket, *m_spawnableAsset); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -185,7 +185,7 @@ namespace UnitTest spawnedEntitiesCount += entities.size(); }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(*m_ticket); m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -230,7 +230,7 @@ namespace UnitTest } }; - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default); + m_manager->SpawnAllEntities(*m_ticket); m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -309,11 +309,13 @@ namespace UnitTest AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); - m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs)); m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | @@ -343,11 +345,13 @@ namespace UnitTest AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(optionalArgs)); + optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); - m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, AZStd::move(highPriortyOptionalArgs)); + highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs)); m_manager->ProcessQueue( AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High | diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index ad53236108..39afa46a19 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -134,7 +134,9 @@ namespace ScriptCanvas::Nodeables::Spawning m_spawnBatchSizes.push_back(view.size()); }; - AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities( - m_spawnTicket, AzFramework::SpawnablePriority_Default, preSpawnCB, spawnCompleteCB); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCB); + optionalArgs.m_completionCallback = AZStd::move(spawnCompleteCB); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, AZStd::move(optionalArgs)); } } From 1e7ac6094982b78a262da2989507fcbf0ab0d566 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:49:25 -0700 Subject: [PATCH 053/105] Reintroduced spawning multiple instances of the same entity The following was changed: - The remapper in AZ::IdUtils now has an additional argument to tell it what to do when it encounters the same source entity id. The original behavior of ignoring the new entity id and returning the first occurrence is the default. The alternative behavior is to store the last known entity id and return that instead. - Split the optional arguments for SpawnAllEntities and SpawnEntities. - SpawnEntities now has an option to continue with the entity mapping from a previous spawn call or to start with a fresh mapping. The latter is the default as the former will come at a performance cost since the mapping table has to be reconstructed. - Entities spawned using SpawnEntities and ReloadEntities now also get the correct entity mapping applied. - Added several new unit tests to cover most of the new functionality. - Fixed some places where the older API version was still called. --- .../AzCore/AzCore/Serialization/IdUtils.h | 28 +- .../AzCore/AzCore/Serialization/IdUtils.inl | 16 +- .../Spawnable/SpawnableEntitiesInterface.h | 23 +- .../Spawnable/SpawnableEntitiesManager.cpp | 111 ++++---- .../Spawnable/SpawnableEntitiesManager.h | 14 +- .../SpawnableEntitiesManagerTests.cpp | 254 +++++++++++++++++- .../Pipeline/NetBindMarkerComponent.cpp | 6 +- .../Libraries/Spawning/SpawnNodeable.cpp | 2 +- 8 files changed, 362 insertions(+), 92 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h index a571379883..98959fe4bd 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h @@ -28,7 +28,13 @@ namespace AZ { namespace IdUtils { - template + /** + * \param AllowDuplicates - If true allows the same id to be registered multiple time, + with the newer value overwriting the stored value. If false, duplicates are not allowed and + the first stored value is kept.The default is false. + */ + + template struct Remapper { /** @@ -138,14 +144,18 @@ namespace AZ * \param context - The serialize context for enumerating the @classPtr elements */ template - static void GenerateNewIdsAndFixRefs(T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) + static void GenerateNewIdsAndFixRefs( + T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr) { if (!context) { AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext); if (!context) { - AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!"); + AZ_Error( + "Serialization", false, + "No serialize context provided! Failed to get component application default serialize context! ComponentApp is " + "not started or input serialize context should not be null!"); return; } } @@ -156,8 +166,16 @@ namespace AZ { if (idGenerator) { - auto it = newIdMap.emplace(originalId, idGenerator()); - return it.first->second; + if constexpr(AllowDuplicates) + { + auto it = newIdMap.insert_or_assign(originalId, idGenerator()); + return it.first->second; + } + else + { + auto it = newIdMap.emplace(originalId, idGenerator()); + return it.first->second; + } } return originalId; } diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl index 01617cf5aa..0dc87dfe18 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.inl @@ -30,8 +30,10 @@ namespace AZ bool m_isModifiedContainer; }; - template - unsigned int Remapper::RemapIds(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, AZ::SerializeContext* context, bool replaceId) + template + unsigned int Remapper::RemapIds( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdMapper& mapper, + AZ::SerializeContext* context, bool replaceId) { if (!context) { @@ -152,16 +154,18 @@ namespace AZ return replaced; } - template - unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) + template + unsigned int Remapper::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/) { unsigned int replaced = RemapIds(classPtr, classUuid, mapper, context, true); replaced += RemapIds(classPtr, classUuid, mapper, context, false); return replaced; } - template - unsigned int Remapper::RemapIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, AZ::SerializeContext* context) + template + unsigned int Remapper::RemapIdsAndIdRefs( + void* classPtr, const AZ::Uuid& classUuid, const typename Remapper::IdReplacer& mapper, + AZ::SerializeContext* context) { if (!context) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 5d7ce79641..2ad1db60a4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -172,7 +172,7 @@ namespace AzFramework using ClaimEntitiesCallback = AZStd::function; using BarrierCallback = AZStd::function; - struct SpawnEntitiesOptionalArgs final + struct SpawnAllEntitiesOptionalArgs final { //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. @@ -186,6 +186,25 @@ namespace AzFramework SpawnablePriority m_priority { SpawnablePriority_Default }; }; + struct SpawnEntitiesOptionalArgs final + { + //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This + //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. + EntityPreInsertionCallback m_preInsertionCallback; + //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that + //! made the function call. The returned list of entities contains all the newly created entities. + EntitySpawnCallback m_completionCallback; + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + AZ::SerializeContext* m_serializeContext{ nullptr }; + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + //! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this + //! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be + //! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact + //! performance, especially if a large number of entities are present on a ticket. + bool m_referencePreviouslySpawnedEntities{ false }; + }; + struct DespawnAllEntitiesOptionalArgs final { //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that @@ -250,7 +269,7 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs - virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index c11ce94bb2..a482c1d4f9 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -50,7 +50,7 @@ namespace AzFramework } } - void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs) + void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized."); @@ -75,6 +75,7 @@ namespace AzFramework optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext; queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback); + queueEntry.m_referencePreviouslySpawnedEntities = optionalArgs.m_referencePreviouslySpawnedEntities; QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } @@ -256,21 +257,11 @@ namespace AzFramework } } - AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext) - { - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); - - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone); - return clone; - } - AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext) + EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) { - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneEntityIdMap, &serializeContext); + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneMap, &serializeContext); } bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) @@ -297,13 +288,9 @@ namespace AzFramework spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); spawnedEntities.emplace_back(clone); @@ -311,16 +298,8 @@ namespace AzFramework } // loadAll is true if every entity has been spawned only once - if (spawnedEntities.size() == entitiesToSpawnSize) - { - ticket.m_loadAll = true; - } - else - { - // Case where there were already spawns from a previous request - ticket.m_loadAll = false; - } - + ticket.m_loadAll = (spawnedEntities.size() == entitiesToSpawnSize); + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { @@ -329,11 +308,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. if (request.m_completionCallback) @@ -360,14 +338,34 @@ namespace AzFramework { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; + AZ_Assert( + spawnedEntities.size() == spawnedEntityIndices.size(), + "The indices for the spawned entities has gone out of sync with the entities."); - // Keep track how many entities there were in the array initially + // Keep track of how many entities there were in the array initially size_t spawnedEntitiesInitialCount = spawnedEntities.size(); // These are 'template' entities we'll be cloning from const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = request.m_entityIndices.size(); + // Reconstruct the template to entity mapping. + EntityIdMap templateToCloneEntityIdMap; + if (!request.m_referencePreviouslySpawnedEntities) + { + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); + } + else + { + templateToCloneEntityIdMap.reserve(spawnedEntitiesInitialCount + entitiesToSpawnSize); + SpawnableConstIndexEntityContainerView indexEntityView( + spawnedEntities.begin(), spawnedEntityIndices.begin(), spawnedEntities.size()); + for (auto& entry : indexEntityView) + { + templateToCloneEntityIdMap.insert_or_assign(entitiesToSpawn[entry.GetIndex()]->GetId(), entry.GetEntity()->GetId()); + } + } + spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); @@ -375,15 +373,11 @@ namespace AzFramework { if (index < entitiesToSpawn.size()) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[index]; - - AZ::Entity* clone = request.m_serializeContext->CloneObject(&entityTemplate); + AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[index], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); spawnedEntities.push_back(clone); spawnedEntityIndices.push_back(index); - } } ticket.m_loadAll = false; @@ -396,11 +390,10 @@ namespace AzFramework } // Add to the game context, now the entities are active - AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(), - [](AZ::Entity* entity) + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity); - }); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } if (request.m_completionCallback) { @@ -475,39 +468,43 @@ namespace AzFramework // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); + + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + if (ticket.m_loadAll) { // The new spawnable may have a different number of entities and since the intent of the user was - // to load every, simply start over. + // to spawn every entity, simply start over. ticket.m_spawnedEntityIndices.clear(); - size_t entitiesToSpawnSize = entities.size(); - - // Map keeps track of ids from template (spawnable) to clone (instance) - // Allowing patch ups of fields referring to entityIds outside of a given entity - EntityIdMap templateToCloneEntityIdMap; templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entities[i]; - - AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, *request.m_serializeContext); - + AZ::Entity* clone = CloneSingleEntity(*entities[i], templateToCloneEntityIdMap, *request.m_serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - ticket.m_spawnedEntities.emplace_back(clone); + ticket.m_spawnedEntities.push_back(clone); ticket.m_spawnedEntityIndices.push_back(i); } } else { size_t entitiesSize = entities.size(); + templateToCloneEntityIdMap.reserve(entitiesSize); for (size_t index : ticket.m_spawnedEntityIndices) { - ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], *request.m_serializeContext) : nullptr); + // It's possible for the new spawnable to have a different number of entities, so guard against this. + // It's also possible that the entities have moved within the spawnable to a new index. This can't be + // detected and will result in the incorrect entities being spawned. + if (index < entitiesSize) + { + AZ::Entity* clone = CloneSingleEntity(*entities[index], templateToCloneEntityIdMap, *request.m_serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + ticket.m_spawnedEntities.push_back(clone); + } } } ticket.m_spawnable = AZStd::move(request.m_spawnable); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index e6db19557f..638559f3f1 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -37,7 +37,7 @@ namespace AzFramework AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0); using EntityIdMap = AZStd::unordered_map; - + enum class CommandQueueStatus : bool { HasCommandsLeft, @@ -57,7 +57,7 @@ namespace AzFramework // The following functions are thread safe // - void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; @@ -114,6 +114,7 @@ namespace AzFramework Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; + bool m_referencePreviouslySpawnedEntities; }; struct DespawnAllEntitiesCommand { @@ -183,12 +184,9 @@ namespace AzFramework CommandQueueStatus ProcessQueue(Queue& queue); - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, - AZ::SerializeContext& serializeContext); - - AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); - + AZ::Entity* CloneSingleEntity( + const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext); + bool ProcessRequest(SpawnAllEntitiesCommand& request); bool ProcessRequest(SpawnEntitiesCommand& request); bool ProcessRequest(DespawnAllEntitiesCommand& request); diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 8637684b3c..32ad7d8ea9 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace UnitTest @@ -81,6 +82,42 @@ namespace UnitTest } } + void CreateRecursiveHierarchy() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + AZ::EntityId parent; + for (size_t i=0; i& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + parent = entity->GetId(); + } + } + + void CreateSingleParent() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + size_t numElements = entities.size(); + if (numElements > 0) + { + AZ::EntityId parent = entities[0]->GetId(); + for (size_t i = 0; i < numElements; ++i) + { + AZStd::unique_ptr& entity = entities[i]; + auto component = entity->CreateComponent(); + if (i > 0) + { + component->SetParent(parent); + } + } + } + } + protected: AZ::Data::Asset* m_spawnableAsset { nullptr }; AzFramework::SpawnableEntitiesManager* m_manager { nullptr }; @@ -104,7 +141,7 @@ namespace UnitTest { spawnedEntitiesCount += entities.size(); }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); @@ -112,6 +149,37 @@ namespace UnitTest EXPECT_EQ(NumEntities, spawnedEntitiesCount); } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SetParentOnSpawnedEntities_LineageIsPreserved) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateRecursiveHierarchy(); + + auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parentId; + bool isFirst = true; + for (const AZ::Entity* entity : entities) + { + if (!isFirst) + { + auto transform = entity->GetTransform(); + ASSERT_NE(nullptr, transform); + EXPECT_EQ(parentId, transform->GetParentId()); + } + else + { + isFirst = false; + } + parentId = entity->GetId(); + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash) { { @@ -126,6 +194,170 @@ namespace UnitTest // SpawnEntities // + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_Call_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SpawnTheSameEntity_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 1; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 0 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_MultipleSpawns_AllEntitiesSpawned) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = + [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForNewBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it != entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = false; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForContinuedBatch_AllPointToLatestParent) + { + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + CreateSingleParent(); + + AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector parents; + + auto callback = + [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + AZ::EntityId parent = (*entities.begin())->GetId(); + parents.push_back(parent); + auto it = entities.begin(); + ++it; // Skip the first as that is the parent. + for (; it!=entities.end(); ++it) + { + AZ::TransformInterface* transform = (*it)->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + optionalArgs.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, indices, optionalArgs); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_NE(parents[0], parents[1]); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedAcrossBatches_AllPointToLatestParent) + { + FillSpawnable(4); + CreateSingleParent(); + + // Spawn a regular batch but with two parents and store the id of the last entity. This will the parent for the next batch. + AZ::EntityId parent; + auto getParent = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + ASSERT_NE(entities.begin(), entities.end()); + parent = (*AZStd::prev(entities.end()))->GetId(); + }; + + AzFramework::SpawnEntitiesOptionalArgs optionalArgsFirstBatch; + optionalArgsFirstBatch.m_completionCallback = AZStd::move(getParent); + optionalArgsFirstBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {0, 1, 2, 3, 0}, AZStd::move(optionalArgsFirstBatch)); + + // Next, spawn all the entities that have a reference to the parent that was just stored. + auto parentCheck = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + for (auto& it : entities) + { + AZ::TransformInterface* transform = it->GetTransform(); + ASSERT_NE(nullptr, transform); + ASSERT_EQ(parent, transform->GetParentId()); + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch; + optionalArgsSecondBatch.m_completionCallback = AZStd::move(parentCheck); + optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = true; + m_manager->SpawnEntities(*m_ticket, {1, 2, 3}, AZStd::move(optionalArgsSecondBatch)); + + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash) { { @@ -186,7 +418,7 @@ namespace UnitTest }; m_manager->SpawnAllEntities(*m_ticket); - m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -199,7 +431,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -231,7 +463,7 @@ namespace UnitTest }; m_manager->SpawnAllEntities(*m_ticket); - m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback)); m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_TRUE(allValidEntityIds); @@ -244,7 +476,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ListIndicesAndEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -260,7 +492,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->ClaimEntities(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -276,7 +508,7 @@ namespace UnitTest { AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset); - m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback)); + m_manager->Barrier(ticket, AZStd::move(callback)); } m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } @@ -307,12 +539,12 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); - AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs)); @@ -343,12 +575,12 @@ namespace UnitTest defaultPriorityCallId = callCounter++; }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(defaultCallback); optionalArgs.m_priority = AzFramework::SpawnablePriority_Default; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); - AzFramework::SpawnEntitiesOptionalArgs highPriortyOptionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs; highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback); highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High; m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs)); diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp index c93c09cfbc..bd1cf40da8 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp @@ -81,8 +81,10 @@ namespace Multiplayer }; m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, AzFramework::SpawnablePriority_Default, { m_netEntityIndex }, preInsertionCallback); + m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); } } @@ -90,7 +92,7 @@ namespace Multiplayer { if(m_netSpawnTicket.IsValid()) { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket, AzFramework::SpawnablePriority_Default); + AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index 39afa46a19..a7875c2615 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -134,7 +134,7 @@ namespace ScriptCanvas::Nodeables::Spawning m_spawnBatchSizes.push_back(view.size()); }; - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCB); optionalArgs.m_completionCallback = AZStd::move(spawnCompleteCB); AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, AZStd::move(optionalArgs)); From ba02652e6377a022b7588a1c2f59d38063a0646a Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:59:11 -0700 Subject: [PATCH 054/105] [LYN-4200] Fail and log error if required aws config file is not found (#1099) --- .../manager/configuration_manager.py | 7 +- .../resource_mapping_tool.py | 11 +- .../style/editormainwindow_resources.py | 2362 +++++++++-------- .../ResourceMappingTool/utils/aws_utils.py | 13 +- 4 files changed, 1261 insertions(+), 1132 deletions(-) diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py index b679921196..fc188582c7 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py @@ -48,7 +48,8 @@ class ConfigurationManager(object): def configuration(self, new_configuration: ConfigurationManager) -> None: self._configuration = new_configuration - def setup(self, config_path: str) -> None: + def setup(self, config_path: str) -> bool: + result: bool = True logger.info("Setting up default configuration ...") try: normalized_config_path: str = file_utils.normalize_file_path(config_path); @@ -63,5 +64,7 @@ class ConfigurationManager(object): self._configuration.account_id = aws_utils.get_default_account_id() self._configuration.region = aws_utils.get_default_region() except (RuntimeError, FileNotFoundError) as e: - logger.exception(e) + logger.error(e) + result = False logger.debug(self._configuration) + return result diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index e99bf5d441..6a56491b24 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -74,11 +74,18 @@ if __name__ == "__main__": logger.warning("Failed to load style sheet for resource mapping tool") logger.info("Initializing boto3 default session ...") - aws_utils.setup_default_session(arguments.profile) + try: + aws_utils.setup_default_session(arguments.profile) + except RuntimeError as error: + logger.error(error) + environment_utils.cleanup_qt_environment() + exit(-1) logger.info("Initializing configuration manager ...") configuration_manager: ConfigurationManager = ConfigurationManager() - configuration_manager.setup(arguments.config_path) + if not configuration_manager.setup(arguments.config_path): + environment_utils.cleanup_qt_environment() + exit(-1) logger.info("Initializing thread manager ...") thread_manager: ThreadManager = ThreadManager() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py index fb819cac34..67181fa36d 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py @@ -3649,968 +3649,1082 @@ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ Z\x80d\xdf%\x00\x7f\x12T\x1b\x97qJ\x10\x92\xa7\ \x22:BG\x84z\xfa\x9d{\x88\xac\x1d\xf5-\x8f\xc3\ r\xe1\x95\x00\x00\x00\x00IEND\xaeB`\x82\ -\x00\x00;\xfb\ +\x00\x00C\x13\ \x00\ -\x01\xa2\x08x\x9c\xed]\x09\x5cL\xdf\x17\x7f-\xb4 \ -\xc9\xde\xa2\xc5RD%\x7f\x89HB\x96(;E\x14\ -\xd1\x0fQ\xb4\x92(B\xd6V*d/E*[Q\ -hAE\x08\xadZ\x94\x92\xd4\xb4\xafS\xcd\xf4\xfe\xf7\ -\xbey\xed\x13M\xcd\xd4\xc4\xdc\xcf\xe7+\xaff\xde;\ -\xf7\x9e\xfb\xce=\xf7l\x17A\xd8\x90\xfe\x08ll\x88\ -\x18rI\x18Av\x80\xff\xdb\xd8P\xae\xa5\xb8\xd9\x10\ -D\x04ATT\xf0kU\x04\x91\x1a\xcf\x86\xc8\xc9Q\ -\xae}\xc6#\xc8\x0a\x03\xf0?1\xfc\x9a\x1fA\x88g\ -\xd9\x10~~\xca\xf5\x7f\x9c\x08r\xdd\x93\x0d9\xb5B\ -c\xd1 ^A^p\xebAK\x16/X\x05\xff\x0a\ -\xc1\x0d\x1f\xbdi\xbf\x0fx\xa6\xb0\xde\x92\x05\xf3\xd6X\ -\xa6\x16e\x1c\xb8\xe2\x9a\xe8YR\xa69E\xe7E\xc0\ -\x0a[\xb5\xc9*Zk9\xfb\xb1\x9f\x97\xf2\xd9\xf6\xfa\ -\xbf\xb3[\xcfN\x10/\x1eU5)w\x94\xe4\x0c\x0f\ -\xb9%\x13.]~\x9b%\xad]p\xc1naN\x22\ -\xff\xca[Q*\xe3.}\x9e\xa72o\xdeW\xd7\xbd\ -*\xb6\xec;\x9f\x5c\xcf\xb7\xf6\xf5\xcfmX\x99\xb8A\ -\xc5(qm\xdeL\xcd\xc4\x0a\xab\xb0\xcd\xa4\xb1\xab\xcc\ -\xae\xde)8D0\x8c\x9b\xa90\xf9u\xce\xa0\x1f\x1c\ -96\xabm\x87\x84\x1bg\xe9\xeb\xaaD\x8a\x8e\x5c\xe1\ -\x17\xea\xa7zJM\x8c[X\x83\xf8H4e@!\ -\x9br\x7fs\xa2u\xf1\xba\x8c~\xd3O/\xb7`[\ -Sf\xc9\xf1,|\x9c\xee,\xb9\xd1\xee\xc8Y\xce\xc2\ -\x90\xd8\x09\xcb\xa6\xb0\x17\xf6\x0fB\x84l\x9f\x84O\x08\ -\x8b\xcb\xf0\x0f\xd5\x9d\xa4\xbf\x1c\xb1|\xce&\xf4`\xe9\ -\xe1\x89r\x03\xdcy\x7f\x14\xc8\xb8\xc5\xf0\x16\x85\xf7\xd7\ -\x1d^:)\xf0M\x89\xc9\x22\xb3\x91\x93D\xe4g!\ -\xb3\xbf\xd9\x88(\x8e|:\xc2}\xd0\x9a\x02\x19\x97@\ -;\xc1\xac\x85\xba3\x03?\x82\x0f\x98\x8e\xf4\x09\x93\x97\ -\x07\x1f\x08\xff/k\x9d.\xdf\xaa\xfc\xe3\x9b\xa7\x87O\ -\x0b\x17\x08\x8b{\xb1\xdaB\xcf\xf9\xc2T\xb6\xac\xb2\xfe\ -W\xc2\x13\xc3E\xc2\x07f\xe4\x0e\xb99\xe0\x9e\xed\xf1\ -\xf0kde\x8b\xc5\x16zu\x91\x95\xf0\xaf\xa7m\x9e\ -\xd9\x8c\x1c\xe5\x1d:[\xb52B9\x9ckru}\ -\xfa\xf2\x90\x01\xf1C\xe3\xfb\xab<\xcf\xce\xecG\x92^\ -q\x03\xfem\xfe\x94@\xc4\xf2\xbd\xdf<\xdd\x10\xa4\xd4\ -\x92Cf\xeb\x07Rm\xb8\x0a\xb8\xa5\xc4 >\xb6\xc1\ -\xc8\x00p\xcb\x15\x16z\x02\x1e\x93 -\xb9\xe1F\xb6\ -+Cb%\xec\xd8Cm\xac\xc8\xca5\xbc\x19\xfd\xf2\ -\xf5O\xc3\xef\x8d\xb5\x89>\xf5!s\x80S\xd1:6\ -\x8b:\xeb\xe29!\x03nN\x87\xdd\xe3p_\xe4w\ -`\xcd\xf5\xfa\x1d\xdcz\xd7Gl\xcdH_DR\xb6\ -\xd8\xb0^\xfcq?\x9bh!\xc5\x0b)\xa4\xfc\xff\xe9\ -l\xe7\x8f\xe4L\xe20!)\xd7D\xcf\xfa\xb6)6\ -\xc4\xd2a\x86\xf1\xd8\xc0\xd1\xbe$\xd5e\x1c\x0a\xd1\x04\ -\xbb\xef\x03\xf7\xb0Y>\xb7U\xd5\x1dR\xaa#2R\ -\xda\xee\xed\x14\x8e\xbd\x15\xf5\xe9\x1f\xd4^\xdc\xd0\x83\xfd\ -T\x13\xd88w\xce7\x9f\x9aa\x12\x1c{8v\xd4\ -[\x17g\xa8=\xf7\x10\xd7A\xc0m\xa6\x96\x8eYU\ -{\xfc\xd7\x07\xcb\xac\x17\x96\x9e+5\x0e\x00\xae\xd8^\ -\xb6\xbd\xa0\xe21d\xe4\xb8\x05#\xa48\xd6E\x11\xce\ -\xafZ\xca\x11\x9e3\x88\xc4\x158,f\xa5\xb5\xb8\xaa\ -\xbe\x87\xb0\xaeo\xb4\x10\xd1\xeb\xdd\x8c\x95\xf2\xfd\xf93\ -\xaf\xe4\xe8m5!\xa9\xda'\xccfO\xbf\xb5u\xa8\ -\xf3c.\x95C\xfb\xe2J\xec\xe7\x04\xc8\xda-v\x18\ -\x1f^\x94\xbf\xf9\x7f\xaeI\x88=Z.\xab:)\x8c\ -\x7f\xd2dq\x85\xe5\xeeA\x17UB\xa2\x911j\xe0\ -\x17w\xd3\x97\xfa\x9d\xf7Z\x9e\xca.\xa6\xbcA\xdc\xfa\ -\xb0x\xc2%\xb7\x8f\xb6\x11\x8fM\xd9\xb2\xe3\x87\xab?\ -\xc9\x10\x10\xf7\xcb\x1b\x11\x16G^pW\xd6y?\xf2\ -z\xe4d\xbf\x05\xdeuvr\xab\xb8\x8c=2\xf3\x0e\ -\xcf\xb90r\xc9D$;\xd6*s\xfe\x88Z\xee\x1b\ -\xf3E\xe4\xc6_\xd8\xfe\x8a<\x86\xc3~nJ\x85\x04\ -\xe8\xda'\xaf\x81\x96S7\x8f\xac\x1f\x7fa^\xf8\xc2\ -\x98\xd4\xc3\xe2\xa6B\x9a~2\xd5\xeaa\xecb\x0d\x85\ -\x0fg\x7f\x91\xb6\x9by(8\xac\x9eg\xdc\x1c\xd0\x95\ -\xb7\x06\xd3V\xdc\xae\xbf{\x9c\x7f\xd1t\xb6\xa1.\x19\ -ZR\xbe#T\x0ey\x94L[\x9c;2\xd2\xf0\xb4\ -\x8dA\xb6\xd9\xe38\xe7\x22\xf6\xf7\xf3\x83\xca\x04\x22\xfd\ -\xbc\x9d~l}\xael\xc1\x96\xbd\xd1\xc2h\x91y\xc9\ -\xf0\xc8\xf7\xda6\xd3\xeaBr-\xe5\xa7\xd8D;\xbd\ -\x93\xb5\xfb:\x9c\xff\xde=\xeb/w\x10\xfbk)c\ -O\x9as;\x8d\xde\xf1\x86`\x18p\xb0\x88\xfdu\x5c\ -A\xday\xe1\xe4\xf1\x0b&|]\xe8'\xfd)G\xcb\ -\xcez'[\xba\xed\x9a\xc3\xe2\xf2\x1f\xa4\xb6\x9a\x05/\ -\x9a\x13\x84d/2-\x99\xa7\x91?\x5cb\xa8\x08\xbb\ -Z\xa5\xe9\xff\x0e\x18M\xb1\x09u|'\xbb\xc0[p\ -\xb1\x1f\x9f\xc5\xbd\xb4\x11*\xa4\xe4#\xa2+n\xd4O\ -\xb2\xe3'\x9bs\x96\x9a&\xf3\xfe\xf2\xed'7\x12\xcc\ -\x0b\xfd\x90\xfd[\xcd\x9e\xa9\xeb\x04\xc1\x196\x948\xaa\ -\xf4\x97\xcc\x0b~\x89\xed\x87\xb3\xd6\x84\xc5\x89\xccj\x90\ -\x0d\xd9\xcd\xb6\xe9\x1cx\xda\xc3\xe3'7x*\xcf\xb3\ -`\xfbD\xa8\xb4\xe5\x97\xb8e\xaauG\xf3\x17;\x98\ -\x03SU.\x14\x7f\xe1\x15\xff\xb2\xd7/3\xcf_\x83\ -4X\xe7\x09\xb2\xf7\xb3\xceRq;\x01\xc9\x84\xa2m\ -\xda\xc8\x18\xa3p>ad\x92\x99\xdb\x1an'\xf3\xe9\ -\xe1+\xc0m\xf3I\x83\xa3\x83\x91\x8f\x0b\x01K\xbf\xde\ -u\xde 2\xb7\x80\x88<\xd5\xb0\xe09\xce\xbfTd\ -u\xb4\x839[6\x97\xd5\xe1\xd3\x8f\x11\xbb\x98\x1d\xd3\ -\xc3\x97xr\x9d\x9d\x10\x84L\x10\xbb\xb0\xc5\xdep\xfd\ -\x12?\x7f\x8e\xace\xc43\xeafn~\xd2^\xfd\x14\ -\x9e\xbb\x8d\x0b\x1b\xf2\xd9h\xdd\xe3O\xdf\x9c\x82V\xbb\ -\x1e\x8f\x99\xe0&\x90\xf6\xb5\xde\x1d\x91\xcb=?w\xe1\ -\x01\x85\xbd\x1eN\xd7*\xc3\xac~%\x8eHU2u\ -\xdb\xcc\xbd\xfaR?\x92`6\x9c\xd0\x8e\x03\x96%\xea\ -L\x12\xb7\x13\xf7\x8e\x22\x90>O\xb5Y\xa2\xe2_\xbc\ -\x8aG|\xa0\x93\xba\xdfG\xd1A6\xf9!\xea\xe3\xec\ -\xe4\x12L\x89\xf2\xc8\x04\x82\xcc9\xfe\xc8\x15.\xdfc\ -\x11w\xc7\xc8\x01<\xe2\xf2q\x9cHh\xb4\x90\x14\xa2\ -V\x80\xfde\xec\x9co*\xb6\x22\xa3\xd4\xe7:\xf0\x8c\ -T\xd5\x9f\xac$\x97t\x8aT\xb71\xcaq\xbf\x92\xe4\ -\x05%\x9f\xc9\x8f\xa6=}\xff1k\x95\xd2\xb1\xa5\x85\ -?\x14\xf7\x0c\xcb\xfbj\x9crCQ6h\xd6la\ -\xdb\x9f\xd5R\xdf\xb9\x85w\x9e_\x91&\xec\xe5?3\ -\xd2\xa0j\x08\xe9\xe4+a\x9fs\x81a\x0a\xcb\xa4\xfc\ -\xea\xef.\xe0U\x9be\xfb=D]\xfc\x8b\x91\xa1\x97\ -\xcb\xa3E\x8a\xa3g\xbc~\x9b\x22\xce\x17\xb5n\xd6\xc4\ -\xb7%\x15Z\x81\xd6\x02\xbc\xaf\xa2\xb7\xd8\xf9\xce\x1a\x95\ -}\xeb\x98\xb8B\x11\xcf\x837\xa2\x12\xa3\x9c\x87\xbb\x8f\ -\xde\xfat\xe8\xa8\x1b\xa1\xc3$\xcc8\xce\x14\x1d\xb2>\ -z\xc6D-W\xae*A\xb8\xff0\xdf\x92\xc4\x99\x22\ -r\xde_\x22\xdc\x97re^\x19\xb6Q\xed\xcb\x90\x13\ -{\xea\xe7\xfb\xf9\xf2\x95\xff\xf6{\ -\x83\xd1\xf0|\x99\xfe3\xf9\x14\xad\x9e\xba<:\xa9T\ -\xb6yU\xb8\xd3O\xa9\x80=\xbb\x0dr\x7f\x8a\x11\xcc\ -\xfb\xf3,\x5cud\xb0\x93\xc2|\xcfp$\xc3\xeb\xd7\ -\x12u+\xd7\xbc\xcc=\xeau\x99f\x95\xb7\x8cBH\ -\xfe\xab\xbc\xcf\xbd\xffl~\xdf\xc4M\xa145H\xb7\ -@V_\x22\xf7\xb8\x82\xbf\xe7\xc5\xe8'\xf1\xe5>\xbb\ -O\x95\x97G)\x5c|\x11\xaffvn\xe0\x18\xf7p\ -$\x9e\x0b\xde\xbcf\xca\xee\xa5\xe4\xe3v\xe6\xdb\xfb)\ -\xec\x9d=\xf7\x9aP\x5c\x9c\xc1\xa3\xb9\xaf]\xeem\xd4\ -*\x9b\x17\xfa\x98\xb7r\xb4n\xf4\x0f6\xb1\x87\xf1\x0b\ -\x1c\x86f\xd6\x9c\xd1\xaax\xc4\xbb7T\xee\x1a\xfb\xfa\ -\x83\x5cR\x0b\xaf\x0d\x0c\xe4\x90\xc9\xf0\xe2O\x9a~\xfb\ -\xfdG\xcd\x00)\xfd\x8c\xb8\xd8\xda\x88\xe3\xb5?~.\ -\xe6\x1f\x15\xb3\x9d\xeb\xb4\x99\x9b\xaa\xfdiU%\xd4|\ -\xa1\xf8n\xf4\xc5\x95\xddwr\xf5\x5c\x84\xe5T\x1f\xf8\ -!\xc3\xbc\xcb/\x08(\xa1f:C\xa4\x0fo\x88\xfa\ -1\xa4\xae\xbf\xfe}Uv\x15\xf7/\xa3\xe7{>\x97\ -\x0bpE\x9f\x07\xfd\xfaU\xb0\xc0\xe9\xb0\x00\x12\xf9\xd8\ -P\x81\xf8q\xe6\x01R\x8ed.\xa9\x22_~\x8d\xd7\ -QN\x1b\xb9\x17\x11^\xe6\x17?N\xf9\xbe>y\x98\ -\x93\xe41\xbb\x9d\xe3\xd9\xc4\xe4\xa3\xae\x98Tl8\xb0\ -e\xf4\xb6I\x22!g\x9c\xa7\x8fC\x16\xa8\x8d\x9e\x19\ -\x13\x11\x8b\xae\x8a\xca5x;\xe2b\xbe\x18\xf8\x90\x86\ -ml\xd4\xb6\xe4\x91\x15\xf1\xd2\xbc\x0f\xd4\x1d\xc4\xc6\x0b\ - \x12c\xbf\x1a\x1a\xa7\x1d\xaf\x9d\xfc\xdcwAT\xbf\ -U\xf0C\xa9~\xd3t&\x87>\xb2;\xadZ;\xa9\ -\xe6d\xfat\xa7\xb2\x09\xe7b\xbekZ\x5c\x90\xf3Y\ -\xf3\xf2\xe3[g\xa5\x09\x0f#S\x96J\xc6e\xe8\xbc\ -\xd3\xe5\xb1\xbd\x1d\x93!\xa0\xb5\x8d\xa8\xc2\x15;\x9e\xd3\ -Y\xc2b\xa9\xfe\x85\xef\x1c\x22\xb3\xad\x17\xf9\x9e\xe5\xa9\ -tJ\x1f\xefd\xa66\xd8\xbc\xf4\xa4\xa8k\xcc\x18\x0e\ -\xeb\xace\xe4\xf1\xabf\xecw<&lP\xa7\xec\xb7\ -x\xdc\xa4\x8d\x1aU\xd7\x83fF\x16\xc7\xbd*\x1cd\ -W[\xb9\xd8Ne\xbe\xdd\xab\xe5CR\xe7\x7f\x12<\ -[\x94\xb5\xf3\xd1\x9b n\xd9\xb7\xc1+W\xea\x1b,\ -\xf6\xf0\xbc1\x9cOm\x90y\xd6\xe8,92\xa9\x9e\ -\xd3\xc1%z\xaf\xd4\xcf\xa4\xf1\x83\x1eN\xdc9x\xd9\ -\x10D\x93|\xdd\xee\xc6\xf0\xfc\x8d\xdf\xbe~\x12\xdcN\ -\xd2\xe4#\xbc\x9c\x1c\xef\xe3\xcf\xf3h\xaf\x81\xe3 Q\ -\x8b\xc0D\xd1l]\x9d]V\xc6\xb9\xa1\x86gE\x0b\ -\xf4\xbfpK\xe6\x9d7/K\x5c}\xc5;=&\xe7\ -f\xbc\xce\xfb\xb0\x87\x8e[^&\xf8\x85Jyo?\ -\x8d\xf0\xe7\x07\xef\xd4\xe4\x92V\xe0\xd2z\xe25'\x97\ -\xf7\xc8\xf3$]W\x8b\xb9\xf9Y\x19\x9a\xb36\x103\ -\xef\xff\xf8\x99\xf2Tw\x13\x9f\xdb\xf2\xf7\xb9\xd9\xd36\ -\xfd\xb8\xec\x93o\x1d{\xf6\xd0]\xbe\x13\x07\xf6\xce/\ -\x17\x8a\xd5\xb6\xe5T\x19\xe1kt\xaa\xbc\xb0N\xa1\xa1\ -RC\xc8\x8a=\xca\xcc6U\xe9\xa6_\xaa\xa0~b\ -\x92\xd8\xf1g\x0a\xfeW\x5cc\x07\xe5\xe5^\xfey\xc3\ -\xb3>lmL\x10\x87\xb8\xf9!\xc1/:\xc3\xe2\x9d\ -\x90\x18\x81\xddw\x95\xdc\xeb\xce\xa4\xde\xb2\xb6\xd4\xb9\x94\ -v\x8bTV\xa4\xd0@4\x17\x9a\x16\x15R\xba\xd99\ -n\xdc\x07\xa5\xfa\x1f\xb9)D\xf2\xf27\x87\xc8\x11\xb3\ -.\xa5\xea\xab\x889\xbdg\xd7?\x15G\x18\x9b*\x8d\ -\x96\xad[\xdf\xcf\xa3\xe8^\x9e\x95C\xf2\xa3\x9c\xeb\xdf\ -\xb6d\xeaq\x88\xa4\x1fy\xd2pl\xf5{5v\x15\ -\xa9`s\x03.7\x19\x9f\x9c;q3>\x7f\xba+\ -{{\x87\xe6\xdb\xdc\x87\xa7\xcaG\xed\x93\xe0A\xf4\xec\ -\xff\x1bvU+9\xaa\x1f\x9ag\x9f\x14\xefp\xd8l\ -\xb9\x93x\xde\x84\x1d\xbc\xcf\x86F\xae8)8\xf6\xbd\ -Nj\xb8\xc3\xd5=_\xbel\x9f(\xfb\xb8\xac\xe2\x1e\ -\xafYf\xffxG\x84_{\xf2\x96\xcc\xa8\x19\xa2w\ -\xa7\x99\x06\xdd\x8c\x9f!\x99+o\xfd\xd8p&\x9f\x00\ -\xa2z\xab\xe0\x92\xdczR\xd0\xf9`\x0b\xb3Q\x0d\xdf\ -j\xb6\xac\xb6[\xfd\x92\x0c~\x1fp\x22I*\x13\xd5\ -\x99#k\xb4kF\xa5\xb5\xe6>%\xf7is\xb6\xd4\ -\xe4\xee\x96d\x13{<\xe0xD\xbd\xe9X\xcd\x80i\ -\xe6O\xbe\xcdq\x1b\xb6\xa1\xe1\xd3\x10-n\xd7,\xef\ -\x85\x83\x14\xff\xa7v\xf3\xeb\xcf\xb2s\x0ahm\xca\xd4\ -\xd2\xcc\xb93\xe2\x1ck\xa4\x01!\xc7\x06T\x1c(5\ -\x9a\x12\x22\xab#\xc4c\xc39eD\x80\xcd\x0e9\xb4\ -v\xc2\xe9\x8a\xad\xb1\xa7\xce\x19\xbf:PKj8\xb7\ -NpJ\x88\xea\x8a\x10$\xd2\xf4\xf0S\xbf\xcf\x81\xb6\ -\x11\xce\x87\x84\x82\xdf?.\xde`\x03\xde%\xcf\x15\xa3\ -\xf6\x1ey\xc8\x86\xeam\xf3\xe66\x9a-\x9b\xf9p\xe3\ -\x19u\x84{\xf5]\xa3S\xf2\x16Kn\x89\xe7`b\ -\xe4\x92\xe1\x87\xf1\x91\xf0m\xbc\xbbK5\x1d}8\xf5\ -\xf2\x81\xf4\xd9\xaf.e `\xd4v\x1d9\x14\xfa\xc1\ -\xd1\x80\x0bLs9\xad\x8am\xb1\xb3\x16#\xdc\xf1\x97\ -\xd1\xa3S5\x87l\x9e\x96x0\x03Y\xbd\x8cC&\ -\xf3\x93\x86\xd6\xbc\xba\xfez\xa9g\xb7hM\x1aV\xf7\ -\x5c9\xf2\xc8\xfd\xff~\xda\xeeNP\x8eC\xcf=\xda\ -_\xfb\xe6\xcc\x87\x8b\xda\x0ez\x15\x96\x1cI\xcf\xb4\x9d\ -\x1e\x08\x9b\xc379\xe7\x84\x0f\xf9CH\xc5\xe0s~\ -\xe9\x86\x85\x0f\xf5=o\x0f\x10\x93WUd\x0f;\xbc\ -\x90|\xfa\x85\x96A\x88\xe9\x96\xbb7\xc0k\x1e\xb5A\ -g\xf8\x8f\x0c\xcd\x84\xea\x12\x99Y\x19\x0b\xcf\xcf\xac\x96\ -\x9fn\xc7i\x13#\xe5\xf0\x22\xe9ne\xd5\x00\x93=\ -\xcb\x84vz~\x05\xa4:\xad\x04\x93\xe3\x82\x91\xdc\xc1\ -\x86Q\x04S\x0e\xf8\xcd\x03\xbb\xce\xa5F\x9f\xb8\xe3\xf0\ -@c@\xf8\xb0x\x9b\x1d\xe5\xaf\xd3\x05xM\x12\xfd\ -\x068\xa8}\x0f\x92\x14|\x5c|\xf6\xf2\xa0\xcd\x07\xc2\ -<\x8f\x84=\x5c\xf15\xef\x7f{\xed]\x9c\x0f]\x1e\ -\xaf,\x09Fz\xfe\xbb\xa2M\xa6!!?3\xa3\xe6\ -\x88\x5c$\xae\xf2MJ\x1f\x93[\xb2\xf1\xd8\xfb\xab7\ -w|?\xff\xa1\xbf\xc1\xa0X=\xb3\x0b/3\xce\x9c\ -\x93\xd0\xb1\xe1\x1c:g\xd7\xe5\xc3\x01\xcf\x8d\xde\xe6f\xa2\x16;\ -_\xa6\xefX\xfb\x8e7\xb1\x9f\xcdps#g\xa3\x1d\ -\x9b\xceD\x0e\xda\xf1n\xb8\x8c\xfc\xc3\x10[\xded\xa3\ -\xffI\x93V\xca\x0d\xe0\xb1yz\xf0\xb8I\xd5\xfc\xd8\ -\xed\xa7\xe7]\x10\xcf}h#=\x5c\x00\xd9w-Q\ -z 2`\xf6\xba\xbbg\x0clB\x8c\xef\xef\xb98\ -\xc2f{\x82T\xba\xff\xeeJ\xdd\xdd\x85\xb2w^\xf9\ -D\x14\xb0\xab\xe4:\x1c\xd5\x02\x14\xf8\x92\x02\xd5\x07\x18\ -\xae\xd1\xce\xfa\xbc\xeaV\xae\xa2\xee\x0f1\xb3\xef\x07\xc3\ -\xd95\xf7\xbd\x0b\x96\xd7\xc8\xdd\xe6\x85\x18h\xeb\xbbi\ -\x8d\xf5<\xec\xbc:\x7f\xde\xd5\xc3<\x08\xe1?\xc33\ -\x93\xf6\xf4[6Z\xcf3\xc7\xfb\xac|\xed0\xb3\x9a\ -\xff\x09\x83\xd71\x9f}\xc3;\x99\xe3J5\xa4x)\ -\x1e\x1b\xd3\xcb\xca\xda\x19s2,\x14\xfdL\xf7\xb9\x83\ -\xa7\x8c\xf3\xf1_\x19\x1c\x96\xbeSr\xe6\x85\xbdJ;\ -\xa6?\x9b\xb9\xcb\xa1\xcc\x7f\xd5!\xf6\x07Z13\xe4\ -#N^\xbb[\xffi\x8f\xc2S\xfd\xe7f\xe7=\xd7\ -\x15=\xff\xcb\xc9\xb1\x13\xee\xce|\xff\xe5\xb4\xcc\ -\x5c\xf5\xa2\x17\xe9O\xd7gxp\xec\x90`s\xd4\xcd\ -\xbc!g\xdb\xdf\x8ccn\xbc\xda\xcf\x18S]\xc1a\ -?S\xdd\xcd\xbcC\x92\xd6H\xb1_\xaeY22\xdc\ -\xf9\xdbZ\xcb\xd4\xb3\xaf\xf82\xebv]-V\x7fo\ -\xb9^\xe3\xdd\xdaT\x12Z\xaa\x12#\xefpR\x1d\x11\ -6 \xef}\xb2<\xac\x84\xbcz\x8c\xc6\x81\xff\x89\x8d\ -\x9f\xe1y-\xa0\xb2*\xf3n\xd0\xc7\x99\xe7\x05\x90\xe8\ -\xda\x12\x87\x84\xc3\xc4\x0b\xdb\xcde\xdc\x1e\xd8\x1c\xbe<\ -\xf3\xa3\x97i\xadl\xf8F%\xbf\xcf(\x17\x8f\xcd\xd0\ -C\xdaBz'\xb3'^\x0f\x95\x91Yq\x90OO\ -!4\xcf\x99\xdd+ \x13,g.\x0b\xfb#\xf3\xd8\ -\xaf\xab\xa3\xc4\x15{\xa4u\x13t\xb9\xd6_\x99r\xdf\ -F_\xc7\xf8\xd2\x99G'\xee\xe9\xac\x22;\x14\xcd\x8b\ -\xfc\x1a\x86\x94\x82\xa1\x92y\x1e\xb4\xd6Z\xb4t\x91\xdb\ -p\xc7\xdd\xb6\x15y\xf1o\xf4N\x1e\x09\xbd\xb9\xa2\xb6\ -\xde/\xc5\xcf\xd8`\xd4Pg\xe4Wd\x12Y\xdc+\ -\xf6(\xdb\x89u\x8aO\x8b\xd8\x9f\x1c\xd3\xfa\xfa\xa3.\ -\xbf\xf6\xec\xba\xf4\x1a#\xc33\xe5\xeb.\x98aO\xdb\ -(\xfb<\xd9\x9b4\xa7\xf8\xa5\xe2\xb5\xd5\xe9\xf2\xe3?\ -]~'\xafTc\xad\xb3'S4\xcb\xeb\x5c\x91\xe9\ -\x86mlY+\x05\x90\x0c\xcb\x9d\xdbB\xcb_\xea\xee\ -=\x7f.\xf3\xd0\x1d\xee\xd0J\xce\x83\x95\xd7\x92G\xac\ -\xbcm\x5c\x93\xa9\x11\xe7\xbf^\xb4n\xc7X\xb6O\xe7\ -\xc0\xeaD\x8a\xe3\xab\xfa\x10\x11p\xd5\xf9\x95\xc8\xc8%\ -\xeb\xaf+%k\xc6m\xfb\xa9X\x15\xb7\xc20M4\ -\xfb\x15G\xd2\x06\xb2\xdbb\xf6\xeb\x5c\xea\xeb\x83\x05\xcb\ -\x95k6k\xc7\xe8\x7f\x95\xcd\xe2\xbc3\xd8L\x1e\xbd\ -\x5c\xc0\xd5\x804\x04F\xb9\xc7\xa5\x1a\x8eC\x0e\x06\xee\ -5\xe0\x12\x9dxH5\xd0%\x9e-,\xd4\xd7}\x9e\ -W\xf9>\xd3I\xb2\x8f\x1f\xfc\xfa\xb9u\xfe\x8e\xa5\xca\ -\x01\x0e\xa5\xe5\x13v\x9fk\xb0p6\xda$;\xe2\xc5\ -\xce\xf7\xb2\xc7B_\x7f\x1cj\xa2.\xe58\x8d\xf7\xed\ -\xbe\xf0\xec\x8dKGi\xeb\xbdG\xf2W\xef\xd6\xe6\xd1\ -2,\x0dp\xe1T\x91m\xa8\x9b\xa7\xc4\xb5\xeb\x5c\xad\ -\xf9\x95\xa9\x09o#/\x9f\x9eB\xf6\xb3\xc9\xe8?\xf8\ -\xc3\xc4\x1d\xa7#\x8b^\x0a$*,\xb2\xdcu\xbf\xaa\ -?:k\x09R)1\xecD\xe6\xcb\xd4\xad)\xf5\x1f\ -\x84\xf3\xb2\xcb\x84\x96NV\x9aVz\x99p\x9ax\x22\ -\xdb\x80}\x9a\ -\xb4\xc5\xcaq\x84\xca\x17\xa0\x13\xa5W,\xbb\x15\xb4|\ -,\xd40K\xe6N\xca\xb9\xaa\xe5\xf1\xe4\x9a\xcd+\xbe\ -\x19q1y\xa2se\x92\xd7\xd5\x8a'\x9dsy\x87\ -\x8e_\x94\xffX\x94\xacj!.\xbf|\xb9\x0eg\xe8\ -\xa0=\xe4\xfd'3\xa2f\x8e5yzp\xe9\x98\x93\ -\x97%\xd6\x08).\xfc*:-\xfa\xd9\x86\xfd\xbby\ -\x1eI\xe6e\xef/\x18p\x7f\xbbQp\x99\xd5\xb1u\ -\xe6;x\xf3\xd9+]^\xf1\xc9\xbe\x97\xce1|\xb3\ -qM\xd5\x1b\xff\xb8\xc8\xd9u\xda\x03\xa4\x15\xee\x1d\xb1\ -\x9b9z\xe4\xd5\x9f[\xbf%\xf3\xba\x1a\xe5$\x8e\xbc\ -\xc6\x19\xb9)\xe8\xfa\xc7\xf2\xf8\xc3\xd1`\xad\x89x\xcb\ -!\xecG\xd2\x1b\xed\xd5\xef\xe7\x7f\xa7\x93\x13\x16Z\x1e\ -\xdc#\x1d\xf7\x22\xcd\xbf\xd2\xc6\xe5*\xd8b\xb9\xcf\x19\ -\xef&\xe4\x9dgR f\xe2\xa7\xbf\x8bG\xedY\xc1\ -\x8d\x83w\x87]%\xbb\xfb\x8eCV\xfd\xcf\xe4\xca\x19\ -I\xa3i\xc9_2Vp~\xbb\xcb[\x1e\xfca\xde\ -\xe6\xb4o\x1a\xd6gR\x1d\x143\xb7o\x8bJ\x11\x96\ -\xb9\xec\x1e;>G\xc7[\xe8\xb2{\x0d*X\x1b[\ -\x9ft(5`\x86\x00\xb2\xd38za-\x89(\x14\ -5;\xf8\xaa\x9f1\xa7B\x09A\xb7\xe8#\x9fBq\ -~\xd6\xb1\x17\xe2\x82\xab7\xd5_-y8\xf5\xb0\x8f\ -\xcb\xdb;\x8a\x1e\xca\xa9o\x5cW_\xd1\xae\xaf\x9e\xa3\ -E\xda!=\x98\xf4&e\xed\x15\x8f\x05\xecb\xcb\xcf\ -n\x8e\xbezZs\xf7\xd8B\xd2d;\xd4\xd8N\xd3\ -\xc1\xae\xf6\xa6\xb8\xaa\x8e\xba+Pe\x17s\xaa:I\ -\xc9\xdc9\xb7\xe9\xce\xb9\x87\xaaB\xc5[\x95\xc0j>\ -\xf0\xbe\xb1\xdc\xc1\xc0c@_\xf4\xdeUf\x9a\x07\xd6\ -\xf2\xf4\x06\xfdc\x93\x89\xf9\xb6\x0d\x9f=&\x97\xb9\xe4\ -]\x22.=\x95\xe1Xto\x95\xd9\xaa\xe3\x9e.\x93\ -\xdeo\xf9\xb8\xe6P\xa0\xec\xeb\xe7w\x84\xbc\xcakT\ -\x93\xd8n/\x91\x1a\x18<\xf7k\x0c\xd7\x88\x99k\x93\ -\xdeCE\xe9\xb9\x8fm\x10\x9f/\xf9\x85\x13\xb2\xf8\x16\ -x\xea\xcc\x01\x84\xaf{#>\x8c\xff\xb0\xb5\xe4\x86\x9c\ -\xc9\x08\x85\x15i\x867kW\x0c\x05\xc3\xfc\xc8;H\ -*\xd3\xfa\xf9fmv\xc1\xc29@\xe6.\x22\ -f\xb7\x0b\xbc\x94q\xf8Z\xa8\x97I\xf1\x7fg\x8a\xbf\ -p\x8b\x0f|\xd2\xefe:\xbf\xfe-\xca\xff\x87\xbc\x1c\ -h\xf3$D}\xac\x9d\x5c\x826Q\x1eQ`{\xf8\ -\xf6\x12\x7f\xd2'\x0d\xbf\x8f\xd1\xec\xe1%&\xc1\x1cB\ -\xa6nK\xb9\xf5\xec\x07\x95\xcd:\xf2\xde\xfb\xfa=\xca\ -\xd5-\xb39S\x91\xcd\x14#\xd5\xf9\xa4\xaa\xd7H\x0a\ -{\xc2/\x19\x0f~U_A\x01o\xb2C\x7f\x9b\xcc\ -\xbc\xf3\xec\xb2\xf92\x9e\xe07n\xd9f\x22\x8ae\xba\ -\x8e\x06fn\x1a\xdc7\x12\xa6\x9a\x91\x88\xc8\x86\x93\xa6\ -\x1cv1\xeb\xa6\xbf. \xae\x8c\xe6\x84\x9fF\xe2~\ -\xc9x\xf1\xab\x9e2\xc86#\xf9\xf8W;i\xdf\x0d\ -uU\xe5\x1e\xaf\xe1\x93&\x09\xff\xee\xdf\x7f\x1dA\xc6\ -\x97_\xe2\xfe\xbahB\xf5\xd4\x90\xb4%\xfbo`\xa6\ -\xbd\xbb\xb1\xb32t\x10\xd2\xe8R\x82\xcc\x03~\x89)\ -\xa4\xd7\x05\x95\x8c\xb38\xd1z\xba\xaa\xfd\ -\xedt\xa1\x80\xd09\x05\x15z\xfb\xc0\xbc\x5c\xab{\xb1\ -\xf8\x9e\x80j\xe5\xae\x0b\x99y/\x1f\xcb\xa2\xdb\xe4\x0d\ -\x96f\xf2<\xfe\xf5R^|\x81\x83\xb6}b\xa2r\ -\xbe\x09B\xb4.\x9e\x13~(c\xbd\xa4\xdd\xaf\xcf$\ -K\x9d\x22\x17}m\x04q@\x08f[n\xf0\xdfT\ -'\x9cJ\xfc\xac\xac^\x17\x0em\xe5\x93\x02GK}\ -\xb1~\xa2j\x7f\xeb\x92\xcd\xa9\xc3\xd7\xeas\xf4\x1bV\ ->\x9c\x82d\x1b\x19\x07\x17\xdf\x1b\xae*\x9cS\xac/\ -=\xb7\xf2V\xbfOos\x06\x91\x86\x10\xfd\x8b\xef\x8d\ -P\x0d|_br$`.*\x97?B\xe5h\x9a\ -\xcd\x9e\x90\x80M\xdcg\xd2\xd40\xd3\xf1F\x04\x19c\ -\xa4;\xaet\xcc\x0a\xefz\x19n\xbdyf\xe1\xc3\xc2\ -\xe2^\x84\x86\xc9:.\x1d`C\x14:\x22\xf4\xeb\x93\ -\xb5\xa4]\xa0;_V\x9a\xef\x9a\xc3\x8b\xa7\x22\x08I\ -\xb9\x86\x10^\x14Rr\x82\x7f\x84\x89\xd5\xf3b\xafq\ -\xb9\x08\x1cDM$,\xc1z\x91]\xa0}}\x8e\x99\ -\xcc\xf6\x89\xe7\xd8\x84\xe3r\x06\x11m\x0eh\xef=\xc5\ -\x9fTj\x9f\x99W\xedMx\xf3\xe5\x0b\xfbb\xe3\xc1\ -\x81\xaf?\xa5\x89\xdb\x99M\x16_\x17\xfde\xc2}6\ -\xfe\xb9\xa6\xa5\xe6\xe38\xed\x16\x07\x8f\x7f]G\xfe\xec\ -\xbae\xe2\xbc\x90\x8dpH\xe4\x1e\x1cx{;i\xdc\ -\x00\xf1\x90*n\xe3\x8d\xcf\xf7z\xad\xd5\x8eF\xce\xe7\ -\x95\xf5o\xe0 z\xbc\xab\xe1\x97\x88%\x7f)1A\ -7%\x9e\x91\x1e\x84@\x0f\xc0\xb4R%\xb1EUV\ -;r^F\xe8\xf0\x90QF\ -\x09V\xbcN\xb6Al\x8fV\xeb|O^\xbfS\x94\ -\xb3P\x83t\xf4#\xdb\xc5\xd1UWU\xf5\xdd\xb7\x87\ -\xcfT[{X.\xb0\x9fcL\xce\xa0t\xee\xd9\xa7\ -\x0d'Ip\xbeV\xa8x\x9a\x06\x1e\x1c\xf1kF\x92\ -\x00bljp\xc3\xc0fg\xc8;1;\x1e?\xae\ -\x9f\x95\x06\xd3\x1cL\xe5\xfc\x06\x18?'\xf6\xd3\x944\ -\xffiS\xa80\xd9\xcf\xfbx\x8a\xc5\x91\x10\xffw\x83\ -\x057\x22\xa7\x80\xac\xe4\xce\xda\xf7#\x9dMo\xd0=\ -\x1b%\x97\xaf/\xd6\x0f_\xcd\xa9\x22\x09\xe8\x9fY\xba\ -Q\xd7\xb5\xf8!\xf7\x8dp\x99\xad\x92\xd5~\x9ft\x1c\ -5RC\x10\x9dS\x99\xee\xd3\xec\xe7\x94\x8a\xdd\x118\ -c\xb8\xcf>\xc5\xa2n\xe04@\x93\xd6\x001\xa7h\ -!)\xf6b\xf6\x86\xe1>\x9ft\xc6\x88\xd7\xdd\x18\xb1\ -\x01\x99\x9d\x946\xf1E\x88\xbb\x022\xeb\xcd1]\x99\ -\xac~'\xf2e\x1c\xf8\xad\x03\x07\x97\xb2\x9d\xf9\xa0]\ -\xa6\xe4\xa1\x8c\xcc:\xf0\x9cM\xd2x\xba1\x97\xdc|\ -\x8b\x03v\xbf\x22wm]\xb4\x1f\xfdZbr\xd2t\ -\xa4\xb7\xac\xfc\x04D\xe0\xc0s\xdb\xcf\xe1\xf7\xc3\x85\xaf\ -\xaf\x02\x7f\xf7J\xe4\xdf\x18\xf7\xf0u\x89II\x82\x8e\ -\xbb\xfev}\xc4\xb4\xdc\x92\xc3$\ -\xa3_A\xc0Y>\x9b\x05c\x8c\xc2\xfb/Cd\xc6\ -\x07]\xd5\x9d\xe4x/t\x93\x84\xf4c\x0e!\xfb\x0f\ -\x99a\xbc\x86Kj>\xce\x0e\xd1_\x8e\xc0\xb9\xf1\x83\ -\xdd\xc5\xfe\xbe\xc2\x95e\x93\x85\xd5\xb6>\xf4\xd5\xd4\x9b\ -5DwF`\xbf\x14n\xbf~\x0a\xb5\xd6\xc5\xe6\x19\ -\xfdV\x8b\xef\xe6\xb8.=\xc6HLt\x14\xd7\x15\xde\ -\xb3\xc8\x09\x0eI\x0e\x0e\x81\x86\xa76[3\xb6\x0f_\ -\xfa?\xf6yY\xc6\x91\xd7\xc8\xe1\x9bu\x07\x97N\x0d\ -|\x05\x84T\x81\x8c\xcf\xca\x86\xb9b:\xff\x138\xa9\ -\xb8}3\xf4v.Y\xa8\xb1\xc0_U\xf7\xa8\x14\xf4\ -vR\xdc\x9f\x98\xc7\x12\xc1}\xa1=\xd1\x08Fc\xa8\ -a\x08\x80\x0c\xc0f\x00{\x00?\x80w\x00\xd9\x00\xe5\ -\x00$\x00\xf4\x1f\x01\x09\xefs6>\x06~\xf8\x98l\ -\xc6\xc7h\x08\xb51d\xe6\xd6\x01\xcf\x87\x01,\x008\ -\x06\x10\x0a\x90\x03P\xc9\x04\xe3\xcf\xac\xa8\xc4\xc7(\x14\ -\x1f\xb3\x05\xf8\x182\xed\x5c\xa0B\x1b'>\x87\x0f\x02\ -D\x01\x941\xc1\xb8\xf6U\x94\xe1cx\x10\x1fSN\ -f\x9a\x07T\xf8>\x03\xc0\x15\xe0;\x13\x8c\xdd\xdf\x86\ -\xef\xf8\xd8\xceh;\x0fz\x99\xef\x10\x13\x01\x1c\x00r\ -\x99`\x9c\xfev\xe4\xe2c=\xb1-\x1fz\x81\xf7|\ -\x00;\x01\x92\x99`\x5c\xfe5$\xe3c\xcf\xd7Ss\ -\xa0\x0d\xef\xa7\x00\xf8\x00\x10\x99`,\xfeU\x10q\x1e\ -La\xf4\x1chq\x7f\x0e\x80\x95\x00\xf1L\xd0\x7f\x16\ -(\x88\xc7y\xc2A\xef9@h\xfd\xces\x03\x18\x01\ -\x140A\x9fYh\x8d\x02\x9c7\xdc\x04:\xca\x82\x16\ -\xf7\x1a\x08p\x14\xa0\x8a\x09\xfa\xca\x02uT\xe1<\x1a\ -H\x0f\xfe\xb7\xe1\xfd)\x02k\xad\xef\x0b \xe2\xbc\xea\ -\xd6\x1c \xb4\x96\xf9GY\xbc\xefS\x80\xbc:Fh\ -\xb1\x16t\x91\xf7P\x9f\x80k\x0aK\xe6\xf7=T\xe1\ -\xbc\xa3I'$\xb4\xd6\xf7\xa0N\xc9\xd2\xf5\xfa.\x0a\ -p\x1evZ\x1fl\xf1Y\xb8\xa7d\xed\xf1\xfa>\xe2\ -\x09-\xec\x03\x9d\xe4=\xb4)\xf90\x01\xed,\xd0\x07\ ->\x84\x16v\xc2N\xc8}hWd\xe9{\x7f\x0f\x88\ -8O;\x5c\x07Z\xfc\x0d\xfa\x15X\xf6\xfc\xbf\x0f\xc9\ -\x84\x16>\xa3\x0ex\x0f\xfd\x8a\x0eL@+\x0b\x8c\x81\ -\x03\xa1\x85\xef\x98\x0a\xff\xa1o\x99\xe5\xc3\xfd{\x91\x8b\ -\xf3\x98\x1a\xff\xe1\xbcpe\x02\x1aY`,\x5cq^\ -\xb7}\xf7a|\x11+n\xe7\xef\xc7w\x9c\xd7mu\ -~+&\xa0\x8d\x85\x9e\x81U\x1b\xde\xc3\x18\xd3(&\ -\xa0\x8b\x85\x9eA\x14\xa1u\x5c\xf1B\x02+N\xf7_\ -B\x19\xce\xf3F\xfe\x1fc\x02\x9aX\xe8Y\x1c#4\ -\xe7\xe5\x842\x01=,\xf4,B\x09\xcd9Y9L\ -@\x0f\x0b=\x8b\x1cBs>\x1e+'\xeb\xdfC%\ -\xa19\x17\xb3\xb7i\xe9!\x88\xa0\x84\xbd\x10\xc2(a\ -\x0f\x84\x10\x0e\xc1\x16\x10j\x06\xfc\x1c\xfc<\xfc^\xaf\ -\xd3\xce\x10@\xde\xdfg\x02:\x18\x07\xc8\xbfF~\xee\ -\x97@\x0b\x0f\xc8\xa0\xc5Gg\xa3\xa5\x8e\xab\xd0\xb2+\ -\xdb\xd1\x0a/\x13\xb4\xe2\xeeA\xb4\xc2\xef0Zq\xcf\ -\x0a\xad\xf06A\xcb\xae\x1a\xa0\xa5\xcek\xd1b;e\ -\xb4\xe8\xa0,Zh2\xae\xc5\x9c\xf8\xab\xe6\x02\xe4\xfd\ -;&\xa0\x831<\x07\xff/\x04\xfc+uZ\x85V\ -\xfa\xdb\xa251\xbeh]\xe6\x07\x94\x5c\xf2\x13m\xa8\ -*E\x1bj\xabQ\x94T\x87\xa2\x0dd\x80\x06\xcaO\ -p\x0d\x7f\xdfP]\x8a\x92K\xf3\xd0\xfa\xef\x9fPb\ -\xec}\xb4\xf2\xc1q\xb4\xd4u=Zd5\x15%\x18\ -\x8b\xfe-s\xa11\xff\xbe\xb7\xe9\xa0+\xdf\x0b\xcd\xa5\ -\x00\xcfW\xa3U\xcf\x5c\xd0z\xc0\xef\x86\xaa\x12\x140\ -\x17\xednk\xa8.C\xeb\xb3\xbf\xa0\xd5/=\xd0\xd2\ -\x0bZh\xa1\x854e\x1d\xe9\xbb\xf3\xa0\xb1\xf6Bo\ -\xd3A\x07\xbe\x0bc\xb2\xbd\xfc\x86!Z\x1b\x1f\x82\x92\ -+\x8b\xbb\xcd\xef?\xcd\x85\xda\xe4\x08\xb4\x1c\xac\x1fE\ -V\xf2-t\x05&\x18\x8f\xce\xa3\xef\xd7\xdd\x00\xe3^\ -h>\x11-\xbf\xbe\x0b\xad\xfb\xfa\x1am\xa8\xaba(\ -\xdf\xdb\xcd\x83\xfaZ\xb4.\xe3\x1d\x98\x07\xfb\xd1B\xcb\ -\xc9\x94y\xd0\xdbc\xd2y\xf4]\xdec\xef\x9a(Z\ -r^\x13%~|\x04\xd6\xec\xaa\x1e\xe5{\xbbyP\ -G\x04r'\x14-uY\x07\xf4\x03\xb1\xbe(\x0b\xfa\ -\x0e\xe0;\x0f\xd6\xde\xca\x80\xa3(\xb9\xf8G\xaf\xf2\xbd\ -m#\x97\xe5\xa3UO\xce`kQ\x1f\x93\x05}\x03\ -`L\xe1\xbe\x8c\xf8>\x00E\xeb\xebz\x9b\xdd\xd4\x1b\ -\x99\x84\x12?\x07\xa3\xc5'\x17\xb0\xe6\x00]y/\x02\ -\xf6\xed+\x81N\xff\xbe\xb79\xdc\xa9V\x9f\x13\x0f\xf6\ -\x8c\x1bXk\x01] \x82\x96\xb9oFI\xf9\xe9\xbd\ -\xcdV\x9a\x1a\xa9\xf0;fg\xea\xfd\xf1\xeb\xc3\x00\xef\ -O\xd9%=\x94T\x94\xdd\xdb\xec\xecR\x83v\xa4\xb2\ -\xab\xff\xf5\xfe8\xf6E\x80\xf5\xb3\xf4\xa26\xf6\x1e\xd1\ -\xbd\x81\xbd[CM9\xdaPY\x8c\x92+\x8b\xb0\x9f\ -\xd85\xf8=\xbd\x1b\xb98\x17-\xf3\xd0e\xe9\x034\ -\xf2\xbe\xe4\xccR\xb4>7\x99.Vz\ -qS\xef\xc9R8\x07\x5c\xd7\xa3$\x02\xed6\x8a\x9a\ -W7)\xbaio\x8f}o\x03\xfauN\xcc\x03c\ -\x98E\xd3\xf8A\x1d\x91\xa2K\xf5\xb2.\x0d\x9e\x0fc\ -O`\xdc\x18-\x0d\xc6\xa2\x95\x9c^\xcc\xd2\x03@\xff\ -a\x8c\x1dM\xeb>\xf8,\x94\x17\x84\xfdc\xd1\xde\x8f\ -\xd1\x15\xc1\xde\xe3\xaaPWJ\x1c!\x0d\xad*\xf8|\ -\xef\x8f\x7f\xaf\xf2^\x04-<0\x05\xb3\x8d\xd0\xd2\xea\ -\xb3>\x82\xbd\x82\x22\xf3\xbc;\x98\xcdJ\x01\xad\xcb\x88\ -\xa5\xad\x1f`\xafStH\xbe\xf7eXo\x01\xec\xd3\ -`\xdc\x0c-\xb2\x13\xc6yA\xfb-\xd3\xf0\xbe\xa9/\ -\xc2X,\x1a-6\xe3\x06b%\xb6\x8f`\xd8~\x95\ -\xd9\x01x\x08\xed\xf1\xb4\xb4\xba\x94H\xb4\xd0r\x0a\xf3\ -\xbd3P\x96YLBk\x13_\xd0\xd4\x9f\xea\xe7n\ -\xbdO{o\x8d\x97\xf9D0^/;?X\xa4:\ -\xb4\xc2\xdb\x94y\xdf\x17@W\xf9\xcd=hC}\xe7\ -\xed\x83u\xe91x\xfc(\x93\xcdg\x86\xf3_\x18\xcb\ -\xc7\xa1\xc5\xa6\x0e\xed-L\xedC\x01<,\xb2\x9e\x8e\ -\xd6\xffH\xe8t\x9f`\xdc`\xb1\xfd?\x18/\x06\xde\ -\x15\x18\xd7\x03\xd7\xc0\xce\xb6\x9a7^\xcco73\x16\ -E\xab\xc3\xaft\xbaO\x98>sm'\xf3\xca4\x86\ -\xf1_\x10\xad\xf4;\xdc\xf9}\x1f\x90\xfd0\xd6\x9e\xe9\ -\xc7\x09\xae\x01\xd0\x87EC>B\xd5\xa3S\xcc\xdf/\ -\xbaC\x84&\x9b\x1f\xb9\xa2\x08\x8b\x09az9\x89\xd9\ -\xb3Ti\xf2\x0f\xc1\x18\x03\xc2>\xb1\xde\xa7\xbd\x07y\ -\x0fc,`~eg\x1b\x8c\xc7):4\x8d\xf9\xf5\ -$\xa8\xd7\x1e\x94\xa5\xc9\x8fY\xfb\xe5)Zh&\xc9\ -\xfc}\xa3\xe7\x18\xd1\xa8\xfbc\xfb>\xb0\xbfb\xfe1\ -\x12\xc1xY\xfb9\xb8\xf3}\xc3\xf6\x00L\xb8\xa7e\ -$\xff\xc1\x9e\x07\xf6\xbb\xb3\x8d\xf8\xf1!Zh:\x1e\ -\xed}{o'\xb0O\x02\x8b;\xebl\x83\xf9\x02E\ -Vr\xff\x16\xff\xa1\x8c\xcc\xfa\xd8\xe91\xaay{\x97\ -I\xec\xfd\x9d\x00\xd8\x03\xd4D^\xeb<\xff\xb1\xb5\xed\ -\x1f\xb2\x03cv\x7f\x19\x9a\xf2y\xfej\xfe\xffL\xf9\ -\xf7\xf8\x0f\xd6;\x96\xfc\xc7\xf9\xff/\xca\x7f\xa8\xff%\ -t\xdeVN\xd1\xff\xa4\xfb\xc0\x18uE\xff{\x8b\xf9\ -A\x99\xbfot\x1c#\x9a\xf7\x7f\xa9}l\xff\x17\xd7\ -\xe9\xbe\xd5~y\x06\xe6\x8c\x14\xf3\xf7\x8d\xce\xe3T\xfd\ -\xc2\xad\xd3c\x04\xf3\xb4`\xfcu\x9f\xb0\xff\x1c\x9fG\ -\x9b\xfd\xe7\xcdm\xe6\xb7k\xd3\x1b\x98\xfd\xd7\x9a\x06\xfb\ -o=s\xfb\xfe\x9a\xfa\xd5\x05\xfb\xef\xe3\xd3\xcc\xdf/\ -\x06\x8cS\x99\x9b\x0eM\xf1\x12\xb0\x8e\x1b\xd3\xc7LB\ -\xffO\xd8\xe5N\xf7\x09\xe6\x92\x94_\xdf\xfd\xef\xf1\x1f\ -\xfaJ\x8f(b6@X[\xad\xfe\xfb\xe7\xdf\x03|\ -\x06\xe6o\xc2\xb5\x95i\xd7I\xcc\xff\xfb?\xa0\xcf\xd3\ -\xe0\xff-/@KN\xa91\xff\xba\xc6\x08\x80w\x05\ -\xee\x03a\xad\xcd\xce\x00\xab\xad\xc3\xcc\xeb$\x94\xfd0\ -\x16\x98\x86\xfc0XC\xac\xcf\xea\xfe{E:\x07z\ -\xdc\xa33\xf7\xea\xe5\xb1\xc0\xe2\xbf\x12\x9ew\x9a\xf7\xb0\ -aq\xccF\xa2\xbdO?\xad\xd8?\x16\xeb\xef\x1fa\ -.E\xc9\xcb\xeamz\x19\x8d=x\xee*\x91\x86\xf8\ -O\xa0\xfb\x94]\xda\xda\xf7\xd6~\xa8\xbb]5@k\ -\x93\xc3\x7f\x8f\x94\x08\x94\xf8\xf6.^\x13\x8d\x89\xdf\xdd\ -\xee\xa2\xb1fA\xc6[\x9a\xde}\xa8\xd3\x14Y\xf7\x01\ -\x9bF[\x18\x0aRr6:\xd1`|\x1f\x1c\x9b\xbf\ -W\xbf\x11\xc1\xec\xbdU!.\xb4\xe7\x7f\xc8\xd4v\x8c\x9e\xe2\xbf\xef\x01\ -\xacfnW[\x8f\xc7\x84\xc0\xfa/fR\xd8y1\ -\xb0\xc6TW\x1a\xe9g\x0aZ|\x5c\xa5\xf7\xd7\xae\x7f\ -\x81\xff\xf4:\xc3\x09\xa3]\x14\xe8kK\x80\xcc\xb9\xdf\ -\xe5\xb3\x06\xe0\xf92\xf0\xdc\xa1>\xb7\xdf\xeb\x8b\xfc7\ -\x16\xc3\xce\xec\xc1b\x86\xbaT\xffM\xa4\xb9\xfe\xdb\x89\ -\xf9\xd8^\xad\xbbu\x00a\xfeR\xa1\xe9_\x10\xe7M\ -\xcb\xfe\xbf\xf8G/\xf0\x9f\x12_\x02\xd7Y\xe83\x82\ -6\xf9b[%\xbc\xf6\x9ep\x8b\xf3\xde:Q\xff\xf1\ -\xd5M\xba\xd4\xa0\x85\xb1nE\xb6\xb3\xfa\xb6\xdco\x04\ -V\xf7f\x03\x16\xe3\xf8{\x5cG\xab\x82\xceQ\x8f\xdb\ -\xea\x01\xfeC\xde\xc3\x06\xf3q\xe1\x1e\x8d\xf8)\x08\xab\ -1V\x0e\xcfzs\xdf\x8c\xd5\x94\x87{\x10\xa8\x8b\xc3\ -z\xb0\xad\xea\xbf\xfe\xfcJ\x93-\xf7w\x0d\xd6.\xc7\ -\xf4\xfd\xbf\x81\xf7-\x01\xed\xfa\x9d\x01\xb5\xef\xf6\x04\xff\ -\xe1Y\x11T\x19R\x87\xe5\x9d\xc2\xf3\xc1\xe0yQ\xf0\ -'\xc3\xea?\x97\xe4br\xa4\xcf\xcb|z\xa37\xf9\ -\xdfC\x0d\xd6\x7f/g\xd5\x7f\xff'\xf9\x8f\x9d\xff\xe0\ -\xc9:\xff\xe1_\xe4?\xf4\xeb\xb1\xce\x7f\xf9\x07\xf9O\ -\xae\xc7t\xcc\xe2\x93\xf3\xff>]\x8f\xc5\xff\xdf6\xe8\ -\xcf\x83{\x07\xd6\xf9o\xff\x16\xff)\xe7?\x86`\xe7\ -\x86\xf7\xb1\xf3\x1f\xe9|\x06(\x8d1{}\x9c\xffM\ -\xe7\xbf\xde\xde\x87\x16ZJ\xf7\xb5w\x1e\xf2\x9e\xae\xe7\ -?C;+\xcc\xe9o\x07X\xb3\xc1\x98\x8a\x8f\x96.\ -\xfe\xbf\xfb\xbf\xe1\xffx\x94\xf8!\x90N\xdcnn\x94\ -\xf3\x9f\xc3\xb1ZD\xd8\x99\xf0}\xf7\xfcg:\x9e\xff\ -.\x82\xf1\x12\xda8\xebR\xdf4#-\x0a%\xc6=\ -\xc6b\xe2\xdb\x8d\x11\xf4\xff\x9fU\xc7\xec\x83U\xc1\x0e\ -\xb4\xe3\xa9#\xa5\xe6?U\xff?\xe5\x1c\x19Zrp\ -\xff\xc4\xf3\xfa\xec\xcfX\xdd~\xa8\xd7\xff%\xe7\xbf\xbf\ -\xa3'\xff;\xcay\x81\xe7r\xc0:\x7f\xd4\xe3\x7f\x84\ -1_B\x97\xf1\xbb\xf8\x09cQ\xb4\xd8~!Z\xe1\ -g\x8d\xcd\x03(\xab\xa1/\x02\xb3\xf5\xc1\x9c#R\x1d\ -e\xed\x81\xf9g0n\x0f\xda\x04k\xab\xb1Z\xb4\xd0\ -n\x03\xeb\xf7\xc0\xfcT\xe8\xe7\x801>\xd8\xbb\x0e\xe3\ -\xb41_R\x9f\xe5{# \xef\xef\xd3\x95\xff\x1d\xd4\ -\xf2\x82zq\x87\xfcg4\x9a\xcep\x92\xc0r,\x8a\ -\x8f*av\xf8\xb2\xcb\xdb\xc0\xba\xbd\x1f\xd3?\xe0Y\ -.\x15w\xad\xb0\xdc\xc22O\x03\xa0\xc7\xadA\x8b\x8f\ -)S\xf2M\x80\x0ci\x8e5\xeb\xf3\xd2\xea\ -\xeb\xd8\x0dJ[^\x0fF\xd1\xac\x96\xd7\x5c\xadn\x8f\ -=\xa0-9m\xae\x1bZ_\x1f!\xb5\xbe\x9eKl\ -}-Z\xda\xfazp\xdb\xeb\xac\xd6\xd7\x5c\xe1\xad\xaf\ -9\xda^\xdb\xb4\xbef\xfb\xd35\xc2j\xac\xc6j\xac\ -\xc6j\x0ci6\xad/\xff(\x8f\xc3[_\xb7\x93\xef\ -Y\xad\xaf\xb9\xfe\xb4~\xb4]o\xda\xaeGm\xd7\xab\ -?\xado\xed\xd6\xc3V\x04q\xb5_O\xdb\xae\xb7m\ -\xd7\xe3\xb6\xebu\xbb\xf5\xbc\xf5z/\x05~\xa8 \x94\ -qeC\xc4(\xbfWAhj\x1d\xe8zj\xb8N\ -\x13\x86\xeb9\xd5\x00\x0d\xbd\xa0k5\xe0\xcf\xce\xc6i\ -\xb1\xc7ik\xa7c\xd1\xda\xa8\xf4[\x08\xc0\x10 \x02\ -\xa0\x94\x09\xf4\xcc\x8eP\x8a\xd3h\x88\xd3L\xf38\xb4\ -\xf9\x0e/\x80.@\x5c/\xf1\xb8;s#\x0e\xa7\x9d\ -\xb7\xb3c\xd0\xa6\xef\xe2\x007\x01j\x99\xa0?]E\ --\xde\x07\xf1?\x8dA\x9b\xbe\xcb\x13\xfe\xae\xbc\xe1(\ -\xbcOT\xc7\x80J\xdf?1\x01\xcd\xf4\xc6\xa7\x8e\xc6\ -\x80\xd0z\xce\xffM|\xa76\x0f\x9a\xde\x856}\x87\ -r\xe2&\x13\xd0\xc8h\xdc$\xb4\x91\x898\xa0\xac\xec\ -\xcb\xb2\xae\xb3\xa8\xc5\xfb\xdav}\x8fc\x02\xdaz\x0a\ -q\x84\xd6\xfa\x01\xd4\x17\xfa\xd2\xfa\xde]4\xe0}n\ -\xd4i#\x98\x80\xa6\x9eF\x04\xa1Y\x9fgf\x9d\x96\ -Q(%4\xefez\x9b\x96\xdeB\xe3>\xae\xe7\x9e\ -\xb9\xb7E}Fx\x0dkG\xc1\xb3R\xe19R-\ -\xcfn\xeb\x99\xbaB\xb0\xeft\xac\x93\xdcQ\x9f[\x9c\ -5xT\x09\xabOZ\xf9\xc8\x1e\xady}\x0b%~\ -x\x88\x12?=\xc1\xce\xba\x85\xf5\x87+|-\xd1R\ -\x87\x95X=SJ\x9dJ\x86\xd6\xd5k\xdc\xbf3\xee\ -\x19\xf0\xbc=\xf3\x89X\x9f\xb1s\xd7\x8a\xb2)\xb5y\ -\x7f\xd3\xe0Y]\xf5YqX\xed\xe8\xe2\x93\x0b\x9a\xe7\ -\x0d\xfd\xe9c\x9c\xed\x02\xf2\x1c\xcc\xed\xd2\x8b\x9b\xd0\xda\ -\xa4\xb0.\x9f)J.\xceE\xabB\x5c\xd0\xa2#3\ -\x18q\x9e4\x83\xfa.\x8c\x9d\x93\x01\xcf\xf1\x86\xf5\x96\ -\xe9\xd1`\xbdfx\x86\x1f\xe5\x19L\x5cs\x0c\xf4\xbd\ -\xc8F\x11;'\x86\xde\x0d\xd6\xda\x84u\x8c)\xf5\xf6\ -\x98p\x0c`\xdf\xc1<\x85\xf3\xbd3\x0d\xd6\xe3&W\ -\x14\x82~\x15\xa0\x0dU`\x9e\x90\xea\xff\xfc\x1d \x1b\ -*|,z\xbf\xaf\xed\xfa.\x82\xd5Cl<\x93\xb2\ -C\xfa\xabJ\xd0\xda/\xcf\xd0\xca\xfb\x87\xd1R\xd7\xf5\ -h\x89\xbd\x1aZ|B\x15-9\xbb\x0c-\xf3\xdc\x81\ -\xad\x01\xf5?\x12\x01\xb3\xc9\x1d\xdf\xa3\xb2\x18\x93\xa7\xcc\ -Vs\xb5\xf2\xe1IJ\xaduj\x0d\xc8|b\xdc#\ -\xb4\xd4iu\xeb\xba\xdbMg\xb4\xe2\xf5\x9a\xc18\xc2\ -z\xa2\xb0\xae;\xa9\xe0[\x87c\x00\xcffg\x9as\ -\xb6\x01\xed\xf0\x9cQrY~\x87<\xaf\xf4\xb7E\x0b\ -\xcd$;'\xc31}A\x18\xabo\xff\xbbw\xa9&\ -\xca\x0b%\xec\x97\xe8\xfd\xfe\x03\x1a\xe0Y-T\xfb^\ -]\x86\x9d\xa5\xd1\xd4/\x1a\xc7\x15\x9e\xd3[\x9b\x10\xda\ -\xe1\xbda\xed~\x06\xac\x8b\xb4\xf1\x1e\xbc\xbb\xe4\x8a\xa2\ -\xf6\x04\x92I\xd8\x99Q\xd8\xb9\x02]\x95\xd7\xe0\xfe\xc5\ -v\xca\x14\x99@m\x0e\xc4\xf8R\xf4\xe7\xde\xea?x\ -\xff\xaa\x82\xcfS\xa5\x0d\xd6\x0b.\xb4\x9c\xd2}]\x1e\ -\x8cA\xf9\xf5]Tu(\xa8\x1f\x15\xdb\xcd\xed%9\ - \x82\xe9\xb6\xf0\x5c\x94vs\xb3\xbe\x96r6(=\ -\xe6&\x5c[\xcc\xa4\xa8\xcb\x02 o\xcbo\x1b\xf7\xce\ -;\x00\xc6\x1c\xae]\xd4\xe4^}N\x02Zd%G\ -\xbf}\x1c\xe8\x1f\x5c\xf7\xb1\xf3<\xda\xbe\x03\x91\xd7\xd1\ -^\xd1\x87\x00Me\xee:T\xcf\x15\xaeys\x9b\xce\ -\xcf\x12FKN\xa9a\xfaR\xbb\xf7,%\x02;\x9f\ -\xa9\xe7\xfb/\x08\xf4Q\x13\xaa<\x81\xeb\x1d]\xe7$\ -\xd4\x0b\x0e\xca\xa1\xf5\xb9I\xed\xe7\xda\x8f\x04\xb4\xf0\xa0\ -l\xcf\xd7\x22\x06\xfd\xaf\xbc\x7f\x84\xfa;y\xcb\x98\xa2\ -\xcf\xd0\xedy\x14\x19@M\xd6\x90\xf23\xa8\x9f\xc1\xc4\ -\xea?\xc3\xfb\xdf\xe1\xfc\x0f\xe8\xd9\xf9_\xd4+\xf3\x1f\ -\xc8?7\x1d\xaa\xf6\x1cx&\x13E\xef\xa1\xd7\xb3\x84\ -\xb1\xbd\x12u\xf9\x17\xd9;\xf2\x0f\xae\x7f@7\xab\xcb\ -\x88\x05s0\x1d%\xfdJ\xa3\xa0 \x03;\x17\x0d;\ -G\x8c\x9e\xeb\xdf\x1ds\xea\xeb\xdf\xab\x1b\x0c\xe4q\xa3\ -M\xb6\x05Z\xeaZ\xc6b\xd8:\x0f\xf7lE\x87p\ -\xc0\xff\xc3\xf3\xb4\xe8\xa5\x97\xfeQ\xff\xd9\xcf\x18\xfd\xc7\ -X\x14\x9b\xdf\xd8\xf9Q\xf0,)\x88\xbb\x07\xd1\x923\ -K[\xf3\x95\xd1u\xf1\xa1\xfe{m'u\xfd\xb7$\ -\x97q\xfb`\xc0?h\xabh\xdb\xaa\x9e\x9ci\x1eo\ -l\xaf*\xf8\x1bt\x93/p\xffsL\x19\x93q\xd4\ -\x1a\x11\x9e\x11\xca\xa8\xfdO\x87\xfd?\xdb\xe4\xa3\x80v\ -\xce2\xf7\xcdh\xd9%=*\xd8\x8a\x96:\xae\xec:\ -}p\xff\x0b\xd65x\xfe,\xb5\x86\xed\x7f]\xd73\ -N\xf7\xff\x13\xff\x01J\x9d\xd6`t`g\xedA\xfb\ -]K\x00Y\x05\xd7k\xec\x8c\x90&\xfd\xbc\x13\xe7n\ -\xe1\xfe\x92b\xfb\x05\xe0\x9d\x7fI\xb5\xef\xb0\xd5D\xdd\ -a\xac\xfd\x03\xf6\x9f\x8a\xed\x01\xfa%\x9a\xfa\xef\xbc\x96\ -r\xde`\x07\xad\xee[l\xab\xfeC\x1bP\xa1\xe9\x84\ -\xf6\xe7\x91\xb5\xb4\x7f\x01\x19Zq\xef\x10\xa6\xd7t\xd4\ -Hy\xa9\xe0\xbd\x9f\xc7\xd8}/\xe8\x7f\xd53'\xfc\ -\xdc\xcf(\x0a\xc0\xff)\xba]'\xfb\x9f\xd1\xa2\xffP\ -_\xf0\xdc\x0e\xe6s(\xb6?(\xbd\xa0\x05\xf64\x8b\ -\xb0\xf3\xd4K\xce5\xda?\xddp\xfbg\xc7\xe7\x06c\ -\xf6Ox6h\x0f\xec\xf9\xb1sop\x9ea\x806\ -\xbc\xc69Gs\xff\x05\xb1\xf3\x84\x9b\xfa\x01\xe49\xb9\ -\xb2\x08\xb3\xebc~\x92N\xdb\xbf-\x19\xde\xef\xd6\x10\ -i\x83f\xf9D{\xff\x8d\xa9\xea0\x9diL\xe7\xff\ -\xe8\xc1\xfeC\x7f(\x83\xfc_\x9d\xf3\x01\xb6\xfa\xcc\x1ea\xccw\x00i\ -\x86\xf1\x81\xe4\x92<\xea>\x7fR\x1dJ.\xfa\x81\xe5\ -\xcb\xc2\xf83,_\xaa{\xfc\xe8>\xed\xe0\xf9%\xa7\ -\x97\xa0\xb5\x9f\x82\xd0\x86:b\xa7\xfd\xb7\x0d\xc4J\xcc\ -\xb7\xd7\xbb\xb9Z\x22\xd8\x98\x93\x08Y\x9d\xa6\xbbm\x83\ -~/,\xd6\xaa\xa7\xfb\x00\x9e\x07\xe3b`\xec\x03\xd5\ -\xf1\xad\xa9\xc0b\x81\xb0\x18\x9d\x8c\xb7X\x1cjG\xf1\ -\x080\xf7\x9a\x92/\xd5C}\x801\xbbg\xd5QR\ -av;Z`\xceHu\xd8%,\x1f\xb0\xe8\x90<\ -%\xc6\xc6L\x0a\x8bw+u\xdd\x80\xe5\xc6\xc3\xbc\xb9\ -v|\xf8\x91\x88\xc5\xdc2\x9c\x0f{)\xb9=\xd4b\ -\xef\xea\xd2\xdfb>jJ\x1f\x85Z\xf8\xc4D\x9a\xfd\ -\xbbX\xee\xf7F\xaaq\x8b5\xd1>\x98\xfcb\xec\xd8\ -\xc3\x98\xd1]\xedb3\xea\xd2\xa2\xb1\x1a\x04\x9d\xf2_\ -\xc3\xdc\xb6Sj\xedr\xcf`|\x02\xe4\x11#s\x8e\ -\x0aM\xc6\xb7\x1b{(/a\x9e%M\xcf\xc5rt\ -6\xb7\xcbC\xc7\xe2\xd3\xb18\x22F\xcc\x1da\xacn\ -\x01\xcckn\xd9\xaaC]\xbb\xe6\xd7\x07\xeb\x18\xcc\xf3\ -k\xf5.\x17|\xa3\xd4\x04`\xc4{\x00\xe3tn\xec\ -n\x95\x83\x0c\xf3\xb3i\x1e\xfb\x16\xf7+s\xdf\xd2j\ -\xdd\x80100\xfe\x8f!s\x08\xdc\x13\xe6]\xb6\x92\ -\x1b9\x09X\xecI\x97\xc6\x1f\xcb\xe9\x9f\xd9N\x8eU\ -\xf8\x98\xd39\xc7\xa5\xf9y\xd5//\xb5z\x16\x8c%\ -\xc2\xf4\x80.\xdd\x0f\xc82\xcb\xc9h}\xf6\xe7V\xf7\ -\xac|x\x821\xe3\xdf!\xfd]\xcc\xd1\xe8i\xfa\xa9\ -\xcd\x9f\xdc\xa4\xae\xe7\xd8t8\x7f,\xe84\x7f\xda\xc7\ -e\xc2x5,'\x05\xc8\x09\x08\x18?\x08s5\xbb\ -$/:|\x7f\xb5\xbb=\xfeX|mcl-\x8c\ -\x93\xc3e2\xfc=\xd4\x05Z\xa2\xcb\xf3\x07\x93\x9f>\ -\xad\xe5'!\x13\xc8O\xc5\xee\xc9O@#\xd4a\x1a\ -\xe3\xa3\xe1zU|lNs\xdc\x9a\xe1\xe8\xd6\xe8\xca\ -\xbe\x0a\xcf\xb1j\xbf~\xf9t{\xfd\x82\xe3\xdd2\x9f\ -\x0e\xea\x92P\xc7\x87t\x16YO\xc7t\xc5\xd6X\x0f\ -t/\x15\x9ah\xa7\xae?T\xd0E\xf6c\xf4\x83q\ -o\xbaou\x19F\x7f\xc1\xeeQX\xae\x14\x9c\xa3-\ -\x01\xf7U\xadr\xa5\x1a\xe3\xee\xa9\xeao\xa2\xd8\xfc\x86\ -\xebF\xdb\x86\xe5\xdc\xd3A\x7f\xc3t\x9c/O1\xba\ -\xa1\xae\x0b\xf7'0\x9e\x14\xa3\xdf\xf7@\xbb\xe7b\xcf\ -\x8e\xf2\xc6i\x17\xc1tP\xb8'\x81\xf9\x02Pw.\ -4o\xa3?W\x16\xb7\xfb>\x94ct\xcb\x97\x07c\ -\x04\xf7\xd90\x86\x1f\xf2\x19\xc6\xaab\xef\xb0\xa1\xe0\x9f\ -\xe9\x872\x16\xec\xd7!_`\xce\x1c\xdc\xbb\xd4e\xbc\ -\xeb\xc4\xfee=}\xf7/-\xeb\x915\xdewOg\ -\xe9w\xa4\xfa\x19j\x8d\xa1\xfb\xc7\x969Xx\x9c5\ -\xccS\xa3J?\x94\x1b4\xd0\x8f\xed\xdf\xdf\xde\xa3\xc7\ -\xfe\x9d\xba\x0d\x02\xe6\xbb\x80\xb5\x11\xe6\xb7\xc1=:\x16\ -\xf7\x0b\xd7\x1b\xb0~\xc1\xf9@\x99\x1782\xdf\xa3\x95\ -\x0fN4\xd1\x0fk\xa8Q\xad7\x83\xd9Or\xe8m\ -?\xa1n\xbf\x82\xba\xf2\xcd=\x18\x1d\x98\x8e\x0c\xf6\xbb\ -\x94Zo\x12\xf8\x9e\xb65\x9a\xe5\x86\x08\xf6\xaeR\xec\ -W\xb6=a\xbf\x0a\xeb\x90~\x18\x93\x0e\xf8L.\xfd\ -\x85\x96\x9cY\xd2B.\xb7\xcd\xfbhCG\xd3\xbck\ -c;d\x8c\xfd\x90\xba\xfd\x16\xce\x1f+9,\xcf\x0e\ -\xc6\xe5C\x19\xc8d\xf1\xa9\x8d\xf6\xdb\x8e\xed\xe7\xadb\ -\xc2\x99\x8av\x94\xd0l?\xef\xeb\xfe\x8b\xbe\xee?\xfa\ -\x1b\xfcw}\xd6\x7f\xda\xd7\xfd\xd7\x7fC\xfc@_\x8f\ -\xdf\xe8\xcb\xf13X\xfc\xd0`\x04\x09\x87?9\xf0x\ -#\xb6\xe6\x9f\xac\xd6\xbdf\x03\xffi1\x9e\xe1\xf0'\ -G\xf3\xb8\xc38-1\x84r\x84OS\x9c\x16?\xf5\ -{\xb5\x99[C\x01\xb6\x01<\x05( t\xef4\xces\xda\xbf\xdf\x14\x0b\xd1\xa2n\x09\xed\xb6\xde\ -\xc6w\xac\xf3\xdf\x81\xf5\xb1\xc1\xfe\x14\xdaN`\xbd\x10\ -X[\x01\xd6\x0d\x84\xf5\x10\xb0\xfaA\xb4\xd1\xd1\xf9\xf7\ -\x1b\xdes\xbf\x04\xb6\xd7\x845>0_\x15\x16\x8b\xd0\ -\x80\xd5^\x806)X\xbb\x02\xda\x11\xa0\xed\xbd\x93\xfb\ -\xb9N?\xbb\xd0R\x1a\xcb\x8dn v\x9c;O1\ -@\x90\xb1z\x11t\xb3Ya\xb5\x84$1\xfbX+\ -\x93A\xc17\x94\x18{\x1f\xad~~\x91\x92\xeb\xfa\xfd\ -S\xab\xbaV\xd0\xfePd;\xab\xfb4\x80\xefc\xb6\ -\x192%\xee\x03\xda\xba\xaaC/`6\x10,\x07\x18\ -\xab\x87K\xc9\x93\x866LX\xdf\xa7\xc9v\xd3]\xbb\ -#\xf4C\x1cV@I?\xbf\xe2\x9d\xae\xc7\xf2\xb01\ -;r\xdb~\xe1{\xd22\x8f-M5\xb7\xa0\x8d\xa0\ -[v\xdbF[\x09\xeeW\xacM\x0e\xa7\xd4\x06\xf8\xdd\ -\xfc\x06\x7f\x83\xf9\xd6Mc\x80\xd5z\xec:\xef\xab\xc3\ -=\x9b\xee\xd5)\xff\x0b\xb4a\x9fY\xda\x14\x8b@\xa9\ -g0\x09\xed\xd2>}\x9f\x18\x16\xf7C\xe1{5V\ -;\xe2\x8fc\x89\xd97\xe41\x1b)e\x9efb5\ -q\xbb\xe4\x87\x02|\x86g\x0d`\xcf\xaf'\xe2\xb6T\ -\x06?\xbfe\x8d\x1b\xc8\xcb\xe0\xf3\x98m\x0e\xde\xa7\xcc\ -\xd3\xe0\xcf\xcf\xa76\xfe\x16\x9d\x1c\xff}\xe2\xd8w\xa1\ -\xdd\x15\xda\xd5\xe1\xfbE\x891\xa1\xd4(\xc3\xecq\x7f\ -z\x9f!\xcd\xcf\x9c\x9b\xe7_\xa7k\xadQ\xea\x81C\ -\xb9\x01\xe5i\xed\xe7`L\xdeB\x9f=\xa4\x09\xc3Y\ -uJ\x9d4j9\xd5\x8d\xef\x9f\xfb\xe6\xa6\xd8\x1f\xca\ -\xfb\xd7I\x9f\x1bV\x8f\x5c\x1a\xad\xcf\xfeBy\xd7\x92\ -^b\xe3\x81\xc9\x5c s\x1a\xaa\xcb1?\x0cVK\ -\x06;\xa3A\x8a\x22\x07\x9a\xe4\xcf\x14\x8a\xfc)n-\ -\x7fh\xf2A\x03YU~\xd5\x00\xab\xe5\x0d\xe5\x08\xcc\ -\xa3\x87\xe3\xd7\xb2\xc1z\x1aX\x0d\xe1\x10W\x94\xf8\xf1\ -!\x90\xbfn\x1d\xca\xdf\xe2\xae\xc8_\xd0\xb7\x82\xdd#\ -)\xfe7\xd0\xaf\xb6r\x1f\xd6a\x87\xf7\x84\xcf\xa6\xda\ -h_\x7fZ\xaf\x81\x80\xe70n\xa2\xe4\xf4\x22\xec\x1a\ -\xca[\xe8\x83\xa9\xcf\x89\xc7b\x83\xca\xaf\xef\xc6\xee\x0b\ -\xfd\x0aX]\x94\x86n\xaf\xbf\xcd\xfa\x07\xceG\xf8\xac\ -\xda\xc4\x97\x18?0?+\xb8_\x13p~\x16\xdb*\ -\xd1K\xffh\xa5\x7f\xc1\xda\x19\xe5^&\x98}\x1f\xca\ -\xbf&\x1f[\xdb\x1ay\xf4\xd3\xbf\xda\xeb\x9f-\xebm\ -0\x0e\x8d\xfago\xeb\xdf\xbd\xbd\xff`\x86\xfdW\xaf\ -\xed?{{\xffm\xd3\x8b\xd6\x18\xf8lh\xa7\x80f\ -\x091\xa4\x85\x9d\x82\xb3\xfdgq\x9a\xc7\x00\xb8\xe1}\ -\xfa\x9d\xed\xae\x01\xff\x8c\x1b\xfe\x9d\xc6\xef\x06ua\x0c\ -\x83Z<\x97\xca\xdf)g/4\xe9,\xd4\xef\xd1H\ -s\xeb\xdf\xc3so\x80|\x85\xb1\x7f%\x0e\x9a\xcd:\ -}{9\xd6\xbe\xbf0\xbe\xfa\xec2\xac\xde l\x18\x9b\ -\x02\xfd\x96\xd8\xd9\x22\x8dgl\x18R|\xe1\xf0\xb9\x94\ -x\xd56\xf47\xeeA#\xaea\xeb\x1e<\xaf\x07>\ -\xa7\xd4U\x0b\xd3\xb9\xe0zU\x1d\xe1\xf9\xbb\xf1kh\ -\xf4\xe1\xc28\x22\xe2\xbb\xfb\xd8\x99$\xf0\xfb\xb5\x89/\ -(\xfe\xd4\xdf\xf3\xaf\x00\x8b\xedv\xd3\xc1\xc6\x16\xae\xb5\ -X\x1c4\xb4\x05\x00]\x03\xc6v\xfca\xfeP\xe6/\ -\xd4\xd9\xb0\xfd[\x8b\xf5to\xa7\xe6ow\xdf\x9fn\ -\xbd\xbf\xddm\xff\x07:J\xaf{\ +\x01\x9f\xd1x\x9c\xed\x1d\x09\x5c\x8c\xdb\xf7\x9bR!\xd4\ +\xc3#)e\x8dl\xf9{\xc8Z\xf6}_\x1f\x22<\ +\xbb\x92\x84,\xd5\x90%d\x97\x9d,E$B\xa4\x92\ +\x22\x22k\x12\xd9[H4\xed{\xd3\xcc|\xff{\xee\ +\xf7MM\xd3LM3\x93&\xba\xbf\xdfy\xbdI\xf3\ +}\xf7\xdes\xee\xd9\xef9\x04\xc1 T\x09\x18\x0cB\ +\x9f\x98\xa7M\x10\x0b\xd0\xff3\x99\xd4\xe7\xb65\x19D\ +\x18\xfa\x9d\xa9)\xfdy\x00A\xa4\xb7`\x10FF\xd4\ +g\x8f\xd6\x04\xd1d>\xfa?}\xfa\xb3&A\xdc\xdb\ +\xc1 45\xa9\xcf\x8bj\x10\xc4\xe2\xe3\x0cb\xfb\xb8\ +1C\xeb\xd6\xd6\xae\x8d\x1e]w\xf8\xb0A\x13\xe0_\ +\x01j\xc2\xabgZ{\xa0wj\x1b\x0e\x1f\xd4\x7f\x92\ +\xed\x87\xe4\xcf\xa3\x975\x88\xfa\xa2\x9a\xf6\xa8\xd5\x18+\ +\xa2\xc1\x86\xe8e\xdeM\xe3w\x5c>1\xe4\xb9\xd9?\ +\xb5/\xd5\xb09p\xd0\xccP\xe5\xcc\xe5'\xdb\xa6]\ +\xf3h9\xcd\xac\xd6\xf8I?\xaf\x98\xaeh|\xd9P\ +\xc9\xac\xf9 \xb7\x05C\x0f\x8c\xaeU\xbf\xc5\x88\xc6\xcf\ +'\xef\x98\xda\xff\xf9\xf1\xd0eg{t\xeb\xb4\xeb\x80\ +\xf1\xf6\xe8\x87\xcf\xb4\xe6-{\xa4\xd75\xd3\xa4k&\ +\xa7\xbb\xe9\xf4\xb4\xf4w\x09G3\xbb%\xe4Y\xfb\xe9\ +8=>~\x8c\x11\x9c\x909\xfcp\xc8\x94\x93\xd7n\ +\xae&\xec\x08\xe7\xd9\x8c\x98\x06\xdc\x97\xdf\xec\xdb\x11\xe4\ +\x1b\x87\xa0\xd3\xe4\xda9\xad\xbf\xec\xdb\xdf\xe9\xc8I\xf4\ +\xf7\x1b\xfcf\xbex\xc3\x08o2\x83\xe58%\xcc\xe4\ +\x95C\xb2r\xfb^.\x8765\xaa\xa9\x1e\xae4\x17\ +\xfd\xdd5}\xb3\xb6\x9e\xb7\xffG\xf4\x9a\xb7\xd4\x8c\xd8\ +\xca8\xa1j\xa5z9)\xf2HC\xe6\x05b\x01c\ +\x94\xd7\x83\xde\x87\xdc\xd5\xffk\x14U\x83i\x7f\x98\xac\ +=\xfda\xea\xfeN\x1a~\x1f\xfe\xc7lG\x98\x18\x07\ +\xae\x8a\xf9A\x84\xa7f5>=\x99\xd3\xe9g\xc6^\ +\x83\x8e\xcay\x8c`e\x83[ut\x9a\x10hns\ +\x88\xd9\x03\x97~ip1\xae\x1d3uJ\x9c\xee\xc4\ +\x08\x95\x8b\x87\xe6)\x99\xea\xdd\x0c\x22\xbc\xe2\xd6\x0c?\ +\xbc\xcdc\xd3|\x22\xba\xff\xd2\xbf\xdd\xdc\xd4'n\xcf\ +`\x9c\x9e\xca\xd1\xf2\xd9\x11\xdd\xc9\xac\xd9Ym\xb7\xd3\ +*\xfdg\xbe\xd1\x1f\xd7\x0b&\x16\xcd\xfc\xdb\xf5Dg\ +\x15\xaf\xa5i\xbbl\xfa\xefh\xd7\x84X\xa7\xe7\xa0\xb3\ +,\xf1@@\x07e\xad\x89\x8b\x9dM/\x12\xe1C\xb3\ +S\x1d\x1d\xd7\x7f\xb3\x1c\xcb\x989i\xce[\xfd\xc3O\ +c\x996\xcd\x99\x1bV\x92k\xbc3\x0e\x14\x9cd\xa4\ +\x05\xf0N66\xf8\xa0\x14\xde8\xed\xd3\xa3\x80\xec\xbc\ +A\xb3\xae\x13^\xdd\xbc\xf5\xd3\xe3\xcevd\xbex7\ +\xbdN\xf0K\xf3x\x83\xd4D\xe5\xb4\x1b\xb3\x1a\x0fz\ +\xf7E)\xfcBN\xca\xc0\x19\xb7F\x0e:\xd6n\x91\ +\xd7\xc6\x83f\x9d\x17\xadW7]\x9eA\xd6\x99b\x7f\ +vnH\xf8te\xbb\xd5w=,\x18\xa7\xc7\xa6^\ +\x0b\x1b;i\x96\x9e\x8f\xa6\xd6-\x96\x8aE-\xf4\xb0\ +\x0f*\x84\xc9\xa6\x9d\xeb\x9cr^\xd7\x8b\x19\x1a\xf8b\ +\xda\x8b\x0c\xe2\x87\xb3\xd7\xe7mo\x13\x0c\xce&~\x8e\ +b\xedv\xefP\x97\xe0\xd6\x9d\xe9\xcd\x1e\xad\xe9\x17\xa9\ +\xe290\xf8:\x81^\xfe\xbf-\xee\xcd\xcd\x87\x13\xde\ +\x19\x0d\x8e>\xb9\xaei\xd4\x97\x98\x96\xbf\xbd\xc3\x9c\xe1\ +5\x8fh\x13\x979\x83\x0e\xfd\xafo\x8dQ\xea\x84\xdd\ +\x87\xb8M\xcf\x1bo~\xbc\xb4\xd3\x91\x9e\x83\x17=\x99\ +`n\x17{\x7f\xcf\xf5\xa8-\x8f'\xde]f\xb3\x9c\ +11G\xb9\xed\xb4/\x1f;\xd9\x0dv\xea\xd3J%\ +&s\xc8\xa9V\xcb\x8c\x89,\xb5\xf5\xf6*kR\x9b\ +\xef\xf7&:\xd6\x98\x90\xcd\x99\xdf\xb5\x99\xaa\xbf\xc6\x14\ +\xfb\x16\xed\xec\x16Y\xb9X|f\x9c6\x09\xe9\xd3\xc1\ +N\xc5\xa9\x8f\xe5\xed}\xdc\x1a\x89\x89J\xde\xd1c\xdd\ +\x0e6L\xd4\xd2X\xe4\xa6n\xa5\x87^\x7fi\xd1\xd0\ +\x93f\x1b\x8f)\x9bF?\xf3\x1e\xcb\xd6m\xf0j=\ +;\xf1\xfd\x22\x9f\xf3\x9d\x98wW\xe5\xa6F\xa4\x1et\ +z\xc2^xs\xc6\xee\xa8\xd7*1+\xb9\x83\x96x\ +^\xdc=E\x97`/\x09\xea\x1a\x91\xba\xdb\xc9\xb0\xd7\ +\xc4\xa7\x87\xe6\xde\xf4!B\xdd\xa3\x9b\xfc5\xd9=\xd1\ +\x7f\xfad\xe2\x87\xa3\xbaYD\xea\x0e\xa7K\x11\x93\x93\ +\x991w\x1d\x99l\xeb\xe8\xe8{\x8bMZ\x18\x0c\xef\ +\xdfTs\x94K\x81\xde%\xdd\x1a\xce\x1b]\x5c\x1f/\ +\xfep\xe6\xcd\xe7\x1d\x86\xb7\x08\xdb\x5c\x03k\x9f\x86\x86\ +>J\x97\x163f\x12\xedM\xf6M\xbc9\xcfu\xf4\ +\xbac\xb9\xf9jw\x0fu\xb0k\x15\xb9y%\xd7k\ +\x15\xc95y\x9bk\xd43\xa8]~#'7\xbb\xba\ +\xfa\xb3RL\x0e\xd5%\xec>\x9e\xe9\x14\xb6\x7f\xe8\x00\ +5\xcd\xa6K<\x0bR_v\x9c\xdf\x84\xf8\xb6\xca\x0c\ +\xd1\xa9z\xfb\xf4\xddj\xee\xbcy\xee\x9d\x82\xa7F\xb4\ +6h1\xf1\xa0\xd7\xd3\xd6\xca\xa6\xb33\x1d\x86\xf8G\ +\x0e\xfaoG\xd7u'y\xa6~\x8d\x82U~\xd6o\ +\xb0\xefb\xa7\x83\xec9~\x84y\xa6\xebc\xb3\x11o\ +\x7fj(\x99F'_\x18\xe9\xef3\xe8\xbfK\x9d\x02\ +-\x83.LRg\xaeqh\xb1\x86}\xda\xf2\xf01\ +U\x9dC\x9dZ\x87\xed\xd8z\xfc\xaf:y\x9a\xb5\xba\ +\xc5\x9e\x0b\x0fX?(\xebvN\xe6\x84\xfaa\xban\ +=V\xe6\x9b\x04\x8dJ\xd1?\x196g\x95\x9d\x86'\ ++ b\xf9\xf6\x7fB\x88\xd0\xda\x0ei\xb3\x88\x1f-\ +\xb7\x98E\xb8\xb7Z_\xc7\xa6\xe0\xaf\xba=\xbe\x9e\x9b\ +\xa7\xa6Y\xb7GB{\xfb@rJ\xeb!\x9a\xcf\x02\ +\x9f\xc4\xed\xc9\xee\xdbj\x7f\xefa\xad\xd6\xb8u\xde\x18\ +e\xb3*\xf4\xad\x7f\xed5u\x0c-\x19uF\x1e\xde\ +{\xf9\xafZ\xdd\xe6\x9c\xfe\xb8\xe7\x80\x01\xc7i\xebq\ +\xcd\xba\xa9\xbe5#\x93\xa2L\x98\xc3~\x04\xab\xf5\xef\ +\xe2{\xf0\xe1'\xcf\x0e\xb3\xda\xac1\xf2=8\xb8\xd7\ +\x01\xcf\xcc\xbf=F\x84\xb4\x9d\xd6d\x1a\xa7\xa3qG\ +\xb3^\xff\x8b2_\xaenYOoH\xab\xff\xd8\xc3\ +\xd3\xcek\xfd\xc8\x09j\x5c{\xe5\xc8\x7fG\x91\xb7v\ +\xf9\xb9w\x98Q\xf3\xad\xf1\xae\xda\x0c\xab!\xfe6\xb3\ +\xfe\xfb'h\xfd\xee\xa5k\xbe\xdb\xb4\xd9\xd6\xbe\xc1\xe2\ +\x89\xbc\xe4SV7\xae\xbb\x04\x1d\xbco:\xcd,:\ +\xe4Y\x5c\xa3f\xe4\xf5\x87FF/\x8f\xde7\xf9\xa6\ +l\xea\xdab\xd1\xe1N'?\x0e\x1e0\x1aM*\xbc\ +\x81ehp-\x870\xfb\x1e\xc7\xef\x1d\x1b\xf6\x83\xe8\ +t\xb2^L\xc4O\x02mL\x1f\xde|\xf5\x03\x99\xc7\ +\xef\xf4\xdb\xb58\xd5\xd9+\xbb\xc96\xa7\x86\xb7\x08\xb6\ +\xdb\xc8\xef\xdd\xef15^Yu\x1d\xb3\xba\xe0\xaf\xbe\ +W>\xc7{\xfa5m\xda2M\xa7\xaf\x83\x81\xb6e\ +\xe8s\xab\xaeh\xca\xf7\xbfq\xe0\x95\xdb\xfd\xdc/\xdb\ +\x5c;qI\xe3\xc3\xbd\xb3\xa9*\xc1\x06\x11\xee\xd6\xbd\ +\xe6M\x0aL\xb0]\x19\xa2l\xf1\xcaH\xf9\xf4\x89}\ +\xbc)\xceY\xbes-\xdb\x1d_\xfe\xf7\xb8\xd1\x19\xab\ +\xcc~6\x0aNd7\x8b{\xdd\x94\xa2\x9e\x0e\xedg\ +\xac\x08\xb2\x0bZ\xec=={\xf3\xf1.7n[\xfc\ +\x9b\xff9X\xab\xb3V\xd3%!yY\xaa\x8e\xa7\xd7\ +\xd9\x85.o\xa8\xc14s&'9\x10G\xdcoG\ +[\xae\x9e\xdfy~\xcc\xe6\xec\xab\xb1\x8d\xf6\x853>\ +5\xe9\xfbL\xdfc\x96k\xfa\xf1\x05\xfaA\xb9\xb3\x03\ +l.N\x1a\xf7i\xb9\xde\x8f\x9c\xa8@\xe3\xf3\x7f\xad\ +\xde\xec\xfcQ\xab\xbfU\xca\xeb)\xcec\x97[9\x9f\ +\xbe\xb3:\xf0\xc5\x05\xe7F\xa6\xae6\xdb\xdd\x1a\xe6|\ +T\xf3\xe7\x8e\x18}tp\xc8\xf3\x13\xbcK!\xa6\xbe\ +z{\xfc\xf7\xcc\xb5\xb6W\xff\xfa\xba\xa9\xce\xc8v^\ +\x1a\xc1\x13[\x12\x1f\xdcG\xb6\x8cuxD\x0e\xf5\xaf\ +\xf9w\x17B\xbbkD\xf4\x8e{_\x1a\xa1\xa7\x99\xcf\ +\xed\xde\xd2L\xe7\x8d\x8fr\xea)\xd5\xa4\x03\x9dvm\ +\x9e\xac\xaeV\xeby\xf8\xca;\xc3\xfdk\xeen\x5c'\ +\xcfje\x9ef3\xb3\xbeK\x0a2\x16>\xccf\xdc\ +A+\xea\xbc\x5c\xbe\ +\xd9jaV\xcd!l\x8d\x16\xf5\xaf\x9f\xef\x9c\xb4Y\ +\xc3\xd5\x8bXZC\xb9_\x1f\xffu\x91\x99fJ3\ +\xee\x9aeg\xcdoyF9\xbf\xb5+\xb3\xbb\xf9\xd4\ +w\xf3\x8d<\xb7\xec\x1d\x96\xd2\xd6,l\xddn\xcf\x19\ +\xdd{\xdcF\x13\xeb\xfb\xe8\xf8\x8a\xa5\x89^\x06\x015\ +\x0e9\x04\xb48\xd3f\xe2\xf7\xb7\xedU\xee?\x8b\x9b\ +\xf2YoV\x0b?O\x1b\xc2J?|]K\xae\x17\ +\x22L+^J\xd3\x97[\x98\xf1f\x84\xe7\xdd\xe0!\ +\xfa\x08\x95\xce\x0fI\x8e\xf2\xd8+\xbeQ\xddN\xe7f\ +\xdc\xfc\xd0\x95\x810\xbc\xcf\xc5iV\x03t0\xc3\xb2\ +\xe7\x8d\x88\xaes\xd9|-1\xe95\xd9\xc3\xf1\xda\xbe\ +!^\xc6\xf5L\xb7\x0e\xfb\xc1Lx\xab>\xad\x19\xb1\ +z\xcc\x8d7\x13\x8fN\x9e\xadN\x10\x06\xef\xebz\xef\ +\xeab\xfc\xc3E\xd3\xfb\x1fB\xfb\xfd\xfc[Sv\xf5\ +\xe9\xe5\xb9\xae\xbd\xfe\x93\x8d\xa63\xd6\xb6\xab\x81\x84B\ +o5\xefYw.\x05\xd5\xf7\xfcR;\xed~\xe3\xf1\ +\xddf\xd5b\x8cT\x0e\xbe\xc23\x09\x7fg\xc6\x89_\ +z\xd3\xb5\xd1\xeb[\x91\xdfW2Vg\x0e\xa8y\xfb\ +\xac\xca\xde\x17\xdf\xb4\xfdwz~Q\x22|:\xde\x7f\ +lo\xa9\xc1x\xaa\x14\xfc\x82\xe7p\xce.o\xb3\xbd\ +\xb3\xcb\x93u3\xd9*6J-k|2\xd6]\xfa\ +/a\xecN.\x1f\x17\xf8wp\xc7\xfb\xff\xf9u\xab\ +\xd1!\xc0\x0b\xfd\xc2\x95\xec1k\xbe\xcb\xacU\x17\x06\ +4\xf0\x990ud\xfc\xd2\x85\x87\x16\xddn\xb2f\xc8\ +\x9e#\xab\x17\xed\xac\xdd\xa1`JO\xf4%\xdb\x8b\xb6\ +\xea\xc1\xef<\xc3\x13\x0f=\xb9\xb4\xc2*\x81u\x92S\ ++\xe8f\x8d\xcb\xe3\xc7|\xfd/\xd6z\x9b\xab\xf1\x1b\ +\xc4/B\xef.\x9a8\xf7\xc6\xf4\xae\xd3\xea\x13\xab-\ +\xdd\x16\xbf\xd5\xb1oq1g\x14a\xfe\xbc\xd1\x9c\xd0\ +\xae\x7f\xbf\x0e\xb28jm\x9c\x10\x10lK\xae\xecR\ +\xcf@\x97}{\xf0\xb2s\x93o<\xd8\xfa!mn\ +;O\x0e{\xdf\x12\xf8\xde\x95\x99]\xcf\x91\x1a\x8bf\ +\xec\xbdz%\xb5\xdd\xcdv\x8b\xae\x9d\xfa:\xfa\xc8?\ +\xf1\x81\x1f\x1c\xbe\x7fIdwj\x93\xb0&s\xc6j\ +\x8d\x9c[\xe6?\xe6Y\xbc\xecg\xb0\xf5^\xee\xe1M\ +\xc6\xeb{\xc7\xe7\xd5\x1d\xea\xb5\x98a|\x99\x5c\x8e\xb8\ +\xa1f-\xbf5\xdb\xf4]vG\x1e\xfb\xf7S\xe8\xb3\ +\xb8\xee\x9b&\xf6\xab\x93s+.\xe3\xa2\xe3\x08\xe7\x95\ +\x93\xdak\xa5\xc5\xbe\xf81\xe7DP\xc1\x10\xff\xb6u\ +\xaf]V7W\xbb\xc1&U\xdf\xf8\xd6\xf4L\xf0\xe7\ +\x9dq\xbd\x92ek?2,`r\xaf\x03!\xdf8\ +m\xda$\xf4\xe8\x12\x9e\xfc(~\x22\xc3\xdd\xc4q\x80\ +\xf2g\xfbG{\xd4\x8e|\xf9rs\xe5?\xef\x92z\ +\xbfWWIq.\xb8\x98\xcd\x1e\xf3b\xf9\x5c\x8e\xc1\ +\xc2\xa4]&\x88\xb1\xbal\xff\xa7_A\xce\xd8\xd6\xd3\ +\xdb\xbdq\xaa{\xb3\xe7\xa5\xf8\x09\xcf\xb2\x08\xef\x1b\xa4\ +\x09\xf7\xb8\xdf\xc2\x84\x8bw\x1b\x11\xe3\xe6\xf5\x0a_\x82\ +\xb8\xbd\xb1Cn\xdd\x17\xd1v_\x9d^~C3\xfa\ +\xd9\x22\xf29\xa7\x11s`\x01\xc9\xcc7\xab\xdd\xdev\ +\xf1No\xc4k_|\x19\x13\xfb62z\xc4\xd5H\ +\xe7\xad\xc7\xbb\xe4\x0c>n\xd3%q\xee\x12\x02\x11\xff\ +G\x7f\xc2\x93\xb5\xfe\xd0?K'\xbb\xd6\xcb\x8a\xeb\xca\ +hp\xa3\xc9\x89\xc0\x93\x13\xfcgX\xfd\x9c\x14\xbcO\ +c\xd9\xd1/J\xb7s\xd2:\xb6I\xf0\xed`\xb2\xea\ +\xd1\xa5\xce\x8e\x8936\xfb-\x1e?\xba\xa3\xb3e\xff\ +\x96\x8f\x1e\xce\x083\xde\xf8x\xf1(\x8b\xb1\x8dv\xff\ +Pz\xdb\x89g\xb4\x94\x17t$\xd1\x8c0Ug\xb8\ +\xa4n\x9d\xa2M.V~\xbd\xf3\xe3\x88+O\xf5\x92\ +,V|\xeaY\xef\xd0\x95u\xaf\x1a9,\x8b{2\ +H;\x90\xe4\x05mp\xb0\x5c\xb0\x22 \xb6\xdf\xc9\xc9\ +\x8d\xdf\xd7\xd2;\x15\xe5[sd\xab5i\xef}Y\ +#\xb6f\x9f|\xd52b\xb0\xc7v\xaf\x96#\xb6\x7f\ +\xabW?\xec$g\xb8\xd7+\xef\xd5\xadr\xc3\x1a\xf0\ +\x8e}\xb7\xf7J$\x9b~?\x14io\xd4\xd6v\x9c\ +Wo\xd7\xcdHrd\xae\x89\x8ey\xf1\xe3/\xf7\xb7\ +u\x17\x05L\xd6{\xe6\xbbi\xb2A/\xb7\x99:5\ +\xd1~\x9fH\xca\x99\x17\xb39h\xae\x8a\x0f\xbb\xd3I\ +t\xa4\x1b\x10[4\x12\xee\x84\xbf\x1c7i\xed\xa0O\ +c~\x8e\xaa7\xb5M\x04\xbb\xd6\x04\xaf\x88\x87aM\ +\x9b.\x99GD\xce\xbb\xf2\x83\xb1\xd3z\xa3\xf9d2\ +Z\xff\xe6\xe0#]\xea\xf6\xcc5\xdcy8L\xb7\xd5\ +\xfe\x80\xdd\xd3\x82[&\x7f\x08\x09\x22L\x8fjZM\ +h\x8f\x94\xc6\xb6S\xb4j\x866\xfc\x1a\xd6\xe0\xad\xc6\ +\xf5wo|\xff\x193\xd8>\x7f]\x8fh\xed\xb3l\ +\x8fFJc\x16\xab\xb4\xd5%\xb6D\x1c\x9d\xc9c\xd4\ +\xf7l\xf2|Y\xd7\xa1G\x1b\xb0u\xb9\xf1K\xbd\xce\ +\xf6\x9e\xe3\xfdj\xf1\xa4\xda\x8c\x0b>J:\x0d\x88\xd5\ +K\xff\xd3arW\xf7\xf4=\xff\xa5\x85\xb6\xe5\x84\xb9\ +a>\x0d\xa632\xa6\x0e\x1f\xff\xc6\xf2\xafP\xfb\x80\ +\xe9\xce\xef7&\xf4\x18\x10\xc7\xaa\xd1\xe2\x8c\xf2\x93\xab\ +\xea\xe6\xc4\xc1\xf1\x96;\x8dr>\xbeY\xb89\xf8\x00\ ++9b\xf0=D`\xaa\xcc\x06\xad\xf7\x07$\xf9\xf6\ +\x9e\xd6\xc2\xcfq\xc6D\x87\x9d\x9d_\xea&Y\xf4o\ +\x15\xe9\xb6\xccs/c\x5c(\xa2\x7fD\x0c\xc3\x1b;\ +j\x1bu9\x1e\xd6y\xe0a\x9b+A\x93\xdf\x8f\x1e\ +\x18\xf9\x99\xd8\xf3\xb8FT\xb3\xbb\x7f\xeb1u\xf3\xb3\ +Ng\xcdo\xbb\x93az\xe5\x96\xff\xdd\x9d+b\x0d\ +j\xee\xbag\x8a\x04\xc4\xe3\xec6\x0b\xba\xc6}\xed\x91\ +\xd0\xd0B\xc5\x88x\xdc\xc0m\x06\xc3\xb0\xaf\xf7$'\ +\x86\xe9)\xa4\x02<\xfc\xc69\xf4\x8e\xd5\xd4\xa0\xcd\xc5\ +\xad\xe8\x17\xcd?\xd76\xce\xbd\xc8\xa9\x1f?\xb7\x0e\x91\ +Q\xc7\xc2Q\xdbo\x0as\x87\xf2F\xf4\xa1a\xd8\x14\ +\xe6\x8aE\x97Fh\x11\xdf\xda!\xfa=\xdbU\xe9\xd2\ +M\xa5\xc8\xa6.\x9ai?\x12\xc7\xef\x8b\xea\xb6\xda\xa7\ +\xf9\xfa\xcf\xeb\x0f\xfd\xe5\xe9d06\xcfe\xdd\x80\xf0\ +L\x86y\xd2\xc8\xb6\xfd\xaf\x9b$\xce\x0f=\xefS\x7f\ +\xac\xfa\xa5\xd5W\x1c\x13\x02\x88\xfd\x13\xbb\xe6\x9e\x9a\xef\ +\x16}w\xe1\xf0\xcf[&\xf7\x8e\xba\x97\xc9\x08_9\ +\xb3\xcd\xbe]\xa3\x07\x0e\x0b\xfd:\xfb\xdb\xf4\xd9\x86^\ +ji\x1b\x88avZ]w.\xe3\x8d\x1c\xd1jM\ +\x07\x93\xc0K:./V\xc1o\xb3I\xaf/\xef\x8f\ +\xe4\x18tk\xaa\xd9\xf3\xfc\x0bc\xe6\x05eS\xeb1\ +ow?\x1f}\xf1\xfa\xcf\xe8\xff\xa6\xcf\xd9\xb7\xb7\xd7\ +\x9b1'\xf7\xd7\xdc\xcb\xd6ws\x1d\xd5rWd\xc2\ +\xe9\x88\xe9\xc4X5\xe6$\xd2a\xf7s\x87\xd6n]\ +{^[AL^\xa0\xd4G\x97\x18\xba\xa5\xdf\xe6\xc3\ +:\xdc\xa9['\xb57u9\x8a\xfe\xc84\xeb\xf6\xb3\ +m{\x87]\xf2\xeb\xef\xd6\xd8\x8b\xf73\xf9\xe5\xfd\xd7\ +\x0c\xfd7u&\xae\xda6\xad\xf3\xba\xe6\x91J\xe3\xe6\ +\x1b\xeee\x98w\xadG\x5c\x1a\xbc.d\xdc\xe2S\xf5\ +\x88\xc9\xec9\xad\x1e\xb1<\xbb\xf4\xd5%\xea\x1b+\x9b\ +\x12FV\x9b\xcc\x88\x16\xadm\xfdf\x84\x12\xfa\x19F\ +\xaa\xcc\xe7_l7\x84o\x8f\xbcOx\x1f\x8dR;\ +9\xacc\x1d\xa2\x85\xc6<\xe2\xdd(\x97n1\x83\x08\ +\xe5\xe0\xd5}g\xae7\xad=\x87\xd0\x5c\xab\xa5\xcc\xec\ +3\xf6fH\xaf\xa4\x07\xe8OG|\xec\xbfo\xc8\xdc\ +yZ\x17\xedj\xeeX\xdb\x8bh\xd5\xac\xc6\xf8\xc5\xbd\ +\xdb\xd6\xdf\xd0b\xd2\xbeG{\xfbr-\x0e\x1e9\x90\ +k\xbdr\xe2\xe0Y\x0e\xfa\xcf\xd0\x147v$n\x0e\ +S\xc9\xd0KRz|`\xba\xcb\x85\x88\xf3{g\xe5\ +\xee\x1b\xa7\xefy\xbd\x9d\xf3\x05e\xfd\xc0v7:E\ +\xb3\x9e\x8fq\x19\xd6\xf7\x90\xe7\x93!\xad\xd0\xb4/>\ +\xef\xb9~sN\x0b7\x8d\xe17C'\xadm\xf3\x00\ +=\xc3\xe5~\x92\x92G\xffK\xad\xcf?i\xa8\x1f\x93\ +Nh\xf6\xd66\xb4a\xde`\xd4\xde[{\xeed6\ +\xc9|\xa4L\xec\x1d\xf9\x15\xe9\x19?}S\xd6\x0e\xd4\ +{\xbaA\xd5\xd0B=!\x9f\xd8\x9b\xb8\xf3\xe4\xa4\xff\ +\xc6\x1a_\xad\xad\xbaanGb\xf2y\x82\xa1J\xd4\ +\xbf:_\xc9\xdc\xdbw\xcb\x1eUu\xed$\x8b\xbf\xd7\ +[LVE\xbff\xbe\xbb\xa7\xc9L:\x1c\xceza\ +g\xb82\xf9\xf5Q\x1de\xd3\xd6\xfb\xf5B\xe3'\xaa\ +\xb9G\xdf5@\x8a\xe1)W^W\xdb~D\xab\x06\ +=\xb5-\xbf\x9f\xd5g\xec\xbf\xe7\x19A\xe8\x07\x13\x84\ +~@g\x0e:&?}S\xdb!-\xb0%\xd7\x9a\ +\xa1\xff>\xbe\xc3\x16\x06\x11\xb3\xc5\xdcpO\x96\xaf\xf5\ +h\xe6.\xd5\xf53\x1e\xaa\xce~}\xf4\x84\xf5\xb6\xbd\ +\xc8\xea\x7f\xd6\xc1r\xe7]\xde\xbbq\xc7\xb3Z\xa7\xd5\ +\x09[\xea5=^M\x8bX\xbb\xbfy\xf0\x14f_\ +\xa4M\xec\xe8\xb7\xbb/\xf7x\xd2\xae\xf3\x0dU\x99c\ +\xd8!s\xda\x07O\xbf\xc7\x1c\xcb\xd1~\xd9\xc1r\xf8\ +\xec\xad\x1aF\x84M\x84\xde\x8ba\x0e\xed\x17\xd6\xe8`\ +\x93oc\x9a\x95br\xe3\xb6\xd7\xb3\x96\xc1[\xb4\x08\ +\x7f\x9b6\x06\xfd\x1a!]jo\xd6|+\xf7a\x1e\ +\xb5\xba\xc4\x06\x04\xd9\xa8\xdf\xd0Q5e\xfc\xd4X\x8c\ +\x14\xef\x91\x17zv\x5ct\xab\xf7\xb9m\xee;\xf5\xbf\ +\xdc\xef\xb0\x8d1\xcet\x8b\xc6\xe2e\x1e\xbb\x947\xb5\ +\xf1\xb7\xe9\xa5\x7f\x97\xdct\xe9\xb9\x03\x93\x91v\x0a1\ +\xc0\xb0\xb5\xef5k\xaan\x18\xf1\xe9\xda'\xfd\xfaK\ +\xea\xeb\xe5)3\x1f\x05-\x1b\xd1\xf8L\x07U\xd3\xbf\ +\xfe\xeewy\xa3\xa5I\xe8\xdd\xbdd\x5c\xaf\xcf\xb9\x87\ +w;\x99\xac\xf0\xf3\xbfk\xd0\xef\xd6\xa4Z\xca\xa7\x1f\ +\x7f\x9a0e\xbbZ\xf7\xb9\x03-\xe2FO5\x08\xe8\ +\xban\xd0\xf6\x7f]\x86\xabX\xb6u1\x22F\xdc\xce\ +\x1d\xb5K#\xb9`d\xff\xd6w\x1f\x8c\xe9\x1a\xe1\xa0\ +\xd9\x92\xbb\xa4\xcf\x12\xbb\xe5HC\xdc\xbcC\x8bh=\ +\xea\xc5r\xc3.\x8b\xe69\xab\x0d\xeb1\xef\xe4\x7f\xde\ +\xbe\x83\x8fv9\x18\x96\xdf\xb1\xf5\xdd\xa1\xe8\xdbO\x8e\ +\xd8\x9e\x1fb=4\xe4\xb9\xadMv\xdb\x81\xa7k#\ +\xbd;\xc5\xb9\xd9\xbeNC\xbe\xe4em\xb5\xf9+\xe7\ +!\xd1\xf6\xeb\x97eC\x8ef-\xa8\x994\xf6\xe6\xd2\ +\x7fz\x06\xb9G\xa6\x04\xc5\xa3/\x0e[h\xfd\xc4G\ +\x89\xc8\xbe\x1bc8>\xfd\x83ql\x0a\xdaJ\xc3\x09\ +\x17\xff&<\xf2\x07\x1f>\x1f\xd7m\xc5\x93\xcfz\xe6\ +\xc4\xa4\x83/\xbe\x1d\x1ci\xfdU\xfd\xe5{\x86\x85\x0f\ +\x19\x96\xbd\xda\xdd\xd0$\xe6D\x80I0\xf1.\xea\xc1\ +\xb2!\x17\xcf\x9d`\xaei\x1e\x13\xbfJi\xdel\x9f\ +v]?\xccn\xdb/0\xf6\x08\xfc\xb3\xc7\x13\xaf\x8b\ +\xbe\x11\x83\xf3L\x8dbB\xcey\x0e\xed\xeb;7\xdc\ +6:bl\xae\xcb~\xf4D\xd7\xdc\xce\xa1\x84\x07R\ +\xecn\xed\xff\xd8\x93\xf1a\xc7\xbd\xa1S\xf7\xac\x8bc\ +\x9cE[x*\xbaf\xbb\x8f\x19~\xdd\xb2}'\xcc\ +\x1b\xf2\x06\xcd\xe0\xe7\x87\x10\xdf'\xf6\xb7\x91\x98\xe9\xe3\ +\xf7%\xe5\xc3\xd0\xed\x8e\x0c=dCL\xb0\xf4\xbe\x1a\ +\xfeiB\xa3\x03\x99K.\xeb{\xa9(\x9f\x1e\x81\x98\ +\xdd\x88\xd1\xea\x8d\xda_ne\xb9\xa4\xaes\x96\xde\x83\ +\xcc\xa6\xcd\xe3C\x92?\x0c\xcc\xf6\xdf\x8e\x1e\x14i\xce\ +\xb9P\xb7u\xb3\x86\x97\x06-m\xa3\x17rr|\xbe\ +A\xac\xb6\x9d\xde\x9e+\xcdV=\x98\xb0\xe8B]\xce\ +\xdfs\x89\xc8\xc7\xed\x86\x9b %\x1d\xc9\x01\xad\x82\xb3\ +\xa3\xf3kOz\xec\x917\xe6h<\xd2W\x8cx\x07\ +V\x9e\xe7\xee\xe8B,98,%\x06\x09\xfb\x88\x87\ +\x1e#\x8cb\x06.\xf3\xa8\xbb\x93\xccWC\x9b>\xde\ +\x7f)\xd2\x9f\xe3\xaa*\xf7\xcb\xfa\xe1\xd0\xc3\xfar-\xa4T\x0f\ +\x1a0z\xf7\xb2\xb7\xa9\x8f~\xe4\x8e\x5c\xe3\xfa\xec>\ +\xb1(\x1c)acN\xa5\xb5\xbf\xa1\xe2r\xd4\xd9\xb4\ +\xad\x0e\xcb\xa9\xee\xf0\xc3#\xde\xbal\xf7\xf9Z\xc0\xdd\ +\xf2\xa9\xf1+\xa5+}9#\xdf\xbb\xdf\xff\xef\xd4R\ +\x8dh\xff\x94\x95\x0b\x88#\xdf\x9e\x0c\x9a\x9e?x\xc0\ +\xa4 D\xa8\xedUagG\xcd\xadS\xfb\x7f5>\ +-\xb4\xb6\x7f\xfaj\xa7\x9d\xe1\x1c\xe2\xc8c\x0b\xe5A\ +n\x93yK5N\x06\xbe\xbbP_\x95\x196%j\ +\xcd\x0a\xa4\xf7oe\xaa\xdf\x0e\xccP\xaeC\x8cDS\ +\xae\xc3\xd8\x969 ^\x0d\xd91#\xfc\xf5-\x0f \ +\xec$%7|TC\xe7qK\x17\x13\x95;\xeaL\ +r\xc4\xe1\xd0]\xce)\xc4\xc0\xe0\x9e}&\xe43\x02\ +_\xdcP7\xef2ou\xb0E\xe6#K\x9b\xe4\xd8\ +\x19W'\xf1\xfa\xb4$\x1a\xb9\xa9\xc7-\x18R\x13\x1d\ +\xb6\xe1\x8d\xb7\x8c\x08_\xb0<\xe2\xca\x89\x84\xab\xe6\x8c\ +\x18-\xa6\x17\x9a\xfc\xd3.Mw\xd8>D\xa6'R\ +\x05\x1d\x8f>\xb4\xf8y\xf6]G\x15o\x15\xf3\x80\xb0\ +\xfc\xd1H\xa1\x8b\xab\xb3Q\x09\x99M+rr\x5cg\ +\xe9\x9e\xfb\x97 fw:\xec\xf82a\xe0\xc2e\xc9\ +c\x89\xe4\x08\xf7\x8c\x1d\xe6\xc1\xdbR;\xab\xe8\xde\xbc\ +\xe8t\xfc\xc1\x85\x1e3\x03\xfc\xaf\xd6\xbe\xe8b\xfb\xe8\ +\xda\xccN\xb9_\xf4c6\xd9\x5c\xbc\xf3@\xab\xb1\xd5\ +\x93\x9c\xb9\x96\xd7\xfc\xefn\xbb\xe1;q\xd6\x15\x8ei\ +\x7f\xb3\x85D\xcd\xe3G\x9e[\x052G\xfb,M~\ +\xdf\xc6\xe1\x8c\xca\xd7\x1e[\x18\xc17\x89\x01\xce\x83\x07\ +\xaeP\xd35\xfc\xd9*\xd4\xd4\xb5\xc1\xd8\x94k\xc3\xee\ +^\xb9\xa1rzo\xbakw\xa4\xe09X?gn\ +GV\xb2\xf1\xfa\xe3Q>\x8f7\xa5\x1f\xe7\xe8\xeb\x13\ +\xf5j\x18\x22\xf6\xa1\xd2c\x9bF\x80\xf5\x8c\x8e\xbd\x9f\ +u\x9c\xbf\x02-\x7fj\xf2\x97y\xfb#G\xdc\xf9\xaa\ +E4\xd4PR\x0a\x9e\xf83b\xdf\x82\x05+\x9a\xba\ +\xda\x05\xecF6\xf4q\xafnGf<\x89\xe3|\xee\ +\x01\x8ap{\x87\xc5c\xd5\xc9q:\xc4\x8d\x08\xf7s\ +\xb9\x8b?+\xa9\xde\xb3\x9f\x12\x95k\x85\x9e\xd3)\xe0\ +K\xf4\xfaUwn\xab\x87\xab\xc4\xac\xf9Y\x7f\xc2b\ +\xc4\xff\x0f\x8fU\x03=\xf7\xef\x8fK\x8f7\xdf\xf7\xe9\ +^\xe0\xc2\xe3\xb3f\x84~tr\xb8\xf2\xa0\xf7!\x86\ +\x97z\xda,\x820E[\xb4\x860\x8bpw\xcb\xd8\ +\xc4\xd0C\xa2\xa1K\xf7\xc8\xef\xef\xbblB{\xfci\ +\xfd\xa1\xd8\xcfw\xbb?m\x9f\xbe\xdbA\xdb\xe1q\xbc\ +uk_\x15W%\xcd\x03\x9d\xda\xb9\x1c~\xf1\xcd\xa8\ +\xe7\xd5\xcf\xd9>\x13W\xd7\xf3\xb7\x9e\x911\x17)\xd9\ +G\xfe\xfdt\x1f\xbd!):j\xe1\xa5<5\xf7\xe9\ +\x84\x1da\xef\xd2\xa9\xdd\x9b6Zis\xdc\xc8\xd3_\ +\xf7|2<\xf4\xe2[V\xcbg\xb7\xee\xe4\x05\xef~\ +\xcf\xbbY?u\x9a\x11o\xdcuu\xc2\xbbi\x8c\xcd\ +\xbeq\x0d\xd0\xcbu\xd60/\x9e\xf1\x8ex\xd9q~\ +\xcf\xbc\xc5\xbbs\x8f\x91\xa6\xf9\x9d\x08\x82C\xc4\x9c\xb7\ +\xd9~\xb1\x1fb0G\x8e4\xf5\x9d\xfd\xbe\x8b#\xb2\x01\xbe\xcf\ +\x9a\x9f>\xb7\xddS\xfb\xc0n\xf6m\xb3C\xac;2\ +;Yu\x0fvq6\xfdQ\xab\x07\xd2\x8dW[]\ +\xf8\xdc\xb56\x9a\xe5\x07\x86\xdb\xd55\xd3b\x93\x0f\xe8\ +q{\x1cLV\xc2\xeb_H,t\x1b9\xfa\xad\xba\ +\xea\x86\xfc\x90\xfe{T785\x8b\x1ap/om\ +_\xe7\x11\x81\x88\x16\xde\x98\xacI\x8f0^t\xd0\xc5\ +\xec\xfekD1\x09\x0fM.G\xaa\x10>mB\x9a\ +\xfc\xdc\x7f \xdby\x87\xdf\xec\xf8\xcb\x0dk^~7\ +\xa2\xe0\xca'\x87\xb6\x1f\x95\x08c\xada\xdf\x94\xf4\xea\ +\xf0\x9a\x8c\xe8\xab>.\xa9\xb7\xe6\xbe\x03\x93/\x87\x14\ +\xa8\x04+[\x85\x8ckqaD\x7f\xcf\x90\xf9\x93\xeb\ +0]l\x87\xd6^\xa6\xa2\xd9\xf3\xcd\x9au\xba\x9d<\ +\x8f\x0d\x8f5~W[\xdf\xfaXC\x1f\xff\x19\xaf\xee\ +2\x82\xa7\x10\xfbZ&\xaf\xcfU\xcb9\xaa\xd2\xf6A\ +\x13F\x8bGm{g\xcc\xf68\xf6/s\xc0<\xec\ +\x0c\x9c\xb0\xca\xcc\xcb_\x85\xb0Z\xa5\xbfO\xf9\xe0\xcc\ +\xec\xa9#\xbc:\x12\xc4\xed\xb6\xdc\x14'\x8f\xb3\xc7\x12\ +\x17\x8df\x9c6\x19e5\x92\xad[\x7f\xfcp\xdf\x1d\ +zy\xef\xc7\x8e6#\xe08L%\xec\x22R\x0f9\ +e4\x9a\x12\xa7K^\x8a\xef\x1c\xf9Z\xc5hf\x8d\ +(U\xcd\x8b-\xb6\xf4\x0aHX\xa5\xc2\xbc\xeb\xa5\x19\ +\xa04h\xc9\xed\xc9\xb7\xdfL\xed\x98\x98\xa8\xa4\xd5k\ +\xdeR\x13%\xadC\xf6\xb9\x83\x9auGH\xcb\xbb~\ +*\xe8\xdd\xb0\xc6\xc1V\x7f\xf5\x99\xb8w\xd8\x13%\xed\ +\x13G\x97\xb6%\xb2\xdc\xb7\xeah\xe8_\x9f];\xf8\ +\xe1\x92C\x05S\x22\xd2\xea\xed[\x17\xed\xc8RvJ\ +Zu\xed\x8b\xbdC\xf3\x89\xb7\x9c\xcc\xb6\xaf\x19\xb5\xb1\ +\xb9\xe9\x7f\x87w\xae;\xb9\xf4\xc8\xe2\xf5\x83\xae>\xe8\ +\xdd\xcc\xdc\xf5\xb9\x9a\x93\xc7\x1c\x8d,\xff\x05\xb5'\xf6\ +\xd8>{\xfb\x9a\x995\x062\x1f\x1bjd\xf5Z\xa0\ +\xfdz\xecv\xf3\x9a\x9a\xff\xde\xcfz\xa21M\x93 \ +\xd0\xef\xff;4\xc9>\xf8,Ah)\x0djs\xed\ +r\x86r#U\x22XS\x7f\xc9^w\x17=oc\ +\x82\xb9\xa1\xa6\xd5\x8e\xbd\x1e\xdd\x1b{]\xe10\xeb2\ +\x88o\x17j\x06\xd5\x98Z\xa7~\xb2\x81J\xcc\xc8\xae\ +\x17\xd8\x86\xd7\x92//\xb2\x8b\xbd\x9f\x10sCyP\ +|\x07\x22\xef\xd3\xe5\xc1\xfd\xdexg{\xdb\xda\xfd\x8c\ +K \x96\xadw\x1cT\xaf\x8b\x8aWF\xe7w\xf9g\ +\xeb\xf6\xad\xf7\xc9\x09{\xbcG\x13\xc9\xb7\x82\x8c\xf7\xf5\ +\x9a\x10~*a\xcfG\xee}\xbf\x99c\x10Q\x9e\xee\ +z\xeb\xadZL\xbf\xdb\x81Zk_+\x8dZ\xe7q\ +\xa1Y\x1c\xc3\xfc\x87\xb3\x97\x9d\xb2W7\xd3o\x7f\xfb\ +\xa2\xfd\x8d\x1e\xd6\xc8\xf4(A\x80\xdf\xbc\xb1\xcf\xdb\xe0\ +&?\xafk\xfe\xfb\xa3\x9dO\x00\xef\xbaM\xff\x83\xb5\ +\x01\x1d\x16Z\xc1\xc6\xb6u\x9a\xf8=\xfd\x1f\xb3K\xef\ +\xa1y\xb9\xe7\xfe#\xb4\xb2,\xefE\xc5\xe87\xef9\ +\xa3\xf6\x87\xfe'\xa3\x875v;\x12\xc1 \x08\x1c\xc3\ +\xe8bT\xf3\xf1\xda\x09\xe1c\xde\xec\xee\xe1oqx\ +\xf2A\x0d\xc2\xb1Y\x8d\xc6\xa7U\xfa\x1f\xb6Y\xebd\ +8m\xeb\x94t=\x8f\xe9\xe9uw\xd7b\x0e`k\ +\xa1\xa7\x987\xbf\x18\xdf\xce\xe7<\xef\xa0M\xffK\xf1\ +\x8f\x09\xa3\x1f7T\x1b\x13\x1bTn\xd5\x1e\x16\xfa?\ +\xa6\xde\xe9\xcf\xc9\x87\xdd\xd5'\xb6\x1f\xaaf\xda\xdc\xae\ +\xff\xab\xa9\xe1\xf5\xda\xfd\xa4\xc2\x16>\xf3\x03L\x86u\ +\x8c\xe9\xb2s\xa9\xd2\xe9\xbd_\xfe\xa7\xa5j\xa5=\xbd\ +\xc7\x193\xffQ\xdf\x16\xb5[\xf4\xbc\xe0\xdfE\x17\xd2\ +:\x8cl\xfc\xb8\xc3\x95:\xcc\xb9\xebX\xa75\x82\x0d\ +\xd8W\xed\xcc\xdb\x8e[\xdcn\xd1\xd3\x95\x8d\x09N\xe3\ +\x0f\xdb\x03\xd4#\xb6\x5c\xdfa\xa0t\xba\xa1\xdd\x943\ +\x1a\xd35\xb3~\x9a\xf0j\xd4Q\xbd:\xac\xbfg\x0c\ +s\xca\xfb\x89\xe1\x86\xebG\xf8\x9b\xaf~\xa2F\xfc\xb5\ +\xf65\xa9\xdc\xc0\xa2\x87\x91\x96\xb6\xdfr\x08[\x0d\x1f\ +\x08\x1c\x11\ +\xe8\x15\xd5\xf2\xbe\ +\xeaA:\x8d\xbbr\xdb\x05\x02\xf8\x07\xbb\xa2Z\xcf\xaf\ +\xba\x10K\xe3Pb\xfc\x0b\xe0^\x8fUm\xdf\xff\x0e\ +\x10\xc2\x12\xf0\x11I\x88{\xf0+\xeeV\x80\xb9W\x83\ +|`7K\xc0W,\x01\xfe\xc1\xb7\x5c\xed\xd3\xfd}\ + \x85\xc6\xa9X\xfc\x0b\xe0\x1ebK~\x0a0\xe7j\ +\x90/\xf8\xb1\x04\xe2\x86\xa5\xe0\x1f\xe2\x8b\xd5\xb6\xde\xef\ +\x07\xf94nK\xe0_\x00\xf7\x90_\x10\xaa\x00s\xad\ +\x86\x8a\x81P\x96@\x0e\x89\x08\xfc\xcfb\xfdI\xb9\x1b\ +\xcbu\x11\xe8\x90,\xcb\xa64h\x93,\x0bm\xea\xff\ +\xad\xf5\x114'Y+\xf4\x05~O\xff\x9b\xa5\x0e\xf5\ +=\xf8~e\xaf\xa1|\x00\xb85\x13\x83\x7f\xc81\xbb\ +\xa5\x00s\xfc\x05\xf8Fx\x5c\xa1G&\xad\xed@&\ +o\xecI\xa6\xed\x9fHf\x9cYJf^\xb6#\xb3\ +o\xed\x22\xb3\xfd\x0f\x90\xb9\x0f\xce\x91\xb9\xa1\xee\xe8\xe7\ +Y2\xdbo\x1f\xfa\xbd3\x99yi\x1d\x99\xe1\xba\x88\ +L\xdd3\x86Lfv'\x93l\xdbS\xcf\x85\xe7\xc1\ +s\xad\xaa\x04=\xf8\xd0\xb8\x16>\xfb\x90g\xf8\x1b\xe6\ +\xeb\xe9\xd2\xe7U\x17\xe3+u\xe702\xf3\xe2\x1a2\ +\xe7\xbe+\xc9\xfe\x18JrS\xbe\x91\xbc\x9c4\x92\xc7\ +\xce#I\x1e\x8f,up\xb9\xe8\xefrI^v*\ +\xc9I\x8a!\xd9\xefC\xc8\x9c\xa0cd\xc69K2\ +e\xdb\x002i\xb5\x01\xcd\x17\x14\x9a\x16\x92i\x5c\x0b\ +\xe7\xf28*\xc0\xdc\xe4\x07p\xd6\x11\xafNZ\xd5\x86\ +L\xdd=\x86\xcc\xbe\xb9\x13\xe3\x8b\x9b\xc1Bx\xe4\x94\ +\x8e\xe7r\x0e^A>\xc9M\xfdN\xe6G\xdc&\xb3\ +\xbc\xec\xc9\x14\xa7Ad\xd2\xca\x96\x94\xacPL\x19\xe1\ +(\x84{\xb0\x0b\x1e*\xc0\xbc\xe4\x83wt\xfe\x92\xed\ +\xfe\x87\xce\xa5\x05\x99\xff\xea\x16\xc9\xcdL\x92+\xbeK\ +\x1d\x88G\x00O\xc9{r\x99L?f\x8ee\x0c\x9f\ +\xffT\xfa\xde\x14\xc1CV\xf1\x1cR\xb8o\x90\xa4\x00\ +\xf3\x92\x03\xde\xbb\x229\xbd\x96,\x88~\x86\xcee\xde\ +\xaf\xc3\xbb\x88\xc1\xcb\xcb$\xd9\xef\xee\x91\x19g-(\ +]\x01\xf8\x81b\xc8\x85$\x1a\xe7|\xfc\xc3\x9d\x93\xaa\ +{7\x03\x9d\xaf\xa45m\xd1>/#\xd9_\x9e\x91\ +$\x87]\xa9x\x17\x1e\xc3m9\xf6\xb3\xfc\x8e\x83\xf3\xfd\x1d\xf6C\ +\xffB\x1a\xc8c\x15\xd5\xdd`W>\x8eK\x07\xc0=\ +/+\xb9\xb2\xd1T\xa1\xe3\x17\xd3\x00\x9bUT\x8bD\ +q\xf1\x8f\xe4=\xf0\xfc\xdf\xf5\xdc\x0b\x8f\x82\xf8(2\ +u\xd7\xa8_A\x03\x8a\x8f\x7f\xb4\x07\xa0\xeb\xfdJy\ +\x0f\xf6\x04//\x8b\xe4e\xa7\x90\xdc\xac\x14\x1c\xeb\xe1\ +\xe5e\xffR\x9f\x12\xfbs\x18\x99\xb2\xb9_E\xd3\x80\ +b\xe3\x9f\xb6\xf1*T\xcf\xe7\xf1Hnf2\xf6\x19\ +\xe6>\xba@f\xdd\xd8\x86cy\xe0\xb7O\xdd3\x16\ +\x9f\xc3\xd4}\xe31\xff\xc9<\xbf\x92\xcc\xbe\xb9\x83\xcc\ +{z\x19\xd9\xefo\x10]\xa4U\xdc\xbc\xd0\xc8{q\ +\x9dLZ\xd7\xa9\x22\xe3\x06\x8a\x8b\x7f\x88\xd5\xaei\x87\ +\xed\xfb\x8a\x18\xbc\x9ct2?*\x98\xcc\xf2v\xc4\xbe\ +C\xbc\xcf\xd6-\x04\xf2;\x04r<,u\x8arC\ +\xe0\xe7\xca\x968\xb6\x04r\x1ar\x02\x80>y\xf9\xd9\ +\xf2\x9f$\x97Cf\xfb\xee\xa6rP*\xc6O\xa8\xb8\ +\xf8G\x00~=y\xfbv \x0e\x98\xfb\xd8\x83Ls\ +\xf9\x97L\xb25,\xc2o\xb9\xce\x98nal\x19~\ +&\xaf\xefL\xa6\x9fZ\x80\xfd\xfb 7\xe49xH\ +\xfe\x00\xef\xa9 9\xa0\x98\xf8G\xf8\x00\xbe\xcbI\x8e\ +\x93\xdf>\xb2\xf3\xc8\xfc\xf0\x9bd\xda\x81)\xe8\xfc\xb6\ +\x90o\x5c\x9e\x9fg\x80\xf8U\xfa\xc9\xffpN\x89<\ +\xf3\x0b\x0ab_\x92\xc9L\xe3\x8a\xa0\x01\xc5\xc3?\x9f\ +\xef?\xbf&\xb7\xfd\x03\xdd\x11\xf2\xb6\xe0\xb9\x15\x1a\x7f\ +\xa5\xe9 y\xc3\xff\x90\x1e\xe1D\xe5\x99\xc8e\xf0\xc8\ +\x9c\x80\x83\xb4\x1c\xf8\xcd\xf1\x8f\xce~\xc6\x99er\x8a\ +\xe3\xf1H\xf6\xbb\xfbX\xbeS\xb9\x01\xbf(\xd6\x06\xe7\ +t\x85\x1e\x99~\xc4\x8c,\xf8\x1a!\x87u v\x92\ +\xfe\x13\xd9A\xe3\xe5\xed#V,\xfc\x83,Ez\x15\ +\xd8>2\x0f\x1e\x97\xccE\xbac2\xb3\x07}\xe6+\ +\x87\x96S\xb7\x0f\x22\xf3\xdf\x06\xc9\xbe\x1e4\xf2\x9e]\ +!\x93V\xb5&\xe5\xc8\xbf\x14\x0c\xff:8o\x87\xe4\ +\x14\xc8\x8e\xfb\xb0KdR\xe5\xc5\xd5\x04h\xa0)\x99\ +\xb2\xb1'\xd6\x0de\x1d`o\xa6\x1d\xfaW\x9e\xf4\xac\ +8\xf8\xc7z\xb4\x11\x95\xbb#\xdb.\xe1s\xaf\x10\xb8\ +\x17\xa0k\xec\xc7x\x1f\x223\x0d\xe4=\xbbJ&\xd9\ +\xc8\x8d\x07(\x0e\xfe\x11MC\xfe=\xe4V\xcb2@\ +\xdec\x9e\xaf(\xb8\x17\xa0\x81\xd4\x1dCIN|\x94\ +L\xeb\xe3f%\x93\xa9{\xc7\xc9\x8b\x07(\x08\xfe\x91\ +\xce\xbf\xca\x00\xdbg\xb2\x0c\xd0\xf3\xb1\xaeWY\xf2^\ +\x02\x1aH?1\x0f\xdb\xf4\xb2\x8c\x9c\xc0\xc3X\xbf\xfc\ +m\xf0\x0fg\xc3y\x84L\xf6\x12\xd8\xf7`\xe3)X\ +\x8eu\x09:\x07\x1f#\xe4\xfc\x80\x9c\x92v@\x8c0\ +\xd9\xa1\xbb\xfeA\xef?\xbfR\xea\xd8:\xe4\ +\xd0\xa6\x1f\x9eYu\xce\xbe\x00\xdd\x83\xbe\x22m\x9c\x00\ +\xe2\x97i\x07\xa7\xca\xba\xee\xca\xc7\xbf\x95\x1e\x99\x13|\ +B\xaa=\x80\x0165\xbeW\xa5\xe8r_\x04\xfe\xc1\ +'\xc0\xf9\xf1I\xca\x95\xf3\xc8\xac\xab\x1b\xab6\xfe\xe9\ +\xfb\xd8`\xb3K\xb7\x05h\x0f\xaemV<[_R\ +\xb0\xd6\xc7\xb5\x05\xa4\x1d\xb9\x8f\xce\xcb\xaa\x03T2\xfe\ +up\x0d\x05\xb8G/\x15\xfa\x91\x1d\x9d\xb6gl\xd5\ +\xe3\xfd|(\xf4yIwG\x15\xe2\xcc\xf8n\xb1\xf4\ +\xbc\xafr\xf1\x0f\xf1\x91=cp~\x9d4\xa3 \xf6\ +\x15}w\xa6\x8a\xf1~>\x80\x0c\xd8jJr\xd3~\ +H\xb5~NR\xac\xac~\x80J\xc6\x7fSd\xc7.\ +\x90:\xd6\x0b\xb9a\xf8\xfe\x5cU\xd1\xfbK\xe0_\x17\ +\x9f_\xf6\xc7GR\xad\x1f\xf2\x16Sw\x0e\xaf\xc2\xf8\ +\xd7&3/\xae\x95Z\x07\x86\x5c\xcc*\xcb\xfb\xf9\xb0\ +\xb2\x05\x99\xfb\xf8\xa2T\xeb\xe7\xe5f\xcaj\xfbT.\ +\xfe-\xb41\x0e\xa5\x1a\xc8^\x84|\x5c\x9c\x93Y\xd9\ +8\x94\x05\x10\xee\xb2\xfd\xf7K\x87\x7fv.u\x87\xb8\ +*\xe3\xff\xf6\x1e\xe9\xd6\x9e\x9f\x8ds\xed\xaa\xfc\xf9G\ +\xf4\x0by\xaeR\xc5\x03\x10\xdf\xcc\xbch+\xcb\x19\xa8\ +\xba\xf8\xcf\xcd\x90w.D\xa5\xe1\x1fp(\x95\x0c\xc4\ +\xf8_\xfbg\xe2\x1f\xd9~`;T\xc2\x9d\xe9\x0a\xc0\ +\xbf\x94:\xd0\x9f\x8c\xff\xbcL\xaa\x96\xce\x9f~\xfe/\ +\xfd\xa1\xf8G6\xa3\x9cb\xa0\x95\x0bh\x0fp\xec[\ +\x9a\xc1- 3=VWi\xfcg]\xdf*\xe5\xda\ +e\xd6}\x14\x03\x90\xed\x0e\xf9<\xd2\x0c\xea\x0c,\xa8\ +\xba\xfa?\xf0\xbe\x0b\xab\xa4\xce\xf7\x85\x1a\xbdU\xd6\xf7\ +\x8fA\x17\xe7r\xe6=\xf7\x96\x0e\xff\x10\x03t\x99V\ +\x85\xf1\xdf\x14\xdf\xb3\x95\xf6\xce\x1c\xd4{\xabRq\x7f\ +a\xa0\xef\x0e\x16\xc4\xbd\x92j\xfd\x90/\x97\xe24\xb0\ +\xea\xfa\xff\xe8\x9cX\xf0cJ38?>\x90\xc9\xf6\ +\xdd\xaa\xb4\xff\x1f\xfc\xb7\xd2\xd6\xa6\xe5\xfc\xf8\x88k\x9d\ +V\xd9\xf8\x0f\xd0\xbf}W\x8cGi\x06\xf0\x8d\xb4#\ +U0\xf7\x83\x0fh\xdeR\xeb\xfeh\xc0\xbd\x22|\xa7\ +\xb1\xaa\xe2\x1f\xe4\xdfj\x032?\xc2O\xaa\xf5\xc3\xc8\ +\x09p\xa9|\x8f+5\xfes\xee\x80\xde#\ +3\xed+ \xfe\xab\xe8\x99\x96\x14 \xe7\xcdy8\xc9\ +aI\x97\xf3\x08\x03\xe7}\x1c1\xfb=\xee\xff\x08A\ +\xd2\xea\xb6\x94M\x83\xf7J[\xa06\xd3o@\x13r\ +\xaam#'\xde\xafx\xf8\xb7nN\xd5S\x8b\xbaG\ +\xe6\xdc=\x8a\xefHA\x0d>\xf0q`?\x1f\xbfO\ +\xdf\xf2\xaaK\x13YW\x99\xb2\xd54\x83\x9c\xf7\xeb[\ +\xe5\xc5#+\x06\xff\xfc\x1e{\xe5\xd1M\xf9w\x01\x04\ +s!9l\x1c\xe7\x87\xfb\xae\xe0\xeb\x85Zx\x90\xef\ +\x04\xf7\xc5\x92\x90\xee\xcf\xe2\xf7\xd7*F\x13\x95\x8fc\ +\xd1\xeb\xd3\xa1j\xd8\xcaX\x13\x8a\x9b\x1c\x87\xfb\xcc\xc9\ +)\xee!\x1f\xfc\x0b\xf6TD\x9f!\xa7\x15d\x1c\xf4\ +\xd9\x93\xf8\x9e:\xd8\xc3[L\xca\xac\xf1\x0b\xb9\xf2\xdc\ +\x8cD\xb2 \xe6\x05\xce\xff\x85\xfb\x1f\x10C\x80\xef&\ +\xadmO\xf7\xea\xe4\xd3\x84\x82\xd0\x03\xd4\xb0\xdd?\x09\ +\xfbke\x1d\xd0kP\x8e\xf6\xaet\xf8\x17\xec\x99\x8a\ +e\x9a!\xa2\xc9\x81\xb8njN\xd0Q\xaa\xa7bV\ +2\x99\x1f~\x0b\xcbs\x89\xf0 C,\x08\xbe\x03t\ +\x03\xef\x85\xde:\x99\x977\x90i.S\xc9d\xfb\x7f\ +\xe4U'Av\xdc#\x1e&\xeb\x80\x9er\x10/\x91\ +c\xceS\xf9\xf0O\xf3Y\xd0a\xe0\xbcA\x1fT\xa8\ +K\x07\xfd\xcd\xb8\xe9?J\xc85\xcc\xab\x1c{K\xc6\ +\xab\xd0\xb3\xe1,\xcbe \xbb\x0ah\x22\xf7\x89'\xd5\ +\x8f\xb32\xf4\x04\x9a\xe6\xa1\xde\x07\xe7\xe7g\xb9,+\ +\xe7\xee\x11y\xfb\xba$\xc4?\xe5\xa7\x87\xf3\x09\xf9:\ +\xf9o\xee\xa2\xf3\x16_\xe6\xfdu\x9c\xa3\x0b\xb5K\xcb\ +\xb4St\xb1,\x97w\xad\xdf\x9c{\xa7*\xe7\xfcC\ +/:[C2\xcb{\x93\xdcj@r\x12>P=\ +b\xe4\x9b\xef \x19\xfe\xe98\x1d\xf8\xdb\xcb;p\xfd\ +\xe2\xb2\xe6\x0c2\x04\xe2\xe0H\xa6\xcbm\x94\xea\x1f\xaf\ +\xa8\xfa\x9f\xd4:\xa1\x96\x0d\xd8x\xf2\xaa]\x0c\xcf\xc9\ +\xf4\xdcP\x11\xfa\x8c\xe4\xe7_\xcaX\x15\xf0\x8a2c\ +\x94\xfc\x1a\x10H\xaf\x93\xd7\xc0\xb91H\xee\x96\xe0=\ +\xd0\xf3\x9b\x9f3\x22\x97\xfe{\xba\x85\xf2\x18\xees\x03\ +\xbd\xcb\xb3n1\x0c\xe8\x0f%\xe3=O\x19\xf1\xdfL\ +j\xf9\x0cr\xa2\xa8\xd7\xa1\xf8g\x83]'\xed=X\ +Q\x03\xe7F\x80\xfeWl\xcf(9\x96\xed\xbb\x0b\xdb\ +\xd0)\xdb\xfac\xba.\x9fO\x81_\xfb\x9b\xd2\x7f\xe1\ +y\xa9\xce#q\x7fxN\xc2{\x99j\x18\x89\x5c\x07\ +\xaeiVa\xbd@\xca\x85\x7f\xa87\x01\xe7\xaa<\x03\ +\xdfQBzb\xa9:\x00Z\x1b\x9c\x1by\x0e|f\ +\x84u?~\xad\xa17\x81\xf8o\xb8\xc9_1O\x83\ +\x1c\x0c\xb0U!\x06\x09\xb1H\xfc\x1d\x0bm\xda\xffH\ +\x83\x05m\xdb\xa2gB\xdd\x15\xe8'\x0e}\xbe\xe1=\ +\xb2\xd4\xae)m\x80\x0e\x9by\xdeZ\x0e\ +;\x90\xcc]\x87i#\xd3s=\xf6K\xe6\xdc;\x89\ +{\x89s\xbeGQ5\xfcd\x88\xdf\x949\xd0\x9c\xa0\ +N\x1c\x0b\xf8\x93\x22\xe0\x9f\xaf\xa3\x87]*\xf7R\xd8\ +Q\xc1\xe2u\x00\x9a\xae\xe4a\x1f\xf3\x07\xe8K\xd0\xf7\ +\xbd\x04\xcf\x11\x87\x7fq\x83\xcb\xa5r\xb3*\x12\xcfb\ +F\xde3o\xda\xc7\xaf@\xfd\x7f@\x07@<\xaf\xbc\ +2\xaeT\x1d\x00\xd7\x80\x18+sM\xccb\xefCg\ +\x19t\xf0\x12\xef+/\xfe+i@\xadh\xa8\x11\xa8\ +p\xfd\xbf@\x07\xd87\xa1\xdc}\x8f(\x1d`\xb1h\ +\x1d\x00\xc9V\x88\xf3\xc8\xb3\xb7\x16\xe4\x92%o\xe8\x22\ +9\xffW\xa0\x01\xb8\x87z\xe1\x0a\xd9\xff\x0fx5\xda\ +Wiz\x1a`\xdf\x95\x98gJ{\xffE\xdc\x00\x9d\ +.I\x94\xdcTp\xfc\xffb\xdc\x97\x1f\xff\x00\xd6\xcd\ +\xc9\xdcP\xb7\xb2\x17\x83\xe4&\xf4\xea\x84:\xa7\xb9\x0f\ +\xceQ\xf9\x0a\xd6B\xbeK:\x1e\xce\xd7\xc7\xe55\xb2\ +o\xee\x14\xcdk\x14\x15\xffh\xaf\xf2\x9ezUF\xdd\ +\xf2\xf2\xe3\x1f\xe7\xac\x8b\xb8\xaf\x8ct\x02\x90\xe1\x18\xdf\ +\x88>\xe0o ?\x11\xd7g\x02\xbc\x8b\x8aY@\x1e\ +\x9cC7\x92\x93\xf8En[\x09w\xe22N\x89\xb9\ +\x13\xa7\x80\xf8\x87\x5c\x9el\xbf\xfd\xb8\x1eh%\xdce\ +\xe3\xe3_\xf2\xfe\xcf\xa0\xaf!\xbc\x82_\x1b|\x01\x90\ +\xc3\x086\x01\xe8\x85i{\xc7\xd1\xf8n.YL^\ +J\x9fBi\x03lq\xb1\xf1q\x05\xc3?\xd0}\x86\ +\x9b\x15\x95\xc7P9\xb1j~\xff\xe7r\xf4\x7f\xa7x\ +v\xc6\xd9e\xb8\xff!\xaeA[\x98\x87Q\xce\x1c\x0c\ +\xa8}\xe2e'\xd7=e\x7fyJ\x9f%\xd1\xb6\xa6\ +\x22\xe0\x1f\xfc\x9cy\xe17i\x1b\xa5R\xf3\x98\xf8\xfd\ +\xdfM\x10\xb0$\xff\x9e\xaeP\x8e\x85\xf0\xfcK\xcb\xd5\ +\x16\xf4\xc7\xe9\xe08=\xc8>\x90\x1b\x98\xa7\xc8\xe8\x03\ +\x86\xfb \xb8\x8f\xa7\xa8y+\x00\xfe\xc1w\x04>\xa5\ +\xc2\xde\x93\x95\x83w>\xb0h\xdc\xb7D\x10-\x97g\ +\xae\xd0\xc7>R\xf0\xb3\xe2\x5cNA\x80\xdfa\xff\xaa\ +@Lv9\xe5W\x82\xbb\x1c\x10\x03\x82z\x98\xe0K\ +\x07?\x1c\xc4\xb8\xf7 \x0a\xd2\x0eL\xa2{6T\ +\xfa\x1e\xfc\xa9\x90G\xe3\xb8\xf0\xec\x0b\xe1\xbf1\x82P\ +\xe9\x9f\xaf+$C\x85Aa\xed\xa1?\x05Bi\x1c\ +\x17\xc3\xbf\x10\x0d,@\x90\xaf\x00s\xad\x06\xf9B>\ +\x8d\xdb\x12\xb8\x17\xc2?\xd8\x05~\x0a0\xdfj\x90/\ +\xf8\xb1\x04l>QC\x80\x06\x86#HQ\x809W\ +\x83| \x85\xc6\xa9X\xdc\x0b\xe1\x1f\xfc\xc2\xbb\x15`\ +\xde\xd5 \x1f\xd8\xcd*\xf2\xf5\x8b\xc5\xbf\x10\x0d\xe8!\ +\x08Q\x80\xb9W\x83l\x10B\xe3\xb2L\xdc\x8b\xa0\x81\ +\x81\x08\xbe)\xc0\x1a\xaaA:\x88E0\xa0<\xb8\x17\ +\xc2?\x83E\xe9\x8c\xe9\x0a\xb0\x96j(\x1f\x00\xce\xe6\ +\xd38,\x17\xfe\x85h@\x15\x81\x03\xab\xda&\xacJ\ +\x90O\xe3LU\x1a\xdc\x8b\xa0\x81:\x08vT\xd3@\ +\x95\x80|\x1aWud\xc1}5\x0dTI\x90+\xee\ +\xc5\xd0\x80\x03\xabZ\x1fPDH\xa7q#W\xdc\x8b\ +\xa0\x01\x90)\xa0W\xc4*\xc0\x9a\xab\x81\x82X\x1a'\ +2\xc9\xfbr\xd0\x00\xe8\x94`WT\xfb\x07*\x1fB\ +h\x5cH\xa5\xe7\xcb@\x03|\x1f\x11\xf8\x96\xaa}\xc5\ +\xbf\x1eR\xe8\xbd\xd7\x13\xc4\xc9\xaf\x18B4\x00~E\ +\xf0-C|\xa1Z7\xacx\xc8\xa7\xf7z8K\xc0\ +\xa7\xfb\xabp_\x0a\x1d@l\x09|E\x10c\x962\ +\x87\xa4\x1aJ\x81\x13\xd5\xa3zT\x8f\ +\xeaQ=D\x0ef\xf1\x8fe\xf2\xd3\xe0\xe2\x9fK\xf0\ +\xe7\x98\xe2\x9f\xd5\x84?\x0b\xcb\x83\xb2\xe4\x87\xb0\xbc\x11\ +\x96G\xc2\xf2\xaa\x84<+6A\xe5\x92\xf2PX^\ +\x0a\xcbSay+,\x8fK\xc8kay^\x5c\xde\ +\xb7E?L\x09j\xdf\x19\x84>\xf5{\xf4\x8b\x96\x9d\ +(\x90t\x88\xd0[\xfeB\xd0\x17\xc1r\x04G\x11\xdc\ +D\x10TIp\x93\x9e\xc3rzN\x7f\xc9K\xc7\x12\ +z\x0e\xf8\xe0\x0d\x11\xaccQ~y\xb0\xdd9\xac\xca\ +\xb7)\xf9\xc0\xa1\xe7\x14B\xcf\xd1\x90%\x107(\xef\ +>\x08\xad]\x1f\x81\x13\x828\x05X\xa7\xa4\x10G\xcf\ +Y\xbf\xbc{ \xf0\xf7J\x08& x\xa5\x00\xeb\x91\ +\x16^\xd1kP\x92d\x0f\x04\xd6^\x13\x81-\x824\ +\x05X\x83\xac\x90F\xaf\xa5fi{ \xb4\xf6-,\ +E\xe8\x9d)?`\xd3k\x12\xb9\x07\xac\xe24o\xfb\ +\x9b\xad]p\x0flY\x22\xce\x82\xc0\xfa\xe1\xac\xfc\x0e\ +4/\x0e\xd2\xe85\x16\xae\x9fU\x9c\xcfWe^'\ +)\xbcb\x09\xc9\x05\x16%+\x9d*\x7fn\xbf\xec\xfe\ +\xbc\x13\xab\xb8~\x00\xfa\xc2/\x94\xef\xbaE\xb58\xa0\ +\xee\x06\xd4\xd2Z\xd9\x92\xaa1\x08}\x9c\xa0\xd6,\xfc\ +]\xc5\xf5\xfc\x8c\xa3\xd7\xcc_\xff\xfa_\xb2n\xbav\ +\x14\xf4\xd6\x84\x9a\x0a\x19\xee\xd6dN\xe0!\x5c\xdb<\ +?*\x88d\x7fzL\xb2\xdf?\xc0\xfdgr\x1f\x9c\ +\xc5u\x0f\xd3\x0eM\xc7\xf5\x94q\xcf;\x5c\x0bIn\ +{\xb1\x8eU\xa4\xcfWl\xbe\x0f\xae\x97\xa5\x87k\xf8\ +f\xdd\xd8\x86\xd7I\xf5Z)\xbb\xe7\x04\xd4\x97\x80\xbe\ +O\xb9\x0f\xddp\x0d\x02\xa8\xf5&\xa7}\x08a\x15\xd9\ +2\x15\x14\xcf\xd4\xa5\xeb'\x9a\xe2Z\x87\xd03C\x96\ +\x01\xf56\xf2\xdf\x06\xe1\x9aET}a\x99\xee\xdd'\ +\xb3\x8a\xec8\xf9\xdb2\xb8\xdec\x1b\x5coI\x9e\xb5\ +\xc0\xf1>\xe4e\xe3\x9a\xb0P\xb3I\x86\xda\x03\x1cV\ +\x91\x0d+\xdf\xb5\xa39A\x9d\xcb\xdcG\xe7\xe5\xda\x03\ +Cx@\xbd2\x5c\x1bSz\xfe\xc8\xb7\xdf\xe5\x8aw\ +\xa8\x87\x0a\xfc\xecW\x0c\xa8\xef\x96q\xd6B\xda\xf9\xf2\ +}\x17r[{\xf2\xa6^\xb8N\xe4\xaf\x1c\xd0\x97\x19\ +j\x9bIA\x07Ar[?\xd4\xe1\x5c\xdb\x11\xf7\x87\ +\x96xp\x0aHNR,\xc9~w\x9f\xcc}|\x91\ +\xcc\x09>\x81\xe5\x1e\xd4\xe0+\xf8\x16I\xd5\x84\x95t\ +\x0f\xd2~\x90i.\xff\x96\x97\x1f\xc8o\xfdH\x87\xc9\ +\xf6\xdf\x8ff\x22\x81L\xcb\xcd\xc4\xb5\xc2q\xdf{D\ +/T\xefJ\xbd\x22\xfc!\xfd\x07\xeaiA_\x1e\xa8\ +/\x0c\xfd\x02%\x19\x05q\x11\xf8y\xe5\x90\x0b\xf2Y\ +?\xd4\xfc?4C\xa2\x1eB\xec\xcfad\xfa\xd1Y\ +t\x8fF\xedR\xeb\xba\xf2u?\xa8?\x0c\xb4\xc1\xcb\ +\xcb,\xf3\xf9\xb9\x0f\xce\x88\xaf\x15^\x11\xeb\x07\xba\xb7\ +m\x8f\xf8\xdd\xbd\xd2'\xc6\xe5\x90\xb9\xa1\xeeT\xff\xca\ +\xf2\xea/\xb0G\xd6\xfa\xb8>\x7fY:\x04\x9c\x19\xd0\ +\x19%\xac\x9b)\xfb\xfa\xa1\xcf\xe7\x99\xa5\xa5\xcb9\x1e\ +\x17\xf7m\xa6\xfa\xc6K+\xaf)]*\xfd\xc8,\x5c\ +\x1b\xb8\xb4\x01\xfc\x03j\xc2J\xb0\xc72\xae_\x17\xeb\ +8e\xf5\xba\xc9{\xe9C\xd7\x11\x95C\x9d$\xf4\x8c\ +\x0c\xb7\xe5X\x17\x14\xbb\xdd9\x19\xa2\xfb\xe4\xca{\xfd\ +\xe8\xf9\xd03\x13\xd7\xdd\x1538I1d\x8a\xd3 \ +\x89\xeb\xb8JD\x07\xe8|C\xff\xa1\xd2\x06\xeeMU\ +\xb6<\x94y\xfdY\xd7\xb7\x95:\x8fl\x9f\xed\xf2\xaf\ +\x17\x8cd\x5c\xca\xb6\x81\xb8&\x9f\xb8\x015\x19\xa9^\ +\x83\xa5\xee\x81l\xebG6\x08\xf4\xb7\x12\x8f\xfbXJ\ +G\xaf\xa0z\xc9\xb9\xf7O\x8b}7\xf0A\xa83_\ +\x06\xddI\xbf~\xdc?\xfd\x1fl\x9b\x8a\x1b\xd0C\x07\ +\xfb6*`\xed\xb0.\xb0\x87\xc1>\x16\xbd\x01<2\ +\xf3\xbc\x8d\x98^\xe9rX?\x9c\xfd\x1dC\xb1\xee)\ +nd^\xb2-\xeb\xfd\xd2\x03\xe8\xda\xcc\x1e\xa5\xda\x96\ +\xa0\x8f\x95\xa1\x0f\xca\xb4~\xa8\x09.n\xff\xa1\xb64\ +\xfc\xbb\xfc\xf8\x9e0P\xb2\xa74[\x03\xf7M\x11\xec\ +\xcb!\xd7\xf5kc=\x0etx\x91\xeb\xcfJ!S\ +\x9d\x87W\xe0\xfa\x9ba=\x19\xf4hq#\xef\xc5\xf5\ +\xb2\xce_\x85\xad\x9f\x9b\x99\x8c\xce\xc7\x90\x8a\xad\x8d\x88\ +t\xc2\xbc\x17\xe2{,C\xbf\x95B_jE\xac\xff\ +\xf0L\x5c'_$\xfe\xa1o\xc7\xde\xf1\x15\x8b\x7f\x90\ +?o\xee\x8a_\xffS/\xcaoZ!\xeboJ\xf5\ +\xb8\xcd\x11c\xa3\x22}\x1f\xec\xbb\x8a\xe3\x7f\xba\xd8F\ +\x04;Y\xdc\xc8\x09:^q\xf2\x1f\xfc<\x9bz\x97\ +j\x8fH\xa8\x83I\x07\xb0\xff\xce#K\xd5=\xb3\xae\ +0+N\xfe[Q}G\xf2\x9e_\xc32\x08\xf4\x80\ +b\x80~\x07\xf4\x87m\x9e\x8a\x88\xeb\x80\xee\xe9\xed(\ +v\xed\xd0\xeb\x00\xf7\xe7\xac\xb0\xf5S\x00>\x9fd\xfb\ +\xae\xb8\xdfw\x09\x80~\xbd\xc2\xfdA\xe5D\xfb\xa0\xdb\ +\x16D?\x13\xbb~\xd0=\x93\xcb\xf6\x85\xc8\xbc\xfeB\ +?\x85E\x13\x04\xda\xc5\xfb\x06T\x18\xed\xeb\xe0\x1e\x06\ +\xa5\xf5:\xc1\xb2\xafl?\x88\xe4\xeb\x17\xd7\x0b\x01\xf8\ +\xc0\xd6\xfe\xd8\x07\x0b1-\x0a\xe7\xcd\x8b\xc7\xf7\xe4\xb9\ +\x0fp\xee\xb7\x0fA\xe7+Z\xec\xdaa_\xa0\x7f\x87\ +L\xf6\xefr\x81\x9a\xc7\xd0\xcbl\xa31\xd5\x83P\xf8\ +,\xc39\xbc\x016 \x0f\xf3\xa2\x82\xf8\xb7\xd8\xde\xcf\ +\xbe\xe5L\xa6\x9f\xfc\x0f\xdbi\xb8o\xcc\x0a=\xd9\xf7\ +\x01\xbd\x0bb\x81\xf9Q\xc1\xe2\xd7N\x92\xb8\x973\xee\ +\xe1(\xad\xfd\x8b\xe8\x06\xde\x03\xfa+\xac\x0d\xe2\x91`\ +\xc7C?\xd0\x12{\x8a\xf4+\xe8\x0d&\xfa\x10\xb2q\ +\x0f\x9d\x82\x98\x97d.\xb2\x85Rw\x8d\x94\xde\x16D\ +\xef\x05yS\x9a\xbd\x89\x07\x8fKf]\xb6\x93T\xef\ +*\xbe\xfe\xe5T\xads\xe8A\xc5\xf9\xf9\x19\xd7\xbb/\ +~\xa6\x84\xf4)>\x1f\xfa\xfa\xba\xf49a\x9a\xe4 \ +}qv\xd1\xfeI\x14\xd7\xa6i\x10\xf1P\x88\xf3\x14\ +\xc4\xbc(\xf35\x10[\x95\xac\x17\x97(\xfc\xeb\xe2\x18\ +<\xf4\xdf\x12\x89\xce\x9f\x9f\xa8X4\xff\xd9\xd07\x16\ +l\xc0\xcc\xa4\xb2\x97\x9f\xfa\x9d\xea\x93gI\xf5\x81K\ +;0\x19=\xab;\x89\xed\x93\x12\xfd8\xb4\xa9\xbfC\ +sI\xdb7\x1e\xc7~%\xf1-\xc3\xf9\x93@\xe6\x95\ +N\xff\x88>\xb3\xfd\x0f\x88~~\xbe\x90M\x07}\xee\ +O/.\xd97Y\xc4\xc0=\x12\xd6\xb4\xa3\xe2\xa2X\ +v=\xa7\xe2\xda\xa1\xe7\xc9L\xcf\xf5d\xfa1s\xbc\ +'\xe0\xbb\x05^\x0a=\xa7\xa0\xf7\x94$\xeb\xa66\x98\ +C\xf9\x9aJ\xd7w\xcb^?\xf8\x15\x8e\xcfE\xc8\x16\ +\xad\xd7g]\xdfZ\xb4~\xd8+4OI\x06\xf8\xeb\ +\x8a\xceL\xc7\x92\xfd\xe6\x11\xaf\xa0\xfaI\xe4\x89\xec\x03\ +X\xfa\xe0\xe1\xe7S\xfe\xe5r\xf1X\x91\xf8\x87~s\ +\xdc\x94\xaf\x22\xdfD\xf1\x80\x16\xd4\xd9\x04\xfb\x03\xf1F\ +IF\xa6\xe7\xba\xc23\x8f\xf1\x1f\x17Q\xf6\x97$Z\ +:\x0f\xd3Pao\xb2\xf2\xf1U\x91\xfc\x1fb3\x90\ +g\x80\x07\xf4AK\xfe\x8a}\xdc\xd9\xb7\xf7`\x1a\xc5\ +:\x1d\xf8_\x90\x8e\x07\xfd<\xcb\x9cb^6\xf5=\ +9\xaf\x1f|/9\x81\x87e\xf1\xad\xc3\xdaK\xc6\xbf\ +\xd1\x1c3\xce-\xc7\xb178\x0b)\x8e\xbd\xe9x\x15\ +_\xa7\xd1\xa5}\xdf\xa3%\xeas\xcda\xc5\x90\xc9L\ +\xe3B]H\x1e\xeb\x87\x98`\xe6\xf9\x95\xb2\xf6\x99\xe6\ +\xe7\xcf\x8b\xf87=\x81~Zz\x98\xbe`\xde\x18`\ +\xbf\xd1\xdf\x80\xae\x07}\xb4\xa8\x9e\x8d\x8f\xd0\x99\x89\x17\ +\xe9\x0f\x83\xd8\x18\x15\xe3l&\xf3\xfa\x81\xc7C\x1c-\ +\xc5i \x8ds\x99t*\xfe\xdd\x01\xf1\xf9/\xb4\x9d\ +\x9b\xff\xee\x1e\x96\xf3\x05_#\xb1\xcf\x89\xeaw\xa4S\ +\xc8\x0b\x81\xf7\x00\xdf\x009\x9duu\x13\x99\x17\xe6\x89\ +\xf5\x1e\xf0\x8f\xe6\xdcq)\xc2\x11\x7f\xfd\xa5\xd8\xed%\ +\x06\xe2\xed`\xcf@l\x1cb\xc2\x98\xff\xc8\xeeS\xe7\ +\xe7\xbf\x94\x9e\xff\x04\xeb\xdf6\xa0\x98\x8c\xe7$~\xc6\ +g\xbfd\xbfW\xc1>\x8e\x14\xcd@\xec\x87\xa2\xfd\xa2\ +\xbf\x85\xb3\x94\x89t\xb4b=\xfa\xd09\xc2\xfc?7\ +\x13\xef\x19\xce\x0b@2\x13\xd6\x0c\xba<\xee\x01Z\x98\ +\x03'\xd3\xba\xf9\xc0\xcf\x7f*=\xff\x8d\xbf\xfe\x0cV\ +\xd1\xfaA\x0f*\xb1~Q\xdf\xd5\x15\xdf/\x12~\x07\ +v\x05\xf4\xa8\xdbj\x8ay\x09\xf0\xc8\xd4\xbd\xe3\xb1N\ +\x05{\x86\xf5\x05\xf8[\xbeM)\x9fu\xf3\x81\x9f\xff\ +Vz\xfe#\xce_3\xc1k\xc68\xca\xcd\xc0zh\ +\xf2\x06\x89\xec\x8b\xb2Ad\x8f6\x1dy\x9c\xed\xb2`\ +=K\xc2\xfcW\xe0]\x80\x93\xd4]\xa30\xe0x\xe6\ +J\x89s\x0c\x14\x11\x84\xf3_\xcb\xce\x7f\xb6\x14\xc0\x91\ +\xe2\xf6?\x95\x14\x84\xf3\x9f\xff\xd8\xfc\xf7?\xfd\xfeC\ +\xf5\xfd\x97\xea\xfbO\xd5\xf7\xdf\xc4\xde\x05\xfb\xa3\xee?\ +\x8a\xd8\x83?\xee\xfe\xab\x98=\xf8\xa3\xee?\x97\xb2\x0f\ +\xbf\xdd\xfdw\xba\xcc\x00I\xd7?\xe0\xd7\xd9\xe1\xd7\xd3\ +\xe1\xd7\xd1\xe1\xd7\xcb\xe1\xd7\xc5\xe1\xd7k\xa8\xae{S\ +\xc9\x83I\xfd(\xc4G0\xf5\x93_\x1f\x83_\x07\x83\ +_\xff\x82_\xe7\xc2\x84\x8fw\xa8\x13a\x84\xc0\x9c\x10\ +\xa8\x13\xd1\xaa\xec:\x11\x22\xce\x85\x06\x82A\x086!\ +\xb8\x8e\xe0\x19\x82\x08\x16\xc5\x83\xe5\x09\x11\xf4\xb3\xaf\xd3\ +\xef\x1aD\xbf[b\xba\x171os\x04\x81\x08R\x7f\ +\x01\xaf\x12\x86T\xfa\xdd\xe6\xc2\xeb\x90`\xee=\x10\xf8\ +\x22(\xa8\x84y\x0bC\x01=\x97\x1e\xe2\xd6 4\xf7\ +\xc9\x08b\x14`\xde\xc2\x10C\xcf\xad\xd8\x1aD\xcc\x9d\ +\xa5\x00s\x15\x07,\xe15\xb0\x8a\xd3\x8c\x22\xee\xbb(\ +<\x14\xa3%\x16u>|\x15`n\x92\x82/\xab\xf8\ +\x99\x863.\xdf\xb3Z\xccw\xa6#\xe4O\xd3&%\ +\x8b3\x8b\x85\x02z\xce\xfc\xbd\x0f\x94\xcf\xbc\xe9\xb8\xf6\ +\x0a}\x9cW\x011\xdb\xac+\x0ed\xce\x9dC\xd8\xdf\ +\x9bs\xff\x14\x99\xed\xbb\x1b\xdf\x8f\x06\x7f[a|G\ +:\xbfS \xabH6\xc9\xce\xdf\xd1\xbc!\xef\x16\xe6\ +\x9c\x1bv\x11\xc7vx\x05yb\x82\x15<\x92\x97\x93\ +F\xb2\xbf<%\xb3|\x9c\xb0\x1fU\x8a\xbc\xa0TV\ +\x91\x5c\x95m\xcf\xd1{S\xf7\x8c\xc1\xf1\x8b\xd2\xee\x8f\ +\x88\x1b\xe0\x93\x87|\x10|\xaf\xa0|y\xad|\x9d@\ +J\x1a\xa7b\xad\x10'\x87{~\xb2\x0d\x1e\xce\x9f\x01\ +\x7fn9\xe8\x89\xaf\xcfH5w\xf0\x1fC.\xbaX\ +:\x91b@\xdc5m\xffDI\xd7\xc0\xd7\xc5\xca?\ +\x7f\xeb\xe68\x97\xa9\xb4\x5c3\xbc\xab\xec\x5c\x9c\xe3\xc1\ +\xcf}\xc4\xf7\x02\xca\x88\xe3\xc3\xdf\xe2\x9c\xa0\xb2i\x89\ +\xafG\x96s\xefu\xf0\xbdX\x88?\x8b\x9f\xc3'\x1c\ +\xe7\x85\xbc\x0b\xb8\xab\x02yS\x90\xe3\x02\xb9\xdbp\xcf\ +\x0a\x9f\x95R\xee\xa1\xb2?=\xa2cA\xa5\xae\xe1U\ +\xb9\xe7O\xe7!\x88\x8b\xaf\xc3\x9ar\x82\x8e\x15\xdd\xf7\ +,\x96w(\xc0\xff\xd1\xb9I?2\x13\xf1\xa0'b\ +\xd7\x00\xeb/\xe3\x9e@\xf9\xe7\x8f\x00\xf8\xb9\xc8\xb9g\ +\xa7\x91\x99\x1e\xab\x0b\xf3\x08\xca|\x16\xce{\xeb\x8ep\ +\xe1#\xf2y\x90c\x06\xb9\x87\xa5\xc4\x06\xcb7\x7f\xfa\ +\x0e;'9\xae\xe4\xdc\x0b\xf2\xa9<8\xfc\xb7\xe5\xe0\ +\xe3\xb0\x06\xfb\x7fH\xf6\x87\x07\xa2q\x10|B~\xfb\ +\x8f\xde\x95yi\x9d\xc8\xf7@^(\x8e\xc5K\xa3\x13\ +\xc0\x9d\x97}\x13D\xe6{Q\xb9a\xdd\xc5\xe1\xb3|\ +\xf3\xb7n!\xf2^\x07\xc4\x1f\x0b\xf3H\xca;w>\ +\xac\xd0\xc3:F\x89\x81\xf0\x0a\xb9\x1eb\x9e-\xf9\xfc\ +!/\x1f\xf1\x10\xce\x8f\x92\xe7\x16\xee\xb9\x83\xee S\ +\x5c\x13\xdf\xb5\x9e^\x227\x11\x06\xe8L2\xcf\x1fr\ +\xfa\xb6\x0f\x11y\xaf+\xdbo\xbf\xec\xf1w|o\xae\ +\x1b\xce\xaf,A\x9bO/\x8b\xcb_+\xc7\xfc)\x1a\ +\x15\xa5\xdfd^(\xf3>\x9c\x04@\xe7H\x22\xbe/\ +< G\x0a\xe7\xe3\xc8:\xff\xfd\x93\xf0\xfd\xb1\xe2\xc4\ +\xcf\xa5j<\xc8\xe1>\x09\xce\xef\x12q_\x9d\xfd\xee\ +\x1e\x95\xe7$\xe3\xfc!\x87\x81\x97\x97Ur\xff/\xad\ +\x95\xcf\xfe\xafi\x87u\xea\x12\xfb\x1f\x19 \xfb\xfe\x8b\ +\xc8w\xe1\x8f\x9c{'e\xcfy\xc0\xf9D\xbdpN\ +\x9d\xf0\xa0\xee\xeb\x89\xfc^\xb9\xf8\x0f\xe4W\xe6G\xde\ +\xc1\xb6\x09\xbe\x9b\x02\x80\xfe\x1f\xf2\xb4\x81ve\xe3?\ +\xdad\xfa\xa9\x85\x22\xef\xfbg]\xdb\x22;\xff\x01\x80\ +\xdc\xa1\xf5\x9d\xa9;$p\xb7\x04\xc9M\x0c\x90s\x22\ +\xd3}\x12]L\xdf\x90\x9f/\xf8\x1e\xce\x19\x17:c\xf9\xaf\ +\xfdp\xce\x1b\xf0\xe5\xack\x9b\xb1\x1e\x09:2\xce\xb3\ +-_\x9e1}_x\x98H\xb9\x08#\xfb\xe6\x8e\xd2\ +t\xc1b\xf3\x07\x19\x0awKA\x87\x843S\x10\x1f\ +\x85u\xe1bg\x0c\xe9o\xdc\xb4\x84\xe28F\xf6\x17\ +\xd8(\xec\xcfO0\x0d\x17\xe1\xa2\x14|\xd0~\x07\xb8\ +\xeb\x07w\xeaE\x0d\x90e8?M\x92\xf9\xc3>\xec\ +\x1c^\xe2\xee~A\xec+:\x87W\x97\xbe\xdb=\xab\ +T{\x11\xdf\xed\xb6\xd0\xc6x\xc2:\x85\xa0\xbfG \ +O\x15\xceM\xd6\xd5\x8d\xe2k\x05p9d\xe6\xe5\x0d\ +\x92\xdb/X\xbf)\x99\xf7\x0by\x99p\x1f\x8f\xff~\ +\xe0\x05\xe2\x06\xe8q\x90\xef\x96\xb8L\x0b\xd9XVX\ +nfyo\xc2k\x06\xff\x04\xc8o\xb8;\x095\xd3\ +\x00\xb7\xa5\xd9\x91\x14O+3\xef\xbb8\xfd\x8b\xb9\xb3\ +\x02\xb5\xca\xf0\xbe\xc1\xbf?\xbd\x22\xf6\x9d\x9c\x84\xf7\xd4\ +\xfd\x1at\x9e!\x8f\xb1p]H\x87\x84\xfcE|>\ +%\xb9\x13\xf0>\xa4(/\xb8\xf4\xf3\xc3\x8f{\x14\x9e\ +%\xb0A\x0a\xdf\x8b\xecS\xc8\xa3\xc4\xfa\x0d\xdc/X\ +\xdbQ,\xad\xc2\x80\x9cu\x9c\xbf\x86\xd6\x0a\xb8\x97f\ +\xe4\xbf\xbd[\x16\xcd\x0b\x02?fC\xcf_\x07\xf3\x82\ +\xdcG\x1eh\x1d\x0e\xf8lQw\xab\x9a\xe19\xe1\xda\ +\x16\xe9?\xc5\xbe;\xdboo!\x8d\x97w\xfe\xa0\x97\ +@-\x1a\xea~\x94\xc4\xfe\x1f~\xbc\xa9\xe8w\xc0\xff\ +VPwW\xc0\xee\x815\xa4\xee\x1d\x87\xcf\x1b\xf8+\ +3\x5c\x17\x93\xd9\xc8\xae\xc69\xae\x90;)\xe0C\x00\ +\x1e\xce\xbf\xf3\x92\xe9eW\xcal\x05\xe6\xcd\xce\xc3\xf4\ +\x82m\x94\x95\xe5\xce\xff\xe5\xc7\xcaD\xf27\xb8\xc3\x01\ +\xf2\x0f\xe8\x16\xdf\x99\xb5\xd0.\xba\xbb\x079\xaahM\ +\xb0\xb6\xcc\x0b\xab\xf0\x9a\x0a\xf1\x0e\xf2\xe8\xf4b|\x07\ +\x18\xe7}\x0b\x9eSn\x01>\xe7@\x97\xc0\xa3\xa1f\ +a\x92\xad\xa1\xb4\xf6\x03?\xceW\xd2\x7f\x8b\xe6\x01\xbc\ +\x9f?\xc0?YB\xc7,\xbc\xcbW\xb2\xde\x14\xe8\x03\ +8o\xfb\xe0T\x5c_\x09x\x00\x9c#\xb8\xbb\x07\xbe\ +g|\x9f\x91\x9fw,\x9d\xee\xc7\xf7\xdf\x8a\xf6\x9f\xa3\ +\xf9\x00\x0f)\x88\x0dG\xf0\x12\xdf;(\xf7\x1e\x15\xf3\ +\xf7\x0b\x82\x5cj\xa7\xf2\xfd\xe7b\xe3\x17Pk\x09p\ +\x8bA\xb4\xfdPY \x18\xbf(=~T\x91\xf7u\ +\xa5\x07\xe1\xf8\x11?~W\x15zt\x8b\x8a\xdfU\xd9\ +\xf8iU\x8f_\xff\x0e\xf9\x03\xbfC\xfeFU\xcd\x9f\ +\xc1yD\x1a\x04\x11\x03?\xd5\x08\x22\x18~*\xd3\xf9\ +G\xd5Y`2\x0f&\xfc\x87Q\xb4\xaf1\xf0S\xad\ +h\xdf!OK\x9f\xa0z\xfa\x14\xe6ii\x8a\xce\xd3\ +\x12\xc2e\x0d\x16\x95\x1b\xb8\x07\xc1c\x16\xc5\x1f\xbeJ\ +\x091\xf43\xf6\xd0\xcf\xac!L3B\xefn\x8e\xe0\ +\x10\x82\xc4\x0a8w\x89\xf4\xb3\x9b\x8b8K\x00\xffC\ +\x10V\x01\xef\x15\x860\xfa]\xc2\xeb\xfe\x15\xef\x16\x9c\ +\x03\x7f\x1fj\xd0\xfbR\xceg\xe8\x8a\xd6!%\xb73\ +\x0e\xb1\x8ahMr|\xd3\xbayaM|d\x1b\x81\ +\x9f\x18|;\xe9\xc7\xe6P9\x13\xe0\xd3-\xbbfj\ +\x22\xab\x88\xce%{7\xc4\xe4\xd6\x1b\xe1\xbc\x02\x5c\x93\ +\x1ej\x09\x09\xda\xedP\xbf\x22-\x01\xc7\xa2\xc0\xee\xc2\ +\xbam\xe9\xfb\xc1?c\x12\xac[\x07\xfb9\xe0\xbd\x12\ +\xd5\xc1G\xf6\x19\xd8\xcbP\xf3\xae\x949\xf0\xcfw\x99\ +\xefN?6\xbb\x14\x9f\x10W\xec\x9c\xa0\xde\x5c)v\ +?\x9f\xb7\x94\xb2\xe7:\x18\xcf\x90K\x22\xbc>\xf0)\ +\x80\x9f\x11h\x00\xd7@\xf0\xdfO\xf91\x84\xfc?P\ +\x03\x04|\x1c\x22\xf4\xf5\xaf\xa5\xbf_\x17\xfb\x8a\xc0\xaf\ +)8\xc0\x7f\x07\xef\xa4\xfch\xc5\xed'\xb0\xff\xc07\ +Y\xbc\xce\x17\x8f\xcc\xba\xbe\xa5\xfc\xef\x07\xbf\xde\x89y\ +\xc5\xfcz\xb0\x0f\xa9\xb8N\x9a\xb6h\xfa\xa6\xf7\x19h\ +T\xf0n5\xcc9ycOa<\x94\xfe~\xf0k\ +\x85y\x0a\xe0\x99C\xc7d\xca\xb07\xe9\xfc\x1b\xa8g\ +U\xb4\x05<\xea\xfey\xf1\xef\x8a\x7f?\xf8\x0d\xd1^\ +\x82?\x80?\x0a\xbeGIZ\xa7\x86\x8a\x8b\xb9\xfc[\ +\xcc\xf7-\x22^\x22\xfe\xfdP3\xd5i`\xb1\xb8N\ +\xde\x93\xcbd\x19y\x0f\xc5\xe7\x0f~\xcf\x9f\x9f\x04\xce\ +\xc2\x03:\x9e+\xc9\xfb\x9bb\xba\x17\xac\x17\x00\xfeM\ +\xc9\xf9\xab.\xb6\xaf\xe1Ny\xe1\xfe\xc5\xbc\xc0|S\ +\x80nJ\x7f\xff\xf6!\xc5\xea\xa5\xe5>\xf6\x90\xf0\xdd\ +\xf4\xfa\x11\xaf\x14\xc4\x1f\xc4@\x85\xe2I\xa5\xe3\x1f\xed\ +\x1f\xf0\x0f\xa0y\xc8\xad\xc0\xb9\x08\x92\xc6\xa3p\xbd\xbd\ +1\xc5\xf6/\xef\xd9\x15\xe18\x84\x88\xf7\x0b\xd8\xf5\xe8\ +o\x81\x06!\xbf\x07\xe7\xf9@\xcc\x03\x7f_2\xbb_\ +\xd0\xcf\x04CD=\xa4\xa2\xf7\xf3{\xbd ~S\x18\ +[\xa1c\xeb\xe0\x7f\x84\xfc\x0c\x88\xf3\xe2{\xde6\xad\ +K\xc5;U\x93eN1\xdc\x81\x5c\x02?\xa9\xc8\xf3\ +\x0fy~;\x86\xe2\xde2 _\xf2^\x5c\xa3\xe8\xd4\ +B\x9b\xaa\x1b\xc6?\xc2\xe8,A\x1c2\xeb\x86\x13Y\ +8g\xe1\x18\x02:\xf7\xe0\x8b\x17\x96\x15\x98vK\xc6\ +\x80\xa8\xf7C]\x1c\xc4\xbf\x0b\xe7\x9a\xfe\x83\x92\xe3\xe8\ +\x99\xa2r/ 7\x12\xe2\x18\x10\xa7\x009\x0b\xf1\x11\ +\xf0\xbd\xc13\xa0\x86\xa5p\xfe\x16\xf8\xcaD\xf0>\x81\ +\xf5\xf3\xcf:\x95\xc3\x00\xfc\x16\xea\xeb\x80\xfc\xc6\xf2V\ +`\x80\xcf\x1e\xeaxB\x0c\x0c\xd7\x92E<\x11\xf2\x93\ +\xb0\xbfVD\xdc\x01\xf2\xc5\xc0\x17*&g\x8d\xd6\x91\ +\xa9\xda\x80\xb9\xf7]1\xae3\x5c\x17b\xff6\xe4\xba\ +B\x1cVp\x80\x9f\x1bbU\xc9\x1b\x8c\x8a\xf1\x16Q\ +\x03\xea2\xc1\xbe\x94\xc23\xf8\xfa9\xf5\xd9\xba\x05\xf6\ +E\x83\xdf\x16x5\xd0>\xaeOq}+>;\xd0\ +?\x04\xe2\xc4\x98F\x817\x8b\x88\xcd\x81\xdf\x19jU\ +\x80\x5c\x869\x96\x91\xab\xc7\xb7\x0d\x8ax.Z\x1b\xd4\ +\x82\x85\xb8\x1b\xce\x0f\x82\x18(\x9d\xfb\x0by\xa4\xb8\x9e\ +\x14\xf0`\xc8SC4\x9e\xed\xbb\x0b\xe7\xffB\xedJ\ +\x1cs<<\x93\xf2\xc1JVG\x8eo\x97$\x16\xf1\ +\xac\xce\xb8n\x0d\xc8p*\x8fX\xe0\x19\xc2>?\xc1\ +\xfb\xed\x82y\xd5\x92\xf9\x05\xf9\xfagI\xfd[\xfa\xdc\ +\xe5\xf2\x00_\xff\xe6\xdb\x1fO*\xf8}\x82 h\x7f\ +(\x82\xfdUi\xf6ge\xdb\xdf\xccJ\xf4\xc6\xc0\xbb\ +\xc1O\xa1IP\xbe\x8aB?E\x8d\x92~\x0az\xce\ +\x0d\x100\x11\xbcG\x90\x8e C\x0c\xa4\xd3\x7f\xc3\xa4\ +\xbf\xc3\xff\xee9\x04\xdc\x12\xb8\x11\x7fv\xb9\xf4w\xf8\ +\xef\xe5\x96\xf8\x1e\xe4C >\x0du\xe7 \xa6\x07\xf9\ +'B|\x82+0\xe7b\xdf\x858\x19\xd4\xbc\x05\xdd\ +\x06\xc7\x82\xe9\xdeeX\xbe\xad-\xc6{\xf8\xeb-\xe4\ +C\xa0\x8f\xf0\xfbH\x80L\x86\x5c\xd7\xfc\x08\xbf\xc2\xba\ +3\xa0\xeb\x0a\xd4\x92\xe5\xefU\xe1\xf7!\xf7\x0f\x06<\ +\x03\xea\xc9\xe0\xba\xd7\xd6\xcd\xb1\xbc\x80\xb8\x92\x90\xbe\x9e\ +Q\xf8\xfd\xc2zDo\xb0n\x8c\xf3\xe2-\x04\xe2g\ +\x88_S\xb9\x08\xf9T~\x1b\x15\xdf\x11\xf8>U\x1f\ +\x06x<\xe4d\x95\xc8o\xc49\x08]q\xfdH\x88\ +\x9b'Q9*E\xdf\x07\x1b\x0b\xed7\xd4\xc3\xc3}\ +;\x84\xeb\xe5\xf2\xeb\xb1!\x1d\x02rQh\x1eN}\ +\x1fz\x06B\xdcxe\x0b\xec+H?\xb5\x80\xea\xaf\ +\x22X\xbb\x13\xfd?U\x9f\x93\xc4u\x04i}\x1f\x7f\ +\x1f\xf0\x022\x15\xea{\x81M\x08\xf8\x02\x9d\x18\xea\x99\ +\xa6\xee\x1c\x86\xf5\xf5\xec\xdb{q\xcc\x98\x9b\x9e\x88\xf5\ +OZ\xfee\xa0\x9f\xe9`\x93\xc1\x9cqn\x08]\x03\ +\x12\xf2xA\xbe\xc2w\xf89p \xfb\xa0G\x8a\x00\ +\xbdP\xf4\x8c\xf0\x93q\xde\x1a\xe7\x99C>\x04\xe8f\ +\xd0O\x03\xe4 \xc4z\xa1\x87\x16\xc4[\xa9\xbb\x0a\xc5\ +\xe8\x98:\x0b\xcbu\xb9)\xdb\x07Sz\xa0`\xcc\x16\ +\xeau\x01\x9e\xf8=\xe2\x8a\xcb\x1e>\xfdR\xe7g\xb9\ +\x0e\xb7\x1c\xb2I\xf0\xfc\xc8t~e\x1d\xff\x07\x9d\xab\ +\xf3\x85\ \x00\x00_\x84\ I\ I*\x00\x08\x00\x00\x00\x17\x00\xfe\x00\x04\x00\x01\x00\x00\ @@ -26805,336 +26919,336 @@ qt_resource_struct = b"\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x01\x00\x00\x00+\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x06\x0c\x00\x01\x00\x00\x00\x01\x00\x00\xde\xc4\ -\x00\x00\x01y\x1f\xa4\xb3Q\ -\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x05\x97+\ +\x00\x00\x01y\xd2\xb2\xf5B\ +\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x05\x9eC\ \x00\x00\x01x\xc7F\xed\xa9\ -\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x05\x95\x7f\ +\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x05\x9c\x97\ \x00\x00\x01x\xc7F\xf0\x97\ -\x00\x00\x16`\x00\x00\x00\x00\x00\x01\x00\x05\x96S\ +\x00\x00\x16`\x00\x00\x00\x00\x00\x01\x00\x05\x9dk\ \x00\x00\x01x\xc7F\xeb4\ -\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x94\x8c\ +\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x9b\xa4\ \x00\x00\x01x\xc7F\xf0\x87\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x1f\x00\x00\x001\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x13\xb4\x00\x00\x00\x00\x00\x01\x00\x05\x80\xf9\ +\x00\x00\x13\xb4\x00\x00\x00\x00\x00\x01\x00\x05\x88\x11\ \x00\x00\x01x\xc7D\x85\xca\ -\x00\x00\x14\x06\x00\x00\x00\x00\x00\x01\x00\x05\x83\x8d\ +\x00\x00\x14\x06\x00\x00\x00\x00\x00\x01\x00\x05\x8a\xa5\ \x00\x00\x01x\xc7D\x85\xcc\ -\x00\x00\x15v\x00\x00\x00\x00\x00\x01\x00\x05\x90T\ +\x00\x00\x15v\x00\x00\x00\x00\x00\x01\x00\x05\x97l\ \x00\x00\x01x\xc7D\x85\xc3\ -\x00\x00\x12\xf0\x00\x00\x00\x00\x00\x01\x00\x05z\x87\ +\x00\x00\x12\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x81\x9f\ \x00\x00\x01x\xc7D\x83\xc5\ -\x00\x00\x12|\x00\x00\x00\x00\x00\x01\x00\x05w\x97\ +\x00\x00\x12|\x00\x00\x00\x00\x00\x01\x00\x05~\xaf\ \x00\x00\x01x\xc7D\x85\xbb\ -\x00\x00\x152\x00\x00\x00\x00\x00\x01\x00\x05\x8d\xc0\ +\x00\x00\x152\x00\x00\x00\x00\x00\x01\x00\x05\x94\xd8\ \x00\x00\x01x\xc7D\x85e\ -\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05x\xe1\ +\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05\x7f\xf9\ \x00\x00\x01x\xc7D\x85\xd0\ -\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\x8b,\ +\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\x92D\ \x00\x00\x01x\xc7D\x84\xb4\ -\x00\x00\x11\xce\x00\x01\x00\x00\x00\x01\x00\x05s\x95\ +\x00\x00\x11\xce\x00\x01\x00\x00\x00\x01\x00\x05z\xad\ \x00\x00\x01x\xc7D\x83\xca\ -\x00\x00\x12(\x00\x00\x00\x00\x00\x01\x00\x05u\xe9\ +\x00\x00\x12(\x00\x00\x00\x00\x00\x01\x00\x05}\x01\ \x00\x00\x01x\xc7D\x85\xd2\ -\x00\x00\x12\x0c\x00\x01\x00\x00\x00\x01\x00\x05u>\ +\x00\x00\x12\x0c\x00\x01\x00\x00\x00\x01\x00\x05|V\ \x00\x00\x01x\xc7D\x84\x12\ -\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05s\xf4\ +\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05{\x0c\ \x00\x00\x01x\xc7D\x84\x12\ -\x00\x00\x12\xc8\x00\x01\x00\x00\x00\x01\x00\x05z+\ +\x00\x00\x12\xc8\x00\x01\x00\x00\x00\x01\x00\x05\x81C\ \x00\x00\x01x\xc7D\x83\xcf\ -\x00\x00\x13\x12\x00\x00\x00\x00\x00\x01\x00\x05{\xd1\ +\x00\x00\x13\x12\x00\x00\x00\x00\x00\x01\x00\x05\x82\xe9\ \x00\x00\x01x\xc7D\x85\xc7\ -\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x82C\ +\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x89[\ \x00\x00\x01x\xc7D\x85\xc8\ -\x00\x00\x13\x90\x00\x00\x00\x00\x00\x01\x00\x05\x7f\xaf\ +\x00\x00\x13\x90\x00\x00\x00\x00\x00\x01\x00\x05\x86\xc7\ \x00\x00\x01x\xc7D\x85d\ -\x00\x00\x13B\x00\x00\x00\x00\x00\x01\x00\x05}\x1b\ +\x00\x00\x13B\x00\x00\x00\x00\x00\x01\x00\x05\x843\ \x00\x00\x01x\xc7D\x85\xce\ -\x00\x00\x12\x5c\x00\x01\x00\x00\x00\x01\x00\x05w3\ +\x00\x00\x12\x5c\x00\x01\x00\x00\x00\x01\x00\x05~K\ \x00\x00\x01x\xc7D\x85-\ -\x00\x00\x15\xc4\x00\x01\x00\x00\x00\x01\x00\x05\x92\xe8\ +\x00\x00\x15\xc4\x00\x01\x00\x00\x00\x01\x00\x05\x9a\x00\ \x00\x00\x01x\xc7D\x83\xcb\ -\x00\x00\x14.\x00\x01\x00\x00\x00\x01\x00\x05\x84\xd7\ +\x00\x00\x14.\x00\x01\x00\x00\x00\x01\x00\x05\x8b\xef\ \x00\x00\x01x\xc7D\x85\xb9\ -\x00\x00\x14Z\x00\x01\x00\x00\x00\x01\x00\x05\x85\xa4\ +\x00\x00\x14Z\x00\x01\x00\x00\x00\x01\x00\x05\x8c\xbc\ \x00\x00\x01x\xc7D\x83\xcc\ -\x00\x00\x13h\x00\x00\x00\x00\x00\x01\x00\x05~e\ +\x00\x00\x13h\x00\x00\x00\x00\x00\x01\x00\x05\x85}\ \x00\x00\x01x\xc7D\x85d\ -\x00\x00\x14x\x00\x00\x00\x00\x00\x01\x00\x05\x86\x04\ +\x00\x00\x14x\x00\x00\x00\x00\x00\x01\x00\x05\x8d\x1c\ \x00\x00\x01x\xc7D\x85\xb6\ -\x00\x00\x15\x94\x00\x00\x00\x00\x00\x01\x00\x05\x91\x9e\ +\x00\x00\x15\x94\x00\x00\x00\x00\x00\x01\x00\x05\x98\xb6\ \x00\x00\x01x\xc7D\x85\xd4\ -\x00\x00\x14\x9c\x00\x00\x00\x00\x00\x01\x00\x05\x87N\ +\x00\x00\x14\x9c\x00\x00\x00\x00\x00\x01\x00\x05\x8ef\ \x00\x00\x01x\xc7D\x84\x0b\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\x88\x98\ +\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\x8f\xb0\ \x00\x00\x01x\xc7D\x84\x0d\ -\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\x89\xe2\ +\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\x90\xfa\ \x00\x00\x01x\xc7D\x84\x0e\ -\x00\x00\x15\x14\x00\x00\x00\x00\x00\x01\x00\x05\x8cv\ +\x00\x00\x15\x14\x00\x00\x00\x00\x00\x01\x00\x05\x93\x8e\ \x00\x00\x01x\xc7D\x84\x0e\ -\x00\x00\x15X\x00\x00\x00\x00\x00\x01\x00\x05\x8f\x0a\ +\x00\x00\x15X\x00\x00\x00\x00\x00\x01\x00\x05\x96\x22\ \x00\x00\x01x\xc7D\x84\x0f\ -\x00\x00\x11\xba\x00\x01\x00\x00\x00\x01\x00\x05s6\ +\x00\x00\x11\xba\x00\x01\x00\x00\x00\x01\x00\x05zN\ \x00\x00\x01x\xc7D\x84\xb5\ -\x00\x00\x15\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x93B\ +\x00\x00\x15\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x9aZ\ \x00\x00\x01x\xc7D\x85\xc5\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x08\x00\x00\x00Q\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00$\x98\x00\x00\x00\x00\x00\x01\x00\x06+\x0e\ +\x00\x00$\x98\x00\x00\x00\x00\x00\x01\x00\x062&\ \x00\x00\x01y+\x8f\x94\x0d\ -\x00\x00#\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x16\xea\ +\x00\x00#\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x1e\x02\ \x00\x00\x01y+\x8f\x94\x0b\ -\x00\x00$\x0a\x00\x00\x00\x00\x00\x01\x00\x06\x1e\xba\ +\x00\x00$\x0a\x00\x00\x00\x00\x00\x01\x00\x06%\xd2\ \x00\x00\x01y+\x8f\x94\x0b\ -\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06&\x8c\ +\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06-\xa4\ \x00\x00\x01x\xc7F\xf7d\ -\x00\x00#\xe6\x00\x00\x00\x00\x00\x01\x00\x06\x1a8\ +\x00\x00#\xe6\x00\x00\x00\x00\x00\x01\x00\x06!P\ \x00\x00\x01x\xc7F\xf6\xff\ -\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x1e\ +\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x166\ \x00\x00\x01x\xc7F\xf6\xed\ -\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06\x22\x0a\ +\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06)\x22\ \x00\x00\x01x\xc7F\xf6\xf0\ -\x00\x00#j\x00\x00\x00\x00\x00\x01\x00\x06\x13\xa0\ +\x00\x00#j\x00\x00\x00\x00\x00\x01\x00\x06\x1a\xb8\ \x00\x00\x01y+\x8f\x94\x0c\ \x00\x00\x16\xba\x00\x02\x00\x00\x009\x00\x00\x00Z\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00 :\x00\x00\x00\x00\x00\x01\x00\x05\xf1\xf4\ +\x00\x00 :\x00\x00\x00\x00\x00\x01\x00\x05\xf9\x0c\ \x00\x00\x01x\xc7F\xf0|\ -\x00\x00#\x0e\x00\x00\x00\x00\x00\x01\x00\x06\x0a1\ +\x00\x00#\x0e\x00\x00\x00\x00\x00\x01\x00\x06\x11I\ \x00\x00\x01x\xc7F\xed\xa1\ -\x00\x00\x1e\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xe2\xb6\ +\x00\x00\x1e\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xe9\xce\ \x00\x00\x01x\xc7F\xef\xf0\ -\x00\x00\x19\x08\x00\x00\x00\x00\x00\x01\x00\x05\xaf\x1d\ +\x00\x00\x19\x08\x00\x00\x00\x00\x00\x01\x00\x05\xb65\ \x00\x00\x01x\xc7F\xf0\xa1\ -\x00\x00!\xbc\x00\x00\x00\x00\x00\x01\x00\x05\xff\xfd\ +\x00\x00!\xbc\x00\x00\x00\x00\x00\x01\x00\x06\x07\x15\ \x00\x00\x01x\xc7F\xf0W\ -\x00\x00\x17@\x00\x00\x00\x00\x00\x01\x00\x05\x9d\xdd\ +\x00\x00\x17@\x00\x00\x00\x00\x00\x01\x00\x05\xa4\xf5\ \x00\x00\x01x\xc7F\xed\xd6\ -\x00\x00\x19\xae\x00\x00\x00\x00\x00\x01\x00\x05\xb2\xec\ +\x00\x00\x19\xae\x00\x00\x00\x00\x00\x01\x00\x05\xba\x04\ \x00\x00\x01x\xc7F\xe9{\ -\x00\x00\x22b\x00\x00\x00\x00\x00\x01\x00\x06\x04c\ +\x00\x00\x22b\x00\x00\x00\x00\x00\x01\x00\x06\x0b{\ \x00\x00\x01x\xc7F\xef\xab\ -\x00\x00\x1b\xde\x00\x00\x00\x00\x00\x01\x00\x05\xc5\xad\ +\x00\x00\x1b\xde\x00\x00\x00\x00\x00\x01\x00\x05\xcc\xc5\ \x00\x00\x01x\xc7F\xcb~\ -\x00\x00\x18,\x00\x00\x00\x00\x00\x01\x00\x05\xa5\x22\ +\x00\x00\x18,\x00\x00\x00\x00\x00\x01\x00\x05\xac:\ \x00\x00\x01x\xc7F\xf0\x90\ -\x00\x00\x1a\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xba\xbf\ +\x00\x00\x1a\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xc1\xd7\ \x00\x00\x01x\xc7F\xe9@\ -\x00\x00\x1e<\x00\x00\x00\x00\x00\x01\x00\x05\xdcu\ +\x00\x00\x1e<\x00\x00\x00\x00\x00\x01\x00\x05\xe3\x8d\ \x00\x00\x01x\xc7F\xf0=\ -\x00\x00 \xe0\x00\x00\x00\x00\x00\x01\x00\x05\xf6-\ +\x00\x00 \xe0\x00\x00\x00\x00\x00\x01\x00\x05\xfdE\ \x00\x00\x01x\xc7F\xf0\x03\ -\x00\x00\x1c\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd0B\ +\x00\x00\x1c\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd7Z\ \x00\x00\x01x\xc7F\xed\xd8\ -\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xec\xea\ +\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xf4\x02\ \x00\x00\x01x\xc7F\xed\xda\ -\x00\x00\x1cT\x00\x00\x00\x00\x00\x01\x00\x05\xcb\x82\ +\x00\x00\x1cT\x00\x00\x00\x00\x00\x01\x00\x05\xd2\x9a\ \x00\x00\x01x\xc7F\xcc\x09\ -\x00\x00\x18b\x00\x00\x00\x00\x00\x01\x00\x05\xa6\x14\ +\x00\x00\x18b\x00\x00\x00\x00\x00\x01\x00\x05\xad,\ \x00\x00\x01x\xc7F\xe9f\ -\x00\x00\x1e\xe8\x00\x00\x00\x00\x00\x01\x00\x05\xe4c\ +\x00\x00\x1e\xe8\x00\x00\x00\x00\x00\x01\x00\x05\xeb{\ \x00\x00\x01x\xc7F\xe9\x86\ -\x00\x00\x1b\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xbc\x0d\ +\x00\x00\x1b\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xc3%\ \x00\x00\x01x\xc7F\xcb\xfe\ -\x00\x00\x1d\x96\x00\x00\x00\x00\x00\x01\x00\x05\xd7\x13\ +\x00\x00\x1d\x96\x00\x00\x00\x00\x00\x01\x00\x05\xde+\ \x00\x00\x01x\xc7F\xe9\x82\ -\x00\x00!\x16\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xeb\ +\x00\x00!\x16\x00\x00\x00\x00\x00\x01\x00\x05\xff\x03\ \x00\x00\x01x\xc7F\xe7N\ -\x00\x00\x17v\x00\x00\x00\x00\x00\x01\x00\x05\x9f\xff\ +\x00\x00\x17v\x00\x00\x00\x00\x00\x01\x00\x05\xa7\x17\ \x00\x00\x01x\xc7F\xe97\ -\x00\x00\x1f\xc4\x00\x00\x00\x00\x00\x01\x00\x05\xee\xfa\ +\x00\x00\x1f\xc4\x00\x00\x00\x00\x00\x01\x00\x05\xf6\x12\ \x00\x00\x01x\xc7F\xe7\xa5\ -\x00\x00\x22\x98\x00\x00\x00\x00\x00\x01\x00\x06\x06c\ +\x00\x00\x22\x98\x00\x00\x00\x00\x00\x01\x00\x06\x0d{\ \x00\x00\x01x\xc7F\xe6\xb8\ -\x00\x00\x19\xe4\x00\x00\x00\x00\x00\x01\x00\x05\xb4\x0b\ +\x00\x00\x19\xe4\x00\x00\x00\x00\x00\x01\x00\x05\xbb#\ \x00\x00\x01x\xc7F\xef\xeb\ -\x00\x00\x1d\x16\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xe3\ +\x00\x00\x1d\x16\x00\x00\x00\x00\x00\x01\x00\x05\xda\xfb\ \x00\x00\x01x\xc7F\xef\xe2\ -\x00\x00\x1bn\x00\x00\x00\x00\x00\x01\x00\x05\xc1)\ +\x00\x00\x1bn\x00\x00\x00\x00\x00\x01\x00\x05\xc8A\ \x00\x00\x01x\xc7F\xef\x93\ -\x00\x00\x1af\x00\x00\x00\x00\x00\x01\x00\x05\xb7h\ +\x00\x00\x1af\x00\x00\x00\x00\x00\x01\x00\x05\xbe\x80\ \x00\x00\x01x\xc7F\xef\xee\ -\x00\x00\x19>\x00\x00\x00\x00\x00\x01\x00\x05\xaf\xef\ +\x00\x00\x19>\x00\x00\x00\x00\x00\x01\x00\x05\xb7\x07\ \x00\x00\x01x\xc7F\xf00\ -\x00\x00\x17\xec\x00\x00\x00\x00\x00\x01\x00\x05\xa2\xf2\ +\x00\x00\x17\xec\x00\x00\x00\x00\x00\x01\x00\x05\xaa\x0a\ \x00\x00\x01x\xc7F\xed\xd3\ -\x00\x00\x16\xd0\x00\x00\x00\x00\x00\x01\x00\x05\x9b\x19\ +\x00\x00\x16\xd0\x00\x00\x00\x00\x00\x01\x00\x05\xa21\ \x00\x00\x01x\xc7F\xef\xe6\ -\x00\x00!\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x01P\ +\x00\x00!\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x08h\ \x00\x00\x01x\xc7F\xf0f\ -\x00\x00 p\x00\x00\x00\x00\x00\x01\x00\x05\xf2\xfb\ +\x00\x00 p\x00\x00\x00\x00\x00\x01\x00\x05\xfa\x13\ \x00\x00\x01x\xc7F\xf0k\ -\x00\x00\x1f\x1e\x00\x00\x00\x00\x00\x01\x00\x05\xe5k\ +\x00\x00\x1f\x1e\x00\x00\x00\x00\x00\x01\x00\x05\xec\x83\ \x00\x00\x01x\xc7F\xed\xc2\ -\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xd8!\ +\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xdf9\ \x00\x00\x01x\xc7F\xf0\x0e\ -\x00\x00\x1a&\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xde\ +\x00\x00\x1a&\x00\x00\x00\x00\x00\x01\x00\x05\xbc\xf6\ \x00\x00\x01x\xc7F\xf0%\ -\x00\x00\x18\x98\x00\x00\x00\x00\x00\x01\x00\x05\xa7M\ +\x00\x00\x18\x98\x00\x00\x00\x00\x00\x01\x00\x05\xaee\ \x00\x00\x01x\xc7F\xf0I\ -\x00\x00\x17\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa1s\ +\x00\x00\x17\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa8\x8b\ \x00\x00\x01x\xc7F\xf0,\ -\x00\x00\x22\xce\x00\x00\x00\x00\x00\x01\x00\x06\x08\x1f\ +\x00\x00\x22\xce\x00\x00\x00\x00\x00\x01\x00\x06\x0f7\ \x00\x00\x01x\xc7F\xed\xdd\ -\x00\x00!|\x00\x00\x00\x00\x00\x01\x00\x05\xfe\x91\ +\x00\x00!|\x00\x00\x00\x00\x00\x01\x00\x06\x05\xa9\ \x00\x00\x01x\xc7F\xf04\ -\x00\x00\x1f\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xf0{\ +\x00\x00\x1f\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xf7\x93\ \x00\x00\x01x\xc7F\xf0*\ -\x00\x00\x1er\x00\x00\x00\x00\x00\x01\x00\x05\xdd\xd6\ +\x00\x00\x1er\x00\x00\x00\x00\x00\x01\x00\x05\xe4\xee\ \x00\x00\x01x\xc7F\xed\xc6\ -\x00\x00\x1dV\x00\x00\x00\x00\x00\x01\x00\x05\xd5\xb7\ +\x00\x00\x1dV\x00\x00\x00\x00\x00\x01\x00\x05\xdc\xcf\ \x00\x00\x01x\xc7F\xf0@\ -\x00\x00\x1c\x14\x00\x00\x00\x00\x00\x01\x00\x05\xca)\ +\x00\x00\x1c\x14\x00\x00\x00\x00\x00\x01\x00\x05\xd1A\ \x00\x00\x01x\xc7F\xf0K\ -\x00\x00\x1bB\x00\x00\x00\x00\x00\x01\x00\x05\xbe&\ +\x00\x00\x1bB\x00\x00\x00\x00\x00\x01\x00\x05\xc5>\ \x00\x00\x01x\xc7F\xed\xcb\ -\x00\x00\x1e\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xd9\xa3\ +\x00\x00\x1e\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xe0\xbb\ \x00\x00\x01x\xc7F\xed\xc8\ -\x00\x00 \xb0\x00\x00\x00\x00\x00\x01\x00\x05\xf4(\ +\x00\x00 \xb0\x00\x00\x00\x00\x00\x01\x00\x05\xfb@\ \x00\x00\x01x\xc7F\xef\xa6\ -\x00\x00\x1c\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x7f\ +\x00\x00\x1c\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xd4\x97\ \x00\x00\x01x\xc7F\xed\xce\ -\x00\x00\x17\x10\x00\x00\x00\x00\x00\x01\x00\x05\x9c\xd8\ +\x00\x00\x17\x10\x00\x00\x00\x00\x00\x01\x00\x05\xa3\xf0\ \x00\x00\x01x\xc7F\xf0\x80\ -\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05\xeb\x8c\ +\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05\xf2\xa4\ \x00\x00\x01x\xc7F\xf0:\ -\x00\x00\x19~\x00\x00\x00\x00\x00\x01\x00\x05\xb1p\ +\x00\x00\x19~\x00\x00\x00\x00\x00\x01\x00\x05\xb8\x88\ \x00\x00\x01x\xc7F\xf0(\ -\x00\x00\x222\x00\x00\x00\x00\x00\x01\x00\x06\x02\x83\ +\x00\x00\x222\x00\x00\x00\x00\x00\x01\x00\x06\x09\x9b\ \x00\x00\x01x\xc7F\xef\xdd\ -\x00\x00\x1b\xae\x00\x00\x00\x00\x00\x01\x00\x05\xc3\x18\ +\x00\x00\x1b\xae\x00\x00\x00\x00\x00\x01\x00\x05\xca0\ \x00\x00\x01x\xc7F\xed\xd1\ -\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x05\xf9\xa7\ +\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x06\x00\xbf\ \x00\x00\x01y+\x8f\x93{\ -\x00\x00\x1a\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xb92\ +\x00\x00\x1a\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xc0J\ \x00\x00\x01x\xc7F\xf0\x0c\ -\x00\x00\x18\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa5\ +\x00\x00\x18\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xaf\xbd\ \x00\x00\x01x\xc7F\xed\xa4\ -\x00\x00\x1c\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xd2\x83\ +\x00\x00\x1c\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xd9\x9b\ \x00\x00\x01x\xc7F\xe9<\ -\x00\x00\x0e\xba\x00\x00\x00\x00\x00\x01\x00\x04(&\ +\x00\x00\x0e\xba\x00\x00\x00\x00\x00\x01\x00\x04/>\ \x00\x00\x01y+\x8f\x94\x12\ -\x00\x00\x0d\x0a\x00\x00\x00\x00\x00\x01\x00\x03\x91\x87\ +\x00\x00\x0d\x0a\x00\x00\x00\x00\x00\x01\x00\x03\x98\x9f\ \x00\x00\x01y+\x8f\x93\xdc\ -\x00\x00\x0e\x80\x00\x00\x00\x00\x00\x01\x00\x04#\x0e\ +\x00\x00\x0e\x80\x00\x00\x00\x00\x00\x01\x00\x04*&\ \x00\x00\x01y+\x8f\x94\x08\ -\x00\x00\x0d~\x00\x00\x00\x00\x00\x01\x00\x03\xf4\x82\ +\x00\x00\x0d~\x00\x00\x00\x00\x00\x01\x00\x03\xfb\x9a\ \x00\x00\x01y+\x8f\x93\xcd\ -\x00\x00\x09\xf6\x00\x00\x00\x00\x00\x01\x00\x032\x17\ +\x00\x00\x09\xf6\x00\x00\x00\x00\x00\x01\x00\x039/\ \x00\x00\x01y+\x8f\x94\x10\ -\x00\x00\x09f\x00\x01\x00\x00\x00\x01\x00\x02i(\ +\x00\x00\x09f\x00\x01\x00\x00\x00\x01\x00\x02p@\ \x00\x00\x01x\xc7F\xf5\xfe\ -\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01\x87\xf3\ +\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01\x8f\x0b\ \x00\x00\x01y+\x8f\x93\xce\ -\x00\x00\x08:\x00\x00\x00\x00\x00\x01\x00\x01\xa3\x1b\ +\x00\x00\x08:\x00\x00\x00\x00\x00\x01\x00\x01\xaa3\ \x00\x00\x01y+\x8f\x94\x0a\ -\x00\x00\x0f$\x00\x00\x00\x00\x00\x01\x00\x04jD\ +\x00\x00\x0f$\x00\x00\x00\x00\x00\x01\x00\x04q\x5c\ \x00\x00\x01x\xc7F\xf6G\ -\x00\x00\x0e\x10\x00\x00\x00\x00\x00\x01\x00\x04\x15\xa7\ +\x00\x00\x0e\x10\x00\x00\x00\x00\x00\x01\x00\x04\x1c\xbf\ \x00\x00\x01y+\x8f\x93\xe3\ -\x00\x00\x08\xd2\x00\x00\x00\x00\x00\x01\x00\x02^f\ +\x00\x00\x08\xd2\x00\x00\x00\x00\x00\x01\x00\x02e~\ \x00\x00\x01y+\x8f\x93\xd6\ -\x00\x00\x0eL\x00\x00\x00\x00\x00\x01\x00\x04\x1d\xe2\ +\x00\x00\x0eL\x00\x00\x00\x00\x00\x01\x00\x04$\xfa\ \x00\x00\x01y+\x8f\x94\x09\ -\x00\x00\x09*\x00\x00\x00\x00\x00\x01\x00\x02f\x89\ +\x00\x00\x09*\x00\x00\x00\x00\x00\x01\x00\x02m\xa1\ \x00\x00\x01y+\x8f\x94\x07\ -\x00\x00\x0c \x00\x00\x00\x00\x00\x01\x00\x03u!\ +\x00\x00\x0c \x00\x00\x00\x00\x00\x01\x00\x03|9\ \x00\x00\x01y+\x8f\x93\xd5\ -\x00\x00\x07z\x00\x00\x00\x00\x00\x01\x00\x01\x96/\ +\x00\x00\x07z\x00\x00\x00\x00\x00\x01\x00\x01\x9dG\ \x00\x00\x01y+\x8f\x93\xdf\ -\x00\x00\x08x\x00\x00\x00\x00\x00\x01\x00\x01\xbe\xd0\ +\x00\x00\x08x\x00\x00\x00\x00\x00\x01\x00\x01\xc5\xe8\ \x00\x00\x01x\xc7F\xf6*\ -\x00\x00\x0f\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x87Q\ +\x00\x00\x0f\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x8ei\ \x00\x00\x01y+\x8f\x94\x12\ -\x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xa9(\ +\x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xb0@\ \x00\x00\x01x\xc7F\xf61\ -\x00\x00\x0cb\x00\x00\x00\x00\x00\x01\x00\x03\x81c\ +\x00\x00\x0cb\x00\x00\x00\x00\x00\x01\x00\x03\x88{\ \x00\x00\x01y+\x8f\x93\xda\ -\x00\x00\x07B\x00\x00\x00\x00\x00\x01\x00\x01\x8c\x97\ +\x00\x00\x07B\x00\x00\x00\x00\x00\x01\x00\x01\x93\xaf\ \x00\x00\x01y+\x8f\x94\x11\ -\x00\x00\x08\xf8\x00\x00\x00\x00\x00\x01\x00\x02b\x14\ +\x00\x00\x08\xf8\x00\x00\x00\x00\x00\x01\x00\x02i,\ \x00\x00\x01y+\x8f\x93\xce\ -\x00\x00\x0a\xc0\x00\x00\x00\x00\x00\x01\x00\x03N\xe6\ +\x00\x00\x0a\xc0\x00\x00\x00\x00\x00\x01\x00\x03U\xfe\ \x00\x00\x01y+\x8f\x93\xd8\ -\x00\x00\x09\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x12$\ +\x00\x00\x09\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x19<\ \x00\x00\x01x\xc7F\xf5\xf8\ -\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x01|\xed\ +\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x01\x84\x05\ \x00\x00\x01y+\x8f\x93\xcc\ -\x00\x00\x0a\xf8\x00\x00\x00\x00\x00\x01\x00\x03R\x98\ +\x00\x00\x0a\xf8\x00\x00\x00\x00\x00\x01\x00\x03Y\xb0\ \x00\x00\x01y+\x8f\x93\xdb\ -\x00\x00\x0d\x98\x00\x00\x00\x00\x00\x01\x00\x03\xf8 \ +\x00\x00\x0d\x98\x00\x00\x00\x00\x00\x01\x00\x03\xff8\ \x00\x00\x01y+\x8f\x93\xdc\ -\x00\x00\x0c<\x00\x00\x00\x00\x00\x01\x00\x03|B\ +\x00\x00\x0c<\x00\x00\x00\x00\x00\x01\x00\x03\x83Z\ \x00\x00\x01y+\x8f\x93\xde\ -\x00\x00\x0bZ\x00\x00\x00\x00\x00\x01\x00\x03W)\ +\x00\x00\x0bZ\x00\x00\x00\x00\x00\x01\x00\x03^A\ \x00\x00\x01y+\x8f\x93\xd8\ -\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x039\x16\ +\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x03@.\ \x00\x00\x01x\xc7F\xf60\ -\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04`\xcc\ +\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04g\xe4\ \x00\x00\x01y+\x8f\x94\x13\ -\x00\x00\x0fN\x00\x00\x00\x00\x00\x01\x00\x04\x7f\x0c\ +\x00\x00\x0fN\x00\x00\x00\x00\x00\x01\x00\x04\x86$\ \x00\x00\x01y+\x8f\x93\xe2\ -\x00\x00\x0dD\x00\x00\x00\x00\x00\x01\x00\x03\x95\x12\ +\x00\x00\x0dD\x00\x00\x00\x00\x00\x01\x00\x03\x9c*\ \x00\x00\x01x\xc7F\xf6\x18\ -\x00\x00\x0a0\x00\x00\x00\x00\x00\x01\x00\x034\xb6\ +\x00\x00\x0a0\x00\x00\x00\x00\x00\x01\x00\x03;\xce\ \x00\x00\x01y+\x8f\x93\xd9\ -\x00\x00\x060\x00\x00\x00\x00\x00\x01\x00\x01\x1a\xc3\ +\x00\x00\x060\x00\x00\x00\x00\x00\x01\x00\x01!\xdb\ \x00\x00\x01x\xc7F\xf6\x1b\ -\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03p\xae\ +\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03w\xc6\ \x00\x00\x01y+\x8f\x93\xd7\ -\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x84\xba\ +\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x8b\xd2\ \x00\x00\x01y+\x8f\x93\xfd\ -\x00\x00\x07\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x9bR\ +\x00\x00\x07\xb8\x00\x00\x00\x00\x00\x01\x00\x01\xa2j\ \x00\x00\x01y+\x8f\x94\x0f\ -\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\x90\xf8\ +\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\x98\x10\ \x00\x00\x01y+\x8f\x93\xcb\ -\x00\x00\x0e\xd0\x00\x01\x00\x00\x00\x01\x00\x04/\x99\ +\x00\x00\x0e\xd0\x00\x01\x00\x00\x00\x01\x00\x046\xb1\ \x00\x00\x01x\xc7F\xf5\xf3\ -\x00\x00\x06^\x00\x00\x00\x00\x00\x01\x00\x01zK\ +\x00\x00\x06^\x00\x00\x00\x00\x00\x01\x00\x01\x81c\ \x00\x00\x01y+\x8f\x94\x06\ -\x00\x00\x09\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x0c\xa3\ +\x00\x00\x09\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x13\xbb\ \x00\x00\x01y+\x8f\x93\xd0\ -\x00\x00\x0f\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x93\xb2\ +\x00\x00\x0f\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x9a\xca\ \x00\x00\x01x\xc7F\xf5\xf0\ -\x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\x8c\x97\ +\x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\x93\xaf\ \x00\x00\x01x\xc7F\xf6$\ -\x00\x00\x0d\xe4\x00\x01\x00\x00\x00\x01\x00\x03\xfb\xab\ +\x00\x00\x0d\xe4\x00\x01\x00\x00\x00\x01\x00\x04\x02\xc3\ \x00\x00\x01x\xc7F\xf6\x04\ -\x00\x00\x0c\xec\x00\x00\x00\x00\x00\x01\x00\x03\x8a\xa7\ +\x00\x00\x0c\xec\x00\x00\x00\x00\x00\x01\x00\x03\x91\xbf\ \x00\x00\x01y+\x8f\x94\x0e\ -\x00\x00\x0b\xaa\x00\x00\x00\x00\x00\x01\x00\x03[\xa0\ +\x00\x00\x0b\xaa\x00\x00\x00\x00\x00\x01\x00\x03b\xb8\ \x00\x00\x01x\xc7F\xf6L\ -\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x7f\x95\ +\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x86\xad\ \x00\x00\x01y+\x8f\x93\xe0\ -\x00\x00\x0c\xb0\x00\x00\x00\x00\x00\x01\x00\x03\x86\x07\ +\x00\x00\x0c\xb0\x00\x00\x00\x00\x00\x01\x00\x03\x8d\x1f\ \x00\x00\x01y+\x8f\x93\xdd\ -\x00\x00\x07\xea\x00\x00\x00\x00\x00\x01\x00\x01\x9d\xf4\ +\x00\x00\x07\xea\x00\x00\x00\x00\x00\x01\x00\x01\xa5\x0c\ \x00\x00\x01y+\x8f\x93\xe1\ -\x00\x00\x08\xb4\x00\x00\x00\x00\x00\x01\x00\x02H\xcc\ +\x00\x00\x08\xb4\x00\x00\x00\x00\x00\x01\x00\x02O\xe4\ \x00\x00\x01x\xc7F\xf6D\ -\x00\x00\x11\x06\x00\x00\x00\x00\x00\x01\x00\x05@\xf3\ +\x00\x00\x11\x06\x00\x00\x00\x00\x00\x01\x00\x05H\x0b\ \x00\x00\x01y+\x8f\x93\xd4\ -\x00\x00\x11\x88\x00\x00\x00\x00\x00\x01\x00\x05k\xae\ +\x00\x00\x11\x88\x00\x00\x00\x00\x00\x01\x00\x05r\xc6\ \x00\x00\x01y+\x8f\x93\xd1\ -\x00\x00\x11b\x00\x00\x00\x00\x00\x01\x00\x05Yl\ +\x00\x00\x11b\x00\x00\x00\x00\x00\x01\x00\x05`\x84\ \x00\x00\x01y+\x8f\x93\xcf\ -\x00\x00\x11\xa0\x00\x00\x00\x00\x00\x01\x00\x05o\xc9\ +\x00\x00\x11\xa0\x00\x00\x00\x00\x00\x01\x00\x05v\xe1\ \x00\x00\x01y+\x8f\x93\xd2\ -\x00\x00\x10\x94\x00\x00\x00\x00\x00\x01\x00\x059\xf7\ +\x00\x00\x10\x94\x00\x00\x00\x00\x00\x01\x00\x05A\x0f\ \x00\x00\x01x\xc7F\xf8/\ -\x00\x00\x106\x00\x00\x00\x00\x00\x01\x00\x05!\xc2\ +\x00\x00\x106\x00\x00\x00\x00\x00\x01\x00\x05(\xda\ \x00\x00\x01x\xc7F\xf8,\ -\x00\x00\x11 \x00\x00\x00\x00\x00\x01\x00\x05F\xfd\ +\x00\x00\x11 \x00\x00\x00\x00\x00\x01\x00\x05N\x15\ \x00\x00\x01x\xc7F\xf83\ -\x00\x00\x10\xdc\x00\x00\x00\x00\x00\x01\x00\x05@%\ +\x00\x00\x10\xdc\x00\x00\x00\x00\x00\x01\x00\x05G=\ \x00\x00\x01x\xc7F\xf8(\ -\x00\x00\x10`\x00\x00\x00\x00\x00\x01\x00\x05\x22O\ +\x00\x00\x10`\x00\x00\x00\x00\x00\x01\x00\x05)g\ \x00\x00\x01y+\x8f\x93\xd3\ -\x00\x00\x11J\x00\x00\x00\x00\x00\x01\x00\x05G\xa1\ +\x00\x00\x11J\x00\x00\x00\x00\x00\x01\x00\x05N\xb9\ \x00\x00\x01y+\x8f\x93\xca\ -\x00\x00\x10z\x00\x00\x00\x00\x00\x01\x00\x0509\ +\x00\x00\x10z\x00\x00\x00\x00\x00\x01\x00\x057Q\ \x00\x00\x01y+\x8f\x93\xc9\ -\x00\x00\x10\xbe\x00\x00\x00\x00\x00\x01\x00\x05:\xa6\ +\x00\x00\x10\xbe\x00\x00\x00\x00\x00\x01\x00\x05A\xbe\ \x00\x00\x01y+\x8f\x93\xe3\ " diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py index b0c3c9c1b3..8152d2addc 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py @@ -12,10 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import boto3 from botocore.paginate import (PageIterator, Paginator) from botocore.client import BaseClient -from botocore.exceptions import ClientError +from botocore.exceptions import (ClientError, ConfigNotFound, NoCredentialsError, ProfileNotFound) from typing import Dict, List -from model import (constants, error_messages) +from model import error_messages from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder) """ @@ -65,8 +65,11 @@ def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient: def setup_default_session(profile: str) -> None: - global default_session - default_session = boto3.session.Session(profile_name=profile) + try: + global default_session + default_session = boto3.session.Session(profile_name=profile) + except (ConfigNotFound, ProfileNotFound) as error: + raise RuntimeError(error) def get_default_account_id() -> str: @@ -76,6 +79,8 @@ def get_default_account_id() -> str: except ClientError as error: raise RuntimeError(error_messages.AWS_SERVICE_REQUEST_CLIENT_ERROR_MESSAGE.format( "get_caller_identity", error.response['Error']['Code'], error.response['Error']['Message'])) + except NoCredentialsError as error: + raise RuntimeError(error) def get_default_region() -> str: From 5d7aae9bd899e838e183a7b09a530e18261c4acb Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 3 Jun 2021 11:12:54 -0700 Subject: [PATCH 055/105] SPEC-2513 Fixes to enable w4459 (#1107) * fixing w4459 * Fixes for nounity * putting OLD_APARAM_USER in a common place to avoid duplicated declarations --- .../CryCommon/Maestro/Types/AnimParamType.h | 1 + Code/Framework/AzCore/Tests/AZStd/String.cpp | 16 ++++++------- .../AzCore/Tests/Math/Matrix4x4Tests.cpp | 10 ++++---- Code/Framework/AzCore/Tests/Math/ObbTests.cpp | 24 +++++++++---------- Code/Sandbox/Editor/QtViewPaneManager.cpp | 6 ++--- .../TrackView/SequenceBatchRenderDialog.cpp | 16 ++++++------- .../DiskLightFeatureProcessorInterface.h | 2 -- .../Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp | 10 ++++---- .../Source/RPI.Reflect/Shader/ShaderAsset.cpp | 5 +--- .../RPI.Reflect/Shader/ShaderVariantAsset.cpp | 5 +--- .../StaticLib/GraphCanvas/Styling/Parser.cpp | 6 ++--- .../Animation/Controls/UiTimelineCtrl.cpp | 4 ---- .../Code/Source/Animation/AnimNode.cpp | 1 - .../Code/Source/Cinematics/AnimNode.cpp | 1 - .../Code/Source/Cinematics/AnimPostFXNode.cpp | 1 - .../MicrophoneSystemComponent_Windows.cpp | 16 ++++++------- .../Source/Optimization/Constants.h | 4 ++-- .../Source/Optimization/LineSearch.cpp | 8 +++---- .../Tests/OptimizationTest.cpp | 10 ++++---- .../Benchmarks/PhysXBenchmarksUtilities.h | 6 ++--- .../Common/MSVC/Configurations_msvc.cmake | 1 - 21 files changed, 68 insertions(+), 85 deletions(-) diff --git a/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h b/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h index f7caf2db7f..4614b1638d 100644 --- a/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h +++ b/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h @@ -137,5 +137,6 @@ enum class AnimParamType Invalid = static_cast(0xFFFFFFFF) }; +static const int OLD_APARAM_USER = 100; #endif // CRYINCLUDE_CRYCOMMON_MAESTRO_TYPES_ANIMPARAMTYPE_H diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 5b8c176f02..a48725309a 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -1914,7 +1914,7 @@ namespace UnitTest TEST_F(String, StringView_CompareIsConstexpr) { using TypeParam = char; - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { return "HelloWorld"; }; @@ -1922,7 +1922,7 @@ namespace UnitTest { return "HelloPearl"; }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); constexpr basic_string_view lhsView(compileTimeString1); constexpr basic_string_view rhsView(compileTimeString2); @@ -1937,11 +1937,11 @@ namespace UnitTest TEST_F(String, StringView_CompareOperatorsAreConstexpr) { using TypeParam = char; - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { return "HelloWorld"; }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1(); constexpr basic_string_view compareView(compileTimeString1); static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed"); static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed"); @@ -1955,7 +1955,7 @@ namespace UnitTest { auto swap_test_func = []() constexpr -> basic_string_view { - constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { if constexpr (AZStd::is_same_v) { @@ -1977,7 +1977,7 @@ namespace UnitTest return L"InuWorld"; } }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); basic_string_view lhsView(compileTimeString1); basic_string_view rhsView(compileTimeString2); @@ -2001,7 +2001,7 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr) { - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { if constexpr (AZStd::is_same_v) { @@ -2012,7 +2012,7 @@ namespace UnitTest return L"HelloWorld"; } }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr basic_string_view hashView(compileTimeString1); constexpr size_t compileHash = AZStd::hash>{}(hashView); static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0"); diff --git a/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp index a9baa3ddea..8c4fac86c2 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp @@ -59,7 +59,7 @@ namespace UnitTest TEST(MATH_Matrix4x4, TestCreateFrom) { - float testFloats[] = + float thisTestFloats[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, @@ -67,20 +67,20 @@ namespace UnitTest 13.0f, 14.0f, 15.0f, 16.0f }; float testFloatMtx[16]; - Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats); + Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(thisTestFloats); AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f)); AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f)); AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f)); AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f)); m1.StoreToRowMajorFloat16(testFloatMtx); - AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0); - m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats); + AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0); + m1 = Matrix4x4::CreateFromColumnMajorFloat16(thisTestFloats); AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f)); AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f)); AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f)); AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f)); m1.StoreToColumnMajorFloat16(testFloatMtx); - AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0); + AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0); } TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4) diff --git a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp index de267b4265..5eb9057761 100644 --- a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp @@ -119,10 +119,10 @@ namespace UnitTest TEST(MATH_Obb, Contains) { - const Vector3 position(1.0f, 2.0f, 3.0f); - const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f)); - const Vector3 halfLengths(2.0f, 1.0f, 2.5f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(1.0f, 2.0f, 3.0f); + const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f)); + const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); // test some pairs of points which should be just either side of the Obb boundary EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f))); EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f))); @@ -134,10 +134,10 @@ namespace UnitTest TEST(MATH_Obb, GetDistance) { - const Vector3 position(5.0f, 3.0f, 2.0f); - const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f)); - const Vector3 halfLengths(0.5f, 2.0f, 1.5f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(5.0f, 3.0f, 2.0f); + const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f)); + const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f); EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f); EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f); @@ -146,10 +146,10 @@ namespace UnitTest TEST(MATH_Obb, GetDistanceSq) { - const Vector3 position(1.0f, 4.0f, 3.0f); - const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f)); - const Vector3 halfLengths(1.5f, 3.0f, 1.0f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(1.0f, 4.0f, 3.0f); + const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f)); + const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f); EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f); EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f); diff --git a/Code/Sandbox/Editor/QtViewPaneManager.cpp b/Code/Sandbox/Editor/QtViewPaneManager.cpp index b242f3d914..3949f0516e 100644 --- a/Code/Sandbox/Editor/QtViewPaneManager.cpp +++ b/Code/Sandbox/Editor/QtViewPaneManager.cpp @@ -121,7 +121,7 @@ protected: }; #endif -Q_GLOBAL_STATIC(QtViewPaneManager, s_instance) +Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance) QWidget* QtViewPane::CreateWidget() @@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name) QtViewPaneManager* QtViewPaneManager::instance() { - return s_instance(); + return s_viewPaneManagerInstance(); } bool QtViewPaneManager::exists() { - return s_instance.exists(); + return s_viewPaneManagerInstance.exists(); } void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState) diff --git a/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp index 69322c481c..0abe3ced75 100644 --- a/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp +++ b/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp @@ -59,7 +59,7 @@ namespace { int fps; const char* fpsDesc; - } fps[] = { + } fpsOptions[] = { {24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"}, {48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"} }; @@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog() m_ui->m_resolutionCombo->setCurrentIndex(0); // Fill the FPS combo box. - for (int i = 0; i < AZStd::size(fps); ++i) + for (int i = 0; i < AZStd::size(fpsOptions); ++i) { - m_ui->m_fpsCombo->addItem(fps[i].fpsDesc); + m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc); } m_ui->m_fpsCombo->setCurrentIndex(0); @@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange() m_ui->m_destinationEdit->setText(item.folder); // fps bool bFound = false; - for (int i = 0; i < arraysize(fps); ++i) + for (int i = 0; i < arraysize(fpsOptions); ++i) { - if (item.fps == fps[i].fps) + if (item.fps == fpsOptions[i].fps) { m_ui->m_fpsCombo->setCurrentIndex(i); bFound = true; @@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange() void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex) { - m_customFPS = fps[itemIndex].fps; + m_customFPS = fpsOptions[itemIndex].fps; CheckForEnableUpdateButton(); } @@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item) item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion, m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion); // fps - if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc) + if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc) { item.fps = m_customFPS; } else { - item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps; + item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps; } // prefix item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text(); diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h index 50c0aa2455..ce911fecf7 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h @@ -46,8 +46,6 @@ namespace AZ uint16_t m_padding; // Explicit padding. }; - static constexpr size_t size = sizeof(DiskLightData); - //! DiskLightFeatureProcessorInterface provides an interface to acquire, release, and update a disk light. This is necessary for code outside of //! the Atom features gem to communicate with the DiskLightFeatureProcessor. class DiskLightFeatureProcessorInterface diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp index 5bebc92125..88d103d282 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp @@ -45,7 +45,7 @@ namespace AZ Fence* fenceToSignal) { AZStd::vector vkCommandBuffers; - AZStd::vector vkWaitSemaphores; + AZStd::vector vkWaitSemaphoreVector; // vulkan.h has a #define called vkWaitSemaphores, so we name this differently AZStd::vector vkWaitPipelineStages; AZStd::vector vkSignalSemaphores; VkSubmitInfo submitInfo; @@ -65,11 +65,11 @@ namespace AZ return item->GetNativeSemaphore(); }); vkWaitPipelineStages.reserve(waitSemaphoresInfo.size()); - vkWaitSemaphores.reserve(waitSemaphoresInfo.size()); + vkWaitSemaphoreVector.reserve(waitSemaphoresInfo.size()); AZStd::for_each(waitSemaphoresInfo.begin(), waitSemaphoresInfo.end(), [&](auto& item) { vkWaitPipelineStages.push_back(item.first); - vkWaitSemaphores.push_back(item.second->GetNativeSemaphore()); + vkWaitSemaphoreVector.push_back(item.second->GetNativeSemaphore()); // Wait until the wait semaphores has been submitted for signaling. item.second->WaitEvent(); }); @@ -77,8 +77,8 @@ namespace AZ submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; - submitInfo.waitSemaphoreCount = static_cast(vkWaitSemaphores.size()); - submitInfo.pWaitSemaphores = vkWaitSemaphores.empty() ? nullptr : vkWaitSemaphores.data(); + submitInfo.waitSemaphoreCount = static_cast(vkWaitSemaphoreVector.size()); + submitInfo.pWaitSemaphores = vkWaitSemaphoreVector.empty() ? nullptr : vkWaitSemaphoreVector.data(); submitInfo.pWaitDstStageMask = vkWaitPipelineStages.empty() ? nullptr : vkWaitPipelineStages.data(); submitInfo.commandBufferCount = static_cast(vkCommandBuffers.size()); submitInfo.pCommandBuffers = vkCommandBuffers.empty() ? nullptr : vkCommandBuffers.data(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 0a59772d6c..cf655d43f7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -10,6 +10,7 @@ * */ #include +#include #include #include @@ -34,10 +35,6 @@ namespace AZ uint32_t ShaderAsset::MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType) { - static constexpr uint32_t RhiIndexBitPosition = 30; - static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition; - static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1; - static constexpr uint32_t SubProductTypeBitPosition = 0; static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition; static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp index 7864768351..5bcdcbb569 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp @@ -10,6 +10,7 @@ * */ #include +#include #include #include @@ -24,10 +25,6 @@ namespace AZ uint32_t ShaderVariantAsset::MakeAssetProductSubId( uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId, uint32_t subProductType) { - static constexpr uint32_t RhiIndexBitPosition = 30; - static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition; - static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1; - static constexpr uint32_t SubProductTypeBitPosition = 17; static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition; static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1; diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp index 319996949b..db07c3ca8e 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp @@ -1072,8 +1072,7 @@ namespace GraphCanvas if (!id.empty()) { - Selector selector = Selector::Get(id); - result.emplace_back(selector); + result.emplace_back(Selector::Get(id)); continue; } @@ -1111,8 +1110,7 @@ namespace GraphCanvas { bits.emplace_back(stateSelector); } - Selector selector = aznew CompoundSelector(std::move(bits)); - nestedSelectors.emplace_back(selector); + nestedSelectors.emplace_back(aznew CompoundSelector(std::move(bits))); } } diff --git a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp index 9f7fae6041..0b18539c54 100644 --- a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp +++ b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp @@ -23,10 +23,6 @@ #include #include -static const QColor timeMarkerCol = QColor(255, 0, 255); -static const QColor textCol = QColor(0, 0, 0); -static const QColor ltgrayCol = QColor(110, 110, 110); - QColor InterpolateColor(const QColor& c1, const QColor& c2, float fraction) { const int r = (c2.red() - c1.red()) * fraction + c1.red(); diff --git a/Gems/LyShine/Code/Source/Animation/AnimNode.cpp b/Gems/LyShine/Code/Source/Animation/AnimNode.cpp index 421e9a4fe3..369f080a15 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimNode.cpp +++ b/Gems/LyShine/Code/Source/Animation/AnimNode.cpp @@ -48,7 +48,6 @@ static const EUiAnimCurveType DEFAULT_TRACK_TYPE = eUiAnimCurveType_BezierFloat; // Old serialization values that are no longer // defined in IUiAnimationSystem.h, but needed for conversion: -static const int OLD_APARAM_USER = 100; static const int OLD_ACURVE_GOTO = 21; static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95; static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96; diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp index 7fea8097ed..8398ad47fd 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp @@ -60,7 +60,6 @@ static const EAnimCurveType DEFAULT_TRACK_TYPE = eAnimCurveType_BezierFloat; // Old serialization values that are no longer // defined in IMovieSystem.h, but needed for conversion: -static const int OLD_APARAM_USER = 100; static const int OLD_ACURVE_GOTO = 21; static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95; static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96; diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp index 67b6ab5cdb..992527d2c4 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp @@ -281,7 +281,6 @@ void CAnimPostFXNode::SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bL paramType.Serialize(trackNode, true); // Don't use APARAM_USER because it could change in newer versions // CAnimNode::SerializeAnims will then take care of that - static const unsigned int OLD_APARAM_USER = 100; paramType = static_cast(static_cast(paramType.GetType()) + OLD_APARAM_USER); paramType.Serialize(trackNode, false); } diff --git a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp index 8b3e3c2ce6..2eb840b253 100644 --- a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp +++ b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp @@ -51,11 +51,11 @@ namespace Audio // To avoid errors, we initialize COM here with the same model. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); + const CLSID CLSID_MMDeviceEnumerator_UUID = __uuidof(MMDeviceEnumerator); + const IID IID_IMMDeviceEnumerator_UUID = __uuidof(IMMDeviceEnumerator); HRESULT hresult = CoCreateInstance( - CLSID_MMDeviceEnumerator, nullptr, - CLSCTX_ALL, IID_IMMDeviceEnumerator, + CLSID_MMDeviceEnumerator_UUID, nullptr, + CLSCTX_ALL, IID_IMMDeviceEnumerator_UUID, reinterpret_cast(&m_enumerator) ); @@ -133,8 +133,8 @@ namespace Audio AZ_Assert(m_device != nullptr, "Attempting to start a Microphone session while the device is uninitialized - Windows!\n"); // Get the IAudioClient from the device - const IID IID_IAudioClient = __uuidof(IAudioClient); - HRESULT hresult = m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audioClient)); + const IID IID_IAudioClient_UUID = __uuidof(IAudioClient); + HRESULT hresult = m_device->Activate(IID_IAudioClient_UUID, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audioClient)); if (FAILED(hresult)) { @@ -182,8 +182,8 @@ namespace Audio } // Get the IAudioCaptureClient - const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); - hresult = m_audioClient->GetService(IID_IAudioCaptureClient, reinterpret_cast(&m_audioCaptureClient)); + const IID IID_IAudioCaptureClient_UUID = __uuidof(IAudioCaptureClient); + hresult = m_audioClient->GetService(IID_IAudioCaptureClient_UUID, reinterpret_cast(&m_audioCaptureClient)); if (FAILED(hresult)) { diff --git a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h index d480fb9cd0..feb626aa50 100644 --- a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h +++ b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h @@ -27,6 +27,6 @@ namespace NumericalMethods::Optimization const double epsilon = 1e-7; // values recommended in Nocedal and Wright for constants in the Wolfe conditions for satisfactory solution improvement - const double c1 = 1e-4; - const double c2 = 0.9; + const double WolfeConditionsC1 = 1e-4; + const double WolfeConditionsC2 = 0.9; } // namespace NumericalMethods::Optimization diff --git a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp index 1325638dd2..06152446d6 100644 --- a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp +++ b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp @@ -162,16 +162,16 @@ namespace NumericalMethods::Optimization { // if the value of f corresponding to alpha1 isn't sufficiently small compared to f at x0, // then the interval [alpha0 ... alpha1] must bracket a suitable point. - if ((f_alpha1 > f_x0 + c1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0)) + if ((f_alpha1 > f_x0 + WolfeConditionsC1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0)) { return SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0, - f, x0, searchDirection, f_x0, df_x0, c1, c2); + f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); } // otherwise, if the derivative corresponding to alpha1 is large enough, alpha1 already // satisfies the Wolfe conditions and so return alpha1. double df_alpha1 = DirectionalDerivative(f, x0 + alpha1 * searchDirection, searchDirection); - if (fabs(df_alpha1) <= -c2 * df_x0) + if (fabs(df_alpha1) <= -WolfeConditionsC2 * df_x0) { LineSearchResult result; result.m_outcome = LineSearchOutcome::Success; @@ -184,7 +184,7 @@ namespace NumericalMethods::Optimization if (df_alpha1 >= 0.0) { return SelectStepSizeFromInterval(alpha1, alpha0, f_alpha1, f_alpha0, df_alpha1, - f, x0, searchDirection, f_x0, df_x0, c1, c2); + f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); } // haven't found an interval which is guaranteed to bracket a suitable point, diff --git a/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp b/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp index b02f13d8a4..92d7e0dcba 100644 --- a/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp +++ b/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp @@ -158,12 +158,12 @@ namespace NumericalMethods::Optimization double f_x0 = f_alpha0; double df_x0 = df_alpha0; LineSearchResult lineSearchResult = SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0, - testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, c1, c2); + testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success); // check that the Wolfe conditions are satisfied by the returned step size - EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize); - EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0); + EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize); + EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0); } TEST(OptimizationTest, LineSearch_VariousSearchDirections_SatisfiesWolfeCondition) @@ -180,8 +180,8 @@ namespace NumericalMethods::Optimization EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success); // check that the Wolfe conditions are satisfied by the returned step size - EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize); - EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0); + EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize); + EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0); } } diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h index ab9bd58148..0971226644 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h @@ -112,9 +112,9 @@ namespace PhysX::Benchmarks for (double percentile : percentiles) { //ensure the percentile is between 0.0 and 1.0 - const double epsilon = 0.001; - AZ::ClampIfCloseMag(percentile, 0.0, epsilon); - AZ::ClampIfCloseMag(percentile, 1.0, epsilon); + const double testEpsilon = 0.001; + AZ::ClampIfCloseMag(percentile, 0.0, testEpsilon); + AZ::ClampIfCloseMag(percentile, 1.0, testEpsilon); size_t idx = aznumeric_cast(std::round(percentile * (values.size() - 1))); std::nth_element(values.begin(), values.begin() + idx, values.end()); diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 24dffe56a6..f53b8aa769 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -74,7 +74,6 @@ ly_append_configurations_options( /wd4436 # the result of unary operator may be unaligned /wd4450 # declaration hides global declaration /wd4457 # declaration hides function parameter - /wd4459 # declaration hides global declaration # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From 6681a5376844f9e3fcb0faebe6ffe6983c9bfa03 Mon Sep 17 00:00:00 2001 From: mgwynn Date: Thu, 3 Jun 2021 14:13:44 -0400 Subject: [PATCH 056/105] Add the ability to remove tags. Updated some descriptions, and updated some log messages to include parameters. --- scripts/o3de/o3de/project_properties.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py index d8453a2c4f..7a9610a775 100644 --- a/scripts/o3de/o3de/project_properties.py +++ b/scripts/o3de/o3de/project_properties.py @@ -13,12 +13,13 @@ logging.basicConfig() def get_project_props(name: str = None, path: pathlib.Path = None) -> dict: proj_json = manifest.get_project_json_data(project_name=name, project_path=path) if not proj_json: - logger.error('Could not retrieve project.json file') + param = name if name else path + logger.error(f'Could not retrieve project.json file for {param}') return None return proj_json def edit_project_props(proj_path, proj_name, new_origin, new_display, - new_summary, new_icon, new_tag) -> int: + new_summary, new_icon, new_tag, remove_tag) -> int: proj_json = get_project_props(proj_name, proj_path) if not proj_json: @@ -36,6 +37,14 @@ def edit_project_props(proj_path, proj_name, new_origin, new_display, if 'user_tags' not in proj_json: proj_json['user_tags'] = [] proj_json['user_tags'].append(new_tag) + if remove_tag: + if 'user_tags' in proj_json: + if remove_tag in proj_json['user_tags']: + proj_json['user_tags'].remove(remove_tag) + else: + logger.warn(f'{remove_tag} not found in user_tags for removal.') + else: + logger.warn(f'user_tags property not found for removal of tag {remove_tag}.') manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json') return 0 @@ -47,7 +56,8 @@ def _edit_project_props(args: argparse) -> int: args.project_display, args.project_summary, args.project_icon, - args.project_tag) + args.project_tag, + args.remove_tag) def add_parser_args(parser): group = parser.add_mutually_exclusive_group(required=True) @@ -65,7 +75,9 @@ def add_parser_args(parser): group.add_argument('-pi', '--project-icon', type=str, required=False, help='Sets the path to the projects icon resource.') group.add_argument('-pt', '--project-tag', type=str, required=False, - help='Adds a tag to canonical user tags. These tags are intended for documentation and filtering.') + help='Adds a tag to user tags. These tags are intended for documentation and filtering.') + group.add_argument('-rt', '--remove-tag', type=str, required=False, + help='Removes a tag from user tags. These tags are intended for documentation and filtering.') parser.set_defaults(func=_edit_project_props) def add_args(subparsers) -> None: From 7bba5ed2fc6f91a66fd2fbaada65ac130d7b1c87 Mon Sep 17 00:00:00 2001 From: mgwynn Date: Thu, 3 Jun 2021 14:43:13 -0400 Subject: [PATCH 057/105] Incorporated suggestion to use setdefault to handle missing tag property rather than explicitly checking for and creating it. --- scripts/o3de/o3de/project_properties.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py index 7a9610a775..63ff05a8da 100644 --- a/scripts/o3de/o3de/project_properties.py +++ b/scripts/o3de/o3de/project_properties.py @@ -34,9 +34,7 @@ def edit_project_props(proj_path, proj_name, new_origin, new_display, if new_icon: proj_json['icon_path'] = new_icon if new_tag: - if 'user_tags' not in proj_json: - proj_json['user_tags'] = [] - proj_json['user_tags'].append(new_tag) + proj_json.setdefault('user_tags', []).append(new_tag) if remove_tag: if 'user_tags' in proj_json: if remove_tag in proj_json['user_tags']: @@ -75,9 +73,9 @@ def add_parser_args(parser): group.add_argument('-pi', '--project-icon', type=str, required=False, help='Sets the path to the projects icon resource.') group.add_argument('-pt', '--project-tag', type=str, required=False, - help='Adds a tag to user tags. These tags are intended for documentation and filtering.') + help='Adds a tag to user_tags property. These tags are intended for documentation and filtering.') group.add_argument('-rt', '--remove-tag', type=str, required=False, - help='Removes a tag from user tags. These tags are intended for documentation and filtering.') + help='Removes a tag from the user_tags property.') parser.set_defaults(func=_edit_project_props) def add_args(subparsers) -> None: From e99a95d909948d51b02b6fefb3f393eace5b7aca Mon Sep 17 00:00:00 2001 From: mgwynn Date: Thu, 3 Jun 2021 15:02:18 -0400 Subject: [PATCH 058/105] Added copyright header for validation --- scripts/o3de/o3de/project_properties.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py index 63ff05a8da..69bd1b9406 100644 --- a/scripts/o3de/o3de/project_properties.py +++ b/scripts/o3de/o3de/project_properties.py @@ -1,3 +1,14 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + import argparse import json import os From dce87534c7d9a7cb1f686d3368998093cc08b2fa Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:37:16 -0500 Subject: [PATCH 059/105] Removing the Pyside implementation of the project manager python scripts (#1112) * Removing the Pyside implementation of the project manager python scripts * Removing reference to the scripts/project_manager directory The Install_common.cmake script reference to the project_manager directory has been removed. --- cmake/Platform/Common/Install_common.cmake | 1 - scripts/CMakeLists.txt | 1 - scripts/project_manager/CMakeLists.txt | 22 - scripts/project_manager/__init__.py | 10 - scripts/project_manager/projects.py | 804 ------------------ scripts/project_manager/pyside.py | 61 -- scripts/project_manager/tests/__init__.py | 10 - .../project_manager/tests/test_projects.py | 255 ------ scripts/project_manager/tests/test_pyside.py | 54 -- .../ui/create_from_template.ui | 94 -- scripts/project_manager/ui/create_gem.ui | 94 -- scripts/project_manager/ui/create_project.ui | 94 -- .../project_manager/ui/manage_gem_targets.ui | 165 ---- .../project_manager/ui/project_manager.ico | 3 - scripts/project_manager/ui/project_manager.ui | 407 --------- 15 files changed, 2075 deletions(-) delete mode 100644 scripts/project_manager/CMakeLists.txt delete mode 100755 scripts/project_manager/__init__.py delete mode 100755 scripts/project_manager/projects.py delete mode 100755 scripts/project_manager/pyside.py delete mode 100755 scripts/project_manager/tests/__init__.py delete mode 100755 scripts/project_manager/tests/test_projects.py delete mode 100755 scripts/project_manager/tests/test_pyside.py delete mode 100644 scripts/project_manager/ui/create_from_template.ui delete mode 100644 scripts/project_manager/ui/create_gem.ui delete mode 100644 scripts/project_manager/ui/create_project.ui delete mode 100644 scripts/project_manager/ui/manage_gem_targets.ui delete mode 100644 scripts/project_manager/ui/project_manager.ico delete mode 100644 scripts/project_manager/ui/project_manager.ui diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 710a8b266f..11260ab018 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -443,7 +443,6 @@ function(ly_setup_others) install(DIRECTORY ${LY_ROOT_FOLDER}/scripts/bundler - ${LY_ROOT_FOLDER}/scripts/project_manager ${LY_ROOT_FOLDER}/scripts/o3de DESTINATION ./scripts COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index d3c9640665..6df0f4b77f 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -12,5 +12,4 @@ add_subdirectory(detect_file_changes) add_subdirectory(commit_validation) add_subdirectory(o3de) -add_subdirectory(project_manager) add_subdirectory(ctest) diff --git a/scripts/project_manager/CMakeLists.txt b/scripts/project_manager/CMakeLists.txt deleted file mode 100644 index 36c9ad0360..0000000000 --- a/scripts/project_manager/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -ly_download_associated_package(pyside2) - -ly_add_pytest( - NAME test_pyside - PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_pyside.py -) - -ly_add_pytest( - NAME test_projects - PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_projects.py -) \ No newline at end of file diff --git a/scripts/project_manager/__init__.py b/scripts/project_manager/__init__.py deleted file mode 100755 index 4d5680a30d..0000000000 --- a/scripts/project_manager/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# diff --git a/scripts/project_manager/projects.py b/scripts/project_manager/projects.py deleted file mode 100755 index f9f40aa4bf..0000000000 --- a/scripts/project_manager/projects.py +++ /dev/null @@ -1,804 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# PySide project and gem selector GUI - -import os -import pathlib -import sys -import argparse -import json -import logging -import subprocess -from logging.handlers import RotatingFileHandler -from typing import List -from pyside import add_pyside_environment, is_pyside_ready, uninstall_env - -engine_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) -sys.path.append(engine_path) -executable_path = '' - -logger = logging.getLogger() -logger.setLevel(logging.INFO) - -from o3de import disable_gem, enable_gem, cmake, engine_template, manifest, register - -o3de_folder = manifest.get_o3de_folder() -o3de_logs_folder = manifest.get_o3de_logs_folder() -project_manager_log_file_path = o3de_logs_folder / "project_manager.log" -log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1) -formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s') -log_file_handler.setFormatter(formatter) -logger.addHandler(log_file_handler) - -logger.info("Starting Project Manager") - - -def initialize_pyside_from_parser(): - # Parse arguments up top. We need to know the path to our binaries and QT libs in particular to load up - # PySide - parser = argparse.ArgumentParser() - parser.add_argument('--executable-path', required=True, help='Path to Executable to launch with project') - parser.add_argument('--binaries-path', default=None, help='Path to QT Binaries necessary for PySide. If not' - ' provided executable_path folder is assumed') - parser.add_argument('--parent-pid', default=0, help='Process ID of launching process') - - args = parser.parse_args() - - logger.info(f"parent_pid is {args.parent_pid}") - global executable_path - executable_path = args.executable_path - binaries_path = args.binaries_path or os.path.dirname(executable_path) - - # Initialize PySide before imports below. This adds both PySide python modules to the python system interpreter - # path and adds the necessary paths to binaries for the DLLs to be found and load their dependencies - add_pyside_environment(binaries_path) - - -if not is_pyside_ready(): - initialize_pyside_from_parser() - -try: - from PySide2.QtWidgets import QApplication, QDialogButtonBox, QPushButton, QComboBox, QMessageBox, QFileDialog - from PySide2.QtWidgets import QListView, QLabel - from PySide2.QtUiTools import QUiLoader - from PySide2.QtCore import QFile, QObject, Qt, Signal, Slot - from PySide2.QtGui import QIcon, QStandardItemModel, QStandardItem -except ImportError as e: - logger.error(f"Failed to import PySide2 with error {e}") - exit(-1) - -logger.error(f"PySide2 imports successful") - - -class DialogLoggerSignaller(QObject): - send_to_dialog = Signal(str) - - def __init__(self, dialog_logger): - super(DialogLoggerSignaller, self).__init__() - - self.dialog_logger = dialog_logger - - -# Independent class to handle log forwarding. Logger and qt signals both use emit method. -# This class's job is to receive the logger record and then emit the formatted message through -# DialogLoggerSignaller which is what the ProjectDialog handler listens for -class DialogLogger(logging.Handler): - - def __init__(self, log_dialog, log_level=logging.INFO, forward_log_level=logging.WARNING, - message_box_log_level=logging.ERROR): - super(DialogLogger, self).__init__() - - self.log_dialog = log_dialog - self.log_level = log_level - self.forward_log_level = forward_log_level - self.message_box_log_level = message_box_log_level - self.log_records = [] - self.formatter = logging.Formatter('%(levelname)s : %(message)s') - self.setFormatter(self.formatter) - self.signaller = DialogLoggerSignaller(self) - - def emit(self, record): - self.log_records.append(record) - if record.levelno >= self.message_box_log_level: - QMessageBox.warning(None, record.levelname, record.message) - elif record.levelno >= self.forward_log_level: - self.signaller.send_to_dialog.emit(self.format(record)) - - -class ProjectManagerDialog(QObject): - """ - Main project manager dialog is responsible for displaying the project selection list and output pane - """ - - def __init__(self, parent=None): - super(ProjectManagerDialog, self).__init__(parent) - - self.ui_path = (pathlib.Path(__file__).parent / 'ui').resolve() - self.home_folder = manifest.get_home_folder() - - self.log_display = None - self.dialog_logger = DialogLogger(self) - logger.addHandler(self.dialog_logger) - logger.setLevel(logging.INFO) - - self.dialog_logger.signaller.send_to_dialog.connect(self.handle_log_message) - self.mru_file_path = o3de_folder / 'mru.json' - - self.create_from_template_ui_file_path = self.ui_path / 'create_from_template.ui' - self.create_gem_ui_file_path = self.ui_path / 'create_gem.ui' - self.create_project_ui_file_path = self.ui_path / 'create_project.ui' - self.manage_project_gem_targets_ui_file_path = self.ui_path / 'manage_gem_targets.ui' - self.project_manager_icon_file_path = self.ui_path / 'project_manager.ico' - self.project_manager_ui_file_path = self.ui_path / 'project_manager.ui' - - self.project_manager_ui_file = QFile(self.project_manager_ui_file_path.as_posix()) - self.project_manager_ui_file.open(QFile.ReadOnly) - - loader = QUiLoader() - self.dialog = loader.load(self.project_manager_ui_file) - self.dialog.setWindowIcon(QIcon(self.project_manager_icon_file_path.as_posix())) - self.dialog.setFixedSize(self.dialog.size()) - - self.project_list_box = self.dialog.findChild(QComboBox, 'projectListBox') - self.refresh_project_list() - mru = self.get_mru_list() - if len(mru): - last_mru = pathlib.Path(mru[0]).resolve() - for this_slot in range(self.project_list_box.count()): - item_text = self.project_list_box.itemText(this_slot) - if last_mru.as_posix() in item_text: - self.project_list_box.setCurrentIndex(this_slot) - break - - self.create_project_button = self.dialog.findChild(QPushButton, 'createProjectButton') - self.create_project_button.clicked.connect(self.create_project_handler) - self.create_gem_button = self.dialog.findChild(QPushButton, 'createGemButton') - self.create_gem_button.clicked.connect(self.create_gem_handler) - self.create_template_button = self.dialog.findChild(QPushButton, 'createTemplateButton') - self.create_template_button.clicked.connect(self.create_template_handler) - self.create_from_template_button = self.dialog.findChild(QPushButton, 'createFromTemplateButton') - self.create_from_template_button.clicked.connect(self.create_from_template_handler) - - self.add_project_button = self.dialog.findChild(QPushButton, 'addProjectButton') - self.add_project_button.clicked.connect(self.add_project_handler) - self.add_gem_button = self.dialog.findChild(QPushButton, 'addGemButton') - self.add_gem_button.clicked.connect(self.add_gem_handler) - self.add_template_button = self.dialog.findChild(QPushButton, 'addTemplateButton') - self.add_template_button.clicked.connect(self.add_template_handler) - self.add_restricted_button = self.dialog.findChild(QPushButton, 'addRestrictedButton') - self.add_restricted_button.clicked.connect(self.add_restricted_handler) - - self.remove_project_button = self.dialog.findChild(QPushButton, 'removeProjectButton') - self.remove_project_button.clicked.connect(self.remove_project_handler) - self.remove_gem_button = self.dialog.findChild(QPushButton, 'removeGemButton') - self.remove_gem_button.clicked.connect(self.remove_gem_handler) - self.remove_template_button = self.dialog.findChild(QPushButton, 'removeTemplateButton') - self.remove_template_button.clicked.connect(self.remove_template_handler) - self.remove_restricted_button = self.dialog.findChild(QPushButton, 'removeRestrictedButton') - self.remove_restricted_button.clicked.connect(self.remove_restricted_handler) - - self.manage_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton') - self.manage_project_gem_targets_button.clicked.connect(self.manage_project_gem_targets_handler) - - self.log_display = self.dialog.findChild(QLabel, 'logDisplay') - - self.ok_cancel_button = self.dialog.findChild(QDialogButtonBox, 'okCancel') - self.ok_cancel_button.accepted.connect(self.accepted_handler) - - self.dialog.show() - - def refresh_project_list(self) -> None: - projects = manifest.get_all_projects() - self.project_list_box.clear() - for this_slot in range(len(projects)): - display_name = f'{os.path.basename(os.path.normpath(projects[this_slot]))} ({projects[this_slot]})' - self.project_list_box.addItem(display_name) - self.project_list_box.setItemData(self.project_list_box.count() - 1, projects[this_slot], - Qt.ToolTipRole) - - def accepted_handler(self) -> None: - """ - Override for handling "Ok" on main project dialog to first check whether the user has selected a project and - prompt them to if not. If a project is selected will attempt to open it. - :return: None - """ - if not self.project_list_box.currentText(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - self.launch_with_project_path(self.get_selected_project_path()) - - def get_launch_project(self) -> str: - return os.path.normpath(self.get_selected_project_path()) - - def get_executable_launch_params(self) -> list: - """ - Retrieve the necessary launch parameters to make the subprocess launch call with - this is the path - to the executable such as the Editor and the path to the selected project - :return: list of params - """ - launch_params = [executable_path, - f'-regset="/Amazon/AzCore/Bootstrap/project_path={self.get_launch_project()}"'] - return launch_params - - def launch_with_project_path(self, project_path: str) -> None: - """ - Launch the desired application given the selected project - :param project_path: Path to currently selected project - :return: None - """ - logger.info(f'Attempting to open {project_path}') - self.update_mru_list(project_path) - launch_params = self.get_executable_launch_params() - logger.info(f'Launching with params {launch_params}') - subprocess.run(launch_params, env=uninstall_env()) - - def get_selected_project_path(self) -> str: - if self.project_list_box.currentIndex() == -1: - logger.warning("No project selected") - return "" - return self.project_list_box.itemData(self.project_list_box.currentIndex(), Qt.ToolTipRole) - - def get_selected_project_name(self) -> str: - project_data = manifest.get_project_json_data(project_path=self.get_selected_project_path()) - return project_data['project_name'] - - def create_project_handler(self): - """ - Opens the Create Project pane. Retrieves a list of available templates for display - :return: None - """ - loader = QUiLoader() - self.create_project_file = QFile(self.create_project_ui_file_path.as_posix()) - - if not self.create_project_file: - logger.error(f'Failed to create project UI file at {self.create_project_file}') - return - - self.create_project_dialog = loader.load(self.create_project_file) - - if not self.create_project_dialog: - logger.error(f'Failed to load create project dialog file at {self.create_project_file}') - return - - self.create_project_ok_button = self.create_project_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_project_ok_button.accepted.connect(self.create_project_accepted_handler) - - self.create_project_template_list = self.create_project_dialog.findChild(QListView, 'projectTemplates') - self.refresh_create_project_template_list() - - self.create_project_dialog.exec() - - def create_project_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current project. - Updates UI after completion. - :return: None - """ - - selected_item = self.create_project_template_list.selectionModel().currentIndex() - project_template_path = self.create_project_template_list.model().data(selected_item) - if not project_template_path: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Project Name", - manifest.get_o3de_projects_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - project_count = 0 - project_name = "MyNewProject" - while os.path.exists(os.path.join(engine_path, project_name)): - project_name = f"MyNewProject{project_count}" - project_count += 1 - folder_dialog.selectFile(project_name) - project_folder = None - if folder_dialog.exec(): - project_folder = folder_dialog.selectedFiles() - if project_folder: - if engine_template.create_project(project_path=project_folder[0], - template_path=project_template_path) == 0: - # Success - register.register(project_path=project_folder[0]) - self.refresh_project_list() - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Project {project_folder[0]} created.") - msg_box.exec() - return - - def create_gem_handler(self): - """ - Opens the Create Gem pane. Retrieves a list of available templates for display - :return: None - """ - loader = QUiLoader() - self.create_gem_file = QFile(self.create_gem_ui_file_path.as_posix()) - - if not self.create_gem_file: - logger.error(f'Failed to create gem UI file at {self.create_gem_file}') - return - - self.create_gem_dialog = loader.load(self.create_gem_file) - - if not self.create_gem_dialog: - logger.error(f'Failed to load create gem dialog file at {self.create_gem_file}') - return - - self.create_gem_ok_button = self.create_gem_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_gem_ok_button.accepted.connect(self.create_gem_accepted_handler) - - self.create_gem_template_list = self.create_gem_dialog.findChild(QListView, 'gemTemplates') - self.refresh_create_gem_template_list() - - self.create_gem_dialog.exec() - - def create_gem_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current gem. - Updates UI after completion. - :return: None - """ - selected_item = self.create_gem_template_list.selectionModel().currentIndex() - gem_template_path = self.create_gem_template_list.model().data(selected_item) - if not gem_template_path: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - manifest.get_o3de_gems_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - gem_count = 0 - gem_name = "MyNewGem" - while os.path.exists(os.path.join(engine_path, gem_name)): - gem_name = f"MyNewGem{gem_count}" - gem_count += 1 - folder_dialog.selectFile(gem_name) - gem_folder = None - if folder_dialog.exec(): - gem_folder = folder_dialog.selectedFiles() - if gem_folder: - if engine_template.create_gem(gem_path=gem_folder[0], - template_path=gem_template_path) == 0: - # Success - register.register(gem_path=gem_folder[0]) - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Gem {gem_folder[0]} created.") - msg_box.exec() - return - - def create_template_handler(self): - """ - Opens a foldr select dialog and lets the user select the source folder they want to make a template - out of, then opens a second folder select dialog to get where they want to put the template and it name - :return: None - """ - - source_folder = QFileDialog.getExistingDirectory(self.dialog, - "Select a Folder to make a template out of.", - manifest.get_o3de_folder().as_posix()) - if not source_folder: - return - - destination_template_folder_dialog = QFileDialog(self.dialog, - "Select where the template is to be created and named.", - manifest.get_o3de_templates_folder().as_posix()) - destination_template_folder_dialog.setFileMode(QFileDialog.AnyFile) - destination_template_folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - destination_folder = None - if destination_template_folder_dialog.exec(): - destination_folder = destination_template_folder_dialog.selectedFiles() - if not destination_folder: - return - - if engine_template.create_template(source_path=source_folder, - template_path=destination_folder[0]) == 0: - # Success - register.register(template_path=destination_folder[0]) - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Template {destination_folder[0]} created.") - msg_box.exec() - return - - def create_from_template_handler(self): - """ - Opens the Create from_template pane. Retrieves a list of available from_templates for display - :return: None - """ - loader = QUiLoader() - self.create_from_template_file = QFile(self.create_from_template_ui_file_path.as_posix()) - - if not self.create_from_template_file: - logger.error(f'Failed to create from_template UI file at {self.create_from_template_file}') - return - - self.create_from_template_dialog = loader.load(self.create_from_template_file) - - if not self.create_from_template_dialog: - logger.error(f'Failed to load create from_template dialog file at {self.create_from_template_file}') - return - - self.create_from_template_ok_button = self.create_from_template_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_from_template_ok_button.accepted.connect(self.create_from_template_accepted_handler) - - self.create_from_template_list = self.create_from_template_dialog.findChild(QListView, 'genericTemplates') - self.refresh_create_from_template_list() - - self.create_from_template_dialog.exec() - - def create_from_template_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current gem. - Updates UI after completion. - :return: None - """ - create_gem_item = self.get_selected_gem_template() - if not create_gem_item: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - manifest.get_o3de_gems_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - gem_count = 0 - gem_name = "MyNewGem" - while os.path.exists(os.path.join(engine_path, gem_name)): - gem_name = f"MyNewGem{gem_count}" - gem_count += 1 - folder_dialog.selectFile(gem_name) - gem_folder = None - if folder_dialog.exec(): - gem_folder = folder_dialog.selectedFiles() - if gem_folder: - if engine_template.create_gem(gem_folder[0], create_gem_item[1]) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"gem {os.path.basename(os.path.normpath(gem_folder[0]))} created." - " Build your\nnew gem before hitting OK to launch.") - msg_box.exec() - return - - def add_project_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid project. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - manifest.get_o3de_projects_folder().as_posix()) - if project_folder: - if register.register(project_path=project_folder) == 0: - # Success - self.refresh_project_list() - - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Project {project_folder}.") - msg_box.exec() - return - - def add_gem_handler(self): - """ - Open a file search dialog looking for a folder which contains a gem. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - manifest.get_o3de_gems_folder().as_posix()) - if gem_folder: - if register.register(gem_path=gem_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Gem {gem_folder}.") - msg_box.exec() - return - - def add_template_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - manifest.get_o3de_templates_folder().as_posix()) - if template_folder: - if register.register(template_path=template_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Template {template_folder}.") - msg_box.exec() - return - - def add_restricted_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - manifest.get_o3de_restricted_folder().as_posix()) - if restricted_folder: - if register.register(restricted_path=restricted_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Restricted {restricted_folder}.") - msg_box.exec() - return - - def remove_project_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid project. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - manifest.get_o3de_projects_folder().as_posix()) - if project_folder: - if register.register(project_path=project_folder, remove=True) == 0: - # Success - self.refresh_project_list() - - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Project {project_folder}.") - msg_box.exec() - return - - def remove_gem_handler(self): - """ - Open a file search dialog looking for a folder which contains a gem. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - manifest.get_o3de_gems_folder().as_posix()) - if gem_folder: - if register.register(gem_path=gem_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Gem {gem_folder}.") - msg_box.exec() - return - - def remove_template_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - manifest.get_o3de_templates_folder().as_posix()) - if template_folder: - if register.register(template_path=template_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Template {template_folder}.") - msg_box.exec() - return - - def remove_restricted_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - manifest.get_o3de_restricted_folder().as_posix()) - if restricted_folder: - if register.register(restricted_path=restricted_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Restricted {restricted_folder}.") - msg_box.exec() - return - - def manage_project_gem_targets_handler(self): - """ - Opens the Gem management pane. Waits for the load thread to complete if still running and displays all - active gems for the current project as well as all available gems which aren't currently active. - :return: None - """ - - if not self.get_selected_project_path(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - - loader = QUiLoader() - self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix()) - - if not self.manage_project_gem_targets_file: - logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}') - return - - self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file) - - if not self.manage_project_gem_targets_dialog: - logger.error(f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}') - return - - self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Gems for Project:" - f" {self.get_selected_project_name()}") - - self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton') - self.add_gem_button.clicked.connect(self.add_project_gem_targets_handler) - - self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'availableGemTargetsList') - self.refresh_project_gem_targets_available_list() - - self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, - 'removeGemTargetsButton') - self.remove_project_gem_targets_button.clicked.connect(self.remove_project_gem_targets_handler) - - self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'enabledGemTargetsList') - self.refresh_project_gem_targets_enabled_list() - - self.manage_project_gem_targets_dialog.exec() - - - def manage_project_gem_targets_get_selected_available_gems(self) -> list: - selected_items = self.available_gem_targets_list.selectionModel().selectedRows() - return [(self.available_gem_targets_list.model().data(item)) for item in selected_items] - - def manage_project_gem_targets_get_selected_enabled_gems(self) -> list: - selected_items = self.enabled_gem_targets_list.selectionModel().selectedRows() - return [(self.enabled_gem_targets_list.model().data(item)) for item in selected_items] - - def add_project_gem_targets_handler(self) -> None: - gem_paths = manifest.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_available_gems(): - for gem_path in gem_paths: - enable_gem.enable_gem_in_project(gem_path=gem_path, - project_path=self.get_selected_project_path()) - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - return - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - - def remove_project_gem_targets_handler(self): - gem_paths = manifest.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems(): - for gem_path in gem_paths: - disable_gem.disable_gem_in_project(gem_path=gem_path, - project_path=self.get_selected_project_path()) - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - return - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - - def refresh_project_gem_targets_enabled_list(self) -> None: - enabled_project_gem_targets_model = QStandardItemModel() - enabled_project_gems = cmake.get_project_gems(project_path=self.get_selected_project_path()) - for gem_target in sorted(enabled_project_gems): - model_item = QStandardItem(gem_target) - enabled_project_gem_targets_model.appendRow(model_item) - self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model) - - - def refresh_project_gem_targets_available_list(self) -> None: - available_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = cmake.get_project_gems(project_path=self.get_selected_project_path()) - all_gem_targets = manifest.get_all_gems() - for gem_target in sorted(all_gem_targets): - if gem_target not in enabled_project_gem_targets: - model_item = QStandardItem(gem_target) - available_project_gem_targets_model.appendRow(model_item) - self.available_gem_targets_list.setModel(available_project_gem_targets_model) - - - def refresh_create_project_template_list(self) -> None: - self.create_project_template_model = QStandardItemModel() - for project_template_path in manifest.get_project_templates(): - model_item = QStandardItem(project_template_path) - self.create_project_template_model.appendRow(model_item) - self.create_project_template_list.setModel(self.create_project_template_model) - - def refresh_create_gem_template_list(self) -> None: - self.create_gem_template_model = QStandardItemModel() - for gem_template_path in manifest.get_gem_templates(): - model_item = QStandardItem(gem_template_path) - self.create_gem_template_model.appendRow(model_item) - self.create_gem_template_list.setModel(self.create_gem_template_model) - - def refresh_create_from_template_list(self) -> None: - self.create_from_template_model = QStandardItemModel() - for generic_template_path in manifest.get_generic_templates(): - model_item = QStandardItem(generic_template_path) - self.create_from_template_model.appendRow(model_item) - self.create_from_template_list.setModel(self.create_from_template_model) - - def update_mru_list(self, used_project: str) -> None: - """ - Promote a supplied project name to the "most recent" in a given MRU list. - :param used_project: path to project to promote - :param file_path: path to mru list file - :return: None - """ - used_project = os.path.normpath(used_project) - if not os.path.exists(os.path.dirname(self.mru_file_path)): - os.makedirs(os.path.dirname(self.mru_file_path), exist_ok=True) - mru_data = {} - try: - with open(self.mru_file_path, 'r') as mru_file: - mru_data = json.loads(mru_file.read()) - except FileNotFoundError: - pass - except json.JSONDecodeError: - pass - - recent_list = mru_data.get('Projects', []) - recent_list = [item for item in recent_list if item.get('Path') != used_project and - self.is_project_folder(item.get('Path'))] - - new_list = [{'Path': used_project}] - new_list.extend(recent_list) - - mru_data['Projects'] = new_list - try: - with open(self.mru_file_path, 'w') as mru_file: - mru_file.write(json.dumps(mru_data, indent=1)) - except PermissionError as e: - logger.warning(f"Failed to write {self.mru_file_path} with error {e}") - - def get_mru_list(self) -> List[str]: - """ - Retrieve the current MRU list. Does not perform validation that the projects still appear valid - :return: list of full path strings to project folders - """ - if not os.path.exists(os.path.dirname(self.mru_file_path)): - return [] - try: - with open(self.mru_file_path, 'r') as mru_file: - mru_data = json.loads(mru_file.read()) - except FileNotFoundError: - return [] - except json.JSONDecodeError: - logger.error(f'MRU list at {self.mru_file_path} is not valid JSON') - return [] - - recent_list = mru_data.get('Projects', []) - return [item.get('Path') for item in recent_list if item.get('Path') is not None] - - @Slot(str) - def handle_log_message(self, message: str) -> None: - """ - Signal handler for messages from the logger. Displays the most recent warning/error - :param message: formatted log message from DialogLoggerSignaller - :return: - """ - if not self.log_display: - return - self.log_display.setText(message) - self.log_display.setToolTip(message) - - -if __name__ == "__main__": - dialog_app = QApplication(sys.argv) - my_dialog = ProjectManagerDialog() - dialog_app.exec_() - sys.exit(0) diff --git a/scripts/project_manager/pyside.py b/scripts/project_manager/pyside.py deleted file mode 100755 index 80bf327f9b..0000000000 --- a/scripts/project_manager/pyside.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import os -import logging - -from pathlib import Path - -logger = logging.getLogger() - -pyside_initialized = False -old_env = os.environ.copy() - -# Helper to extend OS PATH for pyside to locate our QT binaries based on our build folder -def add_pyside_environment(bin_path): - if is_pyside_ready(): - # No need to reinitialize currently - logger.info("Pyside environment already initialized") - return - global old_env - old_env = os.environ.copy() - binaries_path = Path(os.path.normpath(bin_path)) - platforms_path = binaries_path.joinpath("platforms") - logger.info(f'Adding binaries path {binaries_path}') - os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(platforms_path) - - path = os.environ['PATH'] - - new_path = os.pathsep.join([str(binaries_path), str(platforms_path), path]) - os.environ['PATH'] = new_path - - global pyside_initialized - pyside_initialized = True - - -def is_pyside_ready(): - return pyside_initialized - - -def is_configuration_valid(workspace): - return os.path.basename(workspace.paths.build_directory()) != "debug" - - -def uninstall_env(): - if not is_pyside_ready(): - logger.warning("Pyside not initialized") - return os.environ - - global old_env - if old_env.get("QT_QPA_PLATFORM_PLUGIN_PATH"): - old_env.pop("QT_QPA_PLATFORM_PLUGIN_PATH") - os.environ = old_env - return old_env diff --git a/scripts/project_manager/tests/__init__.py b/scripts/project_manager/tests/__init__.py deleted file mode 100755 index 4d5680a30d..0000000000 --- a/scripts/project_manager/tests/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# diff --git a/scripts/project_manager/tests/test_projects.py b/scripts/project_manager/tests/test_projects.py deleted file mode 100755 index d073cf6c7a..0000000000 --- a/scripts/project_manager/tests/test_projects.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - - -import pytest -''' -import os -import sys -import tempfile -import logging -import pathlib -from unittest.mock import MagicMock - -logger = logging.getLogger() - -# Code lives one folder above -project_manager_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) -sys.path.append(project_manager_path) - -from pyside import add_pyside_environment, is_configuration_valid -from ly_test_tools import WINDOWS - -sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -executable_path = '' -from cmake.Tools import registration -from cmake.Tools import engine_template - - -class ProjectHelper: - def __init__(self): - self._temp_directory = pathlib.Path(tempfile.TemporaryDirectory().name).resolve() - self._temp_directory.mkdir(parents=True, exist_ok=True) - - self.home_path = self._temp_directory - registration.override_home_folder = self.home_path - self.engine_path = registration.get_this_engine_path() - if registration.register(engine_path=self.engine_path): - assert True, f"Failed to register the engine." - - if registration.register_shipped_engine_o3de_objects(): - assert True, f"Failed to register shipped engine objects." - - self.projects_folder = registration.get_o3de_projects_folder() - if not self.projects_folder.is_dir(): - assert True - - self.application = None - self.dialog = None - - def create_empty_projects(self): - self.project_1_dir = self.projects_folder / "Project1" - if engine_template.create_project(project_manager_path=self.project_1_dir): - assert True, f"Failed to create Project1." - - self.project_2_dir = self.projects_folder / "Project2" - if engine_template.create_project(project_manager_path=self.project_2_dir): - assert True, f"Failed to create Project2." - - self.project_3_dir = self.projects_folder / "Project3" - if engine_template.create_project(project_manager_path=self.project_3_dir): - assert True, f"Failed to create Project3." - - self.invalid_project_dir = self.projects_folder / "InvalidProject" - self.invalid_project_dir.mkdir(parents=True, exist_ok=True) - - def setup_dialog_test(self, workspace): - add_pyside_environment(workspace.paths.build_directory()) - - if not is_configuration_valid(workspace): - # This is essentially skipif debug. Our debug tests use our profile version of python, but that means we'd - # need to use the profile version of PySide which works with the profile QT libs which aren't in the debug - # folder we've built. - return None - - from PySide2.QtWidgets import QApplication, QMessageBox - - if QApplication.instance(): - self.application = QApplication.instance() - else: - self.application = QApplication(sys.argv) - assert self.application - - from projects import ProjectManagerDialog - - try: - self.dialog = ProjectManagerDialog(settings_folder=self.home_path) - return self.dialog - except Exception as e: - logger.error(f'Failed to create ProjectManagerDialog with error {e}') - return None - - def create_project_from_template(self, project_name) -> bool: - """ - Uses the dialog to create a temporary project based on the first template found - :param project_name: Name of project to create. Will be created under temp_project_root - :return: True for Success, False for failure - """ - from PySide2.QtWidgets import QWidget, QFileDialog - from projects import ProjectManagerDialog - - QWidget.exec = MagicMock() - self.dialog.create_project_handler() - QWidget.exec.assert_called_once() - - assert len(self.dialog.project_templates), 'Failed to find any project templates' - ProjectManagerDialog.get_selected_project_template = MagicMock(return_value=self.dialog.project_templates[0]) - - QFileDialog.exec = MagicMock() - create_project_path = self.projects_folder / project_name - QFileDialog.selectedFiles = MagicMock(return_value=[create_project_path]) - self.dialog.create_project_accepted_handler() - if create_project_path.is_dir(): - assert True, f"Expected project creation folder not found at {create_project_path}" - - if QWidget.exec.call_count == 2: - assert True, "Message box confirming project creation failed to show" - - -@pytest.fixture -def project_helper(): - return ProjectHelper() - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_logger_handler(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - from PySide2.QtWidgets import QMessageBox - QMessageBox.warning = MagicMock() - logger.error(f'Testing logger') - QMessageBox.warning.assert_called_once() - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_mru_list(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - project_helper.create_empty_projects() - - from PySide2.QtWidgets import QMessageBox - - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 0, f'MRU list unexpectedly had entries: {mru_list}' - - QMessageBox.warning = MagicMock() - my_dialog.add_project(project_helper.invalid_project_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 0, f'MRU list unexpectedly added an invalid project : {mru_list}' - QMessageBox.warning.assert_called_once() - - my_dialog.add_project(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list failed to add project at {project_helper.project_1_dir}' - - my_dialog.add_project(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}' - - my_dialog.update_mru_list(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}' - - my_dialog.add_project(project_helper.project_2_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list failed to add project at {project_helper.project_2_dir}' - - assert mru_list[0] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't first item" - assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item" - - my_dialog.update_mru_list(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list added wrong items {mru_list}' - assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item" - assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item" - - my_dialog.add_project(project_helper.invalid_project_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list added invalid item {mru_list}' - assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item" - assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item" - - my_dialog.add_project(project_helper.project_3_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 3, f'MRU list failed to add {project_helper.project_3_dir} : {mru_list}' - assert mru_list[0] == project_helper.project_3_dir, f"{project_helper.project_3_dir} wasn't first item" - assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item" - assert mru_list[2] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't third item" - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_create_project(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - project_helper.create_project_from_template("TestCreateProject") - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_add_remove_gems(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - my_project_name = "TestAddRemoveGems" - - project_helper.create_project_from_template(project_manager_path=my_project_name) - my_project_path = project_helper.projects_folder / my_project_name - - from PySide2.QtWidgets import QWidget, QFileDialog - from projects import ProjectManagerDialog - - assert my_dialog.get_selected_project_path() == my_project_path, "TestAddRemoveGems project not selected" - QWidget.exec = MagicMock() - my_dialog.manage_gems_handler() - assert my_dialog.manage_gem_targets_dialog, "No gem management dialog created" - QWidget.exec.assert_called_once() - - if not len(my_dialog.all_gems_list): - assert True, 'Failed to find any gems' - - my_test_gem_path = my_dialog.all_gems_list[0] - gem_data = registration.get_gem_data(my_test_gem_path) - my_test_gem_selection = (my_test_gem_name, my_test_gem_path) - ProjectManagerDialog.get_selected_add_gems = MagicMock(return_value=[my_test_gem_selection]) - - assert my_test_gem_name, "No Name set in test gem" - assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} already in project gem list' - - my_dialog.add_gems_handler() - assert my_test_gem_name in my_dialog.project_gem_list, f'Gem {my_test_gem_name} failed to add to gem list' - - ProjectManagerDialog.get_selected_project_gems = MagicMock(return_value=[my_test_gem_name]) - my_dialog.remove_gems_handler() - assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} still in project gem list' -''' - -def test_project_place_holder(): - pass \ No newline at end of file diff --git a/scripts/project_manager/tests/test_pyside.py b/scripts/project_manager/tests/test_pyside.py deleted file mode 100755 index c9be313f2b..0000000000 --- a/scripts/project_manager/tests/test_pyside.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import pytest -import sys -import os - -pyside_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) -sys.path.append(pyside_path) - -from pyside import add_pyside_environment, is_pyside_ready, is_configuration_valid - -from ly_test_tools import WINDOWS - -import logging -logger = logging.getLogger() - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_add_pyside_environment(workspace): - import_failed = False - try: - import PySide2 - except ImportError: - import_failed = True - - if not import_failed: - cur_path = sys.path - logger.warning(f"Expected to fail initial import but passed. Sys path was {cur_path}") - - assert is_pyside_ready() is False, "Expected pyside not to be initialized yet" - add_pyside_environment(workspace.paths.build_directory()) - assert is_pyside_ready() is True, "Expected pyside to be initialized yet" - - try: - import PySide2 - if not is_configuration_valid(workspace): - return - from PySide2.QtWidgets import QApplication - except ImportError as e: - assert False, f"Failed to import PySide2 with error {e}" - try: - from PySide2.QtWidgets import QApplication - except ImportError as e: - assert False, f"Failed to import QApplication from PySide2.QtWidgets with error {e}" - diff --git a/scripts/project_manager/ui/create_from_template.ui b/scripts/project_manager/ui/create_from_template.ui deleted file mode 100644 index b6a8740594..0000000000 --- a/scripts/project_manager/ui/create_from_template.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createFromTemplateDialog - - - - 0 - 0 - 467 - 288 - - - - Create From Template - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createFromTemplateDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createFromTemplateDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/create_gem.ui b/scripts/project_manager/ui/create_gem.ui deleted file mode 100644 index 5a5fd14a6d..0000000000 --- a/scripts/project_manager/ui/create_gem.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createGemDialog - - - - 0 - 0 - 467 - 288 - - - - Create Gem - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createGemDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createGemDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/create_project.ui b/scripts/project_manager/ui/create_project.ui deleted file mode 100644 index 67a13325b0..0000000000 --- a/scripts/project_manager/ui/create_project.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createProjectDialog - - - - 0 - 0 - 467 - 288 - - - - Create Project - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createProjectDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createProjectDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/manage_gem_targets.ui b/scripts/project_manager/ui/manage_gem_targets.ui deleted file mode 100644 index ea9e607d42..0000000000 --- a/scripts/project_manager/ui/manage_gem_targets.ui +++ /dev/null @@ -1,165 +0,0 @@ - - - manageGemTargetsDialog - - - - 0 - 0 - 702 - 297 - - - - Manage Gem Targets - - - - - 310 - 260 - 71 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 440 - 50 - 250 - 221 - - - - - 0 - 0 - - - - QAbstractItemView::ExtendedSelection - - - - - - 10 - 50 - 250 - 221 - - - - QAbstractItemView::ExtendedSelection - - - - - - 440 - 30 - 251 - 16 - - - - Available Gem Targets - - - - - - 10 - 30 - 251 - 16 - - - - Enabled Gem Targets - - - - - - 264 - 130 - 171 - 23 - - - - Remove Gem Targets >> - - - - - - 264 - 100 - 171 - 23 - - - - << Add Gem Targets - - - - - - 10 - 5 - 400 - 21 - - - - Adding new Gem Targets may require rebuilding - - - - - - - close - accepted() - manageGemTargetsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - close - rejected() - manageGemTargetsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/project_manager.ico b/scripts/project_manager/ui/project_manager.ico deleted file mode 100644 index 597266946a..0000000000 --- a/scripts/project_manager/ui/project_manager.ico +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:113be7ded1969e0d535722382b6d3730eed052a10430cf23804ae7f20414b999 -size 108278 diff --git a/scripts/project_manager/ui/project_manager.ui b/scripts/project_manager/ui/project_manager.ui deleted file mode 100644 index 6b3b5f758c..0000000000 --- a/scripts/project_manager/ui/project_manager.ui +++ /dev/null @@ -1,407 +0,0 @@ - - - Dialog - - - - 0 - 0 - 712 - 395 - - - - - 1 - 0 - - - - O3DE - - - Select and manage your projects for O3DE - - - - - 540 - 360 - 161 - 31 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 30 - 691 - 31 - - - - Current project to launch or manage gems for. - - - - - - 10 - 10 - 47 - 13 - - - - Project - - - - - - 270 - 220 - 431 - 141 - - - - QFrame::Panel - - - QFrame::Sunken - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - 10 - 70 - 241 - 151 - - - - Create - - - - - 10 - 110 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create From Template - - - - - - 10 - 20 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Project - - - - - - 11 - 50 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Gem - - - - - - 11 - 80 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Template - - - - - - - 260 - 70 - 451 - 141 - - - - Registration - - - - - 10 - 80 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Template - - - - - - 10 - 20 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Project - - - - - - 10 - 50 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Gem - - - - - - 10 - 110 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Restricted - - - - - - 230 - 110 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Restricted - - - - - - 230 - 20 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Project - - - - - - 230 - 50 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Gem - - - - - - 230 - 80 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Template - - - - - - - 10 - 230 - 241 - 121 - - - - Manage Project - - - - - 10 - 80 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Server Gem Targets - - - - - - 10 - 50 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Tool Gem Targets - - - - - - 10 - 20 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Runtime Gem Targets - - - - - - - - okCancel - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - From 1b8810b9631a7cd42e141fa23f2cf6bbd1ca1ef0 Mon Sep 17 00:00:00 2001 From: Eric Phister <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:52:21 -0500 Subject: [PATCH 060/105] Remove Project namespace from project template (#1124) In the enabled_gems.cmake file, can remove the Project:: namespace on the project module because it's treated the same as a Gem module now. --- Templates/DefaultProject/Template/Code/enabled_gems.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/DefaultProject/Template/Code/enabled_gems.cmake b/Templates/DefaultProject/Template/Code/enabled_gems.cmake index dfb7d93233..ec45be0743 100644 --- a/Templates/DefaultProject/Template/Code/enabled_gems.cmake +++ b/Templates/DefaultProject/Template/Code/enabled_gems.cmake @@ -10,7 +10,7 @@ # {END_LICENSE} set(ENABLED_GEMS - Project::${Name} + ${Name} Atom_AtomBridge Camera CameraFramework From 1245e0b327b3bbeb05ab4f181fd1403cbeaa908e Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 3 Jun 2021 15:02:03 -0500 Subject: [PATCH 061/105] Adding Tools and Builders alias to the DccScriptingInterface gem targets (#1087) * Adding a Tools and Builders variant to the DccScriptingInterface gem target to allow it to be used as a gem in the AtomTest project * Adding support to ly_create_alias to be able to specify an alias with no dependencies Updated the SettingsRegistry.cmake generation code to support generating a Gem target entry in the cmake_dependencies.*.setreg file when an interface library with no dependencies is parsed --- .../DccScriptingInterface/Code/CMakeLists.txt | 10 ++++++++ cmake/Gems.cmake | 10 ++++---- cmake/SettingsRegistry.cmake | 23 +++++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt index 76cbf6546d..bad0d743c7 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt @@ -9,6 +9,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) + return() +endif() + ly_add_target( NAME DccScriptingInterface.Static STATIC NAMESPACE Gem @@ -38,3 +42,9 @@ ly_add_target( PRIVATE Gem::DccScriptingInterface.Static ) + +# Any 'tool' type applications should use Gem::DccScriptingInterface.Editor: +ly_create_alias(NAME DccScriptingInterface.Tools NAMESPACE Gem TARGETS Gem::DccScriptingInterface.Editor) +# Add an empty 'builders' alias to allow the DccScriptInterface root gem path to be added to the generated +# cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added +ly_create_alias(NAME DccScriptingInterface.Builders NAMESPACE Gem) diff --git a/cmake/Gems.cmake b/cmake/Gems.cmake index d418d5dcd1..169210d991 100644 --- a/cmake/Gems.cmake +++ b/cmake/Gems.cmake @@ -29,9 +29,6 @@ function(ly_create_alias) message(FATAL_ERROR "Provide the namespace of the alias to create using the NAMESPACE keyword") endif() - if (NOT ly_create_alias_TARGETS) - message(FATAL_ERROR "Provide the name of the targets the alias be associated with, using the TARGETS keyword") - endif() if(TARGET ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}) message(FATAL_ERROR "Target already exists, cannot create an alias for it: ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}\n" @@ -78,8 +75,11 @@ function(ly_create_alias) list(APPEND final_targets ${de_aliased_target_name}) endforeach() - ly_parse_third_party_dependencies("${final_targets}") - ly_add_dependencies(${ly_create_alias_NAME} ${final_targets}) + # add_dependencies must be called with at least one dependent target + if(final_targets) + ly_parse_third_party_dependencies("${final_targets}") + ly_add_dependencies(${ly_create_alias_NAME} ${final_targets}) + endif() # now add the final alias: add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME}) diff --git a/cmake/SettingsRegistry.cmake b/cmake/SettingsRegistry.cmake index 4d932601b4..d7ef91d284 100644 --- a/cmake/SettingsRegistry.cmake +++ b/cmake/SettingsRegistry.cmake @@ -25,14 +25,15 @@ set(gems_json_template [[ @target_gem_dependencies_names@ } } -}]] +} +]] ) -set(gem_module_template [[ - "@stripped_gem_target@": - { - "Modules":["$"], - "SourcePaths":["@gem_module_root_relative_to_engine_root@"] - }]] + string(APPEND gem_module_template +[=[ "@stripped_gem_target@":]=] "\n" +[=[ {]=] "\n" +[=[$<$,INTERFACE_LIBRARY>>: "Modules":["$"]]=] "$\n>" +[=[ "SourcePaths":["@gem_module_root_relative_to_engine_root@"]]=] "\n" +[=[ }]=] ) #!ly_get_gem_load_dependencies: Retrieves the list of "load" dependencies for a target @@ -161,10 +162,12 @@ function(ly_delayed_generate_settings_registry) message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist") endif() + get_property(has_manually_added_dependencies TARGET ${gem_target} PROPERTY MANUALLY_ADDED_DEPENDENCIES SET) get_target_property(target_type ${gem_target} TYPE) - if (target_type STREQUAL "INTERFACE_LIBRARY") - # don't use interface libraries here, we only want ones which produce actual binaries. - # we have still already recursed into their dependencies - they'll show up later. + if (target_type STREQUAL "INTERFACE_LIBRARY" AND has_manually_added_dependencies) + # don't use interface libraries here, we only want ones which produce actual binaries unless the target + # is empty. We have still already recursed into their dependencies - they'll show up later. + # When the target has no dependencies however we want to add the gem root path to the generated setreg continue() endif() From 0a9d6f5f0f54419b8bab035379b1f631c97a4a53 Mon Sep 17 00:00:00 2001 From: chcurran Date: Thu, 3 Jun 2021 13:13:22 -0700 Subject: [PATCH 062/105] Bug fixes and improvements brought over from demo work. * Generic Multi Function Call ability added to extensible nodes * Code gen improvements, including allowing for more manually codewritten extension of codegen facilities * CVAR to disable automatic update of deprecated node * Fixed variable sorting error that can apply to parser/runtime added variables * Made Edit/SerializeContext ClassBuilder public, as it was needlessly private * Fixed dangerous Datum::GetValueAddress(), it now checks for an empty storage AZSTd::any, as does Datum::Empty() --- .../AzCore/AzCore/Serialization/EditContext.h | 5 +- .../AzCore/Serialization/SerializeContext.h | 10 +- .../Code/Editor/Components/EditorGraph.cpp | 5 +- .../AutoGen/ScriptCanvasNodeable_Header.jinja | 40 +- .../AutoGen/ScriptCanvasNodeable_Source.jinja | 160 +- .../ScriptCanvas_Nodeable_Macros.jinja | 5 +- .../Code/Include/ScriptCanvas/Core/Core.h | 1 - .../Code/Include/ScriptCanvas/Core/Datum.cpp | 8 +- .../Code/Include/ScriptCanvas/Core/Datum.h | 2 +- .../Code/Include/ScriptCanvas/Core/Node.cpp | 11 +- .../Code/Include/ScriptCanvas/Core/Node.h | 2 + .../Include/ScriptCanvas/Core/NodeableNode.h | 2 + .../Code/Include/ScriptCanvas/Core/PureData.h | 2 + .../Grammar/AbstractCodeModel.cpp | 193 +- .../ScriptCanvas/Grammar/AbstractCodeModel.h | 2 + .../ScriptCanvas/Grammar/ParsingUtilities.cpp | 10 +- .../Include/ScriptCanvas/Grammar/Primitives.h | 28 + .../Grammar/PrimitivesExecution.cpp | 38 +- .../Grammar/PrimitivesExecution.h | 8 + .../Include/ScriptCanvas/Results/ErrorText.h | 6 + ...UnitTest_RunAllTransformNodes.scriptcanvas | 9124 +++++++---------- 21 files changed, 4416 insertions(+), 5246 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h index ed19130579..394a6f047c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h @@ -127,13 +127,14 @@ namespace AZ */ class EditContext { + public: /// @cond EXCLUDE_DOCS class ClassBuilder; class EnumBuilder; using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder /// @endcond - public: + AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0); /** @@ -186,6 +187,7 @@ namespace AZ * look at the unit tests and example to see use cases. * */ + public: class ClassBuilder { friend EditContext; @@ -399,6 +401,7 @@ namespace AZ EnumBuilder* Value(const char* name, E value); }; + private: typedef AZStd::list ClassDataListType; typedef AZStd::unordered_map EnumDataMapType; diff --git a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h index 6806232337..279ce01039 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h @@ -101,6 +101,9 @@ namespace AZ class SerializeContext : public ReflectContext { + static const unsigned int VersionClassDeprecated = (unsigned int)-1; + + public: /// @cond EXCLUDE_DOCS friend class EditContext; class ClassBuilder; @@ -108,9 +111,6 @@ namespace AZ /// @endcond class EnumBuilder; - static const unsigned int VersionClassDeprecated = (unsigned int)-1; - - public: class ClassData; struct EnumerateInstanceCallContext; struct ClassElement; @@ -1131,6 +1131,7 @@ namespace AZ * ->Version(3,&MyVersionConverter) * ->Field("data",&MyStruct::m_data); */ + public: class ClassBuilder { friend class SerializeContext; @@ -1330,7 +1331,8 @@ namespace AZ AZStd::vector* m_currentAttributes = nullptr; }; - EditContext* m_editContext; ///< Pointer to optional edit context. + private: + EditContext* m_editContext; ///< Pointer to optional edit context. UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context AZStd::unordered_multimap m_classNameToUuid; /// Map all class names to their uuid AZStd::unordered_multimap m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp index 6f28cbfe19..616be11dd0 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp @@ -91,7 +91,8 @@ AZ_POP_DISABLE_WARNING #include #include -//// + AZ_CVAR(bool, g_disableDeprecatedNodeUpdates, false, {}, AZ::ConsoleFunctorFlags::Null, + "Disables automatic update attempts of deprecated nodes, so that graphs that require and update can be viewed in their original form"); namespace EditorGraphCpp { @@ -3642,7 +3643,7 @@ namespace ScriptCanvasEditor if (scriptCanvasNode) { - if (scriptCanvasNode->IsDeprecated()) + if (scriptCanvasNode->IsDeprecated() && !g_disableDeprecatedNodeUpdates) { ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration(); if (nodeConfig.IsValid()) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja index 202fad5b60..313fd196b0 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja @@ -22,6 +22,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #pragma once +#include +#include +#include +#include + #include #include @@ -43,6 +48,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {% endif %} {% endif %} +{%- set attribute_Base = Class.attrib['Base'] %} +{% if not Class.attrib['Base'] is defined %} +{% set attribute_Base = "ScriptCanvas::Nodeable" %} +{% endif %} + {% if attribute_Namespace is defined %} namespace {{attribute_Namespace}} { @@ -66,6 +76,9 @@ namespace {{attribute_Namespace}} public: \ AZ_RTTI({{className}}, "{{nodeableClassName|createHashGuid}}"{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}); \ static void Reflect(AZ::ReflectContext* reflection); \ + static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} \ + static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} \ + static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{className}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} \ static const char* GetDescription() { return "{{ macros.GetAttributeAsString(Class.attrib, 'Description') }}"; } \ ScriptCanvas::NodePropertyInterface* GetPropertyInterface(AZ::Crc32 propertyId) override; \ bool IsActive() const override { return false; } \ @@ -83,13 +96,33 @@ public: \ AZ_COMPONENT({{nodeableNodeName}}, {% if Class.attrib['NodeableUuid'] is defined %}"{{Class.attrib['NodeableUuid']}}"{% else %}"{{nodeableNodeName|createHashGuid}}"{% endif %}, ScriptCanvas::Nodes::NodeableNode); static void Reflect(AZ::ReflectContext* context); + + static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} + + static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} + + static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{nodeableNodeName}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} + void ConfigureSlots() override; + +{% if Class.attrib['ExtendConfigureSlots'] is defined %} + void ExtendConfigureSlots([[maybe_unused]] SlotExecution::Ins& ins, [[maybe_unused]] SlotExecution::Outs& latents); + +{% else %} + /* no slot configuration extension, Use Class attribute 'ExtendConfigureSlots' to extend them */ + +{% endif %} void ConfigureVisualExtensions() override; + size_t GenerateFingerprint() const override; -{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %} + +{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %} bool IsEntryPoint() const override { return true; } -{% endif %} + +{% endif %} {{nodeableNodeName}}(); + + {{Class.attrib['NodeDeclarations']}} }; } {% endif %} @@ -100,6 +133,5 @@ public: \ {{ macros.ReportErrors() }} - {% endfor %} -{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja index 71681ed73b..b977daf43b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja @@ -19,15 +19,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include - #include #include #include #include - #include {% for xml in dataFiles %} @@ -47,13 +42,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {%- set attribute_Category = Class.attrib['Category'] %} {%- set attribute_Uuid = Class.attrib['Uuid'] %} {%- set attribute_Icon = Class.attrib['Icon'] %} -{%- set attribute_Base = Class.attrib['Base'] %} {%- set attribute_GeneratePropertyFriend = Class.attrib['GeneratePropertyFriend'] %} {%- set attribute_Version = Class.attrib['Version'] %} {%- set attribute_VersionConverter = Class.attrib['VersionConverter'] %} {%- set attribute_EventHandler = Class.attrib['EventHandler'] %} {%- set attribute_Deprecated = Class.attrib['Deprecated'] %} + +{%- set attribute_Base = Class.attrib['Base'] %} +{% if not Class.attrib['Base'] is defined %} +{% set attribute_Base = "ScriptCanvas::Nodeable" %} +{% endif %} + {% set attribute_Namespace = undefined %} {%- if Class.attrib['Namespace'] is defined %} {% if Class.attrib['Namespace'] != "None" %} @@ -95,19 +95,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {{CollectDisplayGroups('Output')}} {{CollectDisplayGroups('Parameter')}} -{# FOR DEBUGGING / DIAGNOSTIC - -// Standalone (No DisplayGroup) {{ global_standaloneTagMap }} -{% for key, value in global_standaloneTagMap.items() %} -// {{key}} : {{value}} -{% endfor %} - -// DisplayGrouped {{ global_displayGroupMap }} -{% for key, value in global_displayGroupMap.items() %} -// {{key}} : {{value}} -{% endfor %} -#} - {# ----------------------------------------------------------------------------------------- #} {% if attribute_Namespace is defined %} @@ -115,33 +102,6 @@ namespace {{attribute_Namespace}} { {% endif %} -{# Standard "In" function } -{{nodemacro.FunctionSignature(attribute_QualifiedName, Class)}} -{ -{%- for parameter in Class.findall('Parameter') -%} -{% if parameter.attrib['Input'] is defined and parameter.attrib['Input'] == "True" %} - -// this->{{parameter.attrib['Name']}} = arg{{loop.index0}}; - -{%- endif -%} -{% endfor %} - -{% set returnNames = [] %} -{% set returnTypes = [] %} -{%- for return in Class.findall('Parameter') -%} -{%- if return.attrib['Output'] is defined and return.attrib['Output'] == "True" -%} -{% if returnTypes.append(return.attrib['Type']) %}{% endif %} -{% if returnNames.append("this->" + return.attrib['Name']) %}{% endif %} -{%- endif -%} -{%- endfor -%} - -{% if returnNames|length() == 1 %} -return {{returnNames[0]}}; -{% elif returnNames|length() > 1 %} - return AZStd::tuple<{{returnTypes|join(", ")}}>({{returnNames|join(", ")}}); -{% endif %} -} -#} {%- set nodeableNodeName = attribute_Name + 'Node' %} {% set list_outputs = [] %} {% for output in Class.iter('Output') %} @@ -156,43 +116,81 @@ return {{returnNames[0]}}; {% for item in Class.iter('Output') %} {% if item.attrib['DisplayGroup'] is defined %}{% set displayGroup = item.attrib['DisplayGroup'] %}{% endif %} {% endfor %} +{% set branches = [] %} +{% for method in Class.findall('Input') %} +{% for branch in method.findall('Branch') %} +{% if branches.append(branch) %}{% endif %} +{% endfor %} +{% endfor %} {# ExecutionOuts #} // ExecutionOuts begin {{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName)}} +{% if not Class.attrib['ExtendConfigureSlots'] is defined %} size_t {{attribute_QualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }{% endif %} // ExecutionOuts end {# Reflect #} + +{% if Class.attrib['ExtendReflectionSerialize'] is defined %} +{% set ExtendReflectionSerialize = "defined" %} +{% set preSerialize = "serializeBuilder" %} +{% set postSerialize = ";" %} +{% else %} +{% set preSerialize = "" %} +{% set postSerialize = "" %} +{% endif %} + +{% if Class.attrib['ExtendReflectionEdit'] is defined %} +{% set ExtendReflectionEdit = "defined" %} +{% set preEdit = "editorBuilder" %} +{% set postEdit = ";" %} +{% else %} +{% set preEdit = "" %} +{% set postEdit = "" %} +{% endif %} + +{% if Class.attrib['ExtendReflectionBehavior'] is defined %} +{% set ExtendReflectionBehavior = "defined" %} +{% set preBehavior = "behaviorBuilder" %} +{% set postBehavior = ";" %} +{% else %} +{% set preBehavior = "" %} +{% set postBehavior = "" %} +{% endif %} + void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context) { using namespace ScriptCanvas; if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { - serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>() +{% if ExtendReflectionSerialize is defined %} auto {{preSerialize}} = {% else %} {% endif %}serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>(){{postSerialize}} {% if attribute_EventHandler is defined %} - ->EventHandler<{{ attribute_EventHandler }}>() + {{preSerialize}}->EventHandler<{{ attribute_EventHandler }}>(){{postSerialize}} {% endif %} {# Serialized Properties #} {% for Property in Class.iter('Property') %} {% set property_Name = Property.attrib['Name'] %} - ->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}) + {{preSerialize}}->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}){{postSerialize}} {% endfor %} ; +{% if ExtendReflectionSerialize is defined %} + ExtendReflectionSerialize(&{{preSerialize}}); +{% endif %} + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - +{% if ExtendReflectionEdit is defined %} auto {{preEdit}} = {% else %} {% endif %}editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}} + {{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}} {% if attribute_Category is defined %} - ->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}"){{postEdit}} {%- endif %} {% if attribute_Icon is defined %} - ->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}"){{postEdit}} {%- endif %} {% if attribute_Deprecated is defined %} - ->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}"){{postEdit}} {%- endif %} {% set uihandler = 'AZ::Edit::UIHandlers::Default' %} {% for item in Class.iter('Property') %} @@ -203,29 +201,34 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context) {% if item.attrib['Description'] is defined %} {% set description = item.attrib['Description'] %} {% endif %} - // {{ item.attrib['Name'] }} - ->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}") + // {{ item.attrib['Name'] }} + {{preEdit}}->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}"){{postEdit}} {% for EditAttribute in item.iter('EditAttribute') %} - ->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}) + {{preEdit}}->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}){{postEdit}} {% endfor %} {% endfor %} ; +{% if ExtendReflectionEdit is defined %} + ExtendReflectionEdit(&{{preEdit}}); +{% endif %} } } // Behavior Context Reflection if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}") - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List) - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) +{% if ExtendReflectionBehavior is defined %} auto {{preBehavior}} = {% else %} {% endif %}behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}"){{postBehavior}} + {{preBehavior}}->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List){{postBehavior}} + {{preBehavior}}->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common){{postBehavior}} {% for inputMethod in Class.iter('Input') %} {% set methodName = inputMethod.attrib['Name'] %} // {{ inputMethod.attrib['Name'] }} - ->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}) + {{preBehavior}}->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}){{postBehavior}} {% endfor %} - ; +{% if ExtendReflectionBehavior is defined %} + ExtendReflectionBehavior(&{{preBehavior}}); +{% endif %} } } @@ -253,27 +256,32 @@ Nodes::{{ nodeableNodeName }}::{{ nodeableNodeName }}() {# NodeableNode Reflection #} void Nodes::{{ nodeableNodeName }}::Reflect(AZ::ReflectContext* context) { - {{ attribute_QualifiedName }}::Reflect(context); // Serialization Context Reflection if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { - serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>() + {%if ExtendReflectionSerialize is defined%}auto {{preSerialize}} = {%endif%}serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>(){{postSerialize}} {% if attribute_Version is defined %} - ->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}) + {{preSerialize}}->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}){{postSerialize}} {% else %} - ->Version(0) + {{preSerialize}}->Version(0){{postSerialize}} +{% endif %} + ; +{% if ExtendReflectionSerialize is defined %} + ExtendReflectionSerialize(&{{preSerialize}}); {% endif %} - ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; + {% if ExtendReflectionEdit is defined %}auto {{preEdit}} = {%endif%}editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}} + {{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::AutoExpand, true){{postEdit}} + ; +{% if ExtendReflectionEdit is defined %} + ExtendReflectionEdit(&{{preEdit}}); +{% endif %} } } } @@ -324,6 +332,7 @@ void Nodes::{{ nodeableNodeName }}::ConfigureVisualExtensions() RegisterExtension(visualExtensions); } {% endfor %} + OnConfigureVisualExtensions(); } {# ConfigureSlots #} @@ -457,6 +466,9 @@ void Nodes::{{ nodeableNodeName }}::ConfigureSlots() {% endfor %} #} +{% if Class.attrib['ExtendConfigureSlots'] is defined %} + ExtendConfigureSlots(ins, outs); +{% endif %} // Generate the execution map m_slotExecutionMap = SlotExecution::Map(AZStd::move(ins), AZStd::move(outs)); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja index addd24f5d3..a7300791e8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja @@ -349,7 +349,4 @@ void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinitio {%- for executionOut in Class.findall('Output') -%} {{ ExecutionOutDefinition(Class, qualifiedName, executionOut, loop.index0 + branches|length) }} {%- endfor %} - -size_t {{qualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; } - -{% endmacro %} +{% endmacro %} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h index 5b1e5eee76..8f3cefa138 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h @@ -250,7 +250,6 @@ namespace ScriptCanvas }; using ScriptCanvasSettingsRequestBus = AZ::EBus; - } namespace AZStd diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp index 30e55cee49..64c9f2497d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp @@ -1476,9 +1476,11 @@ namespace ScriptCanvas const void* Datum::GetValueAddress() const { - return m_type.GetType() != Data::eType::BehaviorContextObject - ? AZStd::any_cast(&m_storage) - : (*AZStd::any_cast(&m_storage))->Get(); + return !m_storage.empty() + ? m_type.GetType() != Data::eType::BehaviorContextObject + ? AZStd::any_cast(&m_storage) + : (*AZStd::any_cast(&m_storage))->Get() + : nullptr; } bool Datum::Initialize(const Data::Type& type, eOriginality originality, const void* source, const AZ::Uuid& sourceTypeID) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h index 781dcb9ed8..be6f3ee37e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h @@ -389,7 +389,7 @@ namespace ScriptCanvas bool Datum::Empty() const { - return GetValueAddress() == nullptr; + return m_storage.empty() || GetValueAddress() == nullptr; } template diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp index 4a936c8824..98d5e6b121 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp @@ -3577,12 +3577,12 @@ namespace ScriptCanvas } if (targetSlotType == CombinedSlotType::DataOut - && executionSlot.GetType() == CombinedSlotType::ExecutionIn - && executionInCount > 1) + && executionSlot.GetType() == CombinedSlotType::ExecutionIn + && executionInCount > 1) { if (!executionChildSlot || executionChildSlot->GetType() != CombinedSlotType::ExecutionOut) { - return AZ::Failure(AZStd::string("Data out by ExcutionIn must have child out slot")); + return AZ::Failure(AZStd::string("Data out by ExecutionIn must have child out slot")); } } @@ -3626,6 +3626,11 @@ namespace ScriptCanvas return {}; } + Grammar::MultipleFunctionCallFromSingleSlotInfo Node::GetMultipleFunctionCallFromSingleSlotInfo([[maybe_unused]] const Slot& slot) const + { + return {}; + } + VariableId Node::GetVariableIdRead(const Slot*) const { return {}; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h index a2d06686e9..42ffaec8da 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h @@ -715,6 +715,8 @@ namespace ScriptCanvas virtual PropertyFields GetPropertyFields() const; + virtual Grammar::MultipleFunctionCallFromSingleSlotInfo GetMultipleFunctionCallFromSingleSlotInfo(const Slot& slot) const; + virtual VariableId GetVariableIdRead(const Slot*) const; virtual VariableId GetVariableIdWritten(const Slot*) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h index 0193641d97..c1c9db6f8b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h @@ -73,6 +73,8 @@ namespace ScriptCanvas void ConfigureSlots() override; + virtual void OnConfigureVisualExtensions() {} + AZ::Outcome GetBehaviorContextClass() const; ConstSlotsOutcome GetBehaviorContextOutName(const Slot& inSlot) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h index 8426e90515..7b74f3dd81 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h @@ -46,6 +46,8 @@ namespace ScriptCanvas const AZStd::unordered_map>& GetPropertyNameSlotMap() const; + AZ_INLINE AZ::Outcome GetDependencies() const override { return AZ::Success(DependencyReport{}); } + ~PureData() override; protected: diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index 732455857e..db45f6bfe3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -2249,6 +2249,10 @@ namespace ScriptCanvas } #endif AddAllVariablesPreParse(); + if (!IsErrorFree()) + { + return; + } for (auto& nodeEntity : m_source.m_graphData->m_nodes) { @@ -2270,6 +2274,16 @@ namespace ScriptCanvas { AddError(nullptr, ValidationConstPtr(aznew NullEntityInGraph())); } + + if (!IsErrorFree()) + { + return; + } + } + + if (!IsErrorFree()) + { + return; } ParseAutoConnectedEBusHandlerVariables(); @@ -2638,7 +2652,8 @@ namespace ScriptCanvas AZStd::vector inputVariableIds; AZStd::unordered_map inputVariablesById; - for (auto variable : GetVariables()) + auto& variables = GetVariables(); + for (auto variable : variables) { auto constructionRequirement = ParseConstructionRequirement(variable); @@ -2655,18 +2670,30 @@ namespace ScriptCanvas case VariableConstructionRequirement::InputNodeable: { + if (variable->m_datum.Empty()) + { + AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "Empty nodeable datum in variable, probably due to a problem with azrtti declarations")); + break; + } + // I solemnly swear no harm shall come to the nodeable const Nodeable* nodeableSource = reinterpret_cast(variable->m_datum.GetAsDanger()); - AZ_Assert(nodeableSource != nullptr, "the must be a raw nodeable held by this pointer"); - AZ_Assert(azrtti_typeid(nodeableSource) != azrtti_typeid(), "type problem with nodeable"); + + if (!nodeableSource) + { + AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "No raw nodeable held by variable")); + break; + } + nodeablesById.push_back({ variable->m_nodeableNodeId, const_cast(nodeableSource) }); } break; case VariableConstructionRequirement::InputVariable: { - inputVariableIds.push_back(variable->m_sourceVariableId); - inputVariablesById.insert({ variable->m_sourceVariableId, variable }); + auto variableID = variable->m_sourceVariableId.IsValid() ? variable->m_sourceVariableId : VariableId::MakeVariableId(); + inputVariableIds.push_back(variableID); + inputVariablesById.insert({ variableID, variable }); // sort revealed a datum copy issue: type is not preserved, workaround below // m_runtimeInputs.m_variables.emplace_back(variable->m_sourceVariableId, variable->m_datum); } @@ -4310,6 +4337,7 @@ namespace ScriptCanvas { if (auto variable = FindVariable(execution->GetNodeId())) { + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4327,6 +4355,7 @@ namespace ScriptCanvas { auto variable = AZStd::make_shared(); variable->m_datum = Datum(eventHandling->m_handlerName); + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4338,6 +4367,7 @@ namespace ScriptCanvas { if (auto variable = FindVariable(execution->GetNodeId())) { + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4367,12 +4397,159 @@ namespace ScriptCanvas void AbstractCodeModel::ParseMultiExecutionPost(ExecutionTreePtr execution) { ParsePropertyExtractionsPost(execution); + ParseMultipleFunctionCallPost(execution); } void AbstractCodeModel::ParseMultiExecutionPre(ExecutionTreePtr execution) { ParsePropertyExtractionsPre(execution); - } + } + + void AbstractCodeModel::ParseMultipleFunctionCallPost(ExecutionTreePtr execution) + { + auto& id = execution->GetId(); + MultipleFunctionCallFromSingleSlotInfo info = id.m_node->GetMultipleFunctionCallFromSingleSlotInfo(*id.m_slot); + + if (info.functionCalls.empty()) + { + return; + } + + auto parent = execution->ModParent(); + + if (!parent) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), "Null parent in MultipleFunctionCall")); + return; + } + + size_t indexInParentCall = parent->FindChildIndex(execution); + if (indexInParentCall >= parent->GetChildrenCount()) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNoChildren)); + return; + } + + ExecutionChild* executionChildInParent = &parent->ModChild(indexInParentCall); + + const size_t executionInputCount = execution->GetInputCount(); + const size_t thisInputOffset = execution->InputHasThisPointer() ? 1 : 0; + + // the original index has ALL the input from the slots on the node + // create multiple calls with separate function call nodes, but ONLY take the inputs required + // as indicated by the function call info + + AZStd::unordered_set usedSlots; + bool variadicIsFound = false; + + auto createChild = [&](auto parentCall, ExecutionChild* childInParent, auto& functionCallInfo) + { + auto child = CreateChild(parentCall, id.m_node, id.m_slot); + child->SetSymbol(Symbol::FunctionCall); + child->SetName(functionCallInfo.functionName); + child->SetNameLexicalScope(functionCallInfo.lexicalScope); + childInParent->m_execution = child; + return child; + }; + + auto addThisInput = [&](auto functionCall) + { + if (thisInputOffset != 0) + { + if (executionInputCount == 0) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInputForThis)); + return; + } + + const ExecutionInput& input = execution->GetInput(0); + usedSlots.insert(input.m_slot); + functionCall->AddInput(input); + } + }; + + auto addSlotInput = [&](auto functionCall, size_t inputIndex) + { + if (inputIndex >= executionInputCount) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput)); + return; + } + + const ExecutionInput& input = execution->GetInput(inputIndex); + + if (usedSlots.contains(input.m_slot)) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput)); + return; + } + + usedSlots.insert(input.m_slot); + + if (input.m_value->m_source == execution) + { + input.m_value->m_source = functionCall; + } + + functionCall->AddInput(input); + }; + + auto addCall = [&](auto& functionCallInfo, auto childInParent, size_t startingIndex, size_t sentinel, size_t variadicOffset = 0) + { + auto child = createChild(parent, childInParent, functionCallInfo); + addThisInput(child); + + for (size_t index = startingIndex; index < sentinel; ++index) + { + const size_t inputIndex = index + thisInputOffset + variadicOffset; + addSlotInput(child, inputIndex); + } + + child->AddChild({}); + childInParent = &child->ModChild(0); + return AZStd::make_pair(childInParent, child); + }; + + // loop through each call... + for (auto& functionCallInfo : info.functionCalls) + { + // ...first add any pre-variadic calls, using the starting index and the number of args, since they could come in any order, not input slot order... + if (!functionCallInfo.isVariadic) + { + AZStd::pair childInParentAndParent = addCall(functionCallInfo, executionChildInParent, functionCallInfo.startingIndex, functionCallInfo.startingIndex + functionCallInfo.numArguments); + executionChildInParent = childInParentAndParent.first; + parent = childInParentAndParent.second; + } + else + { + // ...then add only one variadic call if there is one... + if (variadicIsFound) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotMultipleVariadic)); + return; + } + + variadicIsFound = true; + const size_t sentinel = executionInputCount == 0 ? 0 : executionInputCount - thisInputOffset; + // ... by looping through the remaining slots, striding by functionCallInfo.numArguments, making repeated calls to the function + for (size_t slotInputIndex = functionCallInfo.startingIndex; slotInputIndex < sentinel; slotInputIndex += functionCallInfo.numArguments) + { + AZStd::pair childInParentAndParent = addCall(functionCallInfo, executionChildInParent, 0, functionCallInfo.numArguments, slotInputIndex); + executionChildInParent = childInParentAndParent.first; + parent = childInParentAndParent.second; + } + } + } + + if (info.errorOnUnusedSlot && usedSlots.size() != executionInputCount) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotUnused)); + } + + // parent now refers to the last child call created + parent->SwapChildren(execution); + execution->Clear(); + } void AbstractCodeModel::ParseNodelingVariables(const Node& node, NodelingType nodelingType) { @@ -5146,6 +5323,6 @@ namespace ScriptCanvas return type == Data::eType::BehaviorContextObject; } - } + } -} +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h index f1ad7b986b..8b8be27fab 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h @@ -387,6 +387,8 @@ namespace ScriptCanvas void ParseMultiExecutionPre(ExecutionTreePtr execution); + void ParseMultipleFunctionCallPost(ExecutionTreePtr execution); + void ParseNodelingVariables(const Node& node, NodelingType nodelingType); void ParseOperatorArithmetic(ExecutionTreePtr execution); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp index e16a3bc815..131209a521 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp @@ -1132,14 +1132,14 @@ namespace ScriptCanvas } else if (variable->m_isExposedToConstruction) { - if (variable->m_sourceVariableId.IsValid()) - { - return VariableConstructionRequirement::InputVariable; - } - else if (variable->m_nodeableNodeId.IsValid()) + if (variable->m_nodeableNodeId.IsValid()) { return VariableConstructionRequirement::InputNodeable; } + else if (variable->m_sourceVariableId.IsValid()) + { + return VariableConstructionRequirement::InputVariable; + } else { AZ_Assert(false, "A member variable in the model has no valid id"); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h index ab047fb580..d7e4fa0f72 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h @@ -164,6 +164,34 @@ namespace ScriptCanvas virtual void PostParseExecutionTreeBody(AbstractCodeModel& /*model*/, ExecutionTreePtr /*execution*/) {} }; + // for now, no return values supported + struct MultipleFunctionCallFromSingleSlotEntry + { + AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotEntry, "{360A23A3-C490-4047-B71E-64E290E441D3}"); + AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotEntry, AZ::SystemAllocator, 0); + + bool isVariadic = false; + AZStd::string functionName; + LexicalScope lexicalScope; + size_t numArguments = 0; // stride in case isVariadic == true + size_t startingIndex = 0; // the index of the slot order + }; + + // for now, no return values supported + struct MultipleFunctionCallFromSingleSlotInfo + { + AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotInfo, "{DF51F08A-8B28-4851-9888-9AB7CC0B90D2}"); + AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotInfo, AZ::SystemAllocator, 0); + + // this could likely be implemented, but needs care to duplicate input that the execution-slot created + // bool errorOnReusedSlot = false; + + bool errorOnUnusedSlot = false; + + // calls are executed in the order they arrive in the vector + AZStd::vector functionCalls; + }; + struct NodeableParse : public AZStd::enable_shared_from_this { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp index ed61d7e838..66f609768d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp @@ -313,6 +313,11 @@ namespace ScriptCanvas return !m_returnValues.empty(); } + bool ExecutionTree::InputHasThisPointer() const + { + return m_inputHasThisPointer; + } + bool ExecutionTree::IsInfiniteLoopDetectionPoint() const { return m_isInfiniteLoopDetectionPoint; @@ -377,6 +382,11 @@ namespace ScriptCanvas m_isInfiniteLoopDetectionPoint = true; } + void ExecutionTree::MarkInputHasThisPointer() + { + m_inputHasThisPointer = true; + } + void ExecutionTree::MarkInputOutputPreprocessed() { m_isInputOutputPreprocessed = true; @@ -587,6 +597,32 @@ namespace ScriptCanvas m_symbol = val; } - } + void ExecutionTree::SwapChildren(ExecutionTreePtr execution) + { + if (execution) + { + m_children.swap(execution->m_children); + for (auto& child : m_children) + { + if (child.m_execution) + { + child.m_execution->SetParent(shared_from_this()); + } + } + + for (auto& orphan : execution->m_children) + { + if (orphan.m_execution) + { + orphan.m_execution->SetParent(execution); + } + } + } + else + { + ClearChildren(); + } + } + } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h index 3d0d75b313..17b86c8da4 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h @@ -190,6 +190,8 @@ namespace ScriptCanvas bool HasReturnValues() const; + bool InputHasThisPointer() const; + bool IsInfiniteLoopDetectionPoint() const; void InsertChild(size_t index, const ExecutionChild& child); @@ -208,6 +210,8 @@ namespace ScriptCanvas void MarkInfiniteLoopDetectionPoint(); + void MarkInputHasThisPointer(); + void MarkInputOutputPreprocessed(); void MarkInternalOut(); @@ -262,6 +266,8 @@ namespace ScriptCanvas void SetSymbol(Symbol val); + void SwapChildren(ExecutionTreePtr execution); + private: // the (possible) slot(s) through which execution exited, along with associated output AZStd::vector m_children; @@ -275,6 +281,8 @@ namespace ScriptCanvas bool m_isInfiniteLoopDetectionPoint = false; + bool m_inputHasThisPointer = false; + bool m_isInputOutputPreprocessed = false; bool m_isInternalOut = false; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h index def4fa7761..68c6a8442a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h @@ -57,6 +57,12 @@ namespace ScriptCanvas constexpr const char* MissingVariableForEBusHandlerAddress = "missing variable for ebus handler address"; constexpr const char* MissingVariableForEBusHandlerAddressConnected = "missing variable for ebus handler address"; constexpr const char* MultipleExecutionOutConnections = "This node has multiple, unordered execution Out connections"; + constexpr const char* MultipleFunctionCallFromSingleSlotMultipleVariadic = "Only one variadic call (the last one) is supported in the multi-call per single slot."; + constexpr const char* MultipleFunctionCallFromSingleSlotNoChildren = "Node missing from parent children."; + constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInput = "Not enough input to support multi call input information."; + constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInputForThis = "Node doesn't have enough input for a parsed this pointer."; + constexpr const char* MultipleFunctionCallFromSingleSlotReused = "Multiple function slot reused an input slot"; + constexpr const char* MultipleFunctionCallFromSingleSlotUnused = "Multiple function slot left an input slot unused."; constexpr const char* MultipleSimulaneousInputValues = "Multiple values routed to the same single input with no way to discern which to take."; constexpr const char* MultipleStartNodes = "Multiple Start nodes in a single graph. Only one is allowed."; constexpr const char* NoChildrenAfterRoot = "No children after parsing function root"; diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas index 45b24bad8d..c3b3599325 100644 --- a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas @@ -3,34 +3,34 @@ - + - + - + - + - + - + - + @@ -62,12 +62,13 @@ + - + @@ -99,12 +100,13 @@ + - + @@ -141,12 +143,13 @@ + - + @@ -178,6 +181,7 @@ + @@ -189,7 +193,7 @@ - + @@ -199,26 +203,26 @@ - + - + - + - + - + - + @@ -250,12 +254,13 @@ + - + @@ -287,12 +292,13 @@ + - + @@ -329,12 +335,646 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -371,12 +1011,13 @@ + - + @@ -408,6 +1049,7 @@ + @@ -419,7 +1061,7 @@ - + @@ -431,7 +1073,7 @@ - + @@ -441,26 +1083,26 @@ - + - + - + - + - + - + @@ -492,12 +1134,13 @@ + - + @@ -529,484 +1172,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1043,12 +1215,13 @@ + - + @@ -1080,21 +1253,10 @@ + - - - - - - - - - - - - @@ -1103,7 +1265,7 @@ - + @@ -1113,26 +1275,26 @@ - + - + - + - + - + - + @@ -1164,12 +1326,13 @@ + - + @@ -1201,200 +1364,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1431,12 +1407,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1446,10 +1466,10 @@ - + - + @@ -1468,6 +1488,7 @@ + @@ -1479,9 +1500,21 @@ - + - + + + + + + + + + + + + + @@ -1489,26 +1522,26 @@ - + - + - + - + - + - + @@ -1540,12 +1573,338 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1577,12 +1936,205 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1619,12 +2171,13 @@ + - + @@ -1639,7 +2192,7 @@ - + @@ -1661,12 +2214,13 @@ + - + @@ -1676,10 +2230,10 @@ - + - + @@ -1698,6 +2252,7 @@ + @@ -1709,19 +2264,19 @@ - + - + - + @@ -1731,26 +2286,26 @@ - + - + - + - + - + - + @@ -1782,12 +2337,13 @@ + - + @@ -1819,12 +2375,13 @@ + - + @@ -1839,7 +2396,7 @@ - + @@ -1861,12 +2418,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1898,6 +2499,7 @@ + @@ -1909,36 +2511,107 @@ - + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1970,12 +2643,13 @@ + - + @@ -2007,12 +2681,13 @@ + - + @@ -2049,12 +2724,205 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2091,12 +2959,13 @@ + - + @@ -2128,6 +2997,7 @@ + @@ -2139,7 +3009,7 @@ - + @@ -2151,7 +3021,7 @@ - + @@ -2161,26 +3031,26 @@ - + - + - + - + - + - + @@ -2212,12 +3082,13 @@ + - + @@ -2249,12 +3120,13 @@ + - + @@ -2291,12 +3163,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2328,6 +3244,7 @@ + @@ -2339,36 +3256,1196 @@ - + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -2405,12 +4482,13 @@ + - + @@ -2447,12 +4525,13 @@ + - + @@ -2484,12 +4563,13 @@ + - + @@ -2521,6 +4601,7 @@ + @@ -2565,1709 +4646,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -4299,12 +4697,13 @@ + - + @@ -4336,12 +4735,13 @@ + - + @@ -4378,12 +4778,13 @@ + - + @@ -4420,12 +4821,13 @@ + - + @@ -4457,6 +4859,7 @@ + @@ -4480,7 +4883,7 @@ - + @@ -4490,26 +4893,26 @@ - + - + - + - + - + - + @@ -4541,12 +4944,13 @@ + - + @@ -4578,12 +4982,13 @@ + - + @@ -4620,850 +5025,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -5495,194 +5063,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -5694,9 +5075,9 @@ - + - + @@ -5704,703 +5085,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + @@ -6410,28 +5123,28 @@ - + - + - + - + - + - + - + @@ -6441,28 +5154,28 @@ - + - + - + - + - + - + - + @@ -6472,28 +5185,28 @@ - + - + - + - + - + - + - + @@ -6503,28 +5216,28 @@ - + - + - + - + - + - + - + @@ -6534,28 +5247,28 @@ - + - + - + - + - + - + - + @@ -6565,28 +5278,28 @@ - + - + - + - + - + - + - + @@ -6596,28 +5309,28 @@ - + - + - + - + - + - + - + @@ -6627,28 +5340,28 @@ - + - + - + - + - + - + - + @@ -6658,28 +5371,28 @@ - + - + - + - + - + - + - + @@ -6689,28 +5402,28 @@ - + - + - + - + - + - + @@ -6720,28 +5433,28 @@ - + - + - + - + - + - + - + @@ -6751,28 +5464,307 @@ - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6788,7 +5780,7 @@ - + @@ -6796,7 +5788,7 @@ - + @@ -6804,7 +5796,7 @@ - + @@ -6822,15 +5814,15 @@ - - - + + + - - - + + + @@ -6838,7 +5830,7 @@ - + @@ -6846,7 +5838,7 @@ - + @@ -6863,46 +5855,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -6911,18 +5867,12 @@ - - - - - - - + @@ -6930,637 +5880,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7586,7 +5906,7 @@ - + @@ -7594,7 +5914,7 @@ - + @@ -7602,7 +5922,595 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7620,15 +6528,15 @@ - - - + + + - - - + + + @@ -7636,7 +6544,7 @@ - + @@ -7644,7 +6552,7 @@ - + @@ -7661,46 +6569,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7709,18 +6581,12 @@ - - - - - - - + @@ -7728,7 +6594,7 @@ - + @@ -7746,15 +6612,15 @@ - - - + + + - - - + + + @@ -7762,7 +6628,7 @@ - + @@ -7771,9 +6637,9 @@ - - - + + + @@ -7783,7 +6649,7 @@ - + @@ -7791,49 +6657,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7851,15 +6675,15 @@ - - - + + + - - - + + + @@ -7867,7 +6691,7 @@ - + @@ -7875,7 +6699,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7893,15 +6759,15 @@ - - - + + + - - - + + + @@ -7915,60 +6781,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7980,7 +6798,35 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7988,25 +6834,33 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -8014,7 +6868,7 @@ - + From dbeee91e7bc8d654e41fb420132036d94513a6b5 Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 15:14:19 -0500 Subject: [PATCH 063/105] SPEC-7008: Setting up LargeWorlds main tests to be skipped in Debug builds --- .../dyn_veg/test_DynamicSliceInstanceSpawner.py | 6 ++++++ .../largeworlds/dyn_veg/test_EmptyInstanceSpawner.py | 6 ++++++ .../landscape_canvas/test_GraphComponentSync.py | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py index ead1e8779c..9235b302b8 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_DynamicSliceInstanceSpawner.py @@ -16,6 +16,7 @@ import logging # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole @@ -46,6 +47,11 @@ class TestDynamicSliceInstanceSpawner(object): @pytest.mark.parametrize("launcher_platform", ['windows_editor']) def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + # Ensure temp level does not already exist file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py index ca71cd2137..83263c614a 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_EmptyInstanceSpawner.py @@ -16,6 +16,7 @@ import logging # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra logger = logging.getLogger(__name__) @@ -40,6 +41,11 @@ class TestEmptyInstanceSpawner(object): @pytest.mark.SUITE_main @pytest.mark.dynveg_area def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py index 855764fa6f..943d0cb985 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py @@ -23,6 +23,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') @@ -46,6 +47,11 @@ class TestGraphComponentSync(object): @pytest.mark.BAT @pytest.mark.SUITE_main def test_LandscapeCanvas_SlotConnections_UpdateComponentReferences(self, request, editor, level, launcher_platform): + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ @@ -122,6 +128,11 @@ class TestGraphComponentSync(object): """ Verifies a Gradient Mixer can be setup in Landscape Canvas and all references are property set. """ + + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + cfg_args = [level] expected_lines = [ From ed0fab894b6cad4d8e151c4c109bf69307b8a285 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 13:30:28 -0700 Subject: [PATCH 064/105] Added DiffuseGlobalIllumination level component --- .../DiffuseGlobalIlluminationComponent.cpp | 44 +++++++++ .../DiffuseGlobalIlluminationComponent.h | 38 ++++++++ ...ffuseGlobalIlluminationComponentConfig.cpp | 32 +++++++ ...DiffuseGlobalIlluminationComponentConfig.h | 43 +++++++++ ...fuseGlobalIlluminationComponentConstants.h | 23 +++++ ...eGlobalIlluminationComponentController.cpp | 92 +++++++++++++++++++ ...useGlobalIlluminationComponentController.h | 55 +++++++++++ .../DiffuseProbeGridComponent.cpp | 2 +- .../DiffuseProbeGridComponent.h | 4 +- .../DiffuseProbeGridComponentConstants.h | 0 .../DiffuseProbeGridComponentController.cpp | 4 +- .../DiffuseProbeGridComponentController.h | 2 +- ...itorDiffuseGlobalIlluminationComponent.cpp | 82 +++++++++++++++++ ...EditorDiffuseGlobalIlluminationComponent.h | 40 ++++++++ .../EditorDiffuseProbeGridComponent.cpp | 2 +- .../EditorDiffuseProbeGridComponent.h | 4 +- .../CommonFeatures/Code/Source/Module.cpp | 8 +- ...egration_commonfeatures_editor_files.cmake | 6 +- ...omlyintegration_commonfeatures_files.cmake | 14 ++- 19 files changed, 478 insertions(+), 17 deletions(-) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponent.cpp (96%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponent.h (90%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentConstants.h (100%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentController.cpp (99%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridComponentController.h (98%) create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/EditorDiffuseProbeGridComponent.cpp (99%) rename Gems/AtomLyIntegration/CommonFeatures/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/EditorDiffuseProbeGridComponent.h (97%) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..b14bcf28b8 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,44 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include + +namespace AZ +{ + namespace Render + { + + DiffuseGlobalIlluminationComponent::DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + void DiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("DiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(DiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common); + } + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..a54e5868cd --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h @@ -0,0 +1,38 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::DiffuseGlobalIlluminationComponent, DiffuseGlobalIlluminationComponentTypeId , BaseClass); + + DiffuseGlobalIlluminationComponent() = default; + DiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp new file mode 100644 index 0000000000..406da9db2e --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp @@ -0,0 +1,32 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentConfig::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("QualityLevel", &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel) + ; + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h new file mode 100644 index 0000000000..23296967a5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -0,0 +1,43 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + enum class DiffuseGlobalIlluminationQualityLevel : uint32_t + { + Low, + Medium, + High, + + Count + }; + + class DiffuseGlobalIlluminationComponentConfig final + : public ComponentConfig + { + public: + AZ_RTTI(DiffuseGlobalIlluminationComponentConfig, "{0D0835D6-6094-4EF8-BEAC-5FF8A4E4C119}", ComponentConfig); + AZ_CLASS_ALLOCATOR(DiffuseGlobalIlluminationComponentConfig, SystemAllocator, 0); + + static void Reflect(ReflectContext* context); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h new file mode 100644 index 0000000000..e89aa65584 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConstants.h @@ -0,0 +1,23 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +namespace AZ +{ + namespace Render + { + static constexpr const char* const DiffuseGlobalIlluminationComponentTypeId = "{D51F8033-EF0D-4A13-BED3-5B193555B8D2}"; + static constexpr const char* const EditorDiffuseGlobalIlluminationComponentTypeId = "{169378DD-4052-4A60-BD63-90B02CFA69C1}"; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp new file mode 100644 index 0000000000..4c7f37bd4a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -0,0 +1,92 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +//#include + +#include +//#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationComponentController::Reflect(ReflectContext* context) + { + DiffuseGlobalIlluminationComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &DiffuseGlobalIlluminationComponentController::m_configuration); + } + } + + void DiffuseGlobalIlluminationComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("DiffuseGlobalIlluminationService", 0x11b9cbe1)); + } + + void DiffuseGlobalIlluminationComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + + DiffuseGlobalIlluminationComponentController::DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config) + : m_configuration(config) + { + } + + void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) + { + m_entityId = entityId; + } + + void DiffuseGlobalIlluminationComponentController::Deactivate() + { + //m_postProcessInterface = nullptr; + m_entityId.SetInvalid(); + } + + void DiffuseGlobalIlluminationComponentController::SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config) + { + m_configuration = config; + OnConfigChanged(); + } + + const DiffuseGlobalIlluminationComponentConfig& DiffuseGlobalIlluminationComponentController::GetConfiguration() const + { + return m_configuration; + } + + void DiffuseGlobalIlluminationComponentController::OnConfigChanged() + { + // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. + //const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + //DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); + //DisplayMapperConfigurationDescriptor desc; + //desc.m_operationType = m_configuration.m_displayMapperOperation; + //desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; + //desc.m_ldrColorGradingLut = m_configuration.m_ldrColorGradingLut; + //desc.m_acesParameterOverrides = m_configuration.m_acesParameterOverrides; + //fp->RegisterDisplayMapperConfiguration(desc); + } + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h new file mode 100644 index 0000000000..8700e1ffb5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h @@ -0,0 +1,55 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +#include + +//#include +//#include + +namespace AZ +{ + namespace Render + { + class DiffuseGlobalIlluminationComponentController final + { + public: + friend class EditorDiffuseGlobalIlluminationComponent; + + AZ_TYPE_INFO(AZ::Render::DiffuseGlobalIlluminationComponentController, "{7DE7D2A0-2526-447C-A11F-C31EE1332C26}"); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + DiffuseGlobalIlluminationComponentController() = default; + DiffuseGlobalIlluminationComponentController(const DiffuseGlobalIlluminationComponentConfig& config); + + void Activate(EntityId entityId); + void Deactivate(); + void SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config); + const DiffuseGlobalIlluminationComponentConfig& GetConfiguration() const; + + private: + AZ_DISABLE_COPY(DiffuseGlobalIlluminationComponentController); + + void OnConfigChanged(); + + DiffuseGlobalIlluminationComponentConfig m_configuration; + EntityId m_entityId; + }; + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp similarity index 96% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp index 509cbb86e6..b042f594c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h similarity index 90% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h index 964ce157bd..b77dceefac 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h similarity index 100% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentConstants.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentConstants.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp index 5fb835de15..f5ac36e7f6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h similarity index 98% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h index 4122a07ba2..ef606d2170 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp new file mode 100644 index 0000000000..bdb5686e89 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp @@ -0,0 +1,82 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +//#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" + +#include +#include + +namespace AZ +{ + namespace Render + { + void EditorDiffuseGlobalIlluminationComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Diffuse Global Illumination", "Diffuse Global Illumination configuration") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png") + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c) })) + ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://") + ; + + editContext->Class( + "ToneMapperComponentControl", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseGlobalIlluminationComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class("DiffuseGlobalIlluminationComponentConfig", "") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->DataElement(Edit::UIHandlers::ComboBox, &DiffuseGlobalIlluminationComponentConfig::m_qualityLevel, "Quality Level", "Quality Level") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Low, "Low") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::Medium, "Medium") + ->EnumAttribute(DiffuseGlobalIlluminationQualityLevel::High, "High") + ; + } + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->ConstantProperty("EditorDiffuseGlobalIlluminationComponentTypeId", BehaviorConstant(Uuid(EditorDiffuseGlobalIlluminationComponentTypeId))) + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation); + } + } + + EditorDiffuseGlobalIlluminationComponent::EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config) + : BaseClass(config) + { + } + + u32 EditorDiffuseGlobalIlluminationComponent::OnConfigurationChanged() + { + m_controller.OnConfigChanged(); + return Edit::PropertyRefreshLevels::AttributesAndValues; + } + } +} diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h new file mode 100644 index 0000000000..2a478665c5 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorDiffuseGlobalIlluminationComponent final + : public AzToolsFramework::Components::EditorComponentAdapter + { + public: + + using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorDiffuseGlobalIlluminationComponent, EditorDiffuseGlobalIlluminationComponentTypeId, BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorDiffuseGlobalIlluminationComponent() = default; + EditorDiffuseGlobalIlluminationComponent(const DiffuseGlobalIlluminationComponentConfig& config); + + //! EditorRenderComponentAdapter overrides... + AZ::u32 OnConfigurationChanged() override; + }; + + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp similarity index 99% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index 1e5b959803..caf9ecd007 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h similarity index 97% rename from Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h rename to Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h index 15c46d45ba..2e013ca479 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index 2ef4e1e229..df8552e5c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -47,7 +48,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -111,6 +113,7 @@ namespace AZ EntityReferenceComponent::CreateDescriptor(), GradientWeightModifierComponent::CreateDescriptor(), DiffuseProbeGridComponent::CreateDescriptor(), + DiffuseGlobalIlluminationComponent::CreateDescriptor(), DeferredFogComponent::CreateDescriptor(), SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(), AttachmentComponent::CreateDescriptor(), @@ -142,6 +145,7 @@ namespace AZ EditorEntityReferenceComponent::CreateDescriptor(), EditorGradientWeightModifierComponent::CreateDescriptor(), EditorDiffuseProbeGridComponent::CreateDescriptor(), + EditorDiffuseGlobalIlluminationComponent::CreateDescriptor(), EditorDeferredFogComponent::CreateDescriptor(), SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(), EditorAttachmentComponent::CreateDescriptor(), diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index e58f72a121..a68c54e85f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -23,8 +23,10 @@ set(FILES Source/CoreLights/EditorDirectionalLightComponent.cpp Source/Decals/EditorDecalComponent.h Source/Decals/EditorDecalComponent.cpp - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/EditorDiffuseGlobalIlluminationComponent.cpp Source/Grid/EditorGridComponent.h Source/Grid/EditorGridComponent.cpp Source/ImageBasedLights/EditorImageBasedLightComponent.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e13d1d37d6..7745306785 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -43,10 +43,16 @@ set(FILES Source/Decals/DecalComponent.cpp Source/Decals/DecalComponentController.h Source/Decals/DecalComponentController.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponent.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.h - Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponent.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.cpp Source/Grid/GridComponent.h Source/Grid/GridComponent.cpp Source/Grid/GridComponentConfig.cpp From d90a3d46a7ac393a12807fe66eacd4f9bab7c8f1 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 3 Jun 2021 15:59:45 -0500 Subject: [PATCH 065/105] Support for nested slice conversions (#1121) This set of changes enables conversions for singly-nested slices. Multiple nesting hierarchies are only partially supported at this point. Conversion is also significantly more deterministic, which makes it easier to convert single slices without needing to reconvert every slice or level that relies on it as well. Changes: - Added version of Instance::AddInstance() that takes in an alias to allow for deterministic aliases - Added a "SliceConverterEditorEntityContextComponent" that's used to specifically disable entity activation on creation. The disabling is done this way vs adding a new public API, because the disable shouldn't be required in any normal case outside of this tool. - Disabled more AWS gems for the SliceConverter, as they're unneeded and cause issues if they're around in the tool. - Added a small null check to the Camera Controller. - Added the actual support for slice instance conversion. This instantiates the entities, applies the data patches, turns them into a prefab instance, and generates a JSON patch out of the changes. --- .../AzCore/AzCore/Slice/SliceComponent.h | 7 +- .../Prefab/Instance/Instance.cpp | 13 +- .../Prefab/Instance/Instance.h | 1 + .../SerializeContextTools/Application.cpp | 19 +- .../SerializeContextTools/SliceConverter.cpp | 230 +++++++++++++++--- .../SerializeContextTools/SliceConverter.h | 31 ++- ...iceConverterEditorEntityContextComponent.h | 65 +++++ Code/Tools/SerializeContextTools/main.cpp | 3 +- .../serializecontexttools_files.cmake | 1 + .../Code/Source/CameraComponentController.cpp | 5 +- .../gem_autoload.serializecontexttools.setreg | 6 + 11 files changed, 330 insertions(+), 51 deletions(-) create mode 100644 Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h diff --git a/Code/Framework/AzCore/AzCore/Slice/SliceComponent.h b/Code/Framework/AzCore/AzCore/Slice/SliceComponent.h index 5ef8df0617..0c9bad6d4a 100644 --- a/Code/Framework/AzCore/AzCore/Slice/SliceComponent.h +++ b/Code/Framework/AzCore/AzCore/Slice/SliceComponent.h @@ -971,6 +971,10 @@ namespace AZ */ void RestoreCachedInstances(); + /// Returns data flags for use when instantiating an instance of this slice. + /// These data flags include those harvested from the entire slice ancestry. + const DataFlagsPerEntity& GetDataFlagsForInstances() const; + protected: ////////////////////////////////////////////////////////////////////////// @@ -1004,9 +1008,6 @@ namespace AZ DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId); const DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId) const; - /// Returns data flags for use when instantiating an instance of this slice. - /// These data flags include those harvested from the entire slice ancestry. - const DataFlagsPerEntity& GetDataFlagsForInstances() const; void BuildDataFlagsForInstances(); /** diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index 8f483ec818..766a293b4a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -49,8 +50,7 @@ namespace AzToolsFramework m_alias = GenerateInstanceAlias(); m_containerEntity = containerEntity ? AZStd::move(containerEntity) : AZStd::make_unique(); - EntityAlias containerEntityAlias = GenerateEntityAlias(); - RegisterEntity(m_containerEntity->GetId(), containerEntityAlias); + RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName); } Instance::~Instance() @@ -311,8 +311,15 @@ namespace AzToolsFramework Instance& Instance::AddInstance(AZStd::unique_ptr instance) { InstanceAlias newInstanceAlias = GenerateInstanceAlias(); + return AddInstance(AZStd::move(instance), newInstanceAlias); + } + + Instance& Instance::AddInstance(AZStd::unique_ptr instance, InstanceAlias newInstanceAlias) + { AZ_Assert(instance.get(), "instance argument is nullptr"); - AZ_Assert(m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), "InstanceAlias' unique id collision, this should never happen."); + AZ_Assert( + m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), + "InstanceAlias' unique id collision, this should never happen."); instance->m_parent = this; instance->m_alias = newInstanceAlias; return *(m_nestedInstances[newInstanceAlias] = std::move(instance)); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 68bc395012..4a69ade6d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -93,6 +93,7 @@ namespace AzToolsFramework void Reset(); Instance& AddInstance(AZStd::unique_ptr instance); + Instance& AddInstance(AZStd::unique_ptr instance, InstanceAlias instanceAlias); AZStd::unique_ptr DetachNestedInstance(const InstanceAlias& instanceAlias); /** diff --git a/Code/Tools/SerializeContextTools/Application.cpp b/Code/Tools/SerializeContextTools/Application.cpp index 81cc314deb..918afd731c 100644 --- a/Code/Tools/SerializeContextTools/Application.cpp +++ b/Code/Tools/SerializeContextTools/Application.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace AZ { @@ -34,6 +35,9 @@ namespace AZ Application::Application(int argc, char** argv) : AzToolsFramework::ToolsApplication(&argc, &argv) { + // We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here. + RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor()); + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); if (projectPath.empty()) { @@ -110,10 +114,21 @@ namespace AZ AZ::ComponentTypeList Application::GetRequiredSystemComponents() const { - // Use all of the default system components, but also add in the ThumbnailerNullComponent so that components requiring - // a ThumbnailService can still be started up. + // By default, we use all of the standard system components. AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents(); + + // Also add in the ThumbnailerNullComponent so that components requiring a ThumbnailService can still be started up. components.emplace_back(azrtti_typeid()); + + // The Slice Converter requires a specialized variant of the EditorEntityContextComponent that exposes the ability + // to disable the behavior of activating entities on creation. During conversion, the creation flow will be triggered, + // but entity activation requires a significant amount of subsystem initialization that's unneeded for conversion. + // So, to get around this, we swap out EditorEntityContextComponent with SliceConverterEditorEntityContextComponent. + components.erase( + AZStd::remove( + components.begin(), components.end(), azrtti_typeid()), + components.end()); + components.emplace_back(azrtti_typeid()); return components; } } // namespace SerializeContextTools diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index d06534e303..56b3689d6b 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -30,13 +30,16 @@ #include #include #include +#include #include #include #include #include #include +#include #include + // SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data, // and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs. // @@ -99,12 +102,26 @@ namespace AZ bool result = true; rapidjson::StringBuffer scratchBuffer; + // For slice conversion, disable the EditorEntityContextComponent logic that activates entities on creation. + // This prevents a lot of error messages and crashes during conversion due to lack of full environment and subsystem setup. + AzToolsFramework::SliceConverterEditorEntityContextComponent::DisableOnContextEntityLogic(); + // Loop through the list of requested files and convert them. AZStd::vector fileList = Utilities::ReadFileListFromCommandLine(application, "files"); for (AZStd::string& filePath : fileList) { bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun); result = result && convertResult; + + // Clear out all registered prefab templates between each top-level file that gets processed. + auto prefabSystemComponentInterface = AZ::Interface::Get(); + for (auto templateId : m_createdTemplateIds) + { + // We don't just want to call RemoveAllTemplates() because the root template should remain between file conversions. + prefabSystemComponentInterface->RemoveTemplate(templateId); + } + m_aliasIdMapper.clear(); + m_createdTemplateIds.clear(); } DisconnectFromAssetProcessor(); @@ -114,6 +131,13 @@ namespace AZ bool SliceConverter::ConvertSliceFile( AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun) { + /* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert + * the data in memory to a Prefab. + * If the input file is a level file (.ly), we actually need to load the level slice file ("levelentities.editor_xml") from + * within the level file, which effectively is a zip file of the level slice file and a bunch of legacy level files that won't + * be converted, since the systems that would use them no longer exist. + */ + bool result = true; bool packOpened = false; @@ -144,7 +168,7 @@ namespace AZ AZ_STRING_ARG(fileExtension.Native())); } - auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context) + auto callback = [this, &outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context) { if (classId != azrtti_typeid()) { @@ -178,6 +202,13 @@ namespace AZ bool SliceConverter::ConvertSliceToPrefab( AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity) { + /* Given a root slice entity, we convert it to a prefab by doing the following: + * - Locate the SliceComponent + * - Take all the entities directly located on the slice, and put them into a prefab + * - Fix up any top-level entities to have the prefab container entity as their parent + * - If there are any nested slice instances, convert the nested slices to prefabs, then convert the instances. + */ + auto prefabSystemComponentInterface = AZ::Interface::Get(); // Find the slice from the root entity. @@ -192,9 +223,14 @@ namespace AZ SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities(); AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size()); - // Create the Prefab with the entities from the slice + // Create the Prefab with the entities from the slice. + // The entities are added in a separate step so that we can give them deterministic entity aliases that match their entity Ids AZStd::unique_ptr sourceInstance( - prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath)); + prefabSystemComponentInterface->CreatePrefab({}, {}, outputPath)); + for (auto& entity : sliceEntities) + { + sourceInstance->AddEntity(*entity, AZStd::string::format("Entity_%s", entity->GetId().ToString().c_str())); + } // Dispatch events here, because prefab creation might trigger asset loads in rare circumstances. AZ::Data::AssetManager::Instance().DispatchEvents(); @@ -204,12 +240,28 @@ namespace AZ AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity(); FixPrefabEntities(container->get(), sliceEntities); + // Keep track of the template Id we created, we're going to remove it at the end of slice file conversion to make sure + // the data doesn't stick around between file conversions. auto templateId = sourceInstance->GetTemplateId(); if (templateId == AzToolsFramework::Prefab::InvalidTemplateId) { AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n"); return false; } + m_createdTemplateIds.emplace(templateId); + + // Save off a mapping of the original slice entity IDs to the new prefab template entity aliases. + // When converting nested slices, this mapping will be needed to fix up the parent entity hierarchy correctly. + auto entityAliases = sourceInstance->GetEntityAliases(); + for (auto& alias : entityAliases) + { + auto id = sourceInstance->GetEntityId(alias); + auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias); + if (!result.second) + { + AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n"); + } + } // Update the prefab template with the fixed-up data in our prefab instance. AzToolsFramework::Prefab::PrefabDom prefabDom; @@ -254,21 +306,26 @@ namespace AZ // via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.) AzToolsFramework::EditorEntityContextRequestBus::Broadcast( &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity); - containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent()); + if (containerEntity.FindComponent() == nullptr) + { + containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent()); + } + + // Make all the components on the container entity have deterministic component IDs, so that multiple runs of the tool + // on the same slice will produce the same prefab output. We're going to cheat a bit and just use the component type hash + // as the component ID. This would break if we had multiple components of the same type, but that currently doesn't + // happen for the container entity. + auto containerComponents = containerEntity.GetComponents(); + for (auto& component : containerComponents) + { + component->SetId(component->GetUnderlyingComponentType().GetHash()); + } // Reparent any root-level slice entities to the container entity. for (auto entity : sliceEntities) { - AzToolsFramework::Components::TransformComponent* transformComponent = - entity->FindComponent(); - if (transformComponent) - { - if (!transformComponent->GetParentId().IsValid()) - { - transformComponent->SetParent(containerEntity.GetId()); - transformComponent->UpdateCachedWorldTransform(); - } - } + constexpr bool onlySetIfInvalid = true; + SetParentEntity(*entity, containerEntity.GetId(), onlySetIfInvalid); } } @@ -276,9 +333,13 @@ namespace AZ SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance, AZ::SerializeContext* serializeContext, bool isDryRun) { + /* Given a root slice, find all the nested slices and convert them. */ + + // Get the list of nested slices that this slice uses. const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices(); auto prefabSystemComponentInterface = AZ::Interface::Get(); + // For each nested slice, convert it. for (auto& slice : sliceList) { // Get the nested slice asset @@ -312,7 +373,7 @@ namespace AZ return false; } - // Load the prefab template for the newly-created nested prefab. + // Find the prefab template we created for the newly-created nested prefab. // To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path. AZ::IO::Path nestedPrefabPath = assetPath; nestedPrefabPath.ReplaceExtension("prefab"); @@ -346,11 +407,25 @@ namespace AZ } bool SliceConverter::ConvertSliceInstance( - [[maybe_unused]] AZ::SliceComponent::SliceInstance& instance, - [[maybe_unused]] AZ::Data::Asset& sliceAsset, + AZ::SliceComponent::SliceInstance& instance, + AZ::Data::Asset& sliceAsset, AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance) { + /* To convert a slice instance, it's important to understand the similarities and differences between slices and prefabs. + * Both slices and prefabs have the concept of instances of a nested slice/prefab, where each instance can have its own + * set of changed data (transforms, component values, etc). + * For slices, the changed data comes from applying a DataPatch to an instantiated set of entities from the nested slice. + * From prefabs, the changed data comes from Json patches that are applied to the instantiated set of entities from the + * nested prefab. The prefab instance entities also have different IDs than the slice instance entities, so we'll need + * to remap some of them along the way. + * To get from one to the other, we'll need to do the following: + * - Instantiate the nested slice and nested prefab + * - Patch the nested slice instance and fix up the entity ID references + * - Replace the nested prefab instance entities with the fixed-up slice ones + * - Add the nested instance (and the link patch) to the top-level prefab + */ + auto instanceToTemplateInterface = AZ::Interface::Get(); auto prefabSystemComponentInterface = AZ::Interface::Get(); @@ -371,22 +446,83 @@ namespace AZ AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom; instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get())); - // Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will - // be lost. - AZ_Warning( - "Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.", - nestedTemplate->get().GetFilePath().c_str()); + // Instantiate a new instance of the nested slice + SliceComponent* dependentSlice = sliceAsset.Get()->GetComponent(); + [[maybe_unused]] AZ::SliceComponent::InstantiateResult instantiationResult = dependentSlice->Instantiate(); + AZ_Assert(instantiationResult == AZ::SliceComponent::InstantiateResult::Success, "Failed to instantiate instance"); - // Set the container entity of the nested prefab to have the top-level prefab as the parent. - // Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity - // within the level. + // Apply the data patch for this instance of the nested slice. This will provide us with a version of the slice's entities + // with all data overrides applied to them. + DataPatch::FlagsMap sourceDataFlags = dependentSlice->GetDataFlagsForInstances().GetDataFlagsForPatching(); + DataPatch::FlagsMap targetDataFlags = instance.GetDataFlags().GetDataFlagsForPatching(&instance.GetEntityIdToBaseMap()); + AZ::ObjectStream::FilterDescriptor filterDesc(AZ::Data::AssetFilterNoAssetLoading); + + AZ::SliceComponent::InstantiatedContainer sourceObjects(false); + dependentSlice->GetEntities(sourceObjects.m_entities); + dependentSlice->GetAllMetadataEntities(sourceObjects.m_metadataEntities); + + const DataPatch& dataPatch = instance.GetDataPatch(); + auto instantiated = + dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags); + + // Run through all the instantiated entities and fix up their parent hierarchy: + // - Invalid parents need to get set to the container. + // - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity. + // Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's + // parent to that other instance. auto containerEntity = nestedInstance->GetContainerEntity(); - AzToolsFramework::Components::TransformComponent* transformComponent = - containerEntity->get().FindComponent(); - if (transformComponent) + auto containerEntityId = containerEntity->get().GetId(); + for (auto entity : instantiated->m_entities) { - transformComponent->SetParent(topLevelInstance->GetContainerEntityId()); - transformComponent->UpdateCachedWorldTransform(); + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + if (transformComponent) + { + bool onlySetIfInvalid = true; + auto parentId = transformComponent->GetParentId(); + if (parentId.IsValid()) + { + auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId)); + if (parentAlias != m_aliasIdMapper.end()) + { + // Set the container's parent to this entity's parent, and set this entity's parent to the container + // (i.e. go from A->B to A->container->B) + auto newParentId = topLevelInstance->GetEntityId(parentAlias->second); + SetParentEntity(containerEntity->get(), newParentId, false); + onlySetIfInvalid = false; + } + } + + SetParentEntity(*entity, containerEntityId, onlySetIfInvalid); + } + } + + // Replace all the entities in the instance with the new patched ones. + // (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us) + nestedInstance->RemoveNestedEntities( + [](const AZStd::unique_ptr&) + { + return true; + }); + for (auto& entity : instantiated->m_entities) + { + auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId())); + if (entityAlias != m_aliasIdMapper.end()) + { + nestedInstance->AddEntity(*entity, entityAlias->second); + } + else + { + AZ_Assert(false, "Failed to find entity alias."); + nestedInstance->AddEntity(*entity); + } + } + + // Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten + // another entity as its parent. + { + constexpr bool onlySetIfInvalid = true; + SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid); } // Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance, @@ -395,7 +531,22 @@ namespace AZ AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore; instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance); - AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance)); + // When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one + // will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce + // the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains + // a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the + // unique instance ID. + AZStd::string instanceAlias; + auto entityIdMap = instance.GetEntityIdMap(); + if (!entityIdMap.empty()) + { + instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str()); + } + else + { + instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str()); + } + AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias); AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter; instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance); @@ -418,9 +569,26 @@ namespace AZ AzToolsFramework::Prefab::InvalidLinkId); prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId()); + AZ::Interface::Get()->UpdateTemplateInstancesInQueue(); + return true; } + void SliceConverter::SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid) + { + AzToolsFramework::Components::TransformComponent* transformComponent = + entity.FindComponent(); + if (transformComponent) + { + // Only set the parent if we didn't set the onlySetIfInvalid flag, or if we did and the parent is currently invalid + if (!onlySetIfInvalid || !transformComponent->GetParentId().IsValid()) + { + transformComponent->SetParent(parentId); + transformComponent->UpdateCachedWorldTransform(); + } + } + } + void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId) { auto prefabSystemComponentInterface = AZ::Interface::Get(); diff --git a/Code/Tools/SerializeContextTools/SliceConverter.h b/Code/Tools/SerializeContextTools/SliceConverter.h index bec893ff56..82dcf30383 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.h +++ b/Code/Tools/SerializeContextTools/SliceConverter.h @@ -39,24 +39,35 @@ namespace AZ class SliceConverter : public Converter { public: - static bool ConvertSliceFiles(Application& application); + bool ConvertSliceFiles(Application& application); private: - static bool ConnectToAssetProcessor(); - static void DisconnectFromAssetProcessor(); + using TemplateEntityIdPair = AZStd::pair; - static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun); - static bool ConvertSliceToPrefab( + bool ConnectToAssetProcessor(); + void DisconnectFromAssetProcessor(); + + bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun); + bool ConvertSliceToPrefab( AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity); - static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities); - static bool ConvertNestedSlices( + void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities); + bool ConvertNestedSlices( SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance, AZ::SerializeContext* serializeContext, bool isDryRun); - static bool ConvertSliceInstance( + bool ConvertSliceInstance( AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset& sliceAsset, AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance); - static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); - static bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); + void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid); + void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId); + bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId); + + // Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used + // with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs. + AZStd::unordered_map m_aliasIdMapper; + + // Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the + // conversion for that file. + AZStd::unordered_set m_createdTemplateIds; }; } // namespace SerializeContextTools } // namespace AZ diff --git a/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h b/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h new file mode 100644 index 0000000000..166cb2abdf --- /dev/null +++ b/Code/Tools/SerializeContextTools/SliceConverterEditorEntityContextComponent.h @@ -0,0 +1,65 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include + +namespace AzToolsFramework +{ + // This class is an inelegant workaround for use by the Slice Converter to selectively disable entity add/remove logic + // during slice conversion in the EditorEntityContextComponent. Specifically, the standard versions of these methods will + // attempt to activate the entities as they're added. This is both unnecessary and undesirable during slice conversion, since + // entity activation requires a lot of subsystems to be active and valid. + // Instead, by selectively disabling this logic, the entities can remain in an initialized state, which is sufficient for conversion, + // without requiring those extra subsystems. + + // This problem also could have been solved by adding APIs to the EditorEntityContextComponent or the EntityContext, but there aren't + // any other known valid use cases for disabling this logic, so the extra APIs would simply encourage "bad behavior" by using them + // when they likely aren't necessary or desired. + + class SliceConverterEditorEntityContextComponent + : public EditorEntityContextComponent + { + public: + + AZ_COMPONENT(SliceConverterEditorEntityContextComponent, "{1CB0C38F-8E85-4422-91C6-E1F3B9B4B853}"); + + SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {} + + // Simple API to selectively disable this logic *only* when performing slice to prefab conversion. + static void DisableOnContextEntityLogic() + { + m_enableOnContextEntityLogic = false; + } + + protected: + + void OnContextEntitiesAdded([[maybe_unused]] const EntityList& entities) override + { + if (m_enableOnContextEntityLogic) + { + EditorEntityContextComponent::OnContextEntitiesAdded(entities); + } + } + + void OnContextEntityRemoved([[maybe_unused]] const AZ::EntityId& id) override + { + if (m_enableOnContextEntityLogic) + { + EditorEntityContextComponent::OnContextEntityRemoved(id); + } + } + + // By default, act just like the EditorEntityContextComponent + static inline bool m_enableOnContextEntityLogic = true; + }; +} // namespace AzToolsFramework diff --git a/Code/Tools/SerializeContextTools/main.cpp b/Code/Tools/SerializeContextTools/main.cpp index d9f2cfbcc2..28eeb5ebe8 100644 --- a/Code/Tools/SerializeContextTools/main.cpp +++ b/Code/Tools/SerializeContextTools/main.cpp @@ -125,7 +125,8 @@ int main(int argc, char** argv) } else if (AZ::StringFunc::Equal("convert-slice", action.c_str())) { - result = SliceConverter::ConvertSliceFiles(application); + SliceConverter sliceConverter; + result = sliceConverter.ConvertSliceFiles(application); } else { diff --git a/Code/Tools/SerializeContextTools/serializecontexttools_files.cmake b/Code/Tools/SerializeContextTools/serializecontexttools_files.cmake index 814c55ea08..3427357149 100644 --- a/Code/Tools/SerializeContextTools/serializecontexttools_files.cmake +++ b/Code/Tools/SerializeContextTools/serializecontexttools_files.cmake @@ -17,6 +17,7 @@ set(FILES Dumper.h Dumper.cpp main.cpp + SliceConverterEditorEntityContextComponent.h SliceConverter.h SliceConverter.cpp Utilities.h diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index d0a124067b..cad666c1cd 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -178,7 +178,10 @@ namespace Camera if ((!m_viewSystem)||(!m_system)) { // perform first-time init - m_system = gEnv->pSystem; + if (gEnv) + { + m_system = gEnv->pSystem; + } if (m_system) { // Initialize local view. diff --git a/Registry/gem_autoload.serializecontexttools.setreg b/Registry/gem_autoload.serializecontexttools.setreg index e7e88dd6a6..0f74355084 100644 --- a/Registry/gem_autoload.serializecontexttools.setreg +++ b/Registry/gem_autoload.serializecontexttools.setreg @@ -10,6 +10,9 @@ "PythonAssetBuilder.Editor": { "AutoLoad": false }, + "AWSCore": { + "AutoLoad": false + }, "AWSCore.Editor": { "AutoLoad": false }, @@ -21,6 +24,9 @@ }, "AWSMetrics": { "AutoLoad": false + }, + "AWSMetrics.Editor": { + "AutoLoad": false } } } From fda28bb7b2504e7b2d2fc7cbdd344f373bf957ef Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:02:40 -0700 Subject: [PATCH 066/105] LYN-1818 | [USE CASE] Reparenting between different prefab instances by drag/drop in the Outliner (#1088) * Add the last known parent to the prefab undo cache to detect changes in the owning instance. Still WIP. * Progress in handling reparenting. Still WIP, need a change in CreateLink that will be addressed in a separate branch and then merged back. * A few fixes, reparenting now works with entities. Still working on instances. * Fix assert crashing the Editor because of the arguments being in the wrong order. * Handle moving the patches when removing and recreating links when reparenting nested instances. * Rearrange some code to prevent including instance removal in instance update undo node, as it would be redundant and cause errors in some edge cases. * Reorder instance reparenting to account for correct order of operation during undo/redo * Fix order of operations to support multiple operations in one edit (reparenting to non-container entities while changing instance) * Add function to refresh patches on links to allow aliases to be restored correctly on reparenting. * Removed RefreshEntityPatchOnLink function. Introduced a simpler way of handling porting patches. * Removing unnecessary code that was left after testing. * Minor fixes to naming and comments. * Restore previous error, no longer printing the failed patch. * Remove unused includes. * Restore include removed by mistake. * Simplified patches retrieval by using internal function. Renamed some internal functions and variables to be more accurate. --- .../Prefab/PrefabPublicHandler.cpp | 221 +++++++++++++++--- .../Prefab/PrefabPublicHandler.h | 10 +- .../Prefab/PrefabSystemComponent.cpp | 4 +- .../Prefab/PrefabUndoCache.cpp | 24 +- .../AzToolsFramework/Prefab/PrefabUndoCache.h | 13 +- 5 files changed, 217 insertions(+), 55 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 90c3dd10ae..27c812ff9d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -297,7 +297,7 @@ namespace AzToolsFramework m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes - m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); + m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter), parentEntityId); return AZStd::move(patch); } @@ -595,54 +595,199 @@ namespace AzToolsFramework { // Create Undo node on entities if they belong to an instance InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); - - if (owningInstance.has_value()) + if (!owningInstance.has_value()) { - PrefabDom afterState; - AZ::Entity* entity = GetEntityById(entityId); - if (entity) + return; + } + + AZ::Entity* entity = GetEntityById(entityId); + if (!entity) + { + m_prefabUndoCache.PurgeCache(entityId); + return; + } + + PrefabDom beforeState; + AZ::EntityId beforeParentId; + m_prefabUndoCache.Retrieve(entityId, beforeState, beforeParentId); + + PrefabDom afterState; + AZ::EntityId afterParentId; + AZ::TransformBus::EventResult(afterParentId, entityId, &AZ::TransformBus::Events::GetParentId); + + m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity); + + PrefabDom patch; + m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId); + + if (patch.IsArray() && !patch.Empty() && beforeState.IsObject()) + { + bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId); + bool isNewParentOwnedByDifferentInstance = false; + + if (beforeParentId != afterParentId) { - PrefabDom beforeState; - m_prefabUndoCache.Retrieve(entityId, beforeState); + // If the entity parent changed, verify if the owning instance changed too + InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId); + InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId); - m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity); - - PrefabDom patch; - m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState); - - if (patch.IsArray() && !patch.Empty() && beforeState.IsObject()) + if (beforeOwningInstance.has_value() && afterOwningInstance.has_value() && + (&beforeOwningInstance->get() != &afterOwningInstance->get())) { - if (IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId)) - { - m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId); - - // Save these changes as patches to the link - PrefabUndoLinkUpdate* linkUpdate = - aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(entityId))); - linkUpdate->SetParent(parentUndoBatch); - linkUpdate->Capture(patch, owningInstance->get().GetLinkId()); - - linkUpdate->Redo(); - } - else - { - // Update the state of the entity - PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast(entityId))); - state->SetParent(parentUndoBatch); - state->Capture(beforeState, afterState, entityId); - - state->Redo(); - } + isNewParentOwnedByDifferentInstance = true; } + } - // Update the cache - m_prefabUndoCache.Store(entityId, AZStd::move(afterState)); + if (isInstanceContainerEntity) + { + if (isNewParentOwnedByDifferentInstance) + { + Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId); + + PrefabDom afterStateafterReparenting; + m_instanceToTemplateInterface->GenerateDomForEntity(afterStateafterReparenting, *entity); + + PrefabDom newPatch; + m_instanceToTemplateInterface->GeneratePatch(newPatch, afterState, afterStateafterReparenting); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(newPatch, entityId); + + InstanceOptionalReference owningInstanceAfterReparenting = + m_instanceEntityMapperInterface->FindOwningInstance(entityId); + + Internal_HandleContainerOverride( + parentUndoBatch, entityId, newPatch, owningInstanceAfterReparenting->get().GetLinkId()); + } + else + { + Internal_HandleContainerOverride( + parentUndoBatch, entityId, patch, owningInstance->get().GetLinkId()); + } } else { - m_prefabUndoCache.PurgeCache(entityId); + Internal_HandleEntityChange(parentUndoBatch, entityId, beforeState, afterState); + + if (isNewParentOwnedByDifferentInstance) + { + Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId); + } } } + + m_prefabUndoCache.UpdateCache(entityId); + } + + void PrefabPublicHandler::Internal_HandleContainerOverride( + UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId) + { + // Save these changes as patches to the link + PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(entityId))); + linkUpdate->SetParent(undoBatch); + linkUpdate->Capture(patch, linkId); + + linkUpdate->Redo(); + } + + void PrefabPublicHandler::Internal_HandleEntityChange( + UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState) + { + // Update the state of the entity + PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast(entityId))); + state->SetParent(undoBatch); + state->Capture(beforeState, afterState, entityId); + + state->Redo(); + } + + void PrefabPublicHandler::Internal_HandleInstanceChange( + UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId) + { + // If the entity parent changed, verify if the owning instance changed too + InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId); + InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId); + + EntityList entities; + AZStd::vector instances; + + // Retrieve all descendant entities and instances of this entity that belonged to the same owning instance. + RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instances); + + AZStd::vector> instanceUniquePtrs; + AZStd::vector> instancePatches; + + // Remove Entities and Instances from the prior instance + { + // Remove Instances + for (Instance* nestedInstance : instances) + { + auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId()); + + PrefabDom oldLinkPatches; + + if (linkRef.has_value()) + { + auto patches = linkRef->get().GetLinkPatches(); + if (patches.has_value()) + { + oldLinkPatches.CopyFrom(patches->get(), oldLinkPatches.GetAllocator()); + } + } + + auto nestedInstanceUniquePtr = beforeOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); + RemoveLink(nestedInstanceUniquePtr, beforeOwningInstance->get().GetTemplateId(), undoBatch); + + instancePatches.emplace_back(AZStd::make_pair(nestedInstanceUniquePtr.get(), AZStd::move(oldLinkPatches))); + instanceUniquePtrs.emplace_back(AZStd::move(nestedInstanceUniquePtr)); + } + + // Get the previous state of the prior instance for undo/redo purposes + PrefabDom beforeInstanceDomBeforeRemoval; + m_instanceToTemplateInterface->GenerateDomForInstance(beforeInstanceDomBeforeRemoval, beforeOwningInstance->get()); + + // Remove Entities + for (AZ::Entity* nestedEntity : entities) + { + beforeOwningInstance->get().DetachEntity(nestedEntity->GetId()).release(); + } + + // Create the Update node for the prior owning instance + // Instance removal will be taken care of from the RemoveLink function for undo/redo purposes + PrefabUndoHelpers::UpdatePrefabInstance( + beforeOwningInstance->get(), "Update prior prefab instance", beforeInstanceDomBeforeRemoval, undoBatch); + } + + // Add Entities and Instances to new instance + { + // Add Instances + for (auto& instanceUniquePtr : instanceUniquePtrs) + { + afterOwningInstance->get().AddInstance(AZStd::move(instanceUniquePtr)); + } + + // Create Links + for (auto& instanceInfo : instancePatches) + { + // Add a new link with the old dom + CreateLink( + *instanceInfo.first, afterOwningInstance->get().GetTemplateId(), undoBatch, + AZStd::move(instanceInfo.second)); + } + + // Get the previous state of the new instance for undo/redo purposes + PrefabDom afterInstanceDomBeforeAdd; + m_instanceToTemplateInterface->GenerateDomForInstance(afterInstanceDomBeforeAdd, afterOwningInstance->get()); + + // Add Entities + for (AZ::Entity* nestedEntity : entities) + { + afterOwningInstance->get().AddEntity(*nestedEntity); + } + + // Create the Update node for the new owning instance + PrefabUndoHelpers::UpdatePrefabInstance( + afterOwningInstance->get(), "Update new prefab instance", afterInstanceDomBeforeAdd, undoBatch); + } } bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index e7b6f8c932..99fe8e5b67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -90,8 +90,8 @@ namespace AzToolsFramework /** * Creates a link between the templates of an instance and its parent. * - * \param sourceInstance The instance that corresponds to the source template of the link. - * \param targetInstance The id of the target template. + * \param sourceInstance The instance that corresponds to the source template of the link (child). + * \param targetInstance The id of the target template (parent). * \param undoBatch The undo batch to set as parent for this create link action. * \param patch The patch to store in the newly created link dom. * \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not. @@ -134,6 +134,12 @@ namespace AzToolsFramework bool IsCyclicalDependencyFound( InstanceOptionalConstReference instance, const AZStd::unordered_set& templateSourcePaths); + static void Internal_HandleContainerOverride( + UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId); + static void Internal_HandleEntityChange( + UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState); + void Internal_HandleInstanceChange(UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId); + void UpdateLinkPatchesWithNewEntityAliases( PrefabDom& linkPatch, const AZStd::unordered_map& oldEntityAliases, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index c42dbe792a..5f5564b4e1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -706,14 +706,14 @@ namespace AzToolsFramework "Prefab - PrefabSystemComponent::RemoveLink - " "Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' " "from TemplateToLinkIdsMap.", - linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str()); + linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId()); result = RemoveLinkFromTargetTemplate(linkId, link); AZ_Assert(result, "Prefab - PrefabSystemComponent::RemoveLink - " "Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' " "from target Template with Id '%llu'.", - linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str(), link.GetTargetTemplateId()); + linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId(), link.GetTargetTemplateId()); m_linkIdMap.erase(linkId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.cpp index 7891e2c2e0..78c76cc8e5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.cpp @@ -73,14 +73,16 @@ namespace AzToolsFramework } PrefabDom oldData; - Retrieve(entityId, oldData); + AZ::EntityId oldParentId; + Retrieve(entityId, oldData, oldParentId); UpdateCache(entityId); PrefabDom newData; - Retrieve(entityId, newData); + AZ::EntityId newParentId; + Retrieve(entityId, newData, newParentId); - if (newData != oldData) + if (newData != oldData || oldParentId != newParentId) { // display a useful message AZ::Entity* entity = nullptr; @@ -106,7 +108,7 @@ namespace AzToolsFramework // Clear out newly generated data and // replace with original data to ensure debug mode has the same data as profile/release // in the event of the consistency check failing. - m_entitySavedStates[entityId] = AZStd::move(oldData); + m_entitySavedStates[entityId] = {AZStd::move(oldData), oldParentId}; #endif // ENABLE_UNDOCACHE_CONSISTENCY_CHECKS } @@ -140,10 +142,13 @@ namespace AzToolsFramework return; } + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId); + // Capture it PrefabDom entityDom; m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *entity); - m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(entityDom))); + m_entitySavedStates[entityId] = {AZStd::move(entityDom), parentId}; AZLOG("Prefab Undo", "Correctly updated cache for entity of id %llu (%s)", static_cast(entityId), entity->GetName().c_str()); @@ -155,7 +160,7 @@ namespace AzToolsFramework m_entitySavedStates.erase(entityId); } - bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom) + bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId) { auto it = m_entitySavedStates.find(entityId); @@ -164,14 +169,15 @@ namespace AzToolsFramework return false; } - outDom = AZStd::move(m_entitySavedStates[entityId]); + outDom = AZStd::move(m_entitySavedStates[entityId].dom); + parentId = m_entitySavedStates[entityId].parentId; m_entitySavedStates.erase(entityId); return true; } - void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom) + void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId) { - m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(dom))); + m_entitySavedStates[entityId] = {AZStd::move(dom), parentId}; } void PrefabUndoCache::Clear() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.h index 924c93f44b..3a3ac92d7c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoCache.h @@ -46,14 +46,19 @@ namespace AzToolsFramework void Validate(const AZ::EntityId& entityId) override; // Retrieve the last known state for an entity - bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom); + bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId); // Store dom as the cached state of entityId - void Store(const AZ::EntityId& entityId, PrefabDom&& dom); + void Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId); private: - typedef AZStd::unordered_map EntityDomMap; - EntityDomMap m_entitySavedStates; + struct PrefabUndoCacheItem + { + PrefabDom dom; + AZ::EntityId parentId; + }; + typedef AZStd::unordered_map EntityCache; + EntityCache m_entitySavedStates; InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; From 3634277317fa1777cda9849175f1a3b0aeda1423 Mon Sep 17 00:00:00 2001 From: Eric Phister <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 3 Jun 2021 16:15:46 -0500 Subject: [PATCH 067/105] Project dll is not loaded by the AP when opened from the launcher (#1123) * Fixes locating the project dll when using SDK SDK engine usage has project dll in the project build path, but searching for module filepaths for loading would have a passing SystemFile::Exists check but no full filepath was amended to the module. This causes the module to fail to load. * Fix locating project module for UnixLike platforms Fixes the issue with project-centric workflows running GameLauncher, and it opens AP which can't find the project dynamic module. From AP's perspective, the project module is not in the executable directory, which is in engine bin. The SystemFile::Exists check is true on the file because it uses the 'cwd'. In that situation, an absolute path must be obtained for the module to be loaded. * Add missing header to fix UnixLike builds * Applies a suggested change from PR Use operator-> on the AZStd::optional * Add semicolon to a class macro line Prevent auto formatting indenting the following line. --- .../Module/DynamicModuleHandle_UnixLike.cpp | 23 +++++++++++++------ .../Module/DynamicModuleHandle_WinAPI.cpp | 16 ++++++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp index 8e2b40aaca..f9da818b4f 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp @@ -13,9 +13,10 @@ #include #include #include - #include #include +#include + #include #include @@ -61,10 +62,11 @@ namespace AZ // If it doesn't attempt to append the path to the executable path if (!AZ::IO::SystemFile::Exists(fullFilePath.c_str())) { - auto candidatePath = Platform::GetModulePath() / fullFilePath; + AZ::IO::FixedMaxPath candidatePath = Platform::GetModulePath() / fullFilePath; if (AZ::IO::SystemFile::Exists(candidatePath.c_str())) { - fullFilePath = candidatePath; + m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size()); + return; } } @@ -74,19 +76,26 @@ namespace AZ { if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - if(AZ::IO::FixedMaxPath projectModulePath; + if (AZ::IO::FixedMaxPath projectModulePath; settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) { projectModulePath /= fullFilePath; if (AZ::IO::SystemFile::Exists(projectModulePath.c_str())) { - fullFilePath = projectModulePath; + m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size()); } } } } - - m_fileName = AZStd::string_view{fullFilePath.Native()}; + else + { + // The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded. + AZStd::optional absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName); + if (absPathOptional.has_value()) + { + m_fileName.assign(absPathOptional->c_str(), absPathOptional->size()); + } + } } ~DynamicModuleHandleUnixLike() override diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Module/DynamicModuleHandle_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Module/DynamicModuleHandle_WinAPI.cpp index 9daabfb86b..aeb8a81d09 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Module/DynamicModuleHandle_WinAPI.cpp +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Module/DynamicModuleHandle_WinAPI.cpp @@ -24,9 +24,9 @@ namespace AZ : public DynamicModuleHandle { public: - AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0) + AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0); - DynamicModuleHandleWindows(const char* fullFileName) + DynamicModuleHandleWindows(const char* fullFileName) : DynamicModuleHandle(fullFileName) , m_handle(nullptr) { @@ -52,6 +52,7 @@ namespace AZ if (AZ::IO::SystemFile::Exists(candidatePath.c_str())) { m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size()); + return; } } } @@ -65,7 +66,7 @@ namespace AZ // Therefore an existence check is needed if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - if(AZ::IO::FixedMaxPath projectModulePath; + if (AZ::IO::FixedMaxPath projectModulePath; settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) { projectModulePath /= AZStd::string_view(m_fileName); @@ -76,6 +77,15 @@ namespace AZ } } } + else + { + // The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded. + AZStd::optional absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName); + if (absPathOptional.has_value()) + { + m_fileName.assign(absPathOptional->c_str(), absPathOptional->size()); + } + } } ~DynamicModuleHandleWindows() override From d1f23aff62c4644ba7b3f9f8386c8014479f8b3b Mon Sep 17 00:00:00 2001 From: jckand-amzn Date: Thu, 3 Jun 2021 17:38:22 -0500 Subject: [PATCH 068/105] SPEC-7008: Excluding more main tests from Debug test runs --- .../Gem/PythonTests/editor/test_BasicEditorWorkflows.py | 5 +++++ .../largeworlds/landscape_canvas/test_GraphComponentSync.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py b/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py index b045b364a3..f65401f007 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_BasicEditorWorkflows.py @@ -15,6 +15,7 @@ import pytest # Bail on the test if ly_test_tools doesn't exist. pytest.importorskip('ly_test_tools') import ly_test_tools.environment.file_system as file_system +import ly_test_tools._internal.pytest_plugin as internal_plugin import editor_python_test_tools.hydra_test_utils as hydra test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts") @@ -40,6 +41,10 @@ class TestBasicEditorWorkflows(object): @pytest.mark.SUITE_main def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform): + # Skip test if running against Debug build + if "debug" in internal_plugin.build_directory: + pytest.skip("Does not execute against debug builds.") + expected_lines = [ "Create and load new level: True", "New entity creation: True", diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py index 943d0cb985..e7dc046480 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/landscape_canvas/test_GraphComponentSync.py @@ -132,7 +132,7 @@ class TestGraphComponentSync(object): # Skip test if running against Debug build if "debug" in internal_plugin.build_directory: pytest.skip("Does not execute against debug builds.") - + cfg_args = [level] expected_lines = [ From 792176d7640d3b5ac80f8da82c9a407da3705272 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 16:02:16 -0700 Subject: [PATCH 069/105] Cached occlusion plane corner points and AABB in the feature processor --- .../OcclusionCullingPlaneFeatureProcessor.cpp | 49 ++++++++++++++++-- .../OcclusionCullingPlaneFeatureProcessor.h | 4 ++ .../Code/Include/Atom/RPI.Public/Culling.h | 16 +++++- .../RPI/Code/Source/RPI.Public/Culling.cpp | 51 +++++-------------- 4 files changed, 76 insertions(+), 44 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index b9866a925f..ff7c32ba08 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -33,6 +33,7 @@ namespace AZ void OcclusionCullingPlaneFeatureProcessor::Activate() { m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); + m_rpiOcclusionPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize); EnableSceneNotification(); } @@ -48,13 +49,46 @@ namespace AZ } void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender() - { - AZStd::vector occlusionCullingPlanes; - for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + if (m_rpiListNeedsUpdate) { - occlusionCullingPlanes.push_back(occlusionCullingPlane->GetTransform()); + // rebuild the RPI occlusion list + m_rpiOcclusionPlanes.clear(); + + for (auto& occlusionCullingPlane : m_occlusionCullingPlanes) + { + if (!occlusionCullingPlane->GetEnabled()) + { + continue; + } + + RPI::CullingScene::OcclusionPlane rpiOcclusionPlane; + + static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); + static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); + static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); + static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + + const AZ::Transform& transform = occlusionCullingPlane->GetTransform(); + + // convert corners to world space + rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL); + rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); + rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL); + rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR); + + // build world space AABB + AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR); + AZ::Vector3 aabbMax = rpiOcclusionPlane.m_cornerBL.GetMax(rpiOcclusionPlane.m_cornerTR); + rpiOcclusionPlane.m_aabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); + + m_rpiOcclusionPlanes.push_back(rpiOcclusionPlane); + } + + GetParentScene()->GetCullingScene()->SetOcclusionPlanes(m_rpiOcclusionPlanes); + + m_rpiListNeedsUpdate = false; } - GetParentScene()->GetCullingScene()->SetOcclusionCullingPlanes(occlusionCullingPlanes); } OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform) @@ -63,6 +97,8 @@ namespace AZ occlusionCullingPlane->Init(GetParentScene()); occlusionCullingPlane->SetTransform(transform); m_occlusionCullingPlanes.push_back(occlusionCullingPlane); + m_rpiListNeedsUpdate = true; + return occlusionCullingPlane; } @@ -78,18 +114,21 @@ namespace AZ AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list"); m_occlusionCullingPlanes.erase(itEntry); occlusionCullingPlane = nullptr; + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) { AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle"); occlusionCullingPlane->SetTransform(transform); + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) { AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle"); occlusionCullingPlane->SetEnabled(enabled); + m_rpiListNeedsUpdate = true; } void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h index 211254742f..8b3ac3f58d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h @@ -57,6 +57,10 @@ namespace AZ // list of occlusion planes const size_t InitialOcclusionCullingPlanesAllocationSize = 64; OcclusionCullingPlaneVector m_occlusionCullingPlanes; + + // prebuilt list of RPI scene occlusion planes + RPI::CullingScene::OcclusionPlaneVector m_rpiOcclusionPlanes; + bool m_rpiListNeedsUpdate = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 295797d2dd..2a9c133b5c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -215,8 +215,20 @@ namespace AZ void Activate(const class Scene* parentScene); void Deactivate(); + struct OcclusionPlane + { + // World space corners of the occluson plane + Vector3 m_cornerBL; + Vector3 m_cornerBR; + Vector3 m_cornerTL; + Vector3 m_cornerTR; + + Aabb m_aabb; + }; + using OcclusionPlaneVector = AZStd::vector; + //! Sets a list of occlusion planes to be used during the culling process. - void SetOcclusionCullingPlanes(const AZStd::vector& occlusionCullingPlanes) { m_occlusionCullingPlanes = occlusionCullingPlanes; } + void SetOcclusionPlanes(const OcclusionPlaneVector& occlusionPlanes) { m_occlusionPlanes = occlusionPlanes; } //! Notifies the CullingScene that culling will begin for this frame. void BeginCulling(const AZStd::vector& views); @@ -258,7 +270,7 @@ namespace AZ AzFramework::IVisibilityScene* m_visScene = nullptr; CullingDebugContext m_debugCtx; AZStd::concurrency_checker m_cullDataConcurrencyCheck; - AZStd::vector m_occlusionCullingPlanes; + OcclusionPlaneVector m_occlusionPlanes; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 3f28805888..7ee2c4a8d2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -546,60 +546,37 @@ namespace AZ #if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED // setup occlusion culling, if necessary - MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionCullingPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); + MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling(); if (maskedOcclusionCulling) { // frustum cull occlusion planes - using OccluderEntry = AZStd::pair; - AZStd::vector visibleOccluders; - for (const AZ::Transform& transform : m_occlusionCullingPlanes) + using VisibleOcclusionPlane = AZStd::pair; + AZStd::vector visibleOccluders; + for (const auto& occlusionPlane : m_occlusionPlanes) { - static const AZ::Vector3 BL(-0.5f, -0.5f, 0.0f); - static const AZ::Vector3 TR(0.5f, 0.5f, 0.0f); - - AZ::Vector3 P1 = transform.TransformPoint(BL); - AZ::Vector3 P2 = transform.TransformPoint(TR); - - AZ::Vector3 aabbMin = P1.GetMin(P2); - AZ::Vector3 aabbMax = P1.GetMax(P2); - - AZ::Aabb occluderAabb = Aabb::CreateFromMinMax(aabbMin, aabbMax); - if (ShapeIntersection::Overlaps(frustum, occluderAabb)) + if (ShapeIntersection::Overlaps(frustum, occlusionPlane.m_aabb)) { // occluder is visible, compute view space distance and add to list - float depth = (view.GetWorldToViewMatrix() * occluderAabb.GetMin()).GetZ(); - depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occluderAabb.GetMax()).GetZ()); + float depth = (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMin()).GetZ(); + depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMax()).GetZ()); - visibleOccluders.push_back(AZStd::make_pair(transform, depth)); + visibleOccluders.push_back(AZStd::make_pair(occlusionPlane, depth)); } } // sort the occlusion planes by view space distance, front-to-back - AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const OccluderEntry& LHS, const OccluderEntry& RHS) + AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const VisibleOcclusionPlane& LHS, const VisibleOcclusionPlane& RHS) { return LHS.second > RHS.second; }); - for (const OccluderEntry& occluder : visibleOccluders) + for (const VisibleOcclusionPlane& occlusionPlane: visibleOccluders) { - const AZ::Transform& transform = occluder.first; - - // find the corners of the plane - static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); - static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); - static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); - static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); - - Vector3 planeBL = transform.TransformPoint(BL); - Vector3 planeBR = transform.TransformPoint(BR); - Vector3 planeTL = transform.TransformPoint(TL); - Vector3 planeTR = transform.TransformPoint(TR); - // convert to clip-space - Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(planeBL); - Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(planeBR); - Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(planeTL); - Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(planeTR); + Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); + Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL); + Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR); // store to float array float verts[16]; From 85e6d06c2c203b7883053f97e8827689e10a5ae2 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 16:09:10 -0700 Subject: [PATCH 070/105] [default_3rdparty] add 3rd party to engine registration + specific path registration fixes --- .../ProjectManager/Source/PythonBindings.cpp | 22 +++--------- scripts/o3de/o3de/manifest.py | 8 +++++ scripts/o3de/o3de/register.py | 35 +++++++++++++++---- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 7062d886da..e2c84b2b5f 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -383,7 +383,7 @@ namespace O3DE::ProjectManager engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); - engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path",""); + engineInfo.m_thirdPartyPath = Py_To_String(o3deData["default_third_party_folder"]); } auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath); @@ -420,6 +420,7 @@ namespace O3DE::ProjectManager pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString(); + pybind11::str defaultThridPartyFolder = engineInfo.m_thirdPartyPath.toStdString(); auto registrationResult = m_register.attr("register")( enginePath, // engine_path @@ -432,28 +433,15 @@ namespace O3DE::ProjectManager pybind11::none(), // default_engines_folder defaultProjectsFolder, defaultGemsFolder, - defaultTemplatesFolder + defaultTemplatesFolder, + pybind11::none(), // default_restricted_folder + defaultThridPartyFolder ); if (registrationResult.cast() != 0) { result = false; } - - auto manifest = m_manifest.attr("load_o3de_manifest")(); - if (pybind11::isinstance(manifest)) - { - try - { - manifest["third_party_path"] = engineInfo.m_thirdPartyPath.toStdString(); - m_manifest.attr("save_o3de_manifest")(manifest); - } - catch ([[maybe_unused]] const std::exception& e) - { - AZ_Warning("PythonBindings", false, "Failed to set third party path."); - } - } - }); return result; diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index edcd44c525..3fa21721c2 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -97,6 +97,12 @@ def get_o3de_logs_folder() -> pathlib.Path: return logs_folder +def get_o3de_third_party_folder() -> pathlib.Path: + third_party_folder = get_o3de_folder() / '3rdParty' + third_party_folder.mkdir(parents=True, exist_ok=True) + return third_party_folder + + # o3de manifest file methods def get_o3de_manifest() -> pathlib.Path: manifest_path = get_o3de_folder() / 'o3de_manifest.json' @@ -113,6 +119,7 @@ def get_o3de_manifest() -> pathlib.Path: default_gems_folder = get_o3de_gems_folder() default_templates_folder = get_o3de_templates_folder() default_restricted_folder = get_o3de_restricted_folder() + default_third_party_folder = get_o3de_third_party_folder() default_projects_restricted_folder = default_projects_folder / 'Restricted' default_projects_restricted_folder.mkdir(parents=True, exist_ok=True) @@ -129,6 +136,7 @@ def get_o3de_manifest() -> pathlib.Path: json_data.update({'default_gems_folder': default_gems_folder.as_posix()}) json_data.update({'default_templates_folder': default_templates_folder.as_posix()}) json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()}) + json_data.update({'default_third_party_folder': default_third_party_folder.as_posix()}) json_data.update({'projects': []}) json_data.update({'external_subdirectories': []}) diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 68575488dc..2e37f04acf 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -486,7 +486,7 @@ def register_default_engines_folder(json_data: dict, remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_engines_folder() if remove else default_engines_folder, - 'default_engines_folder', remove) + 'default_engines_folder') def register_default_projects_folder(json_data: dict, @@ -494,7 +494,7 @@ def register_default_projects_folder(json_data: dict, remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_projects_folder() if remove else default_projects_folder, - 'default_projects_folder', remove) + 'default_projects_folder') def register_default_gems_folder(json_data: dict, @@ -502,7 +502,7 @@ def register_default_gems_folder(json_data: dict, remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_gems_folder() if remove else default_gems_folder, - 'default_gems_folder', remove) + 'default_gems_folder') def register_default_templates_folder(json_data: dict, @@ -510,16 +510,22 @@ def register_default_templates_folder(json_data: dict, remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_templates_folder() if remove else default_templates_folder, - 'default_templates_folder', remove) + 'default_templates_folder') def register_default_restricted_folder(json_data: dict, default_restricted_folder: str or pathlib.Path, - reset_to_default: bool = False) -> int: + remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_restricted_folder() if remove else default_restricted_folder, - 'default_restricted_folder', remove) + 'default_restricted_folder') +def register_default_third_party_folder(json_data: dict, + default_third_party_folder: str or pathlib.Path, + remove: bool = False) -> int: + return register_default_o3de_object_folder(json_data, + manifest.get_o3de_third_party_folder() if remove else default_third_party_folder, + 'default_third_party_folder') def register(engine_path: str or pathlib.Path = None, project_path: str or pathlib.Path = None, @@ -533,6 +539,7 @@ def register(engine_path: str or pathlib.Path = None, default_gems_folder: str or pathlib.Path = None, default_templates_folder: str or pathlib.Path = None, default_restricted_folder: str or pathlib.Path = None, + default_third_party_folder: str or pathlib.Path = None, external_subdir_engine_path: pathlib.Path = None, external_subdir_project_path: pathlib.Path = None, remove: bool = False, @@ -553,6 +560,7 @@ def register(engine_path: str or pathlib.Path = None, :param default_gems_folder: default gems folder :param default_templates_folder: default templates folder :param default_restricted_folder: default restricted code folder + :param default_third_party_folder: default 3rd party cache folder :param external_subdir_engine_path: Path to the engine to use when registering an external subdirectory. The registration occurs in the engine.json file in this case :param external_subdir_engine_path: Path to the project to use when registering an external subdirectory. @@ -620,6 +628,9 @@ def register(engine_path: str or pathlib.Path = None, elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): result = register_default_restricted_folder(json_data, default_restricted_folder, remove) + elif isinstance(default_third_party_folder, str) or isinstance(default_third_party_folder, pathlib.PurePath): + result = register_default_third_party_folder(json_data, default_third_party_folder, remove) + # engine is done LAST # Now that everything that could have an engine context is done, if the engine is supplied that means this is # registering the engine itself @@ -712,6 +723,15 @@ def remove_invalid_o3de_objects() -> None: f" Set default {default_restricted_folder}") register(default_restricted_folder=default_restricted_folder.as_posix()) + default_third_party_folder = pathlib.Path(json_data['default_third_party_folder']).resolve() + if not default_third_party_folder.is_dir(): + default_third_party_folder = manifest.get_o3de_folder() / '3rdParty' + default_third_party_folder.mkdir(parents=True, exist_ok=True) + logger.warn( + f"Default 3rd Party folder {default_third_party_folder} is invalid." + f" Set default {default_third_party_folder}") + register(default_third_party_folder=default_third_party_folder.as_posix()) + def _run_register(args: argparse) -> int: if args.override_home_folder: @@ -751,6 +771,7 @@ def _run_register(args: argparse) -> int: default_gems_folder=args.default_gems_folder, default_templates_folder=args.default_templates_folder, default_restricted_folder=args.default_restricted_folder, + default_third_party_folder=args.default_third_party_folder, external_subdir_engine_path=args.external_subdirectory_engine_path, external_subdir_project_path=args.external_subdirectory_project_path, remove=args.remove, @@ -804,6 +825,8 @@ def add_parser_args(parser): help='The default templates folder to register/remove.') group.add_argument('-drf', '--default-restricted-folder', type=str, required=False, help='The default restricted folder to register/remove.') + group.add_argument('-dtpf', '--default-third-party-folder', type=str, required=False, + help='The default 3rd Party folder to register/remove.') group.add_argument('-u', '--update', action='store_true', required=False, default=False, help='Refresh the repo cache.') From 816d05ef2d85843c86fd47265cc8cf1a3b55c3c5 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:16:22 -0500 Subject: [PATCH 071/105] Updating manifest.py template query functions (#1113) * Updating manifest.py template query functions The get_project_templates, get_gem_templates and get_generic_templates methods have been renamed to indicate that the methods return the templates that can be used in a create-project, create-gem and create-from-template command of the engine_template.py Updated the print_registration.py script to support outputing project specific gems and templates. Add a unit test script for the manifest.py script. Added unit test to validate the new functions: `get_templates_for_project_creation` `get_templates_for_gem_creation` `get_templates_for_generic_creation` * Implementing the project print registration methods Added implementations of the project print registration methods and tested them locally Removed implementations of the download print registration methods, since they have not went through app-sec review. * Renaming get_restricted_data to get_restricted_json_data Fixed the get_registered method in manifest.py when looking up projects * Updated the print_manifest_json_data calls to return the result --- .../ProjectManager/Source/PythonBindings.cpp | 215 ++++---- scripts/o3de/o3de/manifest.py | 97 ++-- scripts/o3de/o3de/print_registration.py | 496 +++++++++--------- scripts/o3de/tests/CMakeLists.txt | 7 + scripts/o3de/tests/unit_test_manifest.py | 111 ++++ scripts/o3de/tests/unit_test_utils.py | 2 +- 6 files changed, 542 insertions(+), 386 deletions(-) create mode 100644 scripts/o3de/tests/unit_test_manifest.py diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 7062d886da..18b29c496f 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -294,7 +294,8 @@ namespace O3DE::ProjectManager RegisterThisEngine(); return result == 0 && !PyErr_Occurred(); - } catch ([[maybe_unused]] const std::exception& e) + } + catch ([[maybe_unused]] const std::exception& e) { AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what()); return false; @@ -320,25 +321,25 @@ namespace O3DE::ProjectManager bool registrationResult = true; // already registered is considered successful bool pythonResult = ExecuteWithLock( [&] + { + // check current engine path against all other registered engines + // to see if we are already registered + auto allEngines = m_manifest.attr("get_engines")(); + if (pybind11::isinstance(allEngines)) { - // check current engine path against all other registered engines - // to see if we are already registered - auto allEngines = m_manifest.attr("get_engines")(); - if (pybind11::isinstance(allEngines)) + for (auto engine : allEngines) { - for (auto engine : allEngines) + AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); + if (enginePath.Compare(m_enginePath) == 0) { - AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); - if (enginePath.Compare(m_enginePath) == 0) - { - return; - } + return; } } + } - auto result = m_register.attr("register")(m_enginePath.c_str()); - registrationResult = (result.cast() == 0); - }); + auto result = m_register.attr("register")(m_enginePath.c_str()); + registrationResult = (result.cast() == 0); + }); bool finalResult = (registrationResult && pythonResult); AZ_Assert(finalResult, "Registration of this engine failed!"); @@ -378,12 +379,12 @@ namespace O3DE::ProjectManager auto o3deData = m_manifest.attr("load_o3de_manifest")(); if (pybind11::isinstance(o3deData)) { - engineInfo.m_path = Py_To_String(enginePath); - engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); - engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); + engineInfo.m_path = Py_To_String(enginePath); + engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); + engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); - engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); - engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path",""); + engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); + engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData, "third_party_path", ""); } auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath); @@ -391,8 +392,8 @@ namespace O3DE::ProjectManager { try { - engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0"); - engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE"); + engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0"); + engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE"); } catch ([[maybe_unused]] const std::exception& e) { @@ -416,19 +417,19 @@ namespace O3DE::ProjectManager bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo) { bool result = ExecuteWithLock([&] { - pybind11::str enginePath = engineInfo.m_path.toStdString(); - pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); - pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); + pybind11::str enginePath = engineInfo.m_path.toStdString(); + pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); + pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString(); auto registrationResult = m_register.attr("register")( - enginePath, // engine_path - pybind11::none(), // project_path + enginePath, // engine_path + pybind11::none(), // project_path pybind11::none(), // gem_path - pybind11::none(), // external_subdir_path - pybind11::none(), // template_path - pybind11::none(), // restricted_path - pybind11::none(), // repo_uri + pybind11::none(), // external_subdir_path + pybind11::none(), // template_path + pybind11::none(), // restricted_path + pybind11::none(), // repo_uri pybind11::none(), // default_engines_folder defaultProjectsFolder, defaultGemsFolder, @@ -477,12 +478,12 @@ namespace O3DE::ProjectManager QVector gems; auto result = ExecuteWithLockErrorHandling([&] + { + for (auto path : m_manifest.attr("get_engine_gems")()) { - for (auto path : m_manifest.attr("get_engine_gems")()) - { - gems.push_back(GemInfoFromPath(path)); - } - }); + gems.push_back(GemInfoFromPath(path)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -497,13 +498,13 @@ namespace O3DE::ProjectManager QVector gems; auto result = ExecuteWithLockErrorHandling([&] + { + pybind11::str pyProjectPath = projectPath.toStdString(); + for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath)) { - pybind11::str pyProjectPath = projectPath.toStdString(); - for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath)) - { - gems.push_back(GemInfoFromPath(path)); - } - }); + gems.push_back(GemInfoFromPath(path)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -518,12 +519,12 @@ namespace O3DE::ProjectManager // Retrieve the path to the cmake file that lists the enabled gems. pybind11::str enabledGemsFilename; auto result = ExecuteWithLockErrorHandling([&] - { - const pybind11::str pyProjectPath = projectPath.toStdString(); - enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")( - pybind11::none(), // project_name - pyProjectPath); // project_path - }); + { + const pybind11::str pyProjectPath = projectPath.toStdString(); + enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")( + pybind11::none(), // project_name + pyProjectPath); // project_path + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -532,13 +533,13 @@ namespace O3DE::ProjectManager // Retrieve the actual list of names from the cmake file. QVector gemNames; result = ExecuteWithLockErrorHandling([&] + { + const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename); + for (auto gemName : pyGemNames) { - const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename); - for (auto gemName : pyGemNames) - { - gemNames.push_back(Py_To_String(gemName)); - } - }); + gemNames.push_back(Py_To_String(gemName)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -552,13 +553,13 @@ namespace O3DE::ProjectManager bool registrationResult = false; bool result = ExecuteWithLock( [&] - { - pybind11::str projectPath = path.toStdString(); - auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath); + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath); - // Returns an exit code so boolify it then invert result - registrationResult = !pythonRegistrationResult.cast(); - }); + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); return result && registrationResult; } @@ -568,30 +569,30 @@ namespace O3DE::ProjectManager bool registrationResult = false; bool result = ExecuteWithLock( [&] - { - pybind11::str projectPath = path.toStdString(); - auto pythonRegistrationResult = m_register.attr("register")( - pybind11::none(), // engine_path - projectPath, // project_path - pybind11::none(), // gem_path - pybind11::none(), // external_subdir_path - pybind11::none(), // template_path - pybind11::none(), // restricted_path - pybind11::none(), // repo_uri - pybind11::none(), // default_engines_folder - pybind11::none(), // default_projects_folder - pybind11::none(), // default_gems_folder - pybind11::none(), // default_templates_folder - pybind11::none(), // default_restricted_folder - pybind11::none(), // external_subdir_engine_path - pybind11::none(), // external_subdir_project_path - true, // remove - false // force + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")( + pybind11::none(), // engine_path + projectPath, // project_path + pybind11::none(), // gem_path + pybind11::none(), // external_subdir_path + pybind11::none(), // template_path + pybind11::none(), // restricted_path + pybind11::none(), // repo_uri + pybind11::none(), // default_engines_folder + pybind11::none(), // default_projects_folder + pybind11::none(), // default_gems_folder + pybind11::none(), // default_templates_folder + pybind11::none(), // default_restricted_folder + pybind11::none(), // external_subdir_engine_path + pybind11::none(), // external_subdir_project_path + true, // remove + false // force ); - - // Returns an exit code so boolify it then invert result - registrationResult = !pythonRegistrationResult.cast(); - }); + + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); return result && registrationResult; } @@ -649,12 +650,12 @@ namespace O3DE::ProjectManager try { // required - gemInfo.m_name = Py_To_String(data["gem_name"]); + gemInfo.m_name = Py_To_String(data["gem_name"]); // optional gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name); - gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); - gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); + gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); + gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); if (data.contains("Tags")) { @@ -685,7 +686,7 @@ namespace O3DE::ProjectManager try { projectInfo.m_projectName = Py_To_String(projectData["project_name"]); - projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName); + projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName); } catch ([[maybe_unused]] const std::exception& e) { @@ -727,33 +728,33 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath) { return ExecuteWithLockErrorHandling([&] - { - pybind11::str pyGemPath = gemPath.toStdString(); - pybind11::str pyProjectPath = projectPath.toStdString(); + { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); - m_enableGemProject.attr("enable_gem_in_project")( - pybind11::none(), // gem name not needed as path is provided - pyGemPath, - pybind11::none(), // project name not needed as path is provided - pyProjectPath + m_enableGemProject.attr("enable_gem_in_project")( + pybind11::none(), // gem name not needed as path is provided + pyGemPath, + pybind11::none(), // project name not needed as path is provided + pyProjectPath ); - }); + }); } AZ::Outcome PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath) { return ExecuteWithLockErrorHandling([&] - { - pybind11::str pyGemPath = gemPath.toStdString(); - pybind11::str pyProjectPath = projectPath.toStdString(); + { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); - m_disableGemProject.attr("disable_gem_in_project")( - pybind11::none(), // gem name not needed as path is provided - pyGemPath, - pybind11::none(), // project name not needed as path is provided - pyProjectPath + m_disableGemProject.attr("disable_gem_in_project")( + pybind11::none(), // gem name not needed as path is provided + pyGemPath, + pybind11::none(), // project name not needed as path is provided + pyProjectPath ); - }); + }); } bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo) @@ -773,8 +774,8 @@ namespace O3DE::ProjectManager { // required templateInfo.m_displayName = Py_To_String(data["display_name"]); - templateInfo.m_name = Py_To_String(data["template_name"]); - templateInfo.m_summary = Py_To_String(data["summary"]); + templateInfo.m_name = Py_To_String(data["template_name"]); + templateInfo.m_summary = Py_To_String(data["summary"]); // optional if (data.contains("canonical_tags")) @@ -806,7 +807,7 @@ namespace O3DE::ProjectManager QVector templates; bool result = ExecuteWithLock([&] { - for (auto path : m_manifest.attr("get_project_templates")()) + for (auto path : m_manifest.attr("get_templates_for_project_creation")()) { templates.push_back(ProjectTemplateInfoFromPath(path)); } diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index edcd44c525..bcb331d9f4 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -302,64 +302,95 @@ def get_project_external_subdirectories(project_path: pathlib.Path) -> list: project_object['external_subdirectories'])) if 'external_subdirectories' in project_object else [] +def get_project_templates(project_path: pathlib.Path) -> list: + project_object = get_project_json_data(project_path=project_path) + return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(), + project_object['templates'])) + + +def get_project_restricted(project_path: pathlib.Path) -> list: + project_object = get_project_json_data(project_path=project_path) + return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(), + project_object['restricted'])) if 'restricted' in project_object else [] + + # Combined manifest queries def get_all_projects() -> list: - projects_data = set(get_projects()) - projects_data.update(get_engine_projects()) - return list(projects_data) + projects_data = get_projects() + projects_data.extend(get_engine_projects()) + # Remove duplicates from the list + return list(dict.fromkeys(projects_data)) def get_all_gems(project_path: pathlib.Path = None) -> list: - gems_data = set(get_gems()) - gems_data.update(get_engine_gems()) + gems_data = get_gems() + gems_data.extend(get_engine_gems()) if project_path: - gems_data.update(get_project_gems(project_path)) - return list(gems_data) + gems_data.extend(get_project_gems(project_path)) + return list(dict.fromkeys(gems_data)) def get_all_external_subdirectories(project_path: pathlib.Path = None) -> list: - external_subdirectories_data = set(get_external_subdirectories()) - external_subdirectories_data.update(get_engine_external_subdirectories()) + external_subdirectories_data = get_external_subdirectories() + external_subdirectories_data.extend(get_engine_external_subdirectories()) if project_path: - external_subdirectories_data.update(get_project_external_subdirectories(project_path)) - return list(templates_data) + external_subdirectories_data.extend(get_project_external_subdirectories(project_path)) + return list(dict.fromkeys(external_subdirectories_data)) -def get_all_templates() -> list: - templates_data = set(get_templates()) - templates_data.update(get_engine_templates()) - return list(templates_data) +def get_all_templates(project_path: pathlib.Path = None) -> list: + templates_data = get_templates() + templates_data.extend(get_engine_templates()) + if project_path: + templates_data.extend(get_project_templates(project_path)) + return list(dict.fromkeys(templates_data)) def get_all_restricted() -> list: - restricted_data = set(get_restricted()) - restricted_data.update(get_engine_restricted()) - return list(gems_data) + restricted_data = get_restricted() + restricted_data.extend(get_engine_restricted()) + if project_path: + restricted_data.extend(get_project_restricted(project_path)) + return list(dict.fromkeys(restricted_data)) # Template functions -def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element +def get_templates_for_project_creation(): project_templates = [] - for template in get_all_templates(): - if 'Project' in template: - project_templates.append(template) + for template_path in get_all_templates(): + template_path = pathlib.Path(template_path) + template_json_path = pathlib.Path(template_path) / 'template.json' + if not validation.valid_o3de_template_json(template_json_path): + continue + + project_json_path = template_path / 'Template' / 'project.json' + if validation.valid_o3de_project_json(project_json_path): + project_templates.append(template_path) return project_templates -def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element +def get_templates_for_gem_creation(): gem_templates = [] - for template in get_all_templates(): - if 'Gem' in template: - gem_templates.append(template) + for template_path in get_all_templates(): + template_path = pathlib.Path(template_path) + template_json_path = pathlib.Path(template_path) / 'template.json' + if not validation.valid_o3de_template_json(template_json_path): + continue + + gem_json_path = template_path / 'Template' / 'gem.json' + if validation.valid_o3de_gem_json(gem_json_path): + gem_templates.append(template_path) return gem_templates -def get_generic_templates(): # temporary until we have a better way to do this... maybe template_type element - generic_templates = [] - for template in get_all_templates(): - if 'Project' not in template and 'Gem' not in template: - generic_templates.append(template) - return generic_templates +def get_templates_for_generic_creation(): # temporary until we have a better way to do this... maybe template_type element + def filter_project_and_gem_templates_out(template_path, + templates_for_project_creation = get_templates_for_project_creation(), + templates_for_gem_creation = get_templates_for_gem_creation()): + template_path = pathlib.Path(template_path) + return template_path not in templates_for_project_creation and template_path not in templates_for_gem_creation + + return list(filter(filter_project_and_gem_templates_out, get_all_templates())) def get_all_restricted() -> list: @@ -515,7 +546,7 @@ def get_template_json_data(template_name: str = None, return None -def get_restricted_data(restricted_name: str = None, +def get_restricted_json_data(restricted_name: str = None, restricted_path: str or pathlib.Path = None) -> dict or None: if not restricted_name and not restricted_path: logger.error('Must specify either a Restricted name or Restricted Path.') diff --git a/scripts/o3de/o3de/print_registration.py b/scripts/o3de/o3de/print_registration.py index 292f2224bc..e5c7a1afc0 100644 --- a/scripts/o3de/o3de/print_registration.py +++ b/scripts/o3de/o3de/print_registration.py @@ -13,6 +13,7 @@ import argparse import json import hashlib import logging +import pathlib import sys import urllib.parse @@ -21,219 +22,251 @@ from o3de import manifest, validation logger = logging.getLogger() logging.basicConfig() -def print_this_engine(verbose: int) -> None: + +def get_project_path(project_path: pathlib.Path, project_name: str) -> pathlib.Path: + if not project_name and not project_path: + logger.error(f'Must either specify a Project path or Project Name.') + return None + + if not project_path: + project_path = manifest.get_registered(project_name=project_name) + if not project_path: + logger.error(f'Unable to locate project path from the registered manifest json files:' + f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json') + return None + + if not project_path.is_dir(): + logger.error(f'Project path {project_path} is not a folder.') + return None + + return project_path + + +def print_this_engine(verbose: int) -> int: engine_data = manifest.get_this_engine() print(json.dumps(engine_data, indent=4)) + result = True if verbose > 0: - print_engines_data(engine_data) + result = print_manifest_json_data(engine_data, 'engine.json', 'This Engine', + manifest.get_engine_json_data, 'engine_path') + return 0 if result else 1 def print_engines(verbose: int) -> None: engines_data = manifest.get_engines() print(json.dumps(engines_data, indent=4)) + if verbose > 0: - print_engines_data(engines_data) + return print_manifest_json_data(engines_data, 'engine.json', 'Engines', + manifest.get_engine_json_data, 'engine_path') + return 0 -def print_projects(verbose: int) -> None: +def print_projects(verbose: int) -> int: projects_data = manifest.get_projects() print(json.dumps(projects_data, indent=4)) + if verbose > 0: - print_projects_data(projects_data) + return print_manifest_json_data(projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_gems(verbose: int) -> None: +def print_gems(verbose: int) -> int: gems_data = manifest.get_gems() print(json.dumps(gems_data, indent=4)) + if verbose > 0: - print_gems_data(gems_data) + return print_manifest_json_data(gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_templates(verbose: int) -> None: +def print_templates(verbose: int) -> int: templates_data = manifest.get_templates() print(json.dumps(templates_data, indent=4)) + if verbose > 0: - print_templates_data(templates_data) + return print_manifest_json_data(templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_restricted(verbose: int) -> None: +def print_restricted(verbose: int) -> int: restricted_data = manifest.get_restricted() print(json.dumps(restricted_data, indent=4)) - if verbose > 0: - print_restricted_data(restricted_data) -def print_engine_projects(verbose: int) -> None: + if verbose > 0: + return print_manifest_json_data(restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 + + +# Engine output methods +def print_engine_projects(verbose: int) -> int: engine_projects_data = manifest.get_engine_projects() print(json.dumps(engine_projects_data, indent=4)) + if verbose > 0: - print_projects_data(engine_projects_data) + return print_manifest_json_data(engine_projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_engine_gems(verbose: int) -> None: +def print_engine_gems(verbose: int) -> int: engine_gems_data = manifest.get_engine_gems() print(json.dumps(engine_gems_data, indent=4)) + if verbose > 0: - print_gems_data(engine_gems_data) + return print_manifest_json_data(engine_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_engine_templates(verbose: int) -> None: +def print_engine_templates(verbose: int) -> int: engine_templates_data = manifest.get_engine_templates() print(json.dumps(engine_templates_data, indent=4)) + if verbose > 0: - print_templates_data(engine_templates_data) + return print_manifest_json_data(engine_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_engine_restricted(verbose: int) -> None: +def print_engine_restricted(verbose: int) -> int: engine_restricted_data = manifest.get_engine_restricted() print(json.dumps(engine_restricted_data, indent=4)) + if verbose > 0: - print_restricted_data(engine_restricted_data) + return print_manifest_json_data(engine_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 -def print_engine_external_subdirectories(verbose: int) -> None: +def print_engine_external_subdirectories() -> int: external_subdirs_data = manifest.get_engine_external_subdirectories() print(json.dumps(external_subdirs_data, indent=4)) + return 0 -def print_all_projects(verbose: int) -> None: +# Project output methods +def print_project_gems(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_gems_data = manifest.get_project_gems(project_path) + print(json.dumps(project_gems_data, indent=4)) + + if verbose > 0: + return print_manifest_json_data(project_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 + + +def print_project_external_subdirectories(project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + external_subdirs_data = manifest.get_project_external_subdirectories(project_path) + print(json.dumps(external_subdirs_data, indent=4)) + return 0 + + +def print_project_templates(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_templates_data = manifest.get_project_templates(project_path) + print(json.dumps(project_templates_data, indent=4)) + if verbose > 0: + return print_manifest_json_data(project_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 + + +def print_project_restricted(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_restricted_data = manifest.get_project_restricted(project_path) + print(json.dumps(project_restricted_data, indent=4)) + if verbose > 0: + return print_manifest_json_data(project_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 + + +def print_all_projects(verbose: int) -> int: all_projects_data = manifest.get_all_projects() print(json.dumps(all_projects_data, indent=4)) + if verbose > 0: - print_projects_data(all_projects_data) + return print_manifest_json_data(all_projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_all_gems(verbose: int) -> None: +def print_all_gems(verbose: int) -> int: all_gems_data = manifest.get_all_gems() print(json.dumps(all_gems_data, indent=4)) + if verbose > 0: - print_gems_data(all_gems_data) + return print_manifest_json_data(all_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_all_templates(verbose: int) -> None: +def print_all_external_subdirectories() -> int: + all_external_subdirectories_data = manifest.get_all_external_subdirectories() + print(json.dumps(all_external_subdirectories_data, indent=4)) + return 0 + +def print_all_templates(verbose: int) -> int: all_templates_data = manifest.get_all_templates() print(json.dumps(all_templates_data, indent=4)) + if verbose > 0: - print_templates_data(all_templates_data) + return print_manifest_json_data(all_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_all_restricted(verbose: int) -> None: +def print_all_restricted(verbose: int) -> int: all_restricted_data = manifest.get_all_restricted() print(json.dumps(all_restricted_data, indent=4)) + if verbose > 0: - print_restricted_data(all_restricted_data) + return print_manifest_json_data(all_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 -def print_engines_data(engines_data: dict) -> None: +def print_manifest_json_data(uri_json_data: dict, json_filename: str, + print_prefix: str, get_json_func: callable, get_json_data_kw: str) -> int: print('\n') - print("Engines================================================") - for engine_object in engines_data: + print(f"{print_prefix}================================================") + for manifest_uri in uri_json_data: # if it's not local it should be in the cache - engine_uri = engine_object['path'] - parsed_uri = urllib.parse.urlparse(engine_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(engine_uri.encode()) + parsed_uri = urllib.parse.urlparse(manifest_uri) + if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: + repo_sha256 = hashlib.sha256(manifest_uri.encode()) cache_folder = manifest.get_o3de_cache_folder() - engine = cache_folder / str(repo_sha256.hexdigest() + '.json') - print(f'{engine_uri}/engine.json cached as:') + manifest_json_path = cache_folder / str(repo_sha256.hexdigest() + '.json') else: - engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json' + manifest_json_path = pathlib.Path(manifest_uri).resolve() / json_filename - with engine_json.open('r') as f: - try: - engine_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{engine_json} failed to load: {str(e)}') - else: - print(engine_json) - print(json.dumps(engine_json_data, indent=4)) - print('\n') + json_data = get_json_func(**{get_json_data_kwargs: manifest_json_path}) + if json_data: + print(manifest_json_path) + print(json.dumps(json_data, indent=4) + '\n') + return 0 -def print_projects_data(projects_data: dict) -> None: - print('\n') - print("Projects================================================") - for project_uri in projects_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(project_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(project_uri.encode()) - cache_folder = manifest.get_o3de_cache_folder() - project_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - project_json = pathlib.Path(project_uri).resolve() / 'project.json' - - with project_json.open('r') as f: - try: - project_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{project_json} failed to load: {str(e)}') - else: - print(project_json) - print(json.dumps(project_json_data, indent=4)) - print('\n') - - -def print_gems_data(gems_data: dict) -> None: - print('\n') - print("Gems================================================") - for gem_uri in gems_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(gem_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(gem_uri.encode()) - cache_folder = manifest.get_o3de_cache_folder() - gem_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - gem_json = pathlib.Path(gem_uri).resolve() / 'gem.json' - - with gem_json.open('r') as f: - try: - gem_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{gem_json} failed to load: {str(e)}') - else: - print(gem_json) - print(json.dumps(gem_json_data, indent=4)) - print('\n') - - -def print_templates_data(templates_data: dict) -> None: - print('\n') - print("Templates================================================") - for template_uri in templates_data: - # if it's not local it should be in the cache - parsed_uri = urllib.parse.urlparse(template_uri) - if parsed_uri.scheme == 'http' or \ - parsed_uri.scheme == 'https' or \ - parsed_uri.scheme == 'ftp' or \ - parsed_uri.scheme == 'ftps': - repo_sha256 = hashlib.sha256(template_uri.encode()) - cache_folder = manifest.get_o3de_cache_folder() - template_json = cache_folder / str(repo_sha256.hexdigest() + '.json') - else: - template_json = pathlib.Path(template_uri).resolve() / 'template.json' - - with template_json.open('r') as f: - try: - template_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{template_json} failed to load: {str(e)}') - else: - print(template_json) - print(json.dumps(template_json_data, indent=4)) - print('\n') - - -def print_repos_data(repos_data: dict) -> None: +def print_repos_data(repos_data: dict) -> int: print('\n') print("Repos================================================") cache_folder = manifest.get_o3de_cache_folder() @@ -251,29 +284,16 @@ def print_repos_data(repos_data: dict) -> None: print(cache_file) print(json.dumps(repo_json_data, indent=4)) print('\n') - - -def print_restricted_data(restricted_data: dict) -> None: - print('\n') - print("Restricted================================================") - for restricted_path in restricted_data: - restricted_json = pathlib.Path(restricted_path).resolve() / 'restricted.json' - with restricted_json.open('r') as f: - try: - restricted_json_data = json.load(f) - except json.JSONDecodeError as e: - logger.warn(f'{restricted_json} failed to load: {str(e)}') - else: - print(restricted_json) - print(json.dumps(restricted_json_data, indent=4)) - print('\n') + return 0 def register_show_repos(verbose: int) -> None: - repos_data = get_repos() + repos_data = manifest.get_repos() print(json.dumps(repos_data, indent=4)) + if verbose > 0: - print_repos_data(repos_data) + return print_repos_data(repos_data) == 0 + return 0 def register_show(verbose: int) -> None: @@ -281,13 +301,15 @@ def register_show(verbose: int) -> None: print(f"{manifest.get_o3de_manifest()}:") print(json.dumps(json_data, indent=4)) + result = True if verbose > 0: - print_engines_data(manifest.get_engines()) - print_projects_data(manifest.get_all_projects()) - print_gems_data(manifest.get_gems()) - print_templates_data(manifest.get_all_templates()) - print_restricted_data(manifest.get_all_restricted()) - print_repos_data(manifest.get_repos()) + result = print_manifest_json_data(manifest.get_engines()) == 0 and result + result = print_manifest_json_data(manifest.get_all_projects()) == 0 and result + result = print_manifest_json_data(manifest.get_gems()) == 0 and result + result = print_manifest_json_data(manifest.get_all_templates()) == 0 and result + result = print_manifest_json_data(manifest.get_all_restricted()) == 0 and result + result = print_repos_data(manifest.get_repos()) == 0 and result + return 0 if result else 1 def _run_register_show(args: argparse) -> int: @@ -295,75 +317,53 @@ def _run_register_show(args: argparse) -> int: manifest.override_home_folder = args.override_home_folder if args.this_engine: - print_this_engine(args.verbose) - return 0 - + return print_this_engine(args.verbose) elif args.engines: - print_engines(args.verbose) - return 0 + return print_engines(args.verbose) elif args.projects: - print_projects(args.verbose) - return 0 + return print_projects(args.verbose) elif args.gems: - print_gems(args.verbose) - return 0 + return print_gems(args.verbose) elif args.templates: - print_templates(args.verbose) - return 0 + return print_templates(args.verbose) elif args.repos: - register_show_repos(args.verbose) - return 0 + return register_show_repos(args.verbose) elif args.restricted: - print_restricted(args.verbose) - return 0 + return print_restricted(args.verbose) elif args.engine_projects: - print_engine_projects(args.verbose) - return 0 + return print_engine_projects(args.verbose) elif args.engine_gems: - print_engine_gems(args.verbose) - return 0 - elif args.engine_templates: - print_engine_templates(args.verbose) - return 0 - elif args.engine_restricted: - print_engine_restricted(args.verbose) - return 0 + return print_engine_gems(args.verbose) elif args.engine_external_subdirectories: - print_engine_external_subdirectories(args.verbose) - return 0 + return print_engine_external_subdirectories() + elif args.engine_templates: + return print_engine_templates(args.verbose) + elif args.engine_restricted: + return print_engine_restricted(args.verbose) + + elif args.project_gems: + return print_project_gems(args.verbose, args.project_path, args.project_name) + elif args.project_external_subdirectories: + return print_project_external_subdirectories(args.project_path, args.project_name) + elif args.project_templates: + return print_project_templates(args.verbose, args.project_path, args.project_name) + elif args.project_restricted: + return print_project_restricted(args.verbose, args.project_path, args.project_name) elif args.all_projects: - print_all_projects(args.verbose) - return 0 + return print_all_projects(args.verbose) elif args.all_gems: - print_all_gems(args.verbose) - return 0 + return print_all_gems(args.verbose) + elif args.all_external_subdirectories: + return print_all_external_subdirectories() elif args.all_templates: - print_all_templates(args.verbose) - return 0 + return print_all_templates(args.verbose) elif args.all_restricted: - print_all_restricted(args.verbose) - return 0 + return print_all_restricted(args.verbose) - elif args.downloadables: - print_downloadables(args.verbose) - return 0 - if args.downloadable_engines: - print_downloadable_engines(args.verbose) - return 0 - elif args.downloadable_projects: - print_downloadable_projects(args.verbose) - return 0 - elif args.downloadable_gems: - print_downloadable_gems(args.verbose) - return 0 - elif args.downloadable_templates: - print_downloadable_templates(args.verbose) - return 0 else: - register_show(args.verbose) - return 0 + return register_show(args.verbose) def add_parser_args(parser): @@ -376,78 +376,84 @@ def add_parser_args(parser): group = parser.add_mutually_exclusive_group(required=False) group.add_argument('-te', '--this-engine', action='store_true', required=False, default=False, - help='Just the local engines.') + help='Output the current engine path.') group.add_argument('-e', '--engines', action='store_true', required=False, default=False, - help='Just the local engines.') + help='Output the engines registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-p', '--projects', action='store_true', required=False, default=False, - help='Just the local projects.') + help='Output the projects registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-g', '--gems', action='store_true', required=False, default=False, - help='Just the local gems.') + help='Output the gems registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-t', '--templates', action='store_true', required=False, default=False, - help='Just the local templates.') + help='Output the templates registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-r', '--repos', action='store_true', required=False, default=False, - help='Just the local repos. Ignores repos.') + help='Output the repos registered in the global ~/.o3de/o3de_manifest.json. Ignores repos.') group.add_argument('-rs', '--restricted', action='store_true', required=False, default=False, - help='The local restricted folders.') + help='Output the restricted directories registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-ep', '--engine-projects', action='store_true', required=False, default=False, - help='Just the local projects. Ignores repos.') + help='Output the projects registered in the current engine engine.json. Ignores repos.') group.add_argument('-eg', '--engine-gems', action='store_true', required=False, default=False, - help='Just the local gems. Ignores repos') + help='Output the gems registered in the current engine engine.json. Ignores repos') group.add_argument('-et', '--engine-templates', action='store_true', required=False, default=False, - help='Just the local templates. Ignores repos.') + help='Output the templates registered in the current engine engine.json. Ignores repos.') group.add_argument('-ers', '--engine-restricted', action='store_true', required=False, default=False, - help='The restricted folders.') - group.add_argument('-x', '--engine-external-subdirectories', action='store_true', required=False, + help='Output the restricted directories registered in the current engine engine.json.') + group.add_argument('-ees', '--engine-external-subdirectories', action='store_true', required=False, default=False, - help='The external subdirectories.') + help='Output the external subdirectories registered in the current engine engine.json.') + + group.add_argument('-pg', '--project-gems', action='store_true', + default=False, + help='Returns the gems registered with the project.json.') + group.add_argument('-pt', '--project-templates', action='store_true', + default=False, + help='Returns the templates registered with the project.json.') + group.add_argument('-prs', '--project-restricted', action='store_true', + default=False, + help='Returns the restricted directories registered with the project.json.') + group.add_argument('-pes', '--project-external-subdirectories', action='store_true', + default=False, + help='Returns the external subdirectories register with the project.json.') group.add_argument('-ap', '--all-projects', action='store_true', required=False, default=False, - help='Just the local projects. Ignores repos.') + help='Output all projects registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.') group.add_argument('-ag', '--all-gems', action='store_true', required=False, default=False, - help='Just the local gems. Ignores repos') + help='Output all gems registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos') group.add_argument('-at', '--all-templates', action='store_true', required=False, default=False, - help='Just the local templates. Ignores repos.') - group.add_argument('-ars', '--all-restricted', action='store_true', required=False, + help='Output all templates registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.') + group.add_argument('-ares', '--all-restricted', action='store_true', required=False, default=False, - help='The restricted folders.') - - group.add_argument('-d', '--downloadables', action='store_true', required=False, + help='Output all restricted directory registered in the ~/.o3de/o3de_manifest.json and the current engine.json.') + group.add_argument('-aes', '--all-external-subdirectories', action='store_true', default=False, - help='Combine all repos into a single list of resources.') - group.add_argument('-de', '--downloadable-engines', action='store_true', required=False, - default=False, - help='Combine all repos engines into a single list of resources.') - group.add_argument('-dp', '--downloadable-projects', action='store_true', required=False, - default=False, - help='Combine all repos projects into a single list of resources.') - group.add_argument('-dg', '--downloadable-gems', action='store_true', required=False, - default=False, - help='Combine all repos gems into a single list of resources.') - group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False, - default=False, - help='Combine all repos templates into a single list of resources.') + help='Output all external subdirectories registered in the ~/.o3de/o3de_manifest.json and the current engine.json.') parser.add_argument('-v', '--verbose', action='count', required=False, - default=0, - help='How verbose do you want the output to be.') + default=0, + help='How verbose do you want the output to be.') + + project_group = parser.add_mutually_exclusive_group(required=False) + project_group.add_argument('-pp', '--project-path', type=pathlib.Path, + help='The path to a project.') + project_group.add_argument('-pn', '--project-name', type=str, + help='The name of a project.') parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, - help='By default the home folder is the user folder, override it to this folder.') + help='By default the home folder is the user folder, override it to this folder.') parser.set_defaults(func=_run_register_show) diff --git a/scripts/o3de/tests/CMakeLists.txt b/scripts/o3de/tests/CMakeLists.txt index 0526c7740d..82b994e387 100644 --- a/scripts/o3de/tests/CMakeLists.txt +++ b/scripts/o3de/tests/CMakeLists.txt @@ -34,3 +34,10 @@ ly_add_pytest( TEST_SUITE smoke EXCLUDE_TEST_RUN_TARGET_FROM_IDE ) + +ly_add_pytest( + NAME o3de_manifest + PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_manifest.py + TEST_SUITE smoke + EXCLUDE_TEST_RUN_TARGET_FROM_IDE +) diff --git a/scripts/o3de/tests/unit_test_manifest.py b/scripts/o3de/tests/unit_test_manifest.py new file mode 100644 index 0000000000..98430c227b --- /dev/null +++ b/scripts/o3de/tests/unit_test_manifest.py @@ -0,0 +1,111 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import json +import logging +import pytest +import pathlib +from unittest.mock import patch + +from o3de import manifest + + +@pytest.mark.parametrize("valid_project_json_paths, valid_gem_json_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject/Template/project.json')], + [pathlib.Path('D:/o3de/Templates/DefaultGem/Template/gem.json')]) +]) +class TestGetTemplatesForCreation: + @staticmethod + def get_templates() -> list: + return [] + + @staticmethod + def get_project_templates() -> list: + return [] + + @staticmethod + def get_engine_templates() -> list: + return [pathlib.Path('D:/o3de/Templates/DefaultProject'), pathlib.Path('D:/o3de/Templates/DefaultGem')] + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([]) + ] + ) + def test_get_templates_for_generic_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates)\ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates)\ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json,\ + patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) as validate_project_json,\ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_generic_creation() + assert templates == expected_template_paths + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject')]) + ] + ) + def test_get_templates_for_gem_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \ + patch('o3de.validation.valid_o3de_project_json', + side_effect=validate_project_json) as validate_project_json, \ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_project_creation() + assert templates == expected_template_paths + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultGem')]) + ] + ) + def test_get_templates_for_project_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \ + patch('o3de.validation.valid_o3de_project_json', + side_effect=validate_project_json) as validate_project_json, \ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_gem_creation() + assert templates == expected_template_paths \ No newline at end of file diff --git a/scripts/o3de/tests/unit_test_utils.py b/scripts/o3de/tests/unit_test_utils.py index e7d59b507b..4fe22b1f72 100755 --- a/scripts/o3de/tests/unit_test_utils.py +++ b/scripts/o3de/tests/unit_test_utils.py @@ -11,7 +11,7 @@ import pytest -from . import utils +from o3de import utils @pytest.mark.parametrize( "value, expected_result", [ From deb3c5e74a9cfa1725c77711bc584952b4231a79 Mon Sep 17 00:00:00 2001 From: cgalvan Date: Thu, 3 Jun 2021 18:20:59 -0500 Subject: [PATCH 072/105] [LYN-2446] Implemented support for duplicating instances. (#1097) * [LYN-2446] Implemented support for duplicating instances. * [LYN-2446] Addressed PR feedback. * [LYN-2446] Addressed additional PR feedback. --- .../Prefab/Instance/Instance.h | 4 +- .../Instance/InstanceUpdateExecutor.cpp | 24 ++ .../Prefab/PrefabPublicHandler.cpp | 290 +++++++++++++----- .../Prefab/PrefabPublicHandler.h | 27 ++ 4 files changed, 259 insertions(+), 86 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 4a69ade6d2..d14203e2e5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -174,6 +174,8 @@ namespace AzToolsFramework static EntityAlias GenerateEntityAlias(); AliasPath GetAbsoluteInstanceAliasPath() const; + static InstanceAlias GenerateInstanceAlias(); + protected: /** * Gets the entities owned by this instance @@ -190,8 +192,6 @@ namespace AzToolsFramework bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias); AZStd::unique_ptr DetachEntity(const EntityAlias& entityAlias); - static InstanceAlias GenerateInstanceAlias(); - // Provide access to private data members in the serializer friend class JsonInstanceSerializer; friend class InstanceEntityIdMapper; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp index e0d29f2ab1..6194adf784 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp @@ -152,6 +152,30 @@ namespace AzToolsFramework Instance::EntityList newEntities; if (PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom())) { + // If a link was created for a nested instance before the changes were propagated, + // then we associate it correctly here + instanceToUpdate->GetNestedInstances([&](AZStd::unique_ptr& nestedInstance) { + if (nestedInstance->GetLinkId() != InvalidLinkId) + { + return; + } + + for (auto linkId : currentTemplate.GetLinks()) + { + LinkReference nestedLink = m_prefabSystemComponentInterface->FindLink(linkId); + if (!nestedLink.has_value()) + { + continue; + } + + if (nestedLink->get().GetInstanceName() == nestedInstance->GetInstanceAlias()) + { + nestedInstance->SetLinkId(linkId); + break; + } + } + }); + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 27c812ff9d..4656dcf48f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -899,16 +899,29 @@ namespace AzToolsFramework if (!EntitiesBelongToSameInstance(entityIds)) { - return AZ::Failure(AZStd::string("Cannot duplicate multiple " - "entities belonging to different instances with one operation.")); + return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation." + "Change your selection to contain entities in the same instance.")); } // We've already verified the entities are all owned by the same instance, // so we can just retrieve our instance from the first entity in the list. - InstanceOptionalReference commonEntityOwningInstance = GetOwnerInstanceByEntityId(entityIds[0]); - AZ_Assert( - commonEntityOwningInstance.has_value(), - "Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided"); + AZ::EntityId firstEntityIdToDuplicate = entityIds[0]; + InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate); + if (!commonOwningInstance.has_value()) + { + return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided.")); + } + + // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you + // cannot duplicate an instance from itself. + if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate) + { + commonOwningInstance = commonOwningInstance->get().GetParentInstance(); + } + if (!commonOwningInstance.has_value()) + { + return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided.")); + } // This will cull out any entities that have ancestors in the list, since we will end up duplicating // the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances @@ -921,105 +934,63 @@ namespace AzToolsFramework { AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities"); - // Take a snapshot of the instance DOM before we manipulate it - Prefab::PrefabDom instanceDomBefore; - m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonEntityOwningInstance->get()); - AZStd::vector entities; AZStd::vector instances; - // Gather all entities/instances in the hierarchy, but don't detach them because we are duplicating not deleting. EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet); - bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonEntityOwningInstance->get(), entities, instances); + bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances); if (!success) { return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication")); } - // Make a copy of our before instance DOM where we will add our duplicated entities - Prefab::PrefabDom instanceDomAfter; + // Take a snapshot of the instance DOM before we manipulate it + PrefabDom instanceDomBefore; + m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get()); + + // Make a copy of our before instance DOM where we will add our duplicated entities and/or instances + PrefabDom instanceDomAfter; instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator()); - AZStd::unordered_map oldAliasToNewAliasMap; - AZStd::unordered_map aliasToEntityDomMap; + EntityIdList duplicatedEntityAndInstanceIds; - for (AZ::Entity* entity : entities) - { - EntityAliasOptionalReference oldAliasRef = commonEntityOwningInstance->get().GetEntityAlias(entity->GetId()); - AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM"); - EntityAlias oldAlias = oldAliasRef.value(); + // Duplicate any nested entities and instances as requested + AZStd::unordered_map newInstanceAliasToOldInstanceMap; + DuplicateNestedEntitiesInInstance(commonOwningInstance->get(), + entities, instanceDomAfter, duplicatedEntityAndInstanceIds); + DuplicateNestedInstancesInInstance(commonOwningInstance->get(), + instances, instanceDomAfter, duplicatedEntityAndInstanceIds, + newInstanceAliasToOldInstanceMap); - // Give this the outer allocator so that the memory reference will be valid when - // it gets used for AddMember - Prefab::PrefabDom entityDomBefore(&instanceDomAfter.GetAllocator()); - m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity); - - // Keep track of the old alias <-> new alias mapping for this duplicated entity - // so we can fixup references later - EntityAlias newEntityAlias = Instance::GenerateEntityAlias(); - oldAliasToNewAliasMap.insert(AZStd::make_pair(oldAlias, newEntityAlias)); - - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - entityDomBefore.Accept(writer); - - // Store our duplicated Entity DOM with its new alias as a string - // so that we can fixup entity alias references before adding it - // to the Entities member of our instance DOM - QString entityDomString(buffer.GetString()); - aliasToEntityDomMap.insert(AZStd::make_pair(newEntityAlias, entityDomString)); - } - - auto entitiesIter = instanceDomAfter.FindMember(PrefabDomUtils::EntitiesName); - AZ_Assert(entitiesIter != instanceDomAfter.MemberEnd(), "Instance DOM missing the Entities member."); - - // Now that all the duplicated Entity DOMs have been created, we need to iterate - // through them and replace any previous EntityAlias references with the new ones. - // These are more than just parent entity references for nested entities, this will - // also cover any EntityId references that were made in the components between them. - for (auto aliasEntityPair : aliasToEntityDomMap) - { - EntityAlias newEntityAlias = aliasEntityPair.first; - QString newEntityDomString = aliasEntityPair.second; - - // Replace all of the old alias references with the new ones - // We bookend the aliases with \" and also with a / as an extra precaution to prevent - // inadvertently replacing a matching string vs. where an actual EntityId is expected - // This will cover both cases where an alias could be used in a normal entity vs. an instance - for (auto aliasMapIter : oldAliasToNewAliasMap) - { - ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second); - } - - // Create the new Entity DOM from parsing the JSON string - Prefab::PrefabDom entityDomAfter(&instanceDomAfter.GetAllocator()); - entityDomAfter.Parse(newEntityDomString.toUtf8().constData()); - - // Add the new Entity DOM to the Entities member of the instance - rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), instanceDomAfter.GetAllocator()); - entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, instanceDomAfter.GetAllocator()); - } - - PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity duplication"); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); command->SetParent(undoBatch.GetUndoBatch()); - command->Capture(instanceDomBefore, instanceDomAfter, commonEntityOwningInstance->get().GetTemplateId()); - command->RunRedo(); + command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); + command->Redo(); - EntityIdList duplicatedEntityIds; - for (auto aliasMapIter : oldAliasToNewAliasMap) + // Create links for our duplicated instances (if any were duplicated) + for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap) { - EntityAlias newEntityAlias = aliasMapIter.second; + LinkId oldLinkId = oldInstance->GetLinkId(); + auto linkRef = m_prefabSystemComponentInterface->FindLink(oldLinkId); + AZ_Assert( + linkRef.has_value(), "Unable to find link with id '%llu' during instance duplication.", + oldLinkId); - AliasPath absoluteEntityPath = commonEntityOwningInstance->get().GetAbsoluteInstanceAliasPath(); - absoluteEntityPath.Append(newEntityAlias); + PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches(); + AZ_Assert( + linkPatches.has_value(), "Link with id '%llu' is missing patches.", + oldLinkId); - AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath); - duplicatedEntityIds.push_back(newEntityId); + PrefabDom linkPatchesCopy; + linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator()); + + m_prefabSystemComponentInterface->CreateLink( + commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy); } - // Select the duplicated entities - auto selectionUndo = aznew SelectionCommand(duplicatedEntityIds, "Select Duplicated Entities"); + // Select the duplicated entities/instances + auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances"); selectionUndo->SetParent(undoBatch.GetUndoBatch()); ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo); } @@ -1508,8 +1479,159 @@ namespace AzToolsFramework return true; } + void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance, + const AZStd::vector& entities, PrefabDom& domToAddDuplicatedEntitiesUnder, + EntityIdList& duplicatedEntityIds) + { + if (entities.empty()) + { + return; + } + + AZStd::unordered_map oldAliasToNewAliasMap; + AZStd::unordered_map aliasToEntityDomMap; + + for (AZ::Entity* entity : entities) + { + EntityAliasOptionalReference oldAliasRef = commonOwningInstance.GetEntityAlias(entity->GetId()); + AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM"); + EntityAlias oldAlias = oldAliasRef.value(); + + // Give this the outer allocator so that the memory reference will be valid when + // it gets used for AddMember + PrefabDom entityDomBefore(&domToAddDuplicatedEntitiesUnder.GetAllocator()); + m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity); + + // Keep track of the old alias <-> new alias mapping for this duplicated entity + // so we can fixup references later + EntityAlias newEntityAlias = Instance::GenerateEntityAlias(); + oldAliasToNewAliasMap.emplace(oldAlias, newEntityAlias); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + entityDomBefore.Accept(writer); + + // Store our duplicated Entity DOM with its new alias as a string + // so that we can fixup entity alias references before adding it + // to the Entities member of our instance DOM + QString entityDomString(buffer.GetString()); + aliasToEntityDomMap.emplace(newEntityAlias, entityDomString); + } + + auto entitiesIter = domToAddDuplicatedEntitiesUnder.FindMember(PrefabDomUtils::EntitiesName); + AZ_Assert(entitiesIter != domToAddDuplicatedEntitiesUnder.MemberEnd(), "Instance DOM missing the Entities member."); + + // Now that all the duplicated Entity DOMs have been created, we need to iterate + // through them and replace any previous EntityAlias references with the new ones. + // These are more than just parent entity references for nested entities, this will + // also cover any EntityId references that were made in the components between them. + for (auto [newEntityAlias, newEntityDomString] : aliasToEntityDomMap) + { + // Replace all of the old alias references with the new ones + for (auto [oldAlias, newAlias] : oldAliasToNewAliasMap) + { + ReplaceOldAliases(newEntityDomString, oldAlias, newAlias); + } + + // Create the new Entity DOM from parsing the JSON string + PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator()); + entityDomAfter.Parse(newEntityDomString.toUtf8().constData()); + + // Add the new Entity DOM to the Entities member of the instance + rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), domToAddDuplicatedEntitiesUnder.GetAllocator()); + entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator()); + } + + for (auto aliasMapIter : oldAliasToNewAliasMap) + { + EntityAlias newEntityAlias = aliasMapIter.second; + + AliasPath absoluteEntityPath = commonOwningInstance.GetAbsoluteInstanceAliasPath(); + absoluteEntityPath.Append(newEntityAlias); + + AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath); + duplicatedEntityIds.push_back(newEntityId); + } + } + + void PrefabPublicHandler::DuplicateNestedInstancesInInstance(Instance& commonOwningInstance, + const AZStd::vector& instances, PrefabDom& domToAddDuplicatedInstancesUnder, + EntityIdList& duplicatedEntityIds, AZStd::unordered_map& newInstanceAliasToOldInstanceMap) + { + if (instances.empty()) + { + return; + } + + AZStd::unordered_map oldInstanceAliasToNewInstanceAliasMap; + AZStd::unordered_map aliasToInstanceDomMap; + + for (auto instance : instances) + { + PrefabDom nestedInstanceDomBefore; + m_instanceToTemplateInterface->GenerateDomForInstance(nestedInstanceDomBefore, *instance); + + // Keep track of the old alias <-> new alias mapping for this duplicated instance + // so we can fixup references later + InstanceAlias oldAlias = instance->GetInstanceAlias(); + InstanceAlias newInstanceAlias = Instance::GenerateInstanceAlias(); + oldInstanceAliasToNewInstanceAliasMap.emplace(oldAlias, newInstanceAlias); + + // Keep track of our new instance alias with the Instance it was duplicated from, + // so that after all instances are duplicated, we can go back and create links for them + newInstanceAliasToOldInstanceMap.emplace(newInstanceAlias, instance); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + nestedInstanceDomBefore.Accept(writer); + + // Store our duplicated Instance DOM with its new alias as a string + // so that we can fixup instance alias references before adding it + // to the Instances member of our instance DOM + QString instanceDomString(buffer.GetString()); + aliasToInstanceDomMap.emplace(newInstanceAlias, instanceDomString); + } + + auto instancesIter = domToAddDuplicatedInstancesUnder.FindMember(PrefabDomUtils::InstancesName); + AZ_Assert(instancesIter != domToAddDuplicatedInstancesUnder.MemberEnd(), "Instance DOM missing the Instances member."); + + // Now that all the duplicated Instance DOMs have been created, we need to iterate + // through them and replace any previous InstanceAlias references with the new ones. + for (auto [newInstanceAlias, newInstanceDomString]: aliasToInstanceDomMap) + { + // Replace all of the old alias references with the new ones + for (auto [oldAlias, newAlias] : oldInstanceAliasToNewInstanceAliasMap) + { + ReplaceOldAliases(newInstanceDomString, oldAlias, newAlias); + } + + // Create the new Instance DOM from parsing the JSON string + PrefabDom nestedInstanceDomAfter(&domToAddDuplicatedInstancesUnder.GetAllocator()); + nestedInstanceDomAfter.Parse(newInstanceDomString.toUtf8().constData()); + + // Add the new Instance DOM to the Instances member of the instance + rapidjson::Value aliasName(newInstanceAlias.c_str(), newInstanceAlias.length(), domToAddDuplicatedInstancesUnder.GetAllocator()); + instancesIter->value.AddMember(AZStd::move(aliasName), nestedInstanceDomAfter, domToAddDuplicatedInstancesUnder.GetAllocator()); + } + + for (auto aliasMapIter : oldInstanceAliasToNewInstanceAliasMap) + { + InstanceAlias newInstanceAlias = aliasMapIter.second; + + AliasPath absoluteInstancePath = commonOwningInstance.GetAbsoluteInstanceAliasPath(); + absoluteInstancePath.Append(newInstanceAlias); + + AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath); + duplicatedEntityIds.push_back(newEntityId); + } + } + void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias) { + // Replace all of the old alias references with the new ones + // We bookend the aliases with \" and also with a / as an extra precaution to prevent + // inadvertently replacing a matching string vs. where an actual EntityId is expected + // This will cover both cases where an alias could be used in a normal entity vs. an instance QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data()); QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 99fe8e5b67..167791d1c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -73,6 +73,33 @@ namespace AzToolsFramework InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; + + /** + * Duplicate a list of entities owned by a common owning instance by directly + * copying/modifying their entries in the instance DOM + * + * \param commonOwningInstance The common owning instance of all the entities being duplicated. + * \param entities The list of Entities that will be duplicated. + * \param domToAddDuplicatedEntitiesUnder The DOM of the common owning instance where the duplicated + * entity DOM values will be added to. + * \param duplicatedEntityIds A list of EntityIds corresponding to the entities that were duplicated. + */ + void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance, + const AZStd::vector& entities, PrefabDom& domToAddDuplicatedEntitiesUnder, + EntityIdList& duplicatedEntityIds); + /** + * Duplicate a list of instances owned by a common owning instance by directly + * copying/modifying their entries in the instance DOM + * + * \param commonOwningInstance The common owning instance of all the instances being duplicated. + * \param entities The list of Instances that will be duplicated. + * \param domToAddDuplicatedInstancesUnder The DOM of the common owning instance where the duplicated + * instance DOM values will be added to. + * \param duplicatedEntityIds A list of EntityIds corresponding to the instances that were duplicated. + */ + void DuplicateNestedInstancesInInstance(Instance& commonOwningInstance, + const AZStd::vector& instances, PrefabDom& domToAddDuplicatedInstancesUnder, + EntityIdList& duplicatedEntityIds, AZStd::unordered_map& newInstanceAliasToOldInstanceMap); /** * Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch. From 08a2e50ee34ad1a77009334552a1bd94a59ec7a3 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 16:28:15 -0700 Subject: [PATCH 073/105] [default_3rdparty] missed updating a call to register --- Code/Tools/ProjectManager/Source/PythonBindings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 837235cedc..ee4e30846b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -572,6 +572,7 @@ namespace O3DE::ProjectManager pybind11::none(), // default_gems_folder pybind11::none(), // default_templates_folder pybind11::none(), // default_restricted_folder + pybind11::none(), // default_third_party_folder pybind11::none(), // external_subdir_engine_path pybind11::none(), // external_subdir_project_path true, // remove From 29ac17a090912f483ddb1d6853eea18af8058324 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:05:05 -0700 Subject: [PATCH 074/105] SPEC-7135 Provide a method to re-trigger compiler detection for changes that require it (#1092) * SPEC-7135 Provide a method to re-trigger compiler detection for changes that require it * removing check (is wrong and is not necessary) * Invert existance check * add empty lines at the end * Clean is triggering on each build * clearing if the variable is false * test code to understand what is going on... * yeap, looks good * handling "false" in linux/mac * fix for linux/mac * Fixing typos --- scripts/build/Platform/Linux/build_linux.sh | 3 +- scripts/build/Platform/Linux/clean_linux.sh | 37 ++++++++++++++++- scripts/build/Platform/Mac/build_mac.sh | 1 + scripts/build/Platform/Mac/clean_mac.sh | 37 ++++++++++++++++- .../build/Platform/Windows/build_windows.cmd | 3 +- .../build/Platform/Windows/clean_windows.cmd | 41 ++++++++++++++++++- 6 files changed, 116 insertions(+), 6 deletions(-) diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index d96fd32371..ef1b1dcb96 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -14,6 +14,7 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_linux.sh +source $BASEDIR/clean_linux.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} @@ -46,4 +47,4 @@ fi echo [ci_build] cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(grep -c processor /proc/cpuinfo) -- ${CMAKE_NATIVE_BUILD_ARGS} cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(grep -c processor /proc/cpuinfo) -- ${CMAKE_NATIVE_BUILD_ARGS} -popd \ No newline at end of file +popd diff --git a/scripts/build/Platform/Linux/clean_linux.sh b/scripts/build/Platform/Linux/clean_linux.sh index a21527d319..a314859877 100755 --- a/scripts/build/Platform/Linux/clean_linux.sh +++ b/scripts/build/Platform/Linux/clean_linux.sh @@ -12,6 +12,16 @@ set -o errexit # exit on the first failure encountered +# Jenkins defines environment variables for parameters and passes "false" to variables +# that are not set. Here we clear them if they are false so we can also just define them +# from command line +if [[ "${CLEAN_ASSETS}" == "false" ]]; then + CLEAN_ASSETS= +fi +if [[ "${CLEAN_OUTPUT_DIRECTORY}" == "false" ]]; then + CLEAN_OUTPUT_DIRECTORY= +fi + if [[ -n "$CLEAN_ASSETS" ]]; then echo "[ci_build] CLEAN_ASSETS option set" for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") @@ -23,10 +33,35 @@ if [[ -n "$CLEAN_ASSETS" ]]; then done fi +# If the node label changes, we issue a clean output since node changes can change SDK/CMake/toolchains/etc +LAST_CONFIGURE_NODE_LABEL_FILE=ci_last_node_label.txt +if [[ -n "$NODE_LABEL" ]]; then + if [[ -d $OUTPUT_DIRECTORY ]]; then + pushd $OUTPUT_DIRECTORY + if [[ -e ${LAST_CONFIGURE_NODE_LABEL_FILE} ]]; then + LAST_NODE_LABEL=$(<${LAST_CONFIGURE_NODE_LABEL_FILE}) + else + LAST_NODE_LABEL= + fi + # Detect if the node label has changed + if [[ "${LAST_NODE_LABEL}" != "${NODE_LABEL}" ]]; then + echo [ci_build] Last run was done with node label \"${LAST_NODE_LABEL}\", new node label is \"${NODE_LABEL}\", forcing CLEAN_OUTPUT_DIRECTORY + CLEAN_OUTPUT_DIRECTORY=1 + fi + popd + fi +fi + if [[ -n "$CLEAN_OUTPUT_DIRECTORY" ]]; then echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set" if [[ -d $OUTPUT_DIRECTORY ]]; then echo "[ci_build] Deleting \"${OUTPUT_DIRECTORY}\"" rm -rf ${OUTPUT_DIRECTORY} fi -fi \ No newline at end of file +fi + +mkdir -p ${OUTPUT_DIRECTORY} +# Save the node label +pushd $OUTPUT_DIRECTORY +echo "${NODE_LABEL}" > ${LAST_CONFIGURE_NODE_LABEL_FILE} +popd diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index 473a968d98..4a61f97fe4 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -14,6 +14,7 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_mac.sh +source $BASEDIR/clean_mac.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Mac/clean_mac.sh b/scripts/build/Platform/Mac/clean_mac.sh index a21527d319..a314859877 100755 --- a/scripts/build/Platform/Mac/clean_mac.sh +++ b/scripts/build/Platform/Mac/clean_mac.sh @@ -12,6 +12,16 @@ set -o errexit # exit on the first failure encountered +# Jenkins defines environment variables for parameters and passes "false" to variables +# that are not set. Here we clear them if they are false so we can also just define them +# from command line +if [[ "${CLEAN_ASSETS}" == "false" ]]; then + CLEAN_ASSETS= +fi +if [[ "${CLEAN_OUTPUT_DIRECTORY}" == "false" ]]; then + CLEAN_OUTPUT_DIRECTORY= +fi + if [[ -n "$CLEAN_ASSETS" ]]; then echo "[ci_build] CLEAN_ASSETS option set" for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") @@ -23,10 +33,35 @@ if [[ -n "$CLEAN_ASSETS" ]]; then done fi +# If the node label changes, we issue a clean output since node changes can change SDK/CMake/toolchains/etc +LAST_CONFIGURE_NODE_LABEL_FILE=ci_last_node_label.txt +if [[ -n "$NODE_LABEL" ]]; then + if [[ -d $OUTPUT_DIRECTORY ]]; then + pushd $OUTPUT_DIRECTORY + if [[ -e ${LAST_CONFIGURE_NODE_LABEL_FILE} ]]; then + LAST_NODE_LABEL=$(<${LAST_CONFIGURE_NODE_LABEL_FILE}) + else + LAST_NODE_LABEL= + fi + # Detect if the node label has changed + if [[ "${LAST_NODE_LABEL}" != "${NODE_LABEL}" ]]; then + echo [ci_build] Last run was done with node label \"${LAST_NODE_LABEL}\", new node label is \"${NODE_LABEL}\", forcing CLEAN_OUTPUT_DIRECTORY + CLEAN_OUTPUT_DIRECTORY=1 + fi + popd + fi +fi + if [[ -n "$CLEAN_OUTPUT_DIRECTORY" ]]; then echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set" if [[ -d $OUTPUT_DIRECTORY ]]; then echo "[ci_build] Deleting \"${OUTPUT_DIRECTORY}\"" rm -rf ${OUTPUT_DIRECTORY} fi -fi \ No newline at end of file +fi + +mkdir -p ${OUTPUT_DIRECTORY} +# Save the node label +pushd $OUTPUT_DIRECTORY +echo "${NODE_LABEL}" > ${LAST_CONFIGURE_NODE_LABEL_FILE} +popd diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 3e995e1905..474c1720df 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -13,6 +13,7 @@ REM SETLOCAL EnableDelayedExpansion CALL %~dp0env_windows.cmd +CALL %~dp0clean_windows.cmd IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. @@ -70,4 +71,4 @@ EXIT /b 0 :error POPD -EXIT /b 1 \ No newline at end of file +EXIT /b 1 diff --git a/scripts/build/Platform/Windows/clean_windows.cmd b/scripts/build/Platform/Windows/clean_windows.cmd index 38c1d45c21..60ad445d4d 100644 --- a/scripts/build/Platform/Windows/clean_windows.cmd +++ b/scripts/build/Platform/Windows/clean_windows.cmd @@ -12,6 +12,16 @@ REM SETLOCAL EnableDelayedExpansion +REM Jenkins defines environment variables for parameters and passes "false" to variables +REM that are not set. Here we clear them if they are false so we can also just define them +REM from command line +IF "%CLEAN_ASSETS%"=="false" ( + set CLEAN_ASSETS= +) +IF "%CLEAN_OUTPUT_DIRECTORY%"=="false" ( + set CLEAN_OUTPUT_DIRECTORY= +) + IF DEFINED CLEAN_ASSETS ( ECHO [ci_build] CLEAN_ASSETS option set FOR %%P in (%CMAKE_LY_PROJECTS%) do ( @@ -19,7 +29,26 @@ IF DEFINED CLEAN_ASSETS ( ECHO [ci_build] Deleting "%%P\Cache" DEL /s /q /f %%P\Cache 1>nul ) - ) + ) +) + +REM If the node label changes, we issue a clean output since node changes can change SDK/CMake/toolchains/etc +SET LAST_CONFIGURE_NODE_LABEL_FILE=ci_last_node_label.txt +IF DEFINED NODE_LABEL ( + IF EXIST %OUTPUT_DIRECTORY% ( + PUSHD %OUTPUT_DIRECTORY% + IF EXIST !LAST_CONFIGURE_NODE_LABEL_FILE! ( + FOR /F "delims=" %%x in (%LAST_CONFIGURE_NODE_LABEL_FILE%) DO SET LAST_NODE_LABEL=%%x + ) ELSE ( + SET LAST_NODE_LABEL= + ) + REM Detect if the node label has changed + IF !LAST_NODE_LABEL! NEQ !NODE_LABEL! ( + ECHO [ci_build] Last run was done with node label "!LAST_NODE_LABEL!", new node label is "!NODE_LABEL!", forcing CLEAN_OUTPUT_DIRECTORY + SET CLEAN_OUTPUT_DIRECTORY=1 + ) + POPD + ) ) IF DEFINED CLEAN_OUTPUT_DIRECTORY ( @@ -28,4 +57,12 @@ IF DEFINED CLEAN_OUTPUT_DIRECTORY ( ECHO [ci_build] Deleting "%OUTPUT_DIRECTORY%" DEL /s /q /f %OUTPUT_DIRECTORY% 1>nul ) -) \ No newline at end of file +) + +IF NOT EXIST "%OUTPUT_DIRECTORY%" ( + MKDIR %OUTPUT_DIRECTORY%. +) +REM Save the node label +PUSHD %OUTPUT_DIRECTORY% +ECHO !NODE_LABEL!> !LAST_CONFIGURE_NODE_LABEL_FILE! +POPD From 4405c2275fdc38e04d3d075eaf5e4c8a0a56ea86 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 18:23:01 -0700 Subject: [PATCH 075/105] [default_3rdparty] changed CLI argument type to pathlib.Path and removed optional str type from usage --- scripts/o3de/o3de/register.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 2e37f04acf..4e73edca7f 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -521,7 +521,7 @@ def register_default_restricted_folder(json_data: dict, 'default_restricted_folder') def register_default_third_party_folder(json_data: dict, - default_third_party_folder: str or pathlib.Path, + default_third_party_folder: pathlib.Path, remove: bool = False) -> int: return register_default_o3de_object_folder(json_data, manifest.get_o3de_third_party_folder() if remove else default_third_party_folder, @@ -539,7 +539,7 @@ def register(engine_path: str or pathlib.Path = None, default_gems_folder: str or pathlib.Path = None, default_templates_folder: str or pathlib.Path = None, default_restricted_folder: str or pathlib.Path = None, - default_third_party_folder: str or pathlib.Path = None, + default_third_party_folder: pathlib.Path = None, external_subdir_engine_path: pathlib.Path = None, external_subdir_project_path: pathlib.Path = None, remove: bool = False, @@ -628,7 +628,7 @@ def register(engine_path: str or pathlib.Path = None, elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): result = register_default_restricted_folder(json_data, default_restricted_folder, remove) - elif isinstance(default_third_party_folder, str) or isinstance(default_third_party_folder, pathlib.PurePath): + elif default_third_party_folder: result = register_default_third_party_folder(json_data, default_third_party_folder, remove) # engine is done LAST @@ -825,7 +825,7 @@ def add_parser_args(parser): help='The default templates folder to register/remove.') group.add_argument('-drf', '--default-restricted-folder', type=str, required=False, help='The default restricted folder to register/remove.') - group.add_argument('-dtpf', '--default-third-party-folder', type=str, required=False, + group.add_argument('-dtpf', '--default-third-party-folder', type=pathlib.Path, required=False, help='The default 3rd Party folder to register/remove.') group.add_argument('-u', '--update', action='store_true', required=False, default=False, From 9e7f8e45ebb073c3cb58bb2157d7368fb77f96d0 Mon Sep 17 00:00:00 2001 From: scottr Date: Thu, 3 Jun 2021 18:23:54 -0700 Subject: [PATCH 076/105] [default_3rdparty] fixed typo --- Code/Tools/ProjectManager/Source/PythonBindings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index ee4e30846b..37e636caef 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -421,7 +421,7 @@ namespace O3DE::ProjectManager pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString(); - pybind11::str defaultThridPartyFolder = engineInfo.m_thirdPartyPath.toStdString(); + pybind11::str defaultThirdPartyFolder = engineInfo.m_thirdPartyPath.toStdString(); auto registrationResult = m_register.attr("register")( enginePath, // engine_path @@ -436,7 +436,7 @@ namespace O3DE::ProjectManager defaultGemsFolder, defaultTemplatesFolder, pybind11::none(), // default_restricted_folder - defaultThridPartyFolder + defaultThirdPartyFolder ); if (registrationResult.cast() != 0) From 2449a9322d20d8e91afd4b7cb8813fbcd628429f Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Thu, 3 Jun 2021 18:40:44 -0700 Subject: [PATCH 077/105] Changed the occlusion culling plane model to be on the XZ plane and adjusted the corner point computations --- .../Common/Assets/Models/OcclusionCullingPlane.fbx | 4 ++-- .../OcclusionCullingPlaneFeatureProcessor.cpp | 10 +++++----- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx index b274bfa282..f91d1015f9 100644 --- a/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx +++ b/Gems/Atom/Feature/Common/Assets/Models/OcclusionCullingPlane.fbx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a1f8d75dcd85e8b4aa57f6c0c81af0300ff96915ba3c2b591095c215d5e1d8c -size 12072 +oid sha256:75cdf73fcb9698a76a38294a1cf927a4fb41a34869e0429e1f02bf8d361a7258 +size 20400 diff --git a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp index ff7c32ba08..3c61f616d9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp @@ -64,18 +64,18 @@ namespace AZ RPI::CullingScene::OcclusionPlane rpiOcclusionPlane; - static const Vector3 BL = Vector3(-0.5f, -0.5f, 0.0f); - static const Vector3 BR = Vector3(0.5f, -0.5f, 0.0f); - static const Vector3 TL = Vector3(-0.5f, 0.5f, 0.0f); - static const Vector3 TR = Vector3(0.5f, 0.5f, 0.0f); + static const Vector3 BL = Vector3(-0.5f, 0.0f, -0.5f); + static const Vector3 TL = Vector3(-0.5f, 0.0f, 0.5f); + static const Vector3 TR = Vector3( 0.5f, 0.0f, 0.5f); + static const Vector3 BR = Vector3( 0.5f, 0.0f, -0.5f); const AZ::Transform& transform = occlusionCullingPlane->GetTransform(); // convert corners to world space rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL); - rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL); rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR); + rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR); // build world space AABB AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index 2a9c133b5c..2354a4feea 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -219,9 +219,9 @@ namespace AZ { // World space corners of the occluson plane Vector3 m_cornerBL; - Vector3 m_cornerBR; Vector3 m_cornerTL; Vector3 m_cornerTR; + Vector3 m_cornerBR; Aabb m_aabb; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 7ee2c4a8d2..9f0a17f294 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -574,18 +574,18 @@ namespace AZ { // convert to clip-space Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL); - Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL); Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR); + Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR); // store to float array float verts[16]; projectedBL.StoreToFloat4(&verts[0]); - projectedBR.StoreToFloat4(&verts[4]); - projectedTL.StoreToFloat4(&verts[8]); - projectedTR.StoreToFloat4(&verts[12]); + projectedTL.StoreToFloat4(&verts[4]); + projectedTR.StoreToFloat4(&verts[8]); + projectedBR.StoreToFloat4(&verts[12]); - static uint32_t indices[6] = { 0, 2, 1, 2, 3, 1 }; + static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 }; // render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE); From fe98c34f5060ffe9053932eb0b71838d494787dc Mon Sep 17 00:00:00 2001 From: rhongAMZ <69218254+rhongAMZ@users.noreply.github.com> Date: Thu, 3 Jun 2021 19:59:29 -0700 Subject: [PATCH 078/105] EMFX - Refactor the emfx actor asset loading. (#1101) Refactor emfx asset loading. The mesh asset, skinMetaAsset and morphTargetMeta asset are part of the dependency. --- .../CommandSystem/Source/ImporterCommands.cpp | 5 +- .../RCExt/Actor/ActorGroupExporter.cpp | 52 +++-- .../Pipeline/RCExt/Actor/ActorGroupExporter.h | 3 + .../EMotionFX/Code/EMotionFX/Source/Actor.cpp | 183 ++++++------------ Gems/EMotionFX/Code/EMotionFX/Source/Actor.h | 43 ++-- .../Source/Integration/Assets/ActorAsset.cpp | 3 + .../Integration/Components/ActorComponent.cpp | 46 +---- .../Integration/Components/ActorComponent.h | 4 - .../Components/EditorActorComponent.cpp | 39 +--- .../Editor/Components/EditorActorComponent.h | 4 - 10 files changed, 137 insertions(+), 245 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp index ff05b603c0..08e1ddbb08 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ImporterCommands.cpp @@ -94,8 +94,9 @@ namespace CommandSystem return false; } - actor->LoadRemainingAssets(); - actor->CheckFinalizeActor(); + // Because the actor is directly loaded from disk (without going through an actor asset), we need to ask for a blocking + // load for the asset that actor is depend on. + actor->Finalize(EMotionFX::Actor::LoadRequirement::RequireBlockingLoad); // set the actor id in case we have specified it as parameter if (actorID != MCORE_INVALIDINDEX32) diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp index c7c6b86aea..d5f53e72c5 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.cpp @@ -52,7 +52,7 @@ namespace EMotionFX AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } @@ -117,21 +117,6 @@ namespace EMotionFX ExporterLib::SaveActor(filename, m_actor.get(), MCore::Endian::ENDIAN_LITTLE, GetMeshAssetId(context)); -#ifdef EMOTIONFX_ACTOR_DEBUG - // Use there line to create a log file and inspect detail debug info - AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(filename.c_str(), folderPath); - AZStd::string logFilename = folderPath; - logFilename += "EMotionFXExporter_Log.txt"; - MCore::GetLogManager().CreateLogFile(logFilename.c_str()); - EMotionFX::GetImporter().SetLogDetails(true); - filename += ".xac"; - - // use this line to load the actor from the saved actor file - EMotionFX::Actor* testLoadingActor = EMotionFX::GetImporter().LoadActor(AZStd::string(filename.c_str())); - MCore::Destroy(testLoadingActor); -#endif // EMOTIONFX_ACTOR_DEBUG - static AZ::Data::AssetType emotionFXActorAssetType("{F67CC648-EA51-464C-9F5D-4A9CE41A7F86}"); // from ActorAsset.h in EMotionFX Gem AZ::SceneAPI::Events::ExportProduct& product = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), emotionFXActorAssetType, AZStd::nullopt, AZStd::nullopt); @@ -141,6 +126,26 @@ namespace EMotionFX product.m_legacyPathDependencies.emplace_back(AZStd::move(materialPathReference)); } + // Mesh asset, skin meta asset and morph target meta asset are sub assets for actor asset. + // In here we set them as the dependency of the actor asset. That make sure those assets get automatically loaded before actor asset. + // Default to the first product until we are able to establish a link between mesh and actor (ATOM-13590). + const AZ::Data::AssetType assetDependencyList[] = { + azrtti_typeid(), + azrtti_typeid(), + azrtti_typeid() + }; + + for (const AZ::Data::AssetType& assetDependency : assetDependencyList) + { + AZStd::optional result = GetFirstProductByType(context, assetDependency); + if (result != AZStd::nullopt) + { + AZ::SceneAPI::Events::ExportProduct exportProduct = result.value(); + exportProduct.m_dependencyFlags = AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad); + product.m_productDependencies.emplace_back(exportProduct); + } + } + return SceneEvents::ProcessingResult::Success; } @@ -171,5 +176,20 @@ namespace EMotionFX return AZStd::nullopt; } + + AZStd::optional ActorGroupExporter::GetFirstProductByType( + const ActorGroupExportContext& context, AZ::Data::AssetType type) + { + const AZStd::vector& products = context.m_products.GetProducts(); + for (const AZ::SceneAPI::Events::ExportProduct& product : products) + { + if (product.m_assetType == type) + { + return product; + } + } + + return AZStd::nullopt; + } } // namespace Pipeline } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h index 451d1f4bb4..585126c07b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h +++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/RCExt/Actor/ActorGroupExporter.h @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -44,6 +45,8 @@ namespace EMotionFX //! Get the mesh asset id to which the actor is linked to by default. AZStd::optional GetMeshAssetId(const ActorGroupExportContext& context) const; + static AZStd::optional GetFirstProductByType( + const ActorGroupExportContext& context, AZ::Data::AssetType type); AutoRegisteredActor m_actor; AZStd::vector m_actorMaterialReferences; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp index 403e7ec05a..67f0773aec 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.cpp @@ -125,7 +125,6 @@ namespace EMotionFX Actor::~Actor() { - AZ::Data::AssetBus::MultiHandler::BusDisconnect(); ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorDestroyed, this); GetEventManager().OnDeleteActor(this); @@ -1463,108 +1462,78 @@ namespace EMotionFX return morphTargetMetaAssetInfo.m_assetId.IsValid(); } - void Actor::OnAssetReady(AZ::Data::Asset asset) - { - if (asset == m_meshAsset) - { - m_meshAsset = asset; - } - if (asset == m_skinMetaAsset) - { - m_skinMetaAsset = asset; - } - if (asset == m_morphTargetMetaAsset) - { - m_morphTargetMetaAsset = asset; - } - - CheckFinalizeActor(); - } - - void Actor::CheckFinalizeActor() + void Actor::Finalize(LoadRequirement loadReq) { AZStd::scoped_lock lock(m_mutex); - if (m_meshAsset.IsReady()) + // Load the mesh asset, skin meta asset and morph target asset. + // Those sub assets should have already been setup as dependency of actor asset, so they should already be loaded when we reach here. + // Only exception is that when the actor is not loaded by an actor asset, for which we need to do a blocking load. + if (m_meshAssetId.IsValid()) { - const AZ::Data::AssetId meshAssetId = m_meshAsset.GetId(); - const bool skinMetaAssetExists = DoesSkinMetaAssetExist(meshAssetId); - const bool morphTargetMetaAssetExists = DoesMorphTargetMetaAssetExist(m_meshAsset.GetId()); + // Get the mesh asset. + m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset(m_meshAssetId, AZ::Data::AssetLoadBehavior::PreLoad); - m_skinToSkeletonIndexMap.clear(); - - // Skin and morph target meta assets are ready, fill the runtime mesh data. - if ((!skinMetaAssetExists || m_skinMetaAsset.IsReady()) && - (!morphTargetMetaAssetExists || m_morphTargetMetaAsset.IsReady())) + // Get the skin meta asset. + const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId); + if (DoesSkinMetaAssetExist(m_meshAssetId) && skinMetaAssetId.IsValid()) { - // Optional, not all actors have a skinned meshes. - if (skinMetaAssetExists) + m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset( + skinMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad); + } + + // Get the morph target meta asset. + const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId); + if (DoesMorphTargetMetaAssetExist(m_meshAssetId) && morphTargetMetaAssetId.IsValid()) + { + m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset( + morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad); + } + + if (loadReq == LoadRequirement::RequireBlockingLoad) + { + if (m_skinMetaAsset.IsLoading()) { - m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset); + m_skinMetaAsset.BlockUntilLoadComplete(); } - - ConstructMeshes(m_skinToSkeletonIndexMap); - - // Optional, not all actors have morph targets. - if (morphTargetMetaAssetExists) + if (m_morphTargetMetaAsset.IsLoading()) { - ConstructMorphTargets(); + m_morphTargetMetaAsset.BlockUntilLoadComplete(); } - else + if (m_meshAsset.IsLoading()) { - // Optional, not all actors have morph targets. - const size_t numLODLevels = m_meshAsset->GetLodAssets().size(); - mMorphSetups.Resize(numLODLevels); - for (AZ::u32 i = 0; i < numLODLevels; ++i) - { - mMorphSetups[i] = nullptr; - } + m_meshAsset.BlockUntilLoadComplete(); } - - SetActorReady(); - - // Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances. - //m_meshAsset.Release(); - //m_skinMetaAsset.Release(); - //m_morphTargetMetaAsset.Release(); } } - } - void Actor::LoadRemainingAssets() - { - // Everything is ready already or no (skeleton-only) or an invalid mesh asset assigned. Emit ready signal directly. - if (m_isReady || !m_meshAssetId.IsValid()) + if (m_meshAsset.IsReady()) { - SetActorReady(); - return; + if (m_skinMetaAsset.IsReady()) + { + m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset); + } + ConstructMeshes(); + + if (m_morphTargetMetaAsset.IsReady()) + { + ConstructMorphTargets(); + } + else + { + // Optional, not all actors have morph targets. + const size_t numLODLevels = m_meshAsset->GetLodAssets().size(); + mMorphSetups.Resize(numLODLevels); + for (AZ::u32 i = 0; i < numLODLevels; ++i) + { + mMorphSetups[i] = nullptr; + } + } } - LoadMeshAssetsQueued(); - } - - void Actor::OnAssetReloaded(AZ::Data::Asset asset) - { - if (asset == m_meshAsset) - { - m_meshAsset = asset; - } - if (asset == m_skinMetaAsset) - { - m_skinMetaAsset = asset; - } - if (asset == m_morphTargetMetaAsset) - { - m_morphTargetMetaAsset = asset; - } - - CheckFinalizeActor(); - } - - void Actor::SetActorReady() - { m_isReady = true; ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorReady, this); + // Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances. } // update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc) @@ -2792,37 +2761,6 @@ namespace EMotionFX m_meshAssetId = assetId; } - void Actor::LoadMeshAssetsQueued() - { - AZStd::scoped_lock lock(m_mutex); - - // Mesh asset will be queue loaded on post init. - if (m_meshAssetId.IsValid()) - { - m_isReady = false; - AZ::Data::AssetBus::MultiHandler::BusDisconnect(); - - AZ::Data::AssetBus::MultiHandler::BusConnect(m_meshAssetId); - m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset(m_meshAssetId, AZ::Data::AssetLoadBehavior::Default); - - // Skin meta asset - if (DoesSkinMetaAssetExist(m_meshAssetId)) - { - const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId); - AZ::Data::AssetBus::MultiHandler::BusConnect(skinMetaAssetId); - m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset(skinMetaAssetId, AZ::Data::AssetLoadBehavior::Default); - } - - // Morph target meta asset - if (DoesMorphTargetMetaAssetExist(m_meshAssetId)) - { - const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId); - AZ::Data::AssetBus::MultiHandler::BusConnect(morphTargetMetaAssetId); - m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset(morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::Default); - } - } - } - Node* Actor::FindMeshJoint(const AZ::Data::Asset& lodModelAsset) const { const AZStd::array_view& sourceMeshes = lodModelAsset->GetMeshes(); @@ -2843,7 +2781,7 @@ namespace EMotionFX return mSkeleton->GetNode(0); } - void Actor::ConstructMeshes(const AZStd::unordered_map& skinToSkeletonIndexMap) + void Actor::ConstructMeshes() { AZ_Assert(m_meshAsset.IsReady(), "Mesh asset should be fully loaded and ready."); @@ -2855,7 +2793,8 @@ namespace EMotionFX SetNumLODLevels(numLODLevels, /*adjustMorphSetup=*/false); const uint32 numNodes = mSkeleton->GetNumNodes(); - // Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and GLActor. + // Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and + // GLActor. RemoveAllMaterials(); mMaterials.Resize(numLODLevels); @@ -2866,7 +2805,7 @@ namespace EMotionFX lodLevels[lodLevel].mNodeInfos.Resize(numNodes); // Create a single mesh for the actor. - Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, skinToSkeletonIndexMap); + Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, m_skinToSkeletonIndexMap); // Find an owning joint for the mesh. Node* meshJoint = FindMeshJoint(lodAsset); @@ -2896,13 +2835,14 @@ namespace EMotionFX continue; } - EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer = static_cast(vertexAttributeLayer); + EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer = + static_cast(vertexAttributeLayer); const AZ::u32 numOrgVerts = skinLayer->GetNumAttributes(); AZStd::set localJointIndices = skinLayer->CalcLocalJointIndices(numOrgVerts); const AZ::u32 numLocalJoints = static_cast(localJointIndices.size()); - // The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that anymore. - // Default to dual quat skinning. + // The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that + // anymore. Default to dual quat skinning. const bool dualQuatSkinning = true; if (dualQuatSkinning) { @@ -2970,7 +2910,8 @@ namespace EMotionFX void Actor::ConstructMorphTargets() { - AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(), "Mesh as well as morph target meta asset asset should be fully loaded and ready."); + AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(), + "Mesh as well as morph target meta asset asset should be fully loaded and ready."); AZStd::vector& lodLevels = m_meshLodData.m_lodLevels; const AZStd::array_view>& lodAssets = m_meshAsset->GetLodAssets(); const size_t numLODLevels = lodAssets.size(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h index 9bf0daf046..3fe9711f18 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/Actor.h @@ -63,7 +63,6 @@ namespace EMotionFX * still share the same data from the Actor class. The Actor contains information about the hierarchy/structure of the characters. */ class EMFX_API Actor - : private AZ::Data::AssetBus::MultiHandler { public: AZ_CLASS_ALLOCATOR_DECL @@ -101,6 +100,12 @@ namespace EMotionFX uint8 mFlags; // bitfield with MIRRORFLAG_ prefix }; + enum class LoadRequirement : bool + { + RequireBlockingLoad, + AllowAsyncLoad + }; + //------------------------------------------------ /** @@ -885,36 +890,35 @@ namespace EMotionFX bool GetOptimizeSkeleton() const { return m_optimizeSkeleton; } void SetMeshAssetId(const AZ::Data::AssetId& assetId); - void CheckFinalizeActor(); - void LoadMeshAssetsQueued(); - void LoadRemainingAssets(); + AZ::Data::AssetId GetMeshAssetId() const { return m_meshAssetId; }; const AZ::Data::Asset& GetMeshAsset() const { return m_meshAsset; } const AZ::Data::Asset& GetSkinMetaAsset() const { return m_skinMetaAsset; } const AZ::Data::Asset& GetMorphTargetMetaAsset() const { return m_morphTargetMetaAsset; } - const AZStd::unordered_map& GetSkinToSkeletonIndexMap() const { return m_skinToSkeletonIndexMap; } - void SetMeshAsset(AZ::Data::Asset asset) { m_meshAsset = asset; } - void SetSkinMetaAsset(AZ::Data::Asset asset) { m_skinMetaAsset = asset; } - void SetMorphTargetMetaAsset(AZ::Data::Asset asset) { m_morphTargetMetaAsset = asset; } + /** + * Is the actor fully ready? + * @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized. + **/ + bool IsReady() const { return m_isReady; } /** - * Is the actor fully ready? - * @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized. - **/ - bool IsReady() const { return m_isReady; } + * Finalize the actor with preload assets (mesh, skinmeta and morph target assets). + * LoadRequirement - We won't need a blocking load if the actor is part of the actor asset, as that will trigger the preload assets + * to load and get ready before finalize has been reached. + * However, if we are calling this on an actor that bypassed the asset system (e.g loading the actor directly from disk), it will require + * a blocking load. This option is now being used because emfx editor does not fully integrate with the asset system. + */ + void Finalize(LoadRequirement loadReq = LoadRequirement::AllowAsyncLoad); private: void InsertJointAndParents(AZ::u32 jointIndex, AZStd::unordered_set& includedJointIndices); - // AZ::Data::AssetBus::Handler - void OnAssetReady(AZ::Data::Asset asset) override; - void OnAssetReloaded(AZ::Data::Asset asset) override; - AZStd::unordered_map ConstructSkinToSkeletonIndexMap(const AZ::Data::Asset& skinMetaAsset); - void ConstructMeshes(const AZStd::unordered_map& skinToSkeletonIndexMap); + void ConstructMeshes(); void ConstructMorphTargets(); + Node* FindJointByMeshName(const AZStd::string_view meshName) const; // per node info (shared between lods) @@ -966,9 +970,6 @@ namespace EMotionFX Node* FindMeshJoint(const AZ::Data::Asset& lodModelAsset) const; - void SetActorReady(); - bool m_isReady = false; - Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */ MCore::Array mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */ AZStd::vector mNodeInfos; /**< The per node info, shared between lods. */ @@ -992,7 +993,7 @@ namespace EMotionFX bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */ bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */ bool m_optimizeSkeleton; /**< Indicates if we should perform/ */ - + bool m_isReady = false; /**< If actor as well as its dependent files are fully loaded and initialized.*/ #if defined(EMFX_DEVELOPMENT_BUILD) bool mIsOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */ #endif // EMFX_DEVELOPMENT_BUILD diff --git a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp index 518db289e2..539b656754 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Assets/ActorAsset.cpp @@ -68,6 +68,9 @@ namespace EMotionFX &actorSettings, ""); + assetData->m_emfxActor->Finalize(); + + // Clear out the EMFX raw asset data. assetData->ReleaseEMotionFXData(); if (!assetData->m_emfxActor) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 982b62fbcf..f41ef165c8 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -154,17 +154,15 @@ namespace EMotionFX Actor* actor = m_configuration.m_actorAsset->GetActor(); if (actor) { - OnActorReady(actor); + CheckActorCreation(); } } ////////////////////////////////////////////////////////////////////////// ActorComponent::ActorComponent(const Configuration* configuration) : m_debugDrawRoot(false) - , m_sceneFinishSimHandler([this]( - [[maybe_unused]] AzPhysics::SceneHandle sceneHandle, - float fixedDeltatime - ) + , m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, + float fixedDeltatime) { if (m_actorInstance) { @@ -192,18 +190,9 @@ namespace EMotionFX if (cfg.m_actorAsset.GetId().IsValid()) { - EMotionFX::ActorNotificationBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect(); - EMotionFX::ActorNotificationBus::Handler::BusConnect(); AZ::Data::AssetBus::Handler::BusConnect(cfg.m_actorAsset.GetId()); cfg.m_actorAsset.QueueLoad(); - - // In case the asset was already loaded fully, create the actor directly. - if (cfg.m_actorAsset.IsReady() && - cfg.m_actorAsset->GetActor()) - { - cfg.m_actorAsset->GetActor()->LoadRemainingAssets(); - } } AZ::TickBus::Handler::BusConnect(); @@ -231,7 +220,6 @@ namespace EMotionFX LmbrCentral::AttachmentComponentNotificationBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect(); - EMotionFX::ActorNotificationBus::Handler::BusDisconnect(); DestroyActor(); m_configuration.m_actorAsset.Release(); @@ -314,28 +302,12 @@ namespace EMotionFX Actor* actor = m_configuration.m_actorAsset->GetActor(); AZ_Assert(m_configuration.m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); - actor->LoadRemainingAssets(); - actor->CheckFinalizeActor(); + CheckActorCreation(); } void ActorComponent::OnAssetReloaded(AZ::Data::Asset asset) { - DestroyActor(); - m_configuration.m_actorAsset = asset; - - const Actor* oldActor = m_configuration.m_actorAsset->GetActor(); - AZ::Data::Asset meshAsset = oldActor->GetMeshAsset(); - AZ::Data::Asset skinMetaAsset = oldActor->GetSkinMetaAsset(); - AZ::Data::Asset morphTargetMetaAsset = oldActor->GetMorphTargetMetaAsset(); - - m_configuration.m_actorAsset = asset; - Actor* newActor = m_configuration.m_actorAsset->GetActor(); - AZ_Assert(m_configuration.m_actorAsset.IsReady() && newActor, "Actor asset should be loaded and actor valid."); - - newActor->SetMeshAsset(meshAsset); - newActor->SetSkinMetaAsset(skinMetaAsset); - newActor->SetMorphTargetMetaAsset(morphTargetMetaAsset); - newActor->CheckFinalizeActor(); + OnAssetReady(asset); } bool ActorComponent::IsPhysicsSceneSimulationFinishEventConnected() const @@ -850,13 +822,5 @@ namespace EMotionFX m_actorInstance->RemoveAttachment(targetActorInstance); } } - - void ActorComponent::OnActorReady(Actor* actor) - { - if (m_configuration.m_actorAsset && m_configuration.m_actorAsset->GetActor() == actor) - { - CheckActorCreation(); - } - } } // namespace Integration } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 8188da19b4..4172ce63bc 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -44,7 +44,6 @@ namespace EMotionFX , private LmbrCentral::AttachmentComponentNotificationBus::Handler , private AzFramework::CharacterPhysicsDataRequestBus::Handler , private AzFramework::RagdollPhysicsNotificationBus::Handler - , private EMotionFX::ActorNotificationBus::Handler { public: AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}"); @@ -168,9 +167,6 @@ namespace EMotionFX void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; int GetTickOrder() override; - // ActorNotificationBus::Handler - void OnActorReady(Actor* actor) override; - void CheckActorCreation(); void DestroyActor(); void CheckAttachToEntity(); diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index d22bb0007f..7d5b7ade00 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -156,8 +156,6 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// void EditorActorComponent::Activate() { - EMotionFX::ActorNotificationBus::Handler::BusConnect(); - LoadActorAsset(); const AZ::EntityId entityId = GetEntityId(); @@ -186,8 +184,6 @@ namespace EMotionFX AZ::TickBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect(); - EMotionFX::ActorNotificationBus::Handler::BusDisconnect(); - DestroyActorInstance(); m_actorAsset.Release(); } @@ -234,13 +230,6 @@ namespace EMotionFX AZ::Data::AssetBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusConnect(m_actorAsset.GetId()); m_actorAsset.QueueLoad(); - - // In case the asset was already loaded fully, create the actor directly. - if (m_actorAsset.IsReady() && - m_actorAsset->GetActor()) - { - m_actorAsset->GetActor()->LoadRemainingAssets(); - } } else { @@ -475,27 +464,13 @@ namespace EMotionFX Actor* actor = m_actorAsset->GetActor(); AZ_Assert(m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid."); - actor->LoadRemainingAssets(); - actor->CheckFinalizeActor(); + CheckActorCreation(); } void EditorActorComponent::OnAssetReloaded(AZ::Data::Asset asset) { DestroyActorInstance(); - - const Actor* oldActor = m_actorAsset->GetActor(); - AZ::Data::Asset meshAsset = oldActor->GetMeshAsset(); - AZ::Data::Asset skinMetaAsset = oldActor->GetSkinMetaAsset(); - AZ::Data::Asset morphTargetMetaAsset = oldActor->GetMorphTargetMetaAsset(); - - m_actorAsset = asset; - Actor* newActor = m_actorAsset->GetActor(); - AZ_Assert(m_actorAsset.IsReady() && newActor, "Actor asset should be loaded and actor valid."); - - newActor->SetMeshAsset(meshAsset); - newActor->SetSkinMetaAsset(skinMetaAsset); - newActor->SetMorphTargetMetaAsset(morphTargetMetaAsset); - newActor->CheckFinalizeActor(); + OnAssetReady(asset); } void EditorActorComponent::SetActorAsset(AZ::Data::Asset actorAsset) @@ -505,7 +480,7 @@ namespace EMotionFX Actor* actor = m_actorAsset->GetActor(); if (actor) { - OnActorReady(actor); + CheckActorCreation(); } } @@ -818,14 +793,6 @@ namespace EMotionFX return false; } - void EditorActorComponent::OnActorReady(Actor* actor) - { - if (m_actorAsset && m_actorAsset->GetActor() == actor) - { - CheckActorCreation(); - } - } - void EditorActorComponent::CheckActorCreation() { // Enable/disable debug drawing. diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index 8ddd95c3b7..5dabbe4ada 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -45,7 +45,6 @@ namespace EMotionFX , private AzToolsFramework::EditorComponentSelectionRequestsBus::Handler , private AzToolsFramework::EditorVisibilityNotificationBus::Handler , public AzFramework::BoundsRequestBus::Handler - , private EMotionFX::ActorNotificationBus::Handler { public: AZ_EDITOR_COMPONENT(EditorActorComponent, "{A863EE1B-8CFD-4EDD-BA0D-1CEC2879AD44}"); @@ -142,9 +141,6 @@ namespace EMotionFX void OnAttached(AZ::EntityId targetId) override; void OnDetached(AZ::EntityId targetId) override; - // ActorNotificationBus::Handler - void OnActorReady(Actor* actor) override; - void CheckActorCreation(); void BuildGameEntity(AZ::Entity* gameEntity) override; From f39460e617d56358c83c8227203b83f547d386e5 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 3 Jun 2021 20:08:51 -0700 Subject: [PATCH 079/105] Fix rare re-entrancy issue with CCryEditApp::IdleProcessing (#1134) This issue manifested in a crash in rare circumstances when the Editor lost and gained focus while a modal dialog was active. After investigation, it was discovered that native event processing can lead to IdleProcessing being called again from the main thread while idle processing is still happening. As this is unintentional and generally undesirable, we now guard against this within the IdleProcessing method. --- Code/Sandbox/Editor/CryEdit.cpp | 8 ++++++++ Code/Sandbox/Editor/CryEdit.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index c723e6049a..a972a4bd9b 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -2281,6 +2281,14 @@ int CCryEditApp::IdleProcessing(bool bBackgroundUpdate) return 0; } + // Ensure we don't get called re-entrantly + // This can occur when a nested Qt event loop fires (e.g. by way of a modal dialog calling exec) + if (m_idleProcessingRunning) + { + return 0; + } + QScopedValueRollback guard(m_idleProcessingRunning, true); + //////////////////////////////////////////////////////////////////////// // Call the update function of the engine //////////////////////////////////////////////////////////////////////// diff --git a/Code/Sandbox/Editor/CryEdit.h b/Code/Sandbox/Editor/CryEdit.h index dc4f015faf..e37fb53561 100644 --- a/Code/Sandbox/Editor/CryEdit.h +++ b/Code/Sandbox/Editor/CryEdit.h @@ -335,6 +335,8 @@ private: // If this flag is set, the next OnIdle() will update, even if the app is in the background, and then // this flag will be reset. bool m_bForceProcessIdle = false; + // This is set while IdleProcessing is running to prevent re-entrancy + bool m_idleProcessingRunning = false; // Keep the editor alive, even if no focus is set bool m_bKeepEditorActive = false; // Currently creating a new level From 34449e2fc9085e2cf5d3a7e2836479f4977d65b7 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 3 Jun 2021 20:09:04 -0700 Subject: [PATCH 080/105] Guard GridComponent against arbitrarily high grid sizes (#1135) Also do bounds checking at runtime in the controller for safety. --- .../Code/Source/Grid/EditorGridComponent.cpp | 7 ++++--- .../Code/Source/Grid/GridComponentController.cpp | 6 +++--- .../Code/Source/Grid/GridComponentController.h | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp index f500bdd6e2..81fc3aa170 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/EditorGridComponent.cpp @@ -54,13 +54,14 @@ namespace AZ ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_gridSize, "Grid Size", "Grid width and depth") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinGridSize) + ->Attribute(AZ::Edit::Attributes::Max, GridComponentController::MaxGridSize) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_primarySpacing, "Primary Grid Spacing", "Amount of space between grid lines") - ->Attribute(AZ::Edit::Attributes::Min, 0.01f) + ->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinSpacing) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_secondarySpacing, "Secondary Grid Spacing", "Amount of space between sub-grid lines") - ->Attribute(AZ::Edit::Attributes::Min, 0.01f) + ->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinSpacing) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->DataElement(AZ::Edit::UIHandlers::Color, &GridComponentConfig::m_axisColor, "Axis Color", "Color of the grid axis") ->DataElement(AZ::Edit::UIHandlers::Color, &GridComponentConfig::m_primaryColor, "Primary Color", "Color of the primary grid lines") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp index c2f9c896af..279c8d9035 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp @@ -115,7 +115,7 @@ namespace AZ void GridComponentController::SetSize(float gridSize) { - m_configuration.m_gridSize = gridSize; + m_configuration.m_gridSize = AZStd::clamp(gridSize, MinGridSize, MaxGridSize); m_dirty = true; } @@ -126,7 +126,7 @@ namespace AZ void GridComponentController::SetPrimarySpacing(float gridPrimarySpacing) { - m_configuration.m_primarySpacing = gridPrimarySpacing; + m_configuration.m_primarySpacing = AZStd::max(gridPrimarySpacing, MinSpacing); m_dirty = true; } @@ -137,7 +137,7 @@ namespace AZ void GridComponentController::SetSecondarySpacing(float gridSecondarySpacing) { - m_configuration.m_secondarySpacing = gridSecondarySpacing; + m_configuration.m_secondarySpacing = AZStd::max(gridSecondarySpacing, MinSpacing); m_dirty = true; } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.h index afba6d8327..ad603f11e1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.h @@ -46,6 +46,10 @@ namespace AZ void SetConfiguration(const GridComponentConfig& config); const GridComponentConfig& GetConfiguration() const; + static constexpr float MinGridSize = 0.0f; + static constexpr float MaxGridSize = 1000000.0f; + static constexpr float MinSpacing = 0.01f; + private: AZ_DISABLE_COPY(GridComponentController); From 30eedc1c554c8ae01e5be9d22cc372d757cd7bd0 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 3 Jun 2021 20:09:15 -0700 Subject: [PATCH 081/105] Avoid more sources of camera update re-entrancy that can lead to stack overflow (#1136) --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 3 +++ Gems/Camera/Code/Source/CameraComponentController.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 667179e3cd..0d3405f8f0 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -2887,9 +2887,12 @@ void EditorViewportWidget::UpdateCameraFromViewportContext() AZ::Matrix3x4 matrix; matrix.SetBasisAndTranslation(cameraState.m_side, cameraState.m_forward, cameraState.m_up, cameraState.m_position); auto m = AZMatrix3x4ToLYMatrix3x4(matrix); + + m_updatingCameraPosition = true; SetViewTM(m); SetFOV(cameraState.m_fovOrZoom); m_Camera.SetZRange(cameraState.m_nearClip, cameraState.m_farClip); + m_updatingCameraPosition = false; } void EditorViewportWidget::SetAsActiveViewport() diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index cad666c1cd..78bd131002 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -387,6 +387,11 @@ namespace Camera void CameraComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world) { + if (m_updatingTransformFromEntity) + { + return; + } + if (m_view) { CCamera& camera = m_view->GetCamera(); From 4a1d713227af339b2fbb86eb02c936cc079863c5 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 3 Jun 2021 22:36:34 -0500 Subject: [PATCH 082/105] Fix recursive attempts to open the log file in the GameLauncher (#1114) * Fix recursive attempts to open the log file in the GameLauncher The AzFramework Application has been updated to default the @user@ and @log@ aliases to the /user and /user/log folder respectively if a project isn't set. Fixed the SystemFile class to support negative offsets if Seek() as per standard seek function such as fseek Updated the CrySystem CLog class to use SystemFile instead of FileIOBase to avoid any asserts that would cause CLog::OpenFile to be recursively called infinitely * Removing unused Force Closed variable * AZ::IO::SystemFile build fixes for Unix platforms. Added a copy constructor for LUAEditorContextInterface.h to fix the LuaEditor build * Adding missing includes to the WindowsAPI and Android SystemFile headers --- Code/CryEngine/CrySystem/Log.cpp | 106 +++++++++--------- Code/CryEngine/CrySystem/Log.h | 18 ++- Code/CryEngine/CrySystem/SystemInit.cpp | 2 +- .../Framework/AzCore/AzCore/IO/SystemFile.cpp | 60 ++++++---- Code/Framework/AzCore/AzCore/IO/SystemFile.h | 21 ++-- .../Settings/SettingsRegistryMergeUtils.cpp | 2 + .../Android/AzCore/IO/SystemFile_Android.cpp | 10 +- .../Android/AzCore/IO/SystemFile_Android.h | 6 +- .../Common/Apple/AzCore/IO/SystemFile_Apple.h | 5 +- .../UnixLike/AzCore/IO/SystemFile_UnixLike.h | 6 +- .../AzCore/IO/SystemFile_UnixLikeDefault.cpp | 6 +- .../WinAPI/AzCore/IO/SystemFile_WinAPI.cpp | 8 +- .../WinAPI/AzCore/IO/SystemFile_WinAPI.h | 6 +- .../AzFramework/Application/Application.cpp | 13 ++- .../Source/LUA/LUAEditorContextInterface.h | 46 ++++++++ 15 files changed, 200 insertions(+), 115 deletions(-) diff --git a/Code/CryEngine/CrySystem/Log.cpp b/Code/CryEngine/CrySystem/Log.cpp index b8241ee865..af5170e4e5 100644 --- a/Code/CryEngine/CrySystem/Log.cpp +++ b/Code/CryEngine/CrySystem/Log.cpp @@ -26,6 +26,7 @@ #include #include +#include #ifdef WIN32 #include @@ -88,7 +89,6 @@ CLog::CLog(ISystem* pSystem) m_nMainThreadId = CryGetCurrentThreadId(); - m_logFileHandle = AZ::IO::InvalidHandle; #if defined(KEEP_LOG_FILE_OPEN) m_bFirstLine = true; #endif @@ -162,35 +162,6 @@ void CLog::RegisterConsoleVariables() REGISTER_COMMAND("log_flush", &LogFlushFile, 0, "Flush the log file"); #endif } - /* - //testbed - { - int iSave0 = m_pLogVerbosity->GetIVal(); - int iSave1 = m_pLogFileVerbosity->GetIVal(); - - for(int i=0;i<=4;++i) - { - m_pLogVerbosity->Set(i); - m_pLogFileVerbosity->Set(i); - - LogWithType(eAlways,"CLog selftest: Verbosity=%d FileVerbosity=%d",m_pLogVerbosity->GetIVal(),m_pLogFileVerbosity->GetIVal()); - LogWithType(eAlways,"--------------"); - - LogWithType(eError,"eError"); - LogWithType(eWarning,"eWarning"); - LogWithType(eMessage,"eMessage"); - LogWithType(eInput,"eInput"); - LogWithType(eInputResponse,"eInputResponse"); - - LogWarning("LogWarning()"); - LogError("LogError()"); - LogWithType(eAlways,"--------------"); - } - - m_pLogVerbosity->Set(iSave0); - m_pLogFileVerbosity->Set(iSave1); - } - */ #undef DEFAULT_VERBOSITY } @@ -210,7 +181,7 @@ CLog::~CLog() UnregisterConsoleVariables(); - CloseLogFile(true); + CloseLogFile(); } void CLog::UnregisterConsoleVariables() @@ -224,31 +195,36 @@ void CLog::UnregisterConsoleVariables() } ////////////////////////////////////////////////////////////////////////// -void CLog::CloseLogFile([[maybe_unused]] bool forceClose) +void CLog::CloseLogFile() { - if (m_logFileHandle != AZ::IO::InvalidHandle) - { - AZ::IO::FileIOBase::GetDirectInstance()->Close(m_logFileHandle); - m_logFileHandle = AZ::IO::InvalidHandle; - } + m_logFileHandle.Close(); } ////////////////////////////////////////////////////////////////////////// -AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode) +bool CLog::OpenLogFile(const char* filename, int mode) { - using namespace AZ::IO; - - AZ_Assert(m_logFileHandle == AZ::IO::InvalidHandle, "Attempt to open log file when one is already open. This would lead to a handle leak."); - - if ((!filename) || (filename[0] == 0)) + if (m_logFileHandle.IsOpen()) { - return m_logFileHandle; + // Can only AZ_Assert if a file is open, otherwise the AZ_Assert + // would eventually lead to OpenLogFile being opened up again + AZ_Assert(false, "Attempt to open log file when one is already open. This would lead to a handle leak."); + return false; + } + + if (filename == nullptr || filename[0] == '\0') + { + return false; } // it is assumed that @log@ points at the appropriate place (so for apple, to the user profile dir) - AZ::IO::FileIOBase::GetDirectInstance()->Open(filename, AZ::IO::GetOpenModeFromStringMode(mode), m_logFileHandle); + AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance(); + if (AZ::IO::FixedMaxPath logFilePath; fileSystem->ReplaceAlias(logFilePath, filename)) + { + logFilePath = logFilePath.LexicallyNormal(); + m_logFileHandle.Open(logFilePath.c_str(), mode); + } - if (m_logFileHandle != AZ::IO::InvalidHandle) + if (m_logFileHandle.IsOpen()) { #if defined(KEEP_LOG_FILE_OPEN) m_bFirstLine = true; @@ -257,11 +233,11 @@ AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode) else { #if defined(LINUX) || defined(APPLE) - syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%s]", filename, mode); + syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%d]", filename, mode); #endif } - return m_logFileHandle; + return m_logFileHandle.IsOpen(); } ////////////////////////////////////////////////////////////////////////// @@ -1114,12 +1090,15 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[ if (logToFile) { - if (m_logFileHandle == AZ::IO::InvalidHandle) + if (!m_logFileHandle.IsOpen()) { - OpenLogFile(m_szFilename, "w+t"); + constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND + | AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE + | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY; + OpenLogFile(m_szFilename, openMode); } - if (m_logFileHandle != AZ::IO::InvalidHandle) + if (m_logFileHandle.IsOpen()) { #if defined(KEEP_LOG_FILE_OPEN) if (m_bFirstLine) @@ -1130,9 +1109,9 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[ if (bAdd) { // if adding to a prior line erase the \n at the end. - AZ::IO::FileIOBase::GetDirectInstance()->Seek(m_logFileHandle, -2, AZ::IO::SeekType::SeekFromEnd); + m_logFileHandle.Seek(-2, AZ::IO::SystemFile::SeekMode::SF_SEEK_END); } - AZ::IO::FPutS(tempString.c_str(), m_logFileHandle); + m_logFileHandle.Write(tempString.c_str(), tempString.size()); #if !defined(KEEP_LOG_FILE_OPEN) CloseLogFile(); #endif @@ -1383,6 +1362,23 @@ bool CLog::SetFileName(const char* fileNameOrAbsolutePath, bool backupLogs) CreateBackupFile(); + AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance(); + AZ::IO::FixedMaxPath newLogFilePath; + if (fileSystem->ReplaceAlias(newLogFilePath, m_szFilename)) + { + newLogFilePath = newLogFilePath.LexicallyNormal(); + } + if (m_logFileHandle.IsOpen() && newLogFilePath != m_logFileHandle.Name()) + { + constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND + | AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE + | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY; + if(AZ::IO::SystemFile newLogFile; newLogFile.Open(m_szFilename, openMode)) + { + m_logFileHandle = AZStd::move(newLogFile); + } + } + return true; } @@ -1537,9 +1533,9 @@ const char* CLog::GetModuleFilter() void CLog::FlushAndClose() { #if defined(KEEP_LOG_FILE_OPEN) - if (m_logFileHandle) + if (m_logFileHandle.IsOpen()) { - CloseLogFile(true); + CloseLogFile(); } #endif } diff --git a/Code/CryEngine/CrySystem/Log.h b/Code/CryEngine/CrySystem/Log.h index 911043871a..e19c2d19da 100644 --- a/Code/CryEngine/CrySystem/Log.h +++ b/Code/CryEngine/CrySystem/Log.h @@ -137,8 +137,8 @@ private: // ------------------------------------------------------------------- void LogStringToConsole(const char* szString, ELogType logType, bool bAdd) {} #endif // !defined(EXCLUDE_NORMAL_LOG) - AZ::IO::HandleType OpenLogFile(const char* filename, const char* mode); - void CloseLogFile(bool force = false); + bool OpenLogFile(const char* filename, int mode); + void CloseLogFile(); // will format the message into m_szTemp void FormatMessage(const char* szCommand, ...) PRINTF_PARAMS(2, 3); @@ -152,15 +152,11 @@ private: // ------------------------------------------------------------------- virtual const char* GetAssetScopeString(); #endif - ISystem* m_pSystem; // - float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update - //char m_szTemp[MAX_TEMP_LENGTH_SIZE]; // - char m_szFilename[MAX_FILENAME_SIZE]; // can be with path - mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path - AZ::IO::HandleType m_logFileHandle; - CryStackStringT m_LogMode; //mode m_pLogFile has been opened with - AZ::IO::HandleType m_errFileHandle; - int m_nErrCount; + ISystem* m_pSystem; // + float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update + char m_szFilename[MAX_FILENAME_SIZE]; // can be with path + mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path + AZ::IO::SystemFile m_logFileHandle; bool m_backupLogs; diff --git a/Code/CryEngine/CrySystem/SystemInit.cpp b/Code/CryEngine/CrySystem/SystemInit.cpp index 4a4296bbb5..97a3bbbe54 100644 --- a/Code/CryEngine/CrySystem/SystemInit.cpp +++ b/Code/CryEngine/CrySystem/SystemInit.cpp @@ -1208,7 +1208,7 @@ bool CSystem::Init(const SSystemInitParams& startupParams) { assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); AZ_Warning(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n" - R"(This typically done by setting he "assets" field in the bootstrap.cfg for within a .setreg file)""\n" + R"(This typically done by setting the "assets" field within a .setreg file)""\n" R"(A fallback of %s will be used.)", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, assetPlatform.c_str()); diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp index 7c535d4aaf..6887d22ba7 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp @@ -30,7 +30,7 @@ namespace Platform using FileHandleType = SystemFile::FileHandleType; - void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode); + void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode); SystemFile::SizeType Tell(FileHandleType handle, const SystemFile* systemFile); bool Eof(FileHandleType handle, const SystemFile* systemFile); AZ::u64 ModificationTime(FileHandleType handle, const SystemFile* systemFile); @@ -68,9 +68,8 @@ void SystemFile::CreatePath(const char* fileName) } SystemFile::SystemFile() + : m_handle{ AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE } { - m_fileName[0] = '\0'; - m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE; } SystemFile::~SystemFile() @@ -81,6 +80,25 @@ SystemFile::~SystemFile() } } +SystemFile::SystemFile(SystemFile&& other) + : SystemFile{} +{ + AZStd::swap(m_fileName, other.m_fileName); + AZStd::swap(m_handle, other.m_handle); +} + +SystemFile& SystemFile::operator=(SystemFile&& other) +{ + // Close the current file and take over the SystemFile handle and filename + Close(); + m_fileName = AZStd::move(other.m_fileName); + m_handle = AZStd::move(other.m_handle); + other.m_fileName = {}; + other.m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE; + + return *this; +} + bool SystemFile::Open(const char* fileName, int mode, int platformFlags) { AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Open - %s", fileName); @@ -88,42 +106,42 @@ bool SystemFile::Open(const char* fileName, int mode, int platformFlags) if (fileName) // If we reopen the file we are allowed to have NULL file name { - if (strlen(fileName) > AZ_ARRAY_SIZE(m_fileName) - 1) + if (strlen(fileName) > m_fileName.max_size()) { EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, 0); return false; } // store the filename - azsnprintf(m_fileName, AZ_ARRAY_SIZE(m_fileName), "%s", fileName); + m_fileName = fileName; } if (FileIOBus::HasHandlers()) { bool isOpen = false; bool isHandled = false; - EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName, mode, platformFlags, isOpen); + EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName.c_str(), mode, platformFlags, isOpen); if (isHandled) { return isOpen; } } - AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName); + AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName.c_str()); return PlatformOpen(mode, platformFlags); } bool SystemFile::ReOpen(int mode, int platformFlags) { - AZ_Assert(strlen(m_fileName) > 0, "Missing filename. You must call open first!"); + AZ_Assert(!m_fileName.empty(), "Missing filename. You must call open first!"); return Open(0, mode, platformFlags); } void SystemFile::Close() { - AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName); - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName); + AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName.c_str()); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName.c_str()); if (FileIOBus::HasHandlers()) { @@ -138,9 +156,9 @@ void SystemFile::Close() PlatformClose(); } -void SystemFile::Seek(SizeType offset, SeekMode mode) +void SystemFile::Seek(SeekSizeType offset, SeekMode mode) { - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName, offset); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName.c_str(), offset); if (FileIOBus::HasHandlers()) { @@ -167,15 +185,15 @@ bool SystemFile::Eof() AZ::u64 SystemFile::ModificationTime() { - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName.c_str()); return Platform::ModificationTime(m_handle, this); } SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer) { - AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName, byteSize); - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName, byteSize); + AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize); if (FileIOBus::HasHandlers()) { @@ -193,8 +211,8 @@ SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer) SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize) { - AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName, byteSize); - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName, byteSize); + AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize); if (FileIOBus::HasHandlers()) { @@ -212,14 +230,14 @@ SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize) void SystemFile::Flush() { - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName.c_str()); Platform::Flush(m_handle, this); } SystemFile::SizeType SystemFile::Length() const { - AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName); + AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName.c_str()); return Platform::Length(m_handle, this); } @@ -379,9 +397,9 @@ namespace HasPosixEnumOption(PermissionModeFlags::Write); #undef HasPosixEnumOption -} +} + - FileDescriptorRedirector::FileDescriptorRedirector(int sourceFileDescriptor) : m_sourceFileDescriptor(sourceFileDescriptor) { diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.h b/Code/Framework/AzCore/AzCore/IO/SystemFile.h index 0ce8197b18..065aa124ca 100644 --- a/Code/Framework/AzCore/AzCore/IO/SystemFile.h +++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.h @@ -12,10 +12,11 @@ #pragma once #include -#include -#include +#include #include +#include +#include // Establish a consistent size that works across platforms. It's actually larger than this // on platforms we support, but this is a good least common denominator @@ -51,11 +52,15 @@ namespace AZ }; using SizeType = AZ::IO::Internal::SizeType; + using SeekSizeType = AZ::IO::Internal::SeekSizeType; using FileHandleType = AZ::IO::Internal::FileHandleType; SystemFile(); ~SystemFile(); + SystemFile(SystemFile&&); + SystemFile& operator=(SystemFile&&); + /** * Opens a file. * \param fileName full file name including path @@ -69,7 +74,7 @@ namespace AZ /// Closes a file, if file already close it has no effect. void Close(); /// Seek in current file. - void Seek(SizeType offset, SeekMode mode); + void Seek(SeekSizeType offset, SeekMode mode); /// Get the cursor position in the current file. SizeType Tell(); /// Is the cursor at the end of the file? @@ -87,7 +92,7 @@ namespace AZ /// Return disc offset if possible, otherwise 0 SizeType DiskOffset() const; /// Return file name or NULL if file is not open. - AZ_FORCE_INLINE const char* Name() const { return m_fileName; } + AZ_FORCE_INLINE const char* Name() const { return m_fileName.c_str(); } bool IsOpen() const; /// Return native handle to the file. @@ -124,12 +129,12 @@ namespace AZ private: static void CreatePath(const char * fileName); - + bool PlatformOpen(int mode, int platformFlags); void PlatformClose(); - - FileHandleType m_handle; - char m_fileName[AZ_MAX_PATH_LEN]; + + FileHandleType m_handle; + AZ::IO::FixedMaxPathString m_fileName; }; /** diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 2abef3f808..2d2acd9d44 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -641,6 +641,8 @@ namespace AZ::SettingsRegistryMergeUtils } else { + // Set the default ProjectUserPath to the /user directory + registry.Set(FilePathKey_ProjectUserPath, (engineRoot / "user").LexicallyNormal().Native()); AZ_TracePrintf("SettingsRegistryMergeUtils", R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n", aznumeric_cast(projectPathKey.size()), projectPathKey.data()); diff --git a/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.cpp b/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.cpp index 2b1ebf54aa..c9950d2dfa 100644 --- a/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.cpp +++ b/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.cpp @@ -101,7 +101,7 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags) createPath = (mode & SF_OPEN_CREATE_PATH) == SF_OPEN_CREATE_PATH; } - bool isApkFile = AZ::Android::Utils::IsApkPath(m_fileName); + bool isApkFile = AZ::Android::Utils::IsApkPath(m_fileName.c_str()); if (createPath) { @@ -111,19 +111,19 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags) return false; } - CreatePath(m_fileName); + CreatePath(m_fileName.c_str()); } int errorCode = 0; if (isApkFile) { AZ::u64 size = 0; - m_handle = AZ::Android::APKFileHandler::Open(m_fileName, openMode, size); + m_handle = AZ::Android::APKFileHandler::Open(m_fileName.c_str(), openMode, size); errorCode = EACCES; // general error when a file can't be opened from inside the APK } else { - m_handle = fopen(m_fileName, openMode); + m_handle = fopen(m_fileName.c_str(), openMode); errorCode = errno; } @@ -233,7 +233,7 @@ namespace Platform } } - void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode) + void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode) { if (handle != PlatformSpecificInvalidHandle) { diff --git a/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.h b/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.h index 7ccc6a076e..50255f24d4 100644 --- a/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.h +++ b/Code/Framework/AzCore/Platform/Android/AzCore/IO/SystemFile_Android.h @@ -15,6 +15,9 @@ #include #include #include +#include + +#include namespace AZ { @@ -23,6 +26,7 @@ namespace AZ namespace Internal { using SizeType = AZ::u64; + using SeekSizeType = AZ::s64; using FileHandleType = FILE*; } @@ -37,7 +41,7 @@ namespace AZ #else Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified. #endif - Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. + Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY. // Note: The TRUNC flag destroys the contents of the specified file. diff --git a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h index 902dca4142..3967cafc90 100644 --- a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h +++ b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h @@ -22,9 +22,10 @@ namespace AZ namespace Internal { using SizeType = AZ::u64; + using SeekSizeType = AZ::s64; using FileHandleType = int; } - + namespace PosixInternal { enum class OpenFlags : int @@ -36,7 +37,7 @@ namespace AZ #else Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified. #endif - Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. + Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY. // Note: The TRUNC flag destroys the contents of the specified file. diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/IO/SystemFile_UnixLike.h b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/IO/SystemFile_UnixLike.h index 0f55c0511b..e2d985d84e 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/IO/SystemFile_UnixLike.h +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/IO/SystemFile_UnixLike.h @@ -13,6 +13,9 @@ #include #include +#include + +#include namespace AZ { @@ -21,6 +24,7 @@ namespace AZ namespace Internal { using SizeType = AZ::u64; + using SeekSizeType = AZ::s64; using FileHandleType = int; } @@ -35,7 +39,7 @@ namespace AZ #else Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified. #endif - Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. + Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY. // Note: The TRUNC flag destroys the contents of the specified file. diff --git a/Code/Framework/AzCore/Platform/Common/UnixLikeDefault/AzCore/IO/SystemFile_UnixLikeDefault.cpp b/Code/Framework/AzCore/Platform/Common/UnixLikeDefault/AzCore/IO/SystemFile_UnixLikeDefault.cpp index 3e40936d66..b5c3041113 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLikeDefault/AzCore/IO/SystemFile_UnixLikeDefault.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLikeDefault/AzCore/IO/SystemFile_UnixLikeDefault.cpp @@ -86,9 +86,9 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags) if (createPath) { - CreatePath(m_fileName); + CreatePath(m_fileName.c_str()); } - m_handle = open(m_fileName, desiredAccess, permissions); + m_handle = open(m_fileName.c_str(), desiredAccess, permissions); if (m_handle == PlatformSpecificInvalidHandle) { @@ -119,7 +119,7 @@ namespace Platform { using FileHandleType = AZ::IO::SystemFile::FileHandleType; - void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode) + void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode) { if (handle != PlatformSpecificInvalidHandle) { diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.cpp index e01008b1a0..fa77acb967 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.cpp +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.cpp @@ -209,19 +209,19 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags) if (createPath) { - CreatePath(m_fileName); + CreatePath(m_fileName.c_str()); } # ifdef _UNICODE wchar_t fileNameW[AZ_MAX_PATH_LEN]; size_t numCharsConverted; m_handle = INVALID_HANDLE_VALUE; - if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0) + if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName.c_str(), AZ_ARRAY_SIZE(fileNameW) - 1) == 0) { m_handle = CreateFileW(fileNameW, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0); } # else //!_UNICODE - m_handle = CreateFile(m_fileName, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0); + m_handle = CreateFile(m_fileName.c_str(), dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0); # endif // !_UNICODE if (m_handle == INVALID_HANDLE_VALUE) @@ -261,7 +261,7 @@ namespace Platform { using FileHandleType = AZ::IO::SystemFile::FileHandleType; - void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode) + void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode) { if (handle != PlatformSpecificInvalidHandle) { diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.h b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.h index 7f69a6b66d..09ea48c40b 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.h +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/IO/SystemFile_WinAPI.h @@ -13,6 +13,9 @@ #include #include +#include + +#include namespace AZ { @@ -21,6 +24,7 @@ namespace AZ namespace Internal { using SizeType = AZ::u64; + using SeekSizeType = AZ::s64; using FileHandleType = void*; } @@ -31,7 +35,7 @@ namespace AZ Append = _O_APPEND, // Moves the file pointer to the end of the file before every write operation. Create = _O_CREAT, // Creates a file and opens it for writing. Has no effect if the file specified by filename exists. PermissionMode is required. Temporary = _O_TEMPORARY, // Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified. - Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. + Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists. Truncate = _O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY. // Note: The TRUNC flag destroys the contents of the specified file. diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index c65ba373f8..bc0c9e537a 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -711,8 +711,8 @@ namespace AzFramework } } - AZ::IO::FixedMaxPath projectUserPath; - if (m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath)) + if (AZ::IO::FixedMaxPath projectUserPath; + m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath)) { fileIoBase->SetAlias("@user@", projectUserPath.c_str()); AZ::IO::FixedMaxPath projectLogPath = projectUserPath / "log"; @@ -721,6 +721,15 @@ namespace AzFramework CreateUserCache(projectUserPath, *fileIoBase); } + else + { + AZ::IO::FixedMaxPath fallbackLogPath = GetEngineRoot(); + fallbackLogPath /= "user"; + fileIoBase->SetAlias("@user@", fallbackLogPath.c_str()); + fallbackLogPath /= "log"; + fileIoBase->SetAlias("@log@", fallbackLogPath.c_str()); + fileIoBase->CreatePath(fallbackLogPath.c_str()); + } } } diff --git a/Code/Tools/Standalone/Source/LUA/LUAEditorContextInterface.h b/Code/Tools/Standalone/Source/LUA/LUAEditorContextInterface.h index 1bbfe83126..01051f42e3 100644 --- a/Code/Tools/Standalone/Source/LUA/LUAEditorContextInterface.h +++ b/Code/Tools/Standalone/Source/LUA/LUAEditorContextInterface.h @@ -70,6 +70,52 @@ namespace LUAEditor , m_bIsModified(false) , m_bIsBeingSaved(false) , m_PresetLineAtOpen(1){} + + // Copy constructor does not copy over open file handle + DocumentInfo(const DocumentInfo& other) + : m_assetId(other.m_assetId) + , m_scriptAsset(other.m_scriptAsset) + , m_assetName(other.m_assetName) + , m_displayName(other.m_displayName) + , m_lastKnownModTime(other.m_lastKnownModTime) + , m_sourceControlInfo(other.m_sourceControlInfo) + , m_bSourceControl_Ready(other.m_bSourceControl_Ready) + , m_bSourceControl_BusyGettingStats(other.m_bSourceControl_BusyGettingStats) + , m_bSourceControl_BusyRequestingEdit(other.m_bSourceControl_BusyRequestingEdit) + , m_bSourceControl_CanWrite(other.m_bSourceControl_CanWrite) + , m_bSourceControl_CanCheckOut(other.m_bSourceControl_CanCheckOut) + , m_bDataIsLoaded(other.m_bDataIsLoaded) + , m_bDataIsWritten(other.m_bDataIsWritten) + , m_bCloseAfterSave(other.m_bCloseAfterSave) + , m_bUntitledDocument(other.m_bUntitledDocument) + , m_bIsModified(other.m_bIsModified) + , m_bIsBeingSaved(other.m_bIsBeingSaved) + , m_PresetLineAtOpen(other.m_PresetLineAtOpen) + {} + + DocumentInfo& operator=(const DocumentInfo& other) + { + m_assetId = other.m_assetId; + m_scriptAsset = other.m_scriptAsset; + m_assetName = other.m_assetName; + m_displayName = other.m_displayName; + m_lastKnownModTime = other.m_lastKnownModTime; + m_sourceControlInfo = other.m_sourceControlInfo; + m_bSourceControl_Ready = other.m_bSourceControl_Ready; + m_bSourceControl_BusyGettingStats = other.m_bSourceControl_BusyGettingStats; + m_bSourceControl_BusyRequestingEdit = other.m_bSourceControl_BusyRequestingEdit; + m_bSourceControl_CanWrite = other.m_bSourceControl_CanWrite; + m_bSourceControl_CanCheckOut = other.m_bSourceControl_CanCheckOut; + m_bDataIsLoaded = other.m_bDataIsLoaded; + m_bDataIsWritten = other.m_bDataIsWritten; + m_bCloseAfterSave = other.m_bCloseAfterSave; + m_bUntitledDocument = other.m_bUntitledDocument; + m_bIsModified = other.m_bIsModified; + m_bIsBeingSaved = other.m_bIsBeingSaved; + m_PresetLineAtOpen = other.m_PresetLineAtOpen; + + return *this; + } }; class ContextInterface From f20ae8345a398c7fd763638173f1ec76b65970a5 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 3 Jun 2021 21:58:46 -0700 Subject: [PATCH 083/105] Add Open Project folder menu item --- .../Source/ProjectButtonWidget.cpp | 30 +++++++++---------- .../Source/ProjectButtonWidget.h | 4 --- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index b1dbd984fb..ee4d48fe7f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -11,7 +11,7 @@ */ #include - +#include #include #include @@ -81,18 +81,24 @@ namespace O3DE::ProjectManager m_projectImageLabel = new LabelButton(this); m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight); m_projectImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); }); vLayout->addWidget(m_projectImageLabel); m_projectImageLabel->setPixmap( QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding)); - QMenu* newProjectMenu = new QMenu(this); - m_editProjectAction = newProjectMenu->addAction(tr("Edit Project Settings...")); - newProjectMenu->addSeparator(); - m_copyProjectAction = newProjectMenu->addAction(tr("Duplicate")); - newProjectMenu->addSeparator(); - m_removeProjectAction = newProjectMenu->addAction(tr("Remove from O3DE")); - m_deleteProjectAction = newProjectMenu->addAction(tr("Delete this Project")); + QMenu* menu = new QMenu(this); + menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); + menu->addSeparator(); + menu->addAction(tr("Open Project folder..."), this, [this]() + { + AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path); + }); + menu->addSeparator(); + menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo.m_path); }); + menu->addSeparator(); + menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); + menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); QFrame* footer = new QFrame(this); QHBoxLayout* hLayout = new QHBoxLayout(); @@ -104,17 +110,11 @@ namespace O3DE::ProjectManager QPushButton* projectMenuButton = new QPushButton(this); projectMenuButton->setObjectName("projectMenuButton"); - projectMenuButton->setMenu(newProjectMenu); + projectMenuButton->setMenu(menu); hLayout->addWidget(projectMenuButton); } vLayout->addWidget(footer); - - connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); }); - connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectInfo.m_path); }); - connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectInfo.m_path); }); - connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectInfo.m_path); }); - connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectInfo.m_path); }); } void ProjectButton::SetButtonEnabled(bool enabled) diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index 3ac69b7603..bb61f7354b 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -71,9 +71,5 @@ namespace O3DE::ProjectManager ProjectInfo m_projectInfo; LabelButton* m_projectImageLabel; - QAction* m_editProjectAction; - QAction* m_copyProjectAction; - QAction* m_removeProjectAction; - QAction* m_deleteProjectAction; }; } // namespace O3DE::ProjectManager From fefa46dd6a8c563853b2cae7d3498ad617252881 Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Fri, 4 Jun 2021 02:39:25 -0700 Subject: [PATCH 084/105] Added DiffuseGlobalIlluminationFeatureProcessor and moved the DiffuseProbeGrid files to the DiffuseGlobalIllumination directory --- .../DiffuseComposite.azsl | 12 +- .../DiffuseProbeGridDownsample.azsl | 12 +- ...balIlluminationFeatureProcessorInterface.h | 40 +++++++ ...iffuseProbeGridFeatureProcessorInterface.h | 0 .../Code/Source/CommonSystemComponent.cpp | 20 ++-- ...fuseGlobalIlluminationFeatureProcessor.cpp | 113 ++++++++++++++++++ ...iffuseGlobalIlluminationFeatureProcessor.h | 52 ++++++++ .../DiffuseProbeGrid.cpp | 2 +- .../DiffuseProbeGrid.h | 2 +- .../DiffuseProbeGridBlendDistancePass.cpp | 4 +- .../DiffuseProbeGridBlendDistancePass.h | 0 .../DiffuseProbeGridBlendIrradiancePass.cpp | 4 +- .../DiffuseProbeGridBlendIrradiancePass.h | 0 .../DiffuseProbeGridBorderUpdatePass.cpp | 4 +- .../DiffuseProbeGridBorderUpdatePass.h | 0 .../DiffuseProbeGridClassificationPass.cpp | 4 +- .../DiffuseProbeGridClassificationPass.h | 3 +- .../DiffuseProbeGridFeatureProcessor.cpp | 2 +- .../DiffuseProbeGridFeatureProcessor.h | 4 +- .../DiffuseProbeGridRayTracingPass.cpp | 4 +- .../DiffuseProbeGridRayTracingPass.h | 2 +- .../DiffuseProbeGridRelocationPass.cpp | 4 +- .../DiffuseProbeGridRelocationPass.h | 3 +- .../DiffuseProbeGridRenderPass.cpp | 4 +- .../DiffuseProbeGridRenderPass.h | 0 .../DiffuseProbeGridTextureReadback.cpp | 4 +- .../DiffuseProbeGridTextureReadback.h | 2 +- .../Code/atom_feature_common_files.cmake | 42 +++---- .../atom_feature_common_public_files.cmake | 3 +- ...DiffuseGlobalIlluminationComponentConfig.h | 10 +- ...eGlobalIlluminationComponentController.cpp | 25 ++-- ...useGlobalIlluminationComponentController.h | 7 +- .../DiffuseProbeGridComponentController.h | 2 +- 33 files changed, 291 insertions(+), 99 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h rename Gems/Atom/Feature/Common/Code/Include/Atom/Feature/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessorInterface.h (100%) create mode 100644 Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGrid.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGrid.h (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendDistancePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendDistancePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendIrradiancePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBlendIrradiancePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBorderUpdatePass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridBorderUpdatePass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridClassificationPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridClassificationPass.h (97%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessor.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridFeatureProcessor.h (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRayTracingPass.cpp (99%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRayTracingPass.h (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRelocationPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRelocationPass.h (97%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRenderPass.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridRenderPass.h (100%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridTextureReadback.cpp (98%) rename Gems/Atom/Feature/Common/Code/Source/{DiffuseProbeGrid => DiffuseGlobalIllumination}/DiffuseProbeGridTextureReadback.h (96%) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl index b3b26c4d42..3e2fda8d5d 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_imageScale; } #include @@ -148,13 +151,10 @@ float3 SampleGlobalIBL(uint sampleIndex, uint2 screenCoords, float depth, float3 PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { uint2 screenCoords = IN.m_position.xy; - - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - const float ImageScaleInverse = 1.0f / ImageScale; + float imageScaleInverse = 1.0f / PassSrg::m_imageScale; // compute image coords for the downsampled probe irradiance image - uint2 probeIrradianceCoords = screenCoords * ImageScaleInverse; + uint2 probeIrradianceCoords = screenCoords * imageScaleInverse; float depth = PassSrg::m_depth.Load(screenCoords, sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords, sampleIndex); @@ -165,7 +165,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float3 diffuse = float3(0.0f, 0.0f, 0.0f); if (useProbeIrradiance > 0.0f) { - float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, ImageScale); + float3 irradiance = SampleProbeIrradiance(sampleIndex, probeIrradianceCoords, depth, normal, albedo, PassSrg::m_imageScale); diffuse = (albedo.rgb / PI) * irradiance; } else diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl index e051317567..85772c6577 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl @@ -31,6 +31,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // scale multiplier of the downsampled size to the fullscreen size (e.g., 4) + uint m_outputImageScale; } #include @@ -56,16 +59,13 @@ struct PSOutput // Pixel Shader PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) { - // the downsample is 1/4 resolution - // [GFX TODO][ATOM-6172] Add image scale PassSrg constant to the DiffuseProbeGrid downsample/upsample - const uint ImageScale = 4; - uint2 screenCoords = IN.m_position.xy * ImageScale; + uint2 screenCoords = IN.m_position.xy * PassSrg::m_outputImageScale; float downsampledDepth = 0; float4 downsampledEncodedNormal; - for (uint y = 0; y < ImageScale; ++y) + for (uint y = 0; y < PassSrg::m_outputImageScale; ++y) { - for (uint x = 0; x < ImageScale; ++x) + for (uint x = 0; x < PassSrg::m_outputImageScale; ++x) { float depth = PassSrg::m_depth.Load(screenCoords + int2(x, y), sampleIndex).r; float4 encodedNormal = PassSrg::m_normal.Load(screenCoords + int2(x, y), sampleIndex); diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h new file mode 100644 index 0000000000..88faac1728 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h @@ -0,0 +1,40 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + enum class DiffuseGlobalIlluminationQualityLevel : uint8_t + { + Low, + Medium, + High + }; + + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessorInterface + : public RPI::FeatureProcessor + { + public: + AZ_RTTI(AZ::Render::DiffuseProbeGridFeatureProcessorInterface, "{BD8CA35A-47C3-4FD8-932B-18495EF07527}"); + + virtual void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) = 0; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h rename to Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 00c55cbade..87e134477a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -91,14 +91,15 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -135,6 +136,7 @@ namespace AZ LightingPreset::Reflect(context); ModelPreset::Reflect(context); DiffuseProbeGridFeatureProcessor::Reflect(context); + DiffuseGlobalIlluminationFeatureProcessor::Reflect(context); RayTracingFeatureProcessor::Reflect(context); if (SerializeContext* serialize = azrtti_cast(context)) @@ -191,6 +193,7 @@ namespace AZ AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); // Add SkyBox pass @@ -285,6 +288,7 @@ namespace AZ void CommonSystemComponent::Deactivate() { AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp new file mode 100644 index 0000000000..5040665456 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -0,0 +1,113 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void DiffuseGlobalIlluminationFeatureProcessor::Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext + ->Class() + ->Version(0); + } + } + + void DiffuseGlobalIlluminationFeatureProcessor::Activate() + { + EnableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::Deactivate() + { + DisableSceneNotification(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) + { + m_qualityLevel = qualityLevel; + + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] RPI::RenderPipelinePtr pipeline) + { + UpdatePasses(); + } + + void DiffuseGlobalIlluminationFeatureProcessor::UpdatePasses() + { + float sizeMultiplier = 0.0f; + switch (m_qualityLevel) + { + case DiffuseGlobalIlluminationQualityLevel::Low: + sizeMultiplier = 0.25f; + break; + case DiffuseGlobalIlluminationQualityLevel::Medium: + sizeMultiplier = 0.5f; + break; + case DiffuseGlobalIlluminationQualityLevel::High: + sizeMultiplier = 1.0f; + break; + default: + AZ_Assert(false, "Unknown DiffuseGlobalIlluminationQualityLevel [%d]", m_qualityLevel); + break; + } + + // update the size multiplier on the DiffuseProbeGridDownsamplePass output + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); + const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); + for (RPI::Pass* pass : downsamplePasses) + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } + + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); + downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + + // update the image scale on the DiffuseComposite pass + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); + const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); + for (RPI::Pass* pass : compositePasses) + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); + compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h new file mode 100644 index 0000000000..81dc1487fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h @@ -0,0 +1,52 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + //! This class provides general features and configuration for the diffuse global illumination environment, + //! which consists of DiffuseProbeGrids and the diffuse Global IBL cubemap. + class DiffuseGlobalIlluminationFeatureProcessor final + : public DiffuseGlobalIlluminationFeatureProcessorInterface + { + public: + AZ_RTTI(AZ::Render::DiffuseGlobalIlluminationFeatureProcessor, "{14F7DF46-AA2C-49EF-8A2C-0A7CB7390BB7}", DiffuseGlobalIlluminationFeatureProcessorInterface); + + static void Reflect(AZ::ReflectContext* context); + + DiffuseGlobalIlluminationFeatureProcessor() = default; + virtual ~DiffuseGlobalIlluminationFeatureProcessor() = default; + + void Activate() override; + void Deactivate() override; + + // DiffuseGlobalIlluminationFeatureProcessorInterface overrides + void SetQualityLevel(DiffuseGlobalIlluminationQualityLevel qualityLevel) override; + + private: + AZ_DISABLE_COPY_MOVE(DiffuseGlobalIlluminationFeatureProcessor); + + // RPI::SceneNotificationBus::Handler overrides + void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override; + void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override; + + void UpdatePasses(); + + DiffuseGlobalIlluminationQualityLevel m_qualityLevel = DiffuseGlobalIlluminationQualityLevel::Low; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp index a55d8fc78c..cfdfd9efa4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h index ff6ad719cf..4a732ae7af 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGrid.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp index 2a06dacf3f..04e68136f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp index 4818018ea3..f1fe792542 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp index 8821de8a9d..a24d54d9b7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -18,7 +17,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp index 65f1c2dd5a..8d39e6fcc8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h index 677e16ac42..eef59c3753 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 060d51d1d0..ba24bcc001 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h index 19e9bf1b1d..3fb70830d0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h @@ -12,8 +12,8 @@ #pragma once -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp similarity index 99% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp index 65d71b8272..c77f66645e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -25,7 +24,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h index bb35803e51..910537a2f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp index 86a26f002d..2f0ed373b9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp @@ -10,7 +10,6 @@ * */ -#include #include #include #include @@ -22,7 +21,8 @@ #include #include #include -#include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h similarity index 97% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h index 437ac2f3f5..1900f8b786 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h @@ -12,7 +12,6 @@ #pragma once #include - #include #include #include @@ -22,7 +21,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp index 4f9221a65f..f025079994 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp @@ -10,12 +10,12 @@ * */ -#include -#include #include #include #include #include +#include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp similarity index 98% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp index bc619cc277..5465d0da33 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h similarity index 96% rename from Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h rename to Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h index 1becd6fb3e..6f7ebb9240 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 76b71e5fae..0df5501803 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -119,26 +119,28 @@ set(FILES Source/Decals/AsyncLoadTracker.h Source/Decals/DecalTextureArrayFeatureProcessor.h Source/Decals/DecalTextureArrayFeatureProcessor.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridBorderUpdatePass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRelocationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridClassificationPass.h - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridRenderPass.h - Source/DiffuseProbeGrid/DiffuseProbeGrid.cpp - Source/DiffuseProbeGrid/DiffuseProbeGrid.h - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.cpp - Source/DiffuseProbeGrid/DiffuseProbeGridTextureReadback.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.h - Source/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRayTracingPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendIrradiancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBlendDistancePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridBorderUpdatePass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRelocationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridClassificationPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridRenderPass.h + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGrid.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.cpp + Source/DiffuseGlobalIllumination/DiffuseProbeGridTextureReadback.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.h + Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp Source/DisplayMapper/AcesOutputTransformPass.cpp Source/DisplayMapper/AcesOutputTransformLutPass.cpp Source/DisplayMapper/ApplyShaperLookupTablePass.cpp diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 9034859707..9f9c64dd46 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -22,7 +22,8 @@ set(FILES Include/Atom/Feature/CoreLights/SimpleSpotLightFeatureProcessorInterface.h Include/Atom/Feature/CoreLights/ShadowConstants.h Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h - Include/Atom/Feature/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessorInterface.h + Include/Atom/Feature/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessorInterface.h Include/Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h Include/Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h index 23296967a5..fb99a99e1a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentConfig.h @@ -14,20 +14,12 @@ #include #include +#include namespace AZ { namespace Render { - enum class DiffuseGlobalIlluminationQualityLevel : uint32_t - { - Low, - Medium, - High, - - Count - }; - class DiffuseGlobalIlluminationComponentConfig final : public ComponentConfig { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp index 4c7f37bd4a..a9feb0185c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -11,11 +11,8 @@ */ #include - -//#include - +#include #include -//#include namespace AZ { @@ -55,18 +52,22 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) { - m_entityId = entityId; + AZ_UNUSED(entityId); + + const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + m_featureProcessor = scene->GetFeatureProcessor(); + + OnConfigChanged(); } void DiffuseGlobalIlluminationComponentController::Deactivate() { - //m_postProcessInterface = nullptr; - m_entityId.SetInvalid(); } void DiffuseGlobalIlluminationComponentController::SetConfiguration(const DiffuseGlobalIlluminationComponentConfig& config) { m_configuration = config; + OnConfigChanged(); } @@ -77,15 +78,7 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::OnConfigChanged() { - // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. - //const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - //DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); - //DisplayMapperConfigurationDescriptor desc; - //desc.m_operationType = m_configuration.m_displayMapperOperation; - //desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; - //desc.m_ldrColorGradingLut = m_configuration.m_ldrColorGradingLut; - //desc.m_acesParameterOverrides = m_configuration.m_acesParameterOverrides; - //fp->RegisterDisplayMapperConfiguration(desc); + m_featureProcessor->SetQualityLevel(m_configuration.m_qualityLevel); } } // namespace Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h index 8700e1ffb5..81da772129 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.h @@ -14,12 +14,9 @@ #include #include - +#include #include -//#include -//#include - namespace AZ { namespace Render @@ -49,7 +46,7 @@ namespace AZ void OnConfigChanged(); DiffuseGlobalIlluminationComponentConfig m_configuration; - EntityId m_entityId; + DiffuseGlobalIlluminationFeatureProcessorInterface* m_featureProcessor = nullptr; }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h index ef606d2170..51a17cb2cf 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridComponentController.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include From 05e20803a89253bd8f7ba6ef507242e08d833d72 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:32:06 +0100 Subject: [PATCH 085/105] First pass for getting things ready for grid snap button (#1118) * first pass of change to simplify snapping for snap-to-grid button and fix snapping bug caused by non-uniform scale --- .../AzManipulatorTestFrameworkUtils.h | 25 ++-- .../AzManipulatorTestFrameworkUtils.cpp | 58 +++++---- .../Tests/GridSnappingTest.cpp | 120 +++++++++++++----- .../Manipulators/BaseManipulator.cpp | 5 + .../Manipulators/EditorVertexSelection.cpp | 27 ++-- .../Manipulators/LinearManipulator.cpp | 70 ++++------ .../Manipulators/LinearManipulator.h | 12 +- .../Manipulators/ManipulatorSnapping.cpp | 36 ++++-- .../Manipulators/ManipulatorSnapping.h | 22 ++-- .../Manipulators/MultiLinearManipulator.cpp | 28 ++-- .../Manipulators/MultiLinearManipulator.h | 2 - .../Manipulators/PlanarManipulator.cpp | 50 +++----- .../Manipulators/PlanarManipulator.h | 13 +- .../EditorNonUniformScaleComponentMode.cpp | 48 ++++--- .../EditorTransformComponentSelection.cpp | 39 ++---- 15 files changed, 295 insertions(+), 260 deletions(-) diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h index dcddf1e5a5..f1c32e4d8d 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h @@ -12,17 +12,22 @@ #pragma once -#include -#include #include +#include +#include +#include namespace AzManipulatorTestFramework { - //! Create a linear manipulator with a unit sphere bounds. + //! Create a linear manipulator with a unit sphere bound. AZStd::shared_ptr CreateLinearManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, - const AZ::Vector3& position = AZ::Vector3::CreateZero(), - const float radius = 1.0f); + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + float radius = 1.0f); + + //! Create a planar manipulator with a unit sphere bound. + AZStd::shared_ptr CreatePlanarManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + float radius = 1.0f); //! Create a mouse pick from the specified ray and screen point. AzToolsFramework::ViewportInteraction::MousePick CreateMousePick( @@ -34,14 +39,12 @@ namespace AzManipulatorTestFramework //! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers. AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction( - const AzToolsFramework::ViewportInteraction::MousePick& mousePick, - AzToolsFramework::ViewportInteraction::MouseButtons buttons, + const AzToolsFramework::ViewportInteraction::MousePick& mousePick, AzToolsFramework::ViewportInteraction::MouseButtons buttons, AzToolsFramework::ViewportInteraction::InteractionId interactionId, AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers); //! Create a mouse buttons from the specified mouse button. - AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons( - AzToolsFramework::ViewportInteraction::MouseButton button); + AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton button); //! Create a mouse interaction event from the specified interaction and event. AzToolsFramework::ViewportInteraction::MouseInteractionEvent CreateMouseInteractionEvent( @@ -61,5 +64,5 @@ namespace AzManipulatorTestFramework AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState); //! Default viewport size (1080p) in 16:9 aspect ratio. - const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f); + inline const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f); } // namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp index b255fcae73..985c21cf8e 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace AzManipulatorTestFramework @@ -28,22 +27,21 @@ namespace AzManipulatorTestFramework using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent; using MousePick = AzToolsFramework::ViewportInteraction::MousePick; - AZStd::shared_ptr CreateLinearManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, - const AZ::Vector3& position, - const float radius) + // create a default sphere view for a manipulator for simple intersection + template + void SetupManipulatorView( + AZStd::shared_ptr manipulator, const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, + const AZ::Vector3& position, const float radius) { - auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity()); - manipulator->SetLocalPosition(position); - // unit sphere view auto sphereView = AzToolsFramework::CreateManipulatorViewSphere( AZ::Colors::Red, radius, - [](const MouseInteraction& /*mouseInteraction*/, const bool /*mouseOver*/, - const AZ::Color& defaultColor) - { - return defaultColor; - }, true); + []([[maybe_unused]] const MouseInteraction& mouseInteraction, [[maybe_unused]] const bool mouseOver, + const AZ::Color& defaultColor) + { + return defaultColor; + }, + true); // unit sphere bound AzToolsFramework::Picking::BoundShapeSphere sphereBound; @@ -62,6 +60,26 @@ namespace AzManipulatorTestFramework // this would occur internally when the manipulator is drawn but we must do manually here to ensure that the // bounds will always be valid upon instantiation view->RefreshBound(manipulatorManagerId, manipulator->GetManipulatorId(), sphereBound); + } + + AZStd::shared_ptr CreateLinearManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius) + { + auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity()); + manipulator->SetLocalPosition(position); + + SetupManipulatorView(manipulator, manipulatorManagerId, position, radius); + + return manipulator; + } + + AZStd::shared_ptr CreatePlanarManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius) + { + auto manipulator = AzToolsFramework::PlanarManipulator::MakeShared(AZ::Transform::CreateIdentity()); + manipulator->SetLocalPosition(position); + + SetupManipulatorView(manipulator, manipulatorManagerId, position, radius); return manipulator; } @@ -104,8 +122,7 @@ namespace AzManipulatorTestFramework return buttons; } - MouseInteractionEvent CreateMouseInteractionEvent( - const MouseInteraction& mouseInteraction, MouseEvent event) + MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event) { return MouseInteractionEvent(mouseInteraction, event); } @@ -114,8 +131,7 @@ namespace AzManipulatorTestFramework { AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event( AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, - event); + &AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, event); } AzFramework::CameraState SetCameraStatePosition(const AZ::Vector3& position, AzFramework::CameraState& cameraState) @@ -133,9 +149,7 @@ namespace AzManipulatorTestFramework AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState) { - return { - aznumeric_cast(cameraState.m_viewportSize.GetX() / 2.f), - aznumeric_cast(cameraState.m_viewportSize.GetY() / 2.f) - }; + return { aznumeric_cast(cameraState.m_viewportSize.GetX() / 2.f), + aznumeric_cast(cameraState.m_viewportSize.GetY() / 2.f) }; } -} // namespace UnitTest +} // namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp index e6468c1ebb..d6006ba74f 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp @@ -10,52 +10,55 @@ * */ +#include "AzManipulatorTestFrameworkTestFixtures.h" + #include #include #include -#include "AzManipulatorTestFrameworkTestFixtures.h" -#include -#include -#include #include +#include +#include +#include +#include +#include #include +#include namespace UnitTest { - class GridSnappingFixture - : public ToolsApplicationFixture + class GridSnappingFixture : public ToolsApplicationFixture { public: GridSnappingFixture() : m_viewportManipulatorInteraction(AZStd::make_unique()) - , m_actionDispatcher(AZStd::make_unique(*m_viewportManipulatorInteraction)) - , m_linearManipulator( - AzManipulatorTestFramework::CreateLinearManipulator( - m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), - /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), - /*radius=*/m_boundsRadius)) - {} + , m_actionDispatcher( + AZStd::make_unique(*m_viewportManipulatorInteraction)) + { + } protected: void SetUpEditorFixtureImpl() override { - m_cameraState = AzFramework::CreateIdentityDefaultCamera( - AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); + m_cameraState = + AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); } public: - const float m_boundsRadius = 1.0f; AZStd::unique_ptr m_viewportManipulatorInteraction; AZStd::unique_ptr m_actionDispatcher; - AZStd::shared_ptr m_linearManipulator; AzFramework::CameraState m_cameraState; }; TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize) { + AZStd::shared_ptr linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), + /*radius=*/m_boundsRadius)); + // the initial starting position of the manipulator (in front of the camera) - const auto initialPositionWorld = m_linearManipulator->GetLocalPosition(); + const auto initialPositionWorld = linearManipulator->GetLocalPosition(); // where the manipulator should end up (in front and to the left of the camera) const auto finalPositionWorld = AZ::Vector3(-10.0f, 50.0f, 0.0f); // perspective scale factor for manipulator distance to camera @@ -66,21 +69,18 @@ namespace UnitTest // adjusted final world position taking into account the manipulator position relative to the camera const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound); // calculate the position in screen space of the initial position of the manipulator - const auto initialPositionScreen = - AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); + const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); // calculate the position in screen space of the final position of the manipulator const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState); // callback to update the manipulator's current position - m_linearManipulator->InstallMouseMoveCallback( - [this](const AzToolsFramework::LinearManipulator::Action& action) - { - auto pos = action.LocalPosition(); - m_linearManipulator->SetLocalPosition(pos); - }); + linearManipulator->InstallMouseMoveCallback( + [this, linearManipulator](const AzToolsFramework::LinearManipulator::Action& action) + { + linearManipulator->SetLocalPosition(action.LocalPosition()); + }); - m_actionDispatcher - ->EnableSnapToGrid() + m_actionDispatcher->EnableSnapToGrid() ->GridSize(5.0f) ->CameraState(m_cameraState) ->MousePosition(initialPositionScreen) @@ -89,7 +89,67 @@ namespace UnitTest ->MousePosition(finalPositionScreen) ->MouseLButtonUp() ->ExpectManipulatorNotBeingInteracted() - ->ExpectTrue(m_linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f)) - ; + ->ExpectTrue(linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f)); + } + + template + void ValidateManipulatorSnappingBehavior( + AZStd::shared_ptr manipulator, AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher, + const AzFramework::CameraState& cameraState) + { + manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f))); + + // the initial starting position of the manipulator (in front of the camera) + const auto initialPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.15f); + // where the manipulator should end up (unmoved) + const auto finalPositionWorld = manipulator->GetLocalPosition(); + // where we should move the mouse to + const auto attemptPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.35f); + // calculate the position in screen space of the initial position of the manipulator + const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, cameraState); + // calculate the position in screen space of the final position of the manipulator + const auto attemptPositionScreen = AzFramework::WorldToScreen(attemptPositionWorld, cameraState); + + // callback to update the manipulator's current position + manipulator->InstallMouseMoveCallback( + [manipulator](const typename Manipulator::Action& action) + { + manipulator->SetLocalPosition(action.LocalPosition()); + }); + + actionDispatcher->EnableSnapToGrid() + ->GridSize(1.0f) + ->CameraState(cameraState) + ->MousePosition(initialPositionScreen) + ->MouseLButtonDown() + ->ExpectManipulatorBeingInteracted() + ->MousePosition(attemptPositionScreen) + ->MouseLButtonUp() + ->ExpectManipulatorNotBeingInteracted() + ->ExpectThat(manipulator->GetLocalPosition(), IsCloseTolerance(finalPositionWorld, 0.01f)); + } + + TEST_F(GridSnappingFixture, MouseDownAndMoveLinearManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize) + { + AZStd::shared_ptr linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f), + /*radius=*/m_boundsRadius)); + + linearManipulator->SetAxis(AZ::Vector3::CreateAxisY()); + + ValidateManipulatorSnappingBehavior(linearManipulator, m_actionDispatcher.get(), m_cameraState); + } + + TEST_F(GridSnappingFixture, MouseDownAndMovePlanarManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize) + { + AZStd::shared_ptr planarManipulator(AzManipulatorTestFramework::CreatePlanarManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f), + /*radius=*/m_boundsRadius)); + + planarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); + + ValidateManipulatorSnappingBehavior(planarManipulator, m_actionDispatcher.get(), m_cameraState); } } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp index e7235f0f82..955d10d3bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp @@ -433,6 +433,11 @@ namespace AzToolsFramework return m_manipulatorSpaceWithLocalTransform.GetSpace(); } + const AZ::Vector3& Manipulators::GetNonUniformScale() const + { + return m_manipulatorSpaceWithLocalTransform.GetNonUniformScale(); + } + void Manipulators::SetSpace(const AZ::Transform& worldFromLocal) { m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp index 4d10a4c171..55a8464ba6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp @@ -192,8 +192,7 @@ namespace AzToolsFramework /// for each vertex associated with the translation manipulator to use with offset calculations when updating. template void InitializeVertexLookup( - IndexedTranslationManipulator& translationManipulator, - const AZ::EntityId entityId, const AZ::Vector3& snapOffset) + IndexedTranslationManipulator& translationManipulator, const AZ::EntityId entityId) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -202,7 +201,7 @@ namespace AzToolsFramework AZ::FixedVerticesRequestBus::Bind(fixedVertices, entityId); translationManipulator.Process( - [snapOffset, fixedVertices] + [fixedVertices] (typename IndexedTranslationManipulator::VertexLookup& vertexLookup) { Vertex vertex; @@ -213,7 +212,7 @@ namespace AzToolsFramework if (found) { - vertexLookup.m_start = vertex + AZ::AdaptVertexIn(snapOffset); + vertexLookup.m_start = vertex; vertexLookup.m_offset = Vertex::CreateZero(); } }); @@ -250,10 +249,10 @@ namespace AzToolsFramework // linear manipulator callbacks m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseDownCallback( - [this](const LinearManipulator::Action& action) + [this]([[maybe_unused]] const LinearManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_positionSnapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseMoveCallback( @@ -264,17 +263,17 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseUpCallback( - [this](const LinearManipulator::Action& /*action*/) + [this]([[maybe_unused]] const LinearManipulator::Action& action) { EndBatchMovement(); }); // planar manipulator callbacks m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseDownCallback( - [this](const PlanarManipulator::Action& action) + [this]([[maybe_unused]] const PlanarManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseMoveCallback( @@ -285,17 +284,17 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseUpCallback( - [this](const PlanarManipulator::Action& /*action*/) + [this]([[maybe_unused]] const PlanarManipulator::Action& action) { EndBatchMovement(); }); // surface manipulator callbacks m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseDownCallback( - [this](const SurfaceManipulator::Action& action) + [this]([[maybe_unused]] const SurfaceManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseMoveCallback( @@ -306,7 +305,7 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseUpCallback( - [this](const SurfaceManipulator::Action& /*action*/) + [this]([[maybe_unused]] const SurfaceManipulator::Action& action) { EndBatchMovement(); }); @@ -893,7 +892,7 @@ namespace AzToolsFramework { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), AZ::Vector3::CreateZero()); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); // note: AdaptVertexIn/Out is to ensure we clamp the vertex local Z position to 0 if // dealing with Vector2s when setting the position of the manipulator. const AZ::Vector3 localOffset = diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp index aa84fc5752..34ae28bd13 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp @@ -23,8 +23,8 @@ namespace AzToolsFramework { LinearManipulator::Starter CalculateLinearManipulationDataStart( const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction, - const float intersectionDistance, const AzFramework::CameraState& cameraState) + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance, + const AzFramework::CameraState& cameraState) { const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( @@ -50,28 +50,9 @@ namespace AzToolsFramework manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, startTransition.m_localNormal, start.m_localHitPosition); - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; - - // calculate position amount to snap, to align with grid - const AZ::Vector3 positionSnapOffset = snapping && !gridSnapAction.m_localSnapping - ? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip) - : AZ::Vector3::CreateZero(); - - const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale()); - const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); - // calculate scale amount to snap, to align to round scale value - const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping - ? localRotation.GetInverseFull().TransformVector(CalculateSnappedOffset( - localRotation.TransformVector(localScale), axis, gridSize * scaleRecip)) - : AZ::Vector3::CreateZero(); - start.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; - start.m_positionSnapOffset = positionSnapOffset; - start.m_scaleSnapOffset = scaleSnapOffset; - start.m_localPosition = localTransform.GetTranslation() + positionSnapOffset; - start.m_localScale = localScale + scaleSnapOffset; + start.m_localPosition = localTransform.GetTranslation(); + start.m_localScale = AZ::Vector3(localTransform.GetUniformScale());; start.m_localAxis = axis; // sign to determine which side of the linear axis we pressed // (useful to know when the visual axis flips to face the camera) @@ -87,7 +68,7 @@ namespace AzToolsFramework LinearManipulator::Action CalculateLinearManipulationDataAction( const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction) + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) { const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( @@ -108,31 +89,34 @@ namespace AzToolsFramework GetCameraState(interaction.m_interactionId.m_viewportId)); const AZ::Vector3 axis = TransformDirectionNoScaling(localTransform, fixed.m_axis); - // The local positions have been transformed to the reference frame of the object being manipulated. But they appear in the world - // with non-uniform scale applied, and the object being manipulated will want to work with unscaled deltas, so we need to divide by - // the non-uniform scale here. + // the local positions have been transformed to the reference frame of the object being manipulated, but they appear in the world + // with non-uniform scale applied, the object being manipulated will want to work with unscaled deltas, so we need to divide by + // the non-uniform scale here const AZ::Vector3 hitDelta = (localHitPosition - start.m_localHitPosition) / nonUniformScale; const AZ::Vector3 unsnappedOffset = axis * axis.Dot(hitDelta); - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal * axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal); - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; + const float scaleRecip = + manipulatorInteraction.m_scaleReciprocal * fixed.m_axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal); + const float gridSize = gridSnapParams.m_gridSize; + const bool snapping = gridSnapParams.m_gridSnap; LinearManipulator::Action action; action.m_fixed = fixed; action.m_start = start; action.m_current.m_localPositionOffset = snapping - ? unsnappedOffset + CalculateSnappedOffset(unsnappedOffset, axis, gridSize * scaleRecip) + ? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip) : unsnappedOffset; action.m_current.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; action.m_viewportId = interaction.m_interactionId.m_viewportId; const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); - const AZ::Vector3 scaledUnsnappedOffset = unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale); + const AZ::Vector3 scaledUnsnappedOffset = + unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale); + // how much to adjust the scale based on movement const AZ::Quaternion invLocalRotation = localRotation.GetInverseFull(); action.m_current.m_localScaleOffset = snapping - ? invLocalRotation.TransformVector((scaledUnsnappedOffset + CalculateSnappedOffset(scaledUnsnappedOffset, axis, gridSize * scaleRecip))) + ? invLocalRotation.TransformVector(CalculateSnappedAmount(scaledUnsnappedOffset, axis, gridSize * scaleRecip)) : invLocalRotation.TransformVector(scaledUnsnappedOffset); // record what modifier keys are held during this action @@ -171,19 +155,18 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_starter = CalculateLinearManipulationDataStart( - m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance, + m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance, GetCameraState(interaction.m_interactionId.m_viewportId)); if (m_onLeftMouseDownCallback) { + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); + m_onLeftMouseDownCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction)); } } @@ -195,8 +178,8 @@ namespace AzToolsFramework // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_onMouseMoveCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, + interaction)); } } @@ -208,8 +191,7 @@ namespace AzToolsFramework // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_onLeftMouseUpCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction)); } } @@ -232,8 +214,8 @@ namespace AzToolsFramework GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); const auto action = CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, + mouseInteraction); // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h index 576c543dce..c3d43a2535 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h @@ -20,7 +20,7 @@ namespace AzToolsFramework { - struct GridSnapAction; + struct GridSnapParameters; /// LinearManipulator serves as a visual tool for users to modify values /// in one dimension on an axis defined in 3D space. @@ -68,8 +68,6 @@ namespace AzToolsFramework AZ::Vector3 m_localScale; ///< The current scale of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. AZ::Vector3 m_localAxis; ///< The axis in the local space of the manipulator itself. - AZ::Vector3 m_positionSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. - AZ::Vector3 m_scaleSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to round scale increments. float m_sign; ///< Used to determine which side of the axis we clicked on in case it's flipped to face the camera. AzFramework::ScreenPoint m_screenPosition; ///< The initial position in screen space of the manipulator. }; @@ -91,7 +89,7 @@ namespace AzToolsFramework ViewportInteraction::KeyboardModifiers m_modifiers; int m_viewportId; ///< The id of the viewport this manipulator is being used in. AZ::Vector3 LocalScale() const { return m_start.m_localScale + m_current.m_localScaleOffset; } - AZ::Vector3 LocalScaleOffset() const { return m_start.m_scaleSnapOffset + m_current.m_localScaleOffset; } + AZ::Vector3 LocalScaleOffset() const { return m_current.m_localScaleOffset; } AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localPositionOffset; } AZ::Vector3 LocalPositionOffset() const { return m_current.m_localPositionOffset; } AZ::Vector2 ScreenOffset() const @@ -162,11 +160,11 @@ namespace AzToolsFramework LinearManipulator::Starter CalculateLinearManipulationDataStart( const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction, - float intersectionDistance, const AzFramework::CameraState& cameraState); + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance, + const AzFramework::CameraState& cameraState); LinearManipulator::Action CalculateLinearManipulationDataAction( const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction); + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp index 015ea8a3e3..8a6398d025 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp @@ -42,12 +42,6 @@ namespace AzToolsFramework { } - GridSnapAction::GridSnapAction(const GridSnapParameters& gridSnapParameters, const bool localSnapping) - : m_gridSnapParams(gridSnapParameters) - , m_localSnapping(localSnapping) - { - } - ManipulatorInteraction BuildManipulatorInteraction( const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection) @@ -57,19 +51,39 @@ namespace AzToolsFramework return {localFromWorldUniform.TransformPoint(worldRayOrigin), TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection), - ScaleReciprocal(worldFromLocalUniform), - NonUniformScaleReciprocal(nonUniformScale)}; + NonUniformScaleReciprocal(nonUniformScale), + ScaleReciprocal(worldFromLocalUniform)}; } - AZ::Vector3 CalculateSnappedOffset( - const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + struct SnapAdjustment + { + float m_existingSnapDistance; //!< How far to snap up or down to align to the grid. + float m_nextSnapDistance; //!< The snap increment (will return full signed value (grid size) when distance + //!< moved is greater than half of the grid size in either direction). + }; + + static SnapAdjustment CalculateSnapDistance(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) { // calculate total distance along axis const float axisDistance = axis.Dot(unsnappedPosition); // round to nearest step size const float snappedAxisDistance = floorf((axisDistance / size) + 0.5f) * size; + + return { axisDistance, snappedAxisDistance }; + } + + AZ::Vector3 CalculateSnappedOffset(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + { + const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size); // return offset along axis to snap to step size - return axis * (snappedAxisDistance - axisDistance); + return axis * (snapAdjustment.m_nextSnapDistance - snapAdjustment.m_existingSnapDistance); + } + + AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + { + const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size); + // return offset along axis to snap to step size + return axis * snapAdjustment.m_nextSnapDistance; } AZ::Vector3 CalculateSnappedTerrainPosition( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index e6c70079df..11860780c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -31,24 +31,15 @@ namespace AzToolsFramework float m_gridSize; }; - /// Structure to encapsulate the current grid snapping state. - struct GridSnapAction - { - GridSnapAction(const GridSnapParameters& gridSnapParameters, bool localSnapping); - - GridSnapParameters m_gridSnapParams; - bool m_localSnapping; - }; - /// Structure to hold transformed incoming viewport interaction from world space to manipulator space. struct ManipulatorInteraction { AZ::Vector3 m_localRayOrigin; ///< The ray origin (start) in the reference from of the manipulator. AZ::Vector3 m_localRayDirection; ///< The ray direction in the reference from of the manipulator. - float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the - ///< ray from world space to local space. AZ::Vector3 m_nonUniformScaleReciprocal; ///< Handles inverting any non-uniform scale which was applied ///< separately from the transform. + float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the + ///< ray from world space to local space. }; /// Build a ManipulatorInteraction structure from the incoming viewport interaction. @@ -56,11 +47,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection); - /// Calculate the offset along an axis to adjust a position - /// to stay snapped to a given grid size. + /// Calculate the offset along an axis to adjust a position to stay snapped to a given grid size. + /// @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2, + /// 0.7 snaps to 1.0 -> delta 0.3). AZ::Vector3 CalculateSnappedOffset( const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + /// Return the amount to snap from the starting position given the current grid size. + /// @note A movement of more than half size (in either direction) will cause a snap by size. + AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + /// For a given point on the terrain, calculate the closest xy position snapped to the grid /// (z position is aligned to terrain height, not snapped to z grid) AZ::Vector3 CalculateSnappedTerrainPosition( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp index 566de9a8a6..83c4c28e9a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp @@ -59,17 +59,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const AZStd::vector& fixedAxes, - const AZStd::vector& starterStates, const GridSnapAction& gridSnapAction) + const AZStd::vector& starterStates, const GridSnapParameters& gridSnapParams) { MultiLinearManipulator::Action action; action.m_viewportId = interaction.m_interactionId.m_viewportId; // build up action state for each axis for (size_t fixedIndex = 0; fixedIndex < fixedAxes.size(); ++fixedIndex) { - action.m_actions.push_back( - CalculateLinearManipulationDataAction( - fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, - gridSnapAction, interaction)); + action.m_actions.push_back(CalculateLinearManipulationDataAction( + fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, gridSnapParams, + interaction)); } return action; @@ -79,8 +78,6 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); const AzFramework::CameraState cameraState = GetCameraState(interaction.m_interactionId.m_viewportId); // build up initial start state for each axis @@ -88,20 +85,19 @@ namespace AzToolsFramework { // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap const auto linearStart = CalculateLinearManipulationDataStart( - fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, - rayIntersectionDistance, cameraState); + fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance, + cameraState); m_starters.push_back(linearStart); } if (m_onLeftMouseDownCallback) { - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); // pass action containing all linear actions for each axis to handler m_onLeftMouseDownCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); } } @@ -111,11 +107,9 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); - m_onMouseMoveCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); } } @@ -125,11 +119,9 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); - m_onLeftMouseUpCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); m_starters.clear(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h index 4a99435008..8e31e02605 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h @@ -20,8 +20,6 @@ namespace AzToolsFramework { - struct GridSnapAction; - //! MultiLinearManipulator serves as a visual tool for users to modify values //! in one or more dimensions on axes defined in 3D space. class MultiLinearManipulator diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp index abd5414ce3..6eb96f081c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp @@ -22,8 +22,7 @@ namespace AzToolsFramework { PlanarManipulator::StartInternal PlanarManipulator::CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, + const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance) { const ManipulatorInteraction manipulatorInteraction = @@ -31,8 +30,6 @@ namespace AzToolsFramework worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal); - const AZ::Vector3 axis1 = TransformDirectionNoScaling(localTransform, fixed.m_axis1); - const AZ::Vector3 axis2 = TransformDirectionNoScaling(localTransform, fixed.m_axis2); // initial intersect point const AZ::Vector3 localIntersectionPoint = @@ -43,25 +40,14 @@ namespace AzToolsFramework manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, normal, startInternal.m_localHitPosition); - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; - - // calculate amount to snap to align with grid - const AZ::Vector3 snapOffset = snapping && !gridSnapAction.m_localSnapping - ? CalculateSnappedOffset(localTransform.GetTranslation(), axis1, gridSize * scaleRecip) + - CalculateSnappedOffset(localTransform.GetTranslation(), axis2, gridSize * scaleRecip) - : AZ::Vector3::CreateZero(); - - startInternal.m_snapOffset = snapOffset; - startInternal.m_localPosition = localTransform.GetTranslation() + snapOffset; + startInternal.m_localPosition = localTransform.GetTranslation(); return startInternal; } PlanarManipulator::Action PlanarManipulator::CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, + const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) { const ManipulatorInteraction manipulatorInteraction = @@ -88,20 +74,18 @@ namespace AzToolsFramework const AZ::Vector3 hitDelta = (localHitPosition - startInternal.m_localHitPosition) / nonUniformScale; const AZ::Vector3 unsnappedOffset = axis1.Dot(hitDelta) * axis1 + axis2.Dot(hitDelta) * axis2; - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; const AZ::Vector3 nonUniformScaleRecip = manipulatorInteraction.m_nonUniformScaleReciprocal; - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; + const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; + const float gridSize = gridSnapParams.m_gridSize; + const bool snapping = gridSnapParams.m_gridSnap; Action action; action.m_fixed = fixed; action.m_start.m_localPosition = startInternal.m_localPosition; - action.m_start.m_snapOffset = startInternal.m_snapOffset; action.m_start.m_localHitPosition = startInternal.m_localHitPosition; action.m_current.m_localOffset = snapping - ? unsnappedOffset + - CalculateSnappedOffset(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis1)) + - CalculateSnappedOffset(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis2)) + ? CalculateSnappedAmount(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis1)) + + CalculateSnappedAmount(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis2)) : unsnappedOffset; // record what modifier keys are held during this action @@ -141,18 +125,17 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - m_startInternal = CalculateManipulationDataStart( m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance); if (m_onLeftMouseDownCallback) { + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); + m_onLeftMouseDownCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + gridSnapParams, interaction)); } } @@ -164,8 +147,7 @@ namespace AzToolsFramework m_onMouseMoveCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction)); } } @@ -177,8 +159,7 @@ namespace AzToolsFramework m_onLeftMouseUpCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction)); } } @@ -195,8 +176,7 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); const auto action = CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, mouseInteraction); // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h index 7a3b2a18a9..154ed4c7d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h @@ -21,7 +21,7 @@ namespace AzToolsFramework { class ManipulatorView; - struct GridSnapAction; + struct GridSnapParameters; /// PlanarManipulator serves as a visual tool for users to modify values /// in two dimension in a plane defined two non-collinear axes in 3D space. @@ -58,7 +58,6 @@ namespace AzToolsFramework { AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. }; /// The state of the manipulator during an interaction. @@ -120,7 +119,6 @@ namespace AzToolsFramework { AZ::Vector3 m_localPosition; ///< The starting position of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in world space between the ray and the manipulator when the mouse down event happens. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. }; Fixed m_fixed; @@ -134,12 +132,11 @@ namespace AzToolsFramework static StartInternal CalculateManipulationDataStart( const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, - const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance); + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance); static Action CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction); + const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, + const ViewportInteraction::MouseInteraction& interaction); }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp index 497bcf15d7..51c5406812 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp @@ -37,13 +37,13 @@ namespace AzToolsFramework axisLength, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor, AzFramework::ViewportColors::ZAxisColor); - auto mouseDownCallback = [this](const LinearManipulator::Action& action) { + auto mouseDownCallback = [this]([[maybe_unused]] const LinearManipulator::Action& action) + { AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); - AZ::NonUniformScaleRequestBus::EventResult( nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); - m_initialScale = nonUniformScale + action.m_start.m_scaleSnapOffset; + m_initialScale = nonUniformScale; AZ::NonUniformScaleRequestBus::Event( m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, m_initialScale); @@ -51,29 +51,37 @@ namespace AzToolsFramework m_manipulators->InstallAxisLeftMouseDownCallback(mouseDownCallback); - m_manipulators->InstallAxisMouseMoveCallback([this](const LinearManipulator::Action& action) { - const AZ::Vector3 scaleMultiplier = - (AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale)); + m_manipulators->InstallAxisMouseMoveCallback( + [this](const LinearManipulator::Action& action) + { + const AZ::Vector3 scaleMultiplier = + (AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale)); - AZ::NonUniformScaleRequestBus::Event( - m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, - (scaleMultiplier * m_initialScale).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale))); - }); + AZ::NonUniformScaleRequestBus::Event( + m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, + (scaleMultiplier * m_initialScale) + .GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale))); + }); m_manipulators->InstallUniformLeftMouseDownCallback(mouseDownCallback); - m_manipulators->InstallUniformMouseMoveCallback([this](const LinearManipulator::Action& action) { - const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); }; + m_manipulators->InstallUniformMouseMoveCallback( + [this](const LinearManipulator::Action& action) + { + const auto sumVectorElements = [](const AZ::Vector3& vec) + { + return vec.GetX() + vec.GetY() + vec.GetZ(); + }; - const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement(); - const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement(); - const float scaleMultiplier = AZ::GetClamp( - 1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier, - maxScaleMultiplier); + const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement(); + const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement(); + const float scaleMultiplier = AZ::GetClamp( + 1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier, + maxScaleMultiplier); - AZ::NonUniformScaleRequestBus::Event( - m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale); - }); + AZ::NonUniformScaleRequestBus::Event( + m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale); + }); } NonUniformScaleComponentMode::~NonUniformScaleComponentMode() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 5acfa5df59..db321d6818 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -423,15 +423,14 @@ namespace AzToolsFramework } } - static void InitializeTranslationLookup( - EntityIdManipulators& entityIdManipulators, const AZ::Vector3& snapOffset) + static void InitializeTranslationLookup(EntityIdManipulators& entityIdManipulators) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); for (auto& entityIdLookup : entityIdManipulators.m_lookups) { entityIdLookup.second.m_initial = - AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first) + snapOffset); + AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first)); } } @@ -820,7 +819,7 @@ namespace AzToolsFramework // moving with ctrl - setting override pivotOverrideFrame.m_translationOverride = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - InitializeTranslationLookup(entityIdManipulators, -action.LocalPositionOffset()); + InitializeTranslationLookup(entityIdManipulators); } else { @@ -1277,12 +1276,12 @@ namespace AzToolsFramework // linear translationManipulators->InstallLinearManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const LinearManipulator::Action& action) mutable + [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable { // important to sort entityIds based on hierarchy order when updating transforms BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_positionSnapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -1302,19 +1301,19 @@ namespace AzToolsFramework }); translationManipulators->InstallLinearManipulatorMouseUpCallback( - [this](const LinearManipulator::Action& /*action*/) mutable + [this]([[maybe_unused]] const LinearManipulator::Action& action) mutable { EndRecordManipulatorCommand(); }); // planar translationManipulators->InstallPlanarManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const PlanarManipulator::Action& action) + [this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action) { // important to sort entityIds based on hierarchy order when updating transforms BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -1340,11 +1339,11 @@ namespace AzToolsFramework // surface translationManipulators->InstallSurfaceManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const SurfaceManipulator::Action& action) + [this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action) { BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -3326,26 +3325,16 @@ namespace AzToolsFramework } static void DrawManipulatorGrid( - AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, - const float gridSize, const float localSnapping) + AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, const float gridSize) { const AZ::Matrix3x3 orientation = AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform()); - const AZ::Vector3 unsnappedTranslation = + const AZ::Vector3 translation = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - // calculate the offset to snap by to align the manipulator to the grid - // note: only perform this if we are not snapping in local space - const AZ::Vector3 snappedOffset = !localSnapping - ? CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisX(), gridSize) + - CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisY(), gridSize) - : AZ::Vector3::CreateZero(); - - const AZ::Vector3 snappedTranslation = unsnappedTranslation + snappedOffset; - DrawSnappingGrid( - debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, snappedTranslation), + debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation), gridSize); } @@ -3484,7 +3473,7 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(viewportInfo.m_viewportId); if (gridSnapParams.m_gridSnap && m_entityIdManipulators.m_manipulators) { - DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize, modifiers.Alt()); + DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize); } } } From b73de269ee5e271b0854b6ce30b54d2c839b1433 Mon Sep 17 00:00:00 2001 From: Aaron Ruiz Mora Date: Fri, 4 Jun 2021 15:24:22 +0100 Subject: [PATCH 086/105] Use '' instead of 'Default' in Material Selection widget. (#1140) --- Gems/PhysX/Code/Editor/MaterialIdWidget.cpp | 28 +++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp b/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp index 9ec438101a..44787ddd20 100644 --- a/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp +++ b/Gems/PhysX/Code/Editor/MaterialIdWidget.cpp @@ -19,6 +19,8 @@ namespace PhysX { namespace Editor { + static const char* const DefaultPhysicsMaterialLabel = ""; + AZ::u32 MaterialIdWidget::GetHandlerName() const { return Physics::Edit::MaterialIdSelector; @@ -72,8 +74,7 @@ namespace PhysX auto lockToDefault = [gui]() { - static const char* defaultLabel = "Default"; - gui->addItem(defaultLabel); + gui->addItem(DefaultPhysicsMaterialLabel); gui->setCurrentIndex(0); return false; }; @@ -83,30 +84,31 @@ namespace PhysX return lockToDefault(); } - auto materialAsset = AZ::Data::AssetManager::Instance().GetAsset(m_materialLibraryId, AZ::Data::AssetLoadBehavior::Default); - materialAsset.BlockUntilLoadComplete(); + auto materialLibraryAsset = AZ::Data::AssetManager::Instance().GetAsset(m_materialLibraryId, AZ::Data::AssetLoadBehavior::Default); + materialLibraryAsset.BlockUntilLoadComplete(); - if (materialAsset.Get() == nullptr) + if (materialLibraryAsset.Get() == nullptr) { return lockToDefault(); } - const auto& materialsData = materialAsset.Get()->GetMaterialsData(); + const auto& materials = materialLibraryAsset.Get()->GetMaterialsData(); - if (materialsData.size() == 0) + if (materials.empty()) { return lockToDefault(); } - m_libraryIds.reserve(materialsData.size()); + m_libraryIds.reserve(materials.size() + 1); // Plus one to reserve the first element for default physics material + // Add default physics material first m_libraryIds.push_back(Physics::MaterialId()); - gui->addItem("Default"); + gui->addItem(DefaultPhysicsMaterialLabel); - for (const auto& materialData : materialAsset.Get()->GetMaterialsData()) + for (const auto& material : materials) { - gui->addItem(materialData.m_configuration.m_surfaceType.c_str()); - m_libraryIds.push_back(materialData.m_id); + gui->addItem(material.m_configuration.m_surfaceType.c_str()); + m_libraryIds.push_back(material.m_id); } gui->setCurrentIndex(GetIndexForId(instance)); @@ -116,7 +118,7 @@ namespace PhysX Physics::MaterialId MaterialIdWidget::GetIdForIndex(size_t index) { - if (m_libraryIds.size() <= index) + if (index >= m_libraryIds.size()) { return Physics::MaterialId(); } From 5d4226df16a404708b69cb5a46e8aa297adb1767 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 4 Jun 2021 17:28:43 +0000 Subject: [PATCH 087/105] Get a mesh's morph targets based on the scene graph hierarchy, instead of mesh name (#1128) When building a mesh's morph targets, the exporter has to identify the base mesh in addition to each morph target mesh. Previously this was done by searching the entire scene graph for nodes * of type IBlendShapeData * whose parent's name matches the name of the Atom model This is problematic for a few reasons. The first is that the Atom model's name may have been based on the optimized mesh node. When this happens, the `OptimizedMeshSuffix` that is used in the Scene Graph node's name is stripped off of the Atom model's name. The result is that the *unoptimized* mesh is used as the base mesh for the blend shapes, instead of the optimized blend shape. This of course results in disaster, since the optimizer reorders the vertices, and the base mesh will not match the optimized one. The second is that it is not really necessary to do the search based on the node name at all. All of a mesh's blend shapes are child nodes of the base IMeshData node. With this change, the base mesh is located based on the node data pointer, and all of its child IBlendShapeData nodes are added to the set of blend shapes to process. This way, the Atom model's name isn't involved in the lookup. --- .../Model/MorphTargetExporter.cpp | 83 ++++++++----------- .../RPI.Builders/Model/MorphTargetExporter.h | 5 +- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp index 3d0cbca8e6..f14d73a9cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp @@ -14,9 +14,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -27,62 +29,42 @@ namespace AZ::RPI AZStd::unordered_map MorphTargetExporter::GetBlendShapeInfos( const Containers::Scene& scene, - const AZStd::optional& filterMeshName) const + const MeshData* meshData) const { const Containers::SceneGraph& sceneGraph = scene.GetGraph(); - const auto contentStorage = sceneGraph.GetContentStorage(); - const auto nameStorage = sceneGraph.GetNameStorage(); + + const auto foundBaseMeshIter = AZStd::find_if(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), [meshData](const auto& nodeData) + { + return nodeData.get() == meshData; + }); + if (foundBaseMeshIter == sceneGraph.GetContentStorage().cend()) + { + return {}; + } + + const auto baseMeshNodeIndex = sceneGraph.ConvertToNodeIndex(foundBaseMeshIter); + + const auto childBlendShapeDatas = Containers::MakeDerivedFilterView( + Containers::Views::MakeSceneGraphChildView(sceneGraph, baseMeshNodeIndex, sceneGraph.GetContentStorage().cbegin(), true) + ); AZStd::unordered_map result; - - const auto keyValueView = Containers::Views::MakePairView(nameStorage, contentStorage); - const auto filteredView = Containers::Views::MakeFilterView(keyValueView, Containers::DerivedTypeFilter()); - for (const auto& [name, object] : filteredView) + for (auto it = childBlendShapeDatas.cbegin(); it != childBlendShapeDatas.cend(); ++it) { - const Containers::SceneGraph::NodeIndex sceneNodeIndex = sceneGraph.Find(name.GetPath()); + const Containers::SceneGraph::NodeIndex blendShapeNodeIndex = sceneGraph.ConvertToNodeIndex(it.GetBaseIterator().GetBaseIterator().GetHierarchyIterator()); AZStd::set types; - Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, sceneNodeIndex); - if (types.find(Events::GraphMetaInfo::GetIgnoreVirtualType()) == types.end()) + Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, blendShapeNodeIndex); + if (!types.contains(Events::GraphMetaInfo::GetIgnoreVirtualType())) { - const char* sceneNodePath = name.GetPath(); - const Containers::SceneGraph::NodeIndex nodeIndex = sceneGraph.Find(sceneNodePath); - if (nodeIndex.IsValid()) - { - const AZStd::string meshNodeName = SourceBlendShapeInfo::GetMeshNodeName(sceneGraph, nodeIndex); - if (!filterMeshName.has_value() || - (filterMeshName.has_value() && filterMeshName.value() == meshNodeName)) - { - const AZStd::string blendShapeName = sceneGraph.GetNodeName(nodeIndex).GetName(); - SourceBlendShapeInfo& blendShapeInfo = result[blendShapeName]; - blendShapeInfo.m_sceneNodeIndices.push_back(nodeIndex); - } - } - else - { - AZ_Warning(ModelAssetBuilderComponent::s_builderName, false, "Cannot retrieve scene graph index for blend shape node with path %s.", sceneNodePath); - } + const AZStd::string blendShapeName{sceneGraph.GetNodeName(blendShapeNodeIndex).GetName(), sceneGraph.GetNodeName(blendShapeNodeIndex).GetNameLength()}; + result[blendShapeName].m_sceneNodeIndices.emplace_back(blendShapeNodeIndex); } } return result; } - AZStd::string MorphTargetExporter::SourceBlendShapeInfo::GetMeshNodeName(const Containers::SceneGraph& sceneGraph, - const Containers::SceneGraph::NodeIndex& sceneNodeIndex) - { - const auto* blendShapeData = - azrtti_cast(sceneGraph.GetNodeContent(sceneNodeIndex).get()); - AZ_Assert(blendShapeData, "Cannot get mesh node name from scene node. Node is expected to be a blend shape."); - if (blendShapeData) - { - Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex); - return sceneGraph.GetNodeName(morphMeshParentIndex).GetName(); - } - - return {}; - } - void MorphTargetExporter::ProduceMorphTargets(const Containers::Scene& scene, uint32_t vertexOffset, const ModelAssetBuilderComponent::SourceMeshContent& sourceMesh, @@ -92,9 +74,14 @@ namespace AZ::RPI { const Containers::SceneGraph& sceneGraph = scene.GetGraph(); +#if defined(AZ_ENABLE_TRACING) + const auto baseMeshIt = AZStd::find(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), sourceMesh.m_meshData); + const Containers::SceneGraph::NodeIndex baseMeshIndex = sceneGraph.ConvertToNodeIndex(baseMeshIt); + const AZStd::string_view baseMeshName{sceneGraph.GetNodeName(baseMeshIndex).GetName(), sceneGraph.GetNodeName(baseMeshIndex).GetNameLength()}; +#endif + // Get the blend shapes for the given mesh - const AZStd::string_view meshName = sourceMesh.m_name.GetStringView(); - AZStd::unordered_map blendShapeInfos = GetBlendShapeInfos(scene, meshName); + AZStd::unordered_map blendShapeInfos = GetBlendShapeInfos(scene, sourceMesh.m_meshData.get()); for (const auto& iter : blendShapeInfos) { @@ -109,12 +96,12 @@ namespace AZ::RPI { #if defined(AZ_ENABLE_TRACING) const Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex); - const char* meshNodeName = sceneGraph.GetNodeName(morphMeshParentIndex).GetName(); + const AZStd::string_view sourceMeshName{sceneGraph.GetNodeName(morphMeshParentIndex).GetName(), sceneGraph.GetNodeName(morphMeshParentIndex).GetNameLength()}; #endif - AZ_Assert(AZ::StringFunc::Equal(sourceMesh.m_name.GetCStr(), meshNodeName, /*bCaseSensitive=*/true), - "Scene graph mesh node (%s) has a different name than the product mesh (%s).", - meshNodeName, sourceMesh.m_name.GetCStr()); + AZ_Assert(AZ::StringFunc::Equal(baseMeshName, sourceMeshName, /*bCaseSensitive=*/true), + "Scene graph mesh node (%.*s) has a different name than the product mesh (%.*s).", + AZ_STRING_ARG(sourceMeshName), AZ_STRING_ARG(baseMeshName)); const DataTypes::MatrixType globalTransform = Utilities::BuildWorldTransform(sceneGraph, sceneNodeIndex); BuildMorphTargetMesh(vertexOffset, sourceMesh, productMesh, metaAssetCreator, blendShapeName, blendShapeData, globalTransform, coordSysConverter, scene.GetSourceFilename()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h index 4845d7d1da..32f9e2e508 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h @@ -39,12 +39,9 @@ namespace AZ struct SourceBlendShapeInfo { AZStd::vector m_sceneNodeIndices; - - static AZStd::string GetMeshNodeName(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph, - const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& sceneNodeIndex); }; //! Retrieve all scene graph nodes per blend shape for all available blend shapes. - AZStd::unordered_map GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const AZStd::optional& filterMeshName = AZStd::nullopt) const; + AZStd::unordered_map GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const MeshData* meshData) const; //! Calculate position delta tolerance that is used to indicate whether a given vertex is part of the sparse set of morphed vertices //! or if it will be skipped and optimized out due to a hardly visible or no movement at all. From dcdd63966ed43ec80f56aca75c4ba9a80fcc201a Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Fri, 4 Jun 2021 10:28:56 -0700 Subject: [PATCH 088/105] ATOM-15658 Better option of CreateCommonBuffer requires unique buffer name (#1133) * ATOM-15658 Better option of CreateCommonBuffer requires unique buffer name - Change the CreateCommonBuffer function to not require an unique name by default. - Remove the code for generating unique buffer names. - Add buffer name to BufferAsset so it can be used for device object name instead of using asset file name. - Change RPI::Buffer to use BufferName_AssetUuid as attachment id. --- .../Source/CoreLights/LightCullingPass.cpp | 7 +--- .../Source/CoreLights/LightCullingRemap.cpp | 7 +--- .../Common/Code/Source/Math/MathFilter.cpp | 1 + .../ExposureControlSettings.cpp | 11 +----- .../ExposureControl/ExposureControlSettings.h | 4 +- .../DepthOfFieldReadBackFocusDepthPass.cpp | 3 +- .../ExposureControlRenderProxy.cpp | 1 + .../PostProcessing/EyeAdaptationPass.cpp | 6 +-- .../LuminanceHistogramGeneratorPass.cpp | 7 +--- .../RayTracing/RayTracingFeatureProcessor.cpp | 8 +--- .../Source/SkyBox/SkyBoxFeatureProcessor.cpp | 2 +- .../TransformServiceFeatureProcessor.cpp | 6 +-- .../Code/Source/Utils/GpuBufferHandler.cpp | 4 +- .../Include/Atom/RPI.Public/Buffer/Buffer.h | 2 + .../Atom/RPI.Public/Buffer/BufferSystem.h | 2 +- .../RPI.Public/Buffer/BufferSystemInterface.h | 5 ++- .../Atom/RPI.Reflect/Buffer/BufferAsset.h | 4 ++ .../Model/ModelAssetBuilderComponent.cpp | 2 +- .../Code/Source/RPI.Public/Buffer/Buffer.cpp | 17 ++++++--- .../Source/RPI.Public/Buffer/BufferSystem.cpp | 27 ++++++++----- .../DynamicDraw/DynamicBufferAllocator.cpp | 2 +- .../Source/RPI.Reflect/Buffer/BufferAsset.cpp | 8 +++- .../RPI.Reflect/Buffer/BufferAssetCreator.cpp | 5 ++- .../RPI/Code/Tests/Buffer/BufferTests.cpp | 38 +++++++++++++++---- .../EMotionFXAtom/Code/Source/ActorAsset.cpp | 2 +- 25 files changed, 104 insertions(+), 77 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp index 987ed299b3..a9b43ef1e7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingPass.cpp @@ -311,14 +311,9 @@ namespace AZ { auto tileBufferResolution = GetTileDataBufferResolution(); - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LightList_%s", uuidString.c_str()); + desc.m_bufferName = "LightList"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = tileBufferResolution.m_width * tileBufferResolution.m_height * 256 * sizeof(uint32_t); m_lightList = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp index 42882cec6e..26e4ed9f6e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/LightCullingRemap.cpp @@ -118,14 +118,9 @@ namespace AZ void LightCullingRemap::CreateRemappedLightListBuffer() { - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LightListRemapped_%s", uuidString.c_str()); + desc.m_bufferName = "LightListRemapped"; desc.m_elementSize = RHI::GetFormatSize(LightListRemappedFormat); desc.m_byteCount = m_tileDim.m_width * m_tileDim.m_height * NumBins * MaxLightsPerTile * desc.m_elementSize; m_lightListRemapped = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp b/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp index 8190c3af98..cb6f3e3522 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Math/MathFilter.cpp @@ -74,6 +74,7 @@ namespace AZ desc.m_elementFormat = filters.front()->GetElementFormat(); desc.m_byteCount = totalElementCount * elementSize; desc.m_bufferData = data.data(); + desc.m_isUniqueName = true; auto buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 056f7b7da4..288dd54e2b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -100,16 +100,9 @@ namespace AZ bool ExposureControlSettings::InitCommonBuffer() { - // generate a UUID for the buffer name to keep it unique - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - - AZStd::string bufferName = AZStd::string::format("%s_%s", ExposureControlBufferBaseName, uuidString.c_str()); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::Constant; - desc.m_bufferName = bufferName; + desc.m_bufferName = ExposureControlBufferName; desc.m_byteCount = sizeof(ShaderParameters); desc.m_elementSize = sizeof(ShaderParameters); @@ -117,7 +110,7 @@ namespace AZ if (!m_buffer) { - AZ_Assert(false, "Failed to create the RPI::Buffer[%s] which is used for the exposure control feature.", bufferName.c_str()); + AZ_Assert(false, "Failed to create the RPI::Buffer[%s] which is used for the exposure control feature.", desc.m_bufferName.c_str()); return false; } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h index 8344d6aa09..bfb4237b96 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h @@ -28,8 +28,8 @@ namespace AZ { class PostProcessSettings; - // Base name of the buffer used for the exposure control feature. Usually distinct identifier will be added to this name for each exposure control settings. - static const char* const ExposureControlBufferBaseName = "ExposureControlBuffer"; + // Name of the buffer used for the exposure control feature + static const char* const ExposureControlBufferName = "ExposureControlBuffer"; // The post process sub-settings class for the exposure control feature class ExposureControlSettings final diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp index abd916fcc9..c355b7c8a7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/DepthOfFieldReadBackFocusDepthPass.cpp @@ -47,9 +47,8 @@ namespace AZ m_getDepthPass = static_cast(pass.get()); // Create buffer for read back focus depth. We append static counter to avoid name conflicts. - AZStd::string bufferName = AZStd::string::format("DepthOfFieldReadBackAutoFocusDepthBuffer_%d", s_bufferInstance++); RPI::CommonBufferDescriptor desc; - desc.m_bufferName = bufferName; + desc.m_bufferName = "DepthOfFieldReadBackAutoFocusDepthBuffer"; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; desc.m_byteCount = sizeof(float); desc.m_elementSize = aznumeric_cast(desc.m_byteCount); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp index eb7a3b527f..c70d56aa90 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/ExposureControlRenderProxy.cpp @@ -71,6 +71,7 @@ namespace AZ desc.m_bufferName = bufferName; desc.m_byteCount = sizeof(ShaderParameters); desc.m_elementSize = sizeof(ShaderParameters); + desc.m_isUniqueName = true; m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp index e7d4c47f02..bf293fd3d2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/EyeAdaptationPass.cpp @@ -34,7 +34,7 @@ namespace AZ { namespace Render { - static const char* const EyeAdaptationBufferBaseName = "EyeAdaptationBuffer"; + static const char* const EyeAdaptationBufferName = "EyeAdaptationBuffer"; RPI::Ptr EyeAdaptationPass::Create(const RPI::PassDescriptor& descriptor) { @@ -49,12 +49,10 @@ namespace AZ void EyeAdaptationPass::InitBuffer() { - AZStd::string bufferName = AZStd::string::format("%s_%p", EyeAdaptationBufferBaseName, this); - ExposureCalculationData defaultData; RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = bufferName; + desc.m_bufferName = EyeAdaptationBufferName; desc.m_byteCount = sizeof(ExposureCalculationData); desc.m_elementSize = aznumeric_cast(desc.m_byteCount); desc.m_bufferData = &defaultData; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp index 758c21bc4e..715ebf2945 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LuminanceHistogramGeneratorPass.cpp @@ -62,14 +62,9 @@ namespace AZ void LuminanceHistogramGeneratorPass::CreateHistogramBuffer() { - // generate a UUID for the buffer name to keep it unique when there are multiple render pipelines - AZ::Uuid uuid = AZ::Uuid::CreateRandom(); - AZStd::string uuidString; - uuid.ToString(uuidString); - RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadWrite; - desc.m_bufferName = AZStd::string::format("LuminanceHistogramBuffer_%s", uuidString.c_str()); + desc.m_bufferName = "LuminanceHistogramBuffer"; desc.m_elementSize = sizeof(uint32_t); desc.m_byteCount = NumHistogramBins * sizeof(uint32_t); desc.m_elementFormat = RHI::Format::R32_UINT; diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 10c7c2d378..2db396e36d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -210,12 +210,10 @@ namespace AZ if (m_meshInfoBuffer == nullptr) { - AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); - // allocate the MeshInfo structured buffer RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = AZStd::string::format("RayTracingMeshInfo_%s", uuidString.c_str()); + desc.m_bufferName = "RayTracingMeshInfo"; desc.m_byteCount = newMeshByteCount; desc.m_elementSize = sizeof(MeshInfo); m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); @@ -283,12 +281,10 @@ namespace AZ if (m_materialInfoBuffer == nullptr) { - AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); - // allocate the MaterialInfo structured buffer RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = AZStd::string::format("RayTracingMaterialInfo_%s", uuidString.c_str()); + desc.m_bufferName = "RayTracingMaterialInfo"; desc.m_byteCount = newMaterialByteCount; desc.m_elementSize = sizeof(MaterialInfo); m_materialInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp index 058e243fb7..4a3586a799 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp @@ -193,7 +193,7 @@ namespace AZ RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::Constant; - desc.m_bufferName = AZStd::string::format("SkyboxBuffer_%p", this); + desc.m_bufferName = "SkyboxBuffer"; desc.m_byteCount = byteCount; desc.m_elementSize = byteCount; desc.m_bufferData = &m_physicalSkyData; diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index fb73d0f416..074b09e35d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -89,13 +89,13 @@ namespace AZ // Create the transform buffer, grow by powers of two RPI::CommonBufferDescriptor desc2; desc2.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldBuffer_%" PRIXPTR, reinterpret_cast(this)); + desc2.m_bufferName = "m_objectToWorldBuffer"; desc2.m_byteCount = byteCount; desc2.m_elementSize = elementSize; m_objectToWorldBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc2); - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldHistoryBuffer_%p", this); + desc2.m_bufferName = "m_objectToWorldHistoryBuffer"; m_objectToWorldHistoryBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc2); } else @@ -119,7 +119,7 @@ namespace AZ // Create the normal buffer, grow by powers of two RPI::CommonBufferDescriptor desc2; desc2.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc2.m_bufferName = AZStd::string::format("'m_objectToWorldInverseTransposeBuffer_%" PRIXPTR, reinterpret_cast(this)); + desc2.m_bufferName = "m_objectToWorldInverseTransposeBuffer"; desc2.m_byteCount = byteCount; desc2.m_elementSize = elementSize; diff --git a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp index 13a151f8ac..db78247251 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp @@ -40,13 +40,11 @@ namespace AZ if (m_bufferIndex.IsValid()) { - AZStd::string bufferName = AZStd::string::format("%s_%" PRIXPTR, descriptor.m_bufferName.c_str(), reinterpret_cast(this)); - uint32_t byteCount = RHI::NextPowerOfTwo(GetMax(BufferMinSize, m_elementCount * m_elementSize)); RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = bufferName; + desc.m_bufferName = descriptor.m_bufferName; desc.m_byteCount = byteCount; desc.m_elementSize = descriptor.m_elementSize; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h index 3fef502e92..df24c7591b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/Buffer.h @@ -112,6 +112,8 @@ namespace AZ AZStd::mutex m_pendingUploadMutex; RHI::BufferViewDescriptor m_bufferViewDescriptor; + + RHI::AttachmentId m_attachmentId; }; template diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h index c4b6aa74b4..9f5b150752 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystem.h @@ -35,7 +35,7 @@ namespace AZ // BufferSystemInterface overrides... RHI::Ptr GetCommonBufferPool(CommonBufferPoolType poolType) override; Data::Instance CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) override; - Data::Instance FindCommonBuffer(AZStd::string_view bufferName) override; + Data::Instance FindCommonBuffer(AZStd::string_view uniqueBufferName) override; void Init(); void Shutdown(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h index 1469eed060..39b4b09691 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Buffer/BufferSystemInterface.h @@ -53,6 +53,9 @@ namespace AZ RHI::Format m_elementFormat = RHI::Format::Unknown; // CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) = 0; //! Find a buffer by name. The buffer has to be created by CreateBufferFromCommonPool function - virtual Data::Instance FindCommonBuffer(AZStd::string_view bufferName) = 0; + virtual Data::Instance FindCommonBuffer(AZStd::string_view uniqueBufferName) = 0; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h index d8a6fbb44d..4776933de6 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Buffer/BufferAsset.h @@ -60,11 +60,15 @@ namespace AZ const Data::Asset& GetPoolAsset() const; CommonBufferPoolType GetCommonPoolType() const; + + const AZStd::string& GetName() const; private: // Called by asset creators to assign the asset to a ready state. void SetReady(); + AZStd::string m_name; + AZStd::vector m_buffer; RHI::BufferDescriptor m_bufferDescriptor; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp index ea2bdd0d83..f559a0aba6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/ModelAssetBuilderComponent.cpp @@ -114,7 +114,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(26); // [ATOM-14992] + ->Version(27); // [ATOM-15658] } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp index 470b66c28e..81f02b7435 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/Buffer.cpp @@ -32,10 +32,6 @@ namespace AZ auto buffer = Data::InstanceDatabase::Instance().FindOrCreate( Data::InstanceId::CreateFromAssetId(bufferAsset.GetId()), bufferAsset); - if (buffer && buffer->m_rhiBuffer) - { - buffer->m_rhiBuffer->SetName(Name(bufferAsset.GetHint())); - } return buffer; } @@ -170,6 +166,16 @@ namespace AZ return resultCode; } } + + m_rhiBuffer->SetName(Name(bufferAsset.GetName())); + + // Only generate buffer's attachment id if the buffer is writable + if (RHI::CheckBitsAny(m_rhiBuffer->GetDescriptor().m_bindFlags, + RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::DynamicInputAssembly)) + { + // attachment id = bufferName_bufferInstanceId + m_attachmentId = Name(bufferAsset.GetName() + "_" + bufferAsset.GetId().m_guid.ToString(false, false)); + } return RHI::ResultCode::Success; } @@ -312,7 +318,8 @@ namespace AZ const RHI::AttachmentId& Buffer::GetAttachmentId() const { - return m_rhiBuffer->GetName(); + AZ_Assert(!m_attachmentId.GetStringView().empty(), "Read-only buffer doesn't need attachment id"); + return m_attachmentId; } const RHI::BufferViewDescriptor& Buffer::GetBufferViewDescriptor() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp index 9a9254b0d6..c4b4b28d44 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Buffer/BufferSystem.cpp @@ -152,15 +152,22 @@ namespace AZ } Data::Instance BufferSystem::CreateBufferFromCommonPool(const CommonBufferDescriptor& descriptor) - { - Uuid bufferId = Uuid::CreateName(descriptor.m_bufferName.c_str()); - - // Report error if there is a buffer with same name. - // Note: this shouldn't return the existing buffer because users are expecting a newly created buffer. - if (Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId))) + { + Uuid bufferId; + if (descriptor.m_isUniqueName) { - AZ_Error("BufferSystem", false, "Buffer with same name '%s' already exist", descriptor.m_bufferName.c_str()); - return nullptr; + bufferId = Uuid::CreateName(descriptor.m_bufferName.c_str()); + // Report error if there is a buffer with same name. + // Note: this shouldn't return the existing buffer because users are expecting a newly created buffer. + if (Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId))) + { + AZ_Error("BufferSystem", false, "Buffer with same name '%s' already exist", descriptor.m_bufferName.c_str()); + return nullptr; + } + } + else + { + bufferId = Uuid::CreateRandom(); } RHI::Ptr bufferPool = GetCommonBufferPool(descriptor.m_poolType); @@ -207,9 +214,9 @@ namespace AZ return nullptr; } - Data::Instance BufferSystem::FindCommonBuffer(AZStd::string_view bufferName) + Data::Instance BufferSystem::FindCommonBuffer(AZStd::string_view uniqueBufferName) { - Uuid bufferId = Uuid::CreateName(bufferName.data()); + Uuid bufferId = Uuid::CreateName(uniqueBufferName.data()); return Data::InstanceDatabase::Instance().Find(Data::InstanceId(bufferId)); } } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp index e79aa2e1bb..627b1e9912 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/DynamicDraw/DynamicBufferAllocator.cpp @@ -30,7 +30,7 @@ namespace AZ // Create the ring buffer from common pool RPI::CommonBufferDescriptor desc; desc.m_poolType = RPI::CommonBufferPoolType::DynamicInputAssembly; - desc.m_bufferName = AZStd::string::format("DyanmicBufferRing_%p", this); + desc.m_bufferName = "DyanmicBufferRing"; desc.m_elementSize = 1; desc.m_byteCount = ringBufferSize; m_ringBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp index 6f9d3a9d64..69444ae466 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAsset.cpp @@ -30,7 +30,8 @@ namespace AZ if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(1) + ->Version(2) + ->Field("Name", &BufferAsset::m_name) ->Field("Buffer", &BufferAsset::m_buffer) ->Field("BufferDescriptor", &BufferAsset::m_bufferDescriptor) ->Field("BufferViewDescriptor", &BufferAsset::m_bufferViewDescriptor) @@ -80,5 +81,10 @@ namespace AZ { return m_poolType; } + + const AZStd::string& BufferAsset::GetName() const + { + return m_name; + } } //namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp index 486be9860e..4bd1b53f57 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp @@ -152,7 +152,10 @@ namespace AZ void BufferAssetCreator::SetBufferName(AZStd::string_view name) { - m_asset.SetHint(name); + if (ValidateIsReady()) + { + m_asset->m_name = name; + } } bool BufferAssetCreator::Clone(const Data::Asset& sourceAsset, Data::Asset& clonedResult, Data::AssetId& inOutLastCreatedAssetId) diff --git a/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp b/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp index df3af5f0d2..261b57a568 100644 --- a/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Buffer/BufferTests.cpp @@ -474,6 +474,7 @@ namespace UnitTest desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; desc.m_bufferName = "Buffer1"; desc.m_byteCount = bufferInfo.m_bufferDescriptor.m_byteCount; + desc.m_isUniqueName = true; Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); // buffer created @@ -488,8 +489,33 @@ namespace UnitTest EXPECT_EQ(bufferFound2.get(), nullptr); } - // Failed if creates a buffer with duplicated name with existing buffer - TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBuffer_Fail) + // Failed if creates a buffe which has a same name with existing buffer + // and has m_isUniqueName is enabled + TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBufferEnableUniqueName_Fail) + { + using namespace AZ; + + ExpectedBuffer bufferInfo = CreateValidBuffer(); + + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = "Buffer1"; + desc.m_byteCount = bufferInfo.m_bufferDescriptor.m_byteCount; + desc.m_isUniqueName = true; + + Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + // buffer created + EXPECT_NE(bufferInst.get(), nullptr); + + AZ_TEST_START_ASSERTTEST; + Data::Instance bufferInst2 = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + AZ_TEST_STOP_ASSERTTEST(1); + // buffer NOT created + EXPECT_EQ(bufferInst2.get(), nullptr); + } + + // create a buffer which has a same name with existing buffer + TEST_F(BufferTests, BufferSystem_CreateDuplicatedNamedBuffers_Success) { using namespace AZ; @@ -503,12 +529,10 @@ namespace UnitTest Data::Instance bufferInst = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); // buffer created EXPECT_NE(bufferInst.get(), nullptr); - - AZ_TEST_START_ASSERTTEST; + Data::Instance bufferInst2 = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); - AZ_TEST_STOP_ASSERTTEST(1); - // buffer NOT created - EXPECT_EQ(bufferInst2.get(), nullptr); + // buffer created + EXPECT_NE(bufferInst2.get(), nullptr); } // Buffer instance creation unit tests diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 8dc7f9387c..9f68a7d12c 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -595,7 +595,7 @@ namespace AZ // Create a buffer and populate it with the transforms RPI::CommonBufferDescriptor descriptor; descriptor.m_bufferData = boneTransforms.data(); - descriptor.m_bufferName = AZStd::string::format("BoneTransformBuffer_%s_%s", actorInstance->GetActor()->GetName(), Uuid::CreateRandom().ToString().c_str()); + descriptor.m_bufferName = AZStd::string::format("BoneTransformBuffer_%s", actorInstance->GetActor()->GetName()); descriptor.m_byteCount = boneTransforms.size() * sizeof(float); descriptor.m_elementSize = floatsPerBone * sizeof(float); descriptor.m_poolType = RPI::CommonBufferPoolType::ReadOnly; From 76a6df341b0b05eafb53f4977cd706a80a3b2b3d Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 10:51:47 -0700 Subject: [PATCH 089/105] SPEC-2513 Fixes to enable w4457 --- .../UdpTransport/UdpFragmentQueue.cpp | 4 +- Code/Sandbox/Editor/CVarMenu.cpp | 14 ++-- .../Editor/TrackView/TrackViewAnimNode.cpp | 4 +- .../Editor/TrackView/TrackViewTrack.cpp | 6 +- .../Vulkan/Code/Source/RHI/DescriptorSet.cpp | 4 +- .../Window/ShaderManagementConsoleWindow.cpp | 4 +- .../Source/Editor/QATLControlsTreeModel.cpp | 8 +- .../EMotionFX/Rendering/Common/RenderUtil.cpp | 12 +-- .../EMotionFX/Source/MultiThreadScheduler.cpp | 4 +- .../PropertyWidgets/MotionDataHandler.cpp | 6 +- .../StaticLib/GraphCanvas/Styling/Parser.cpp | 80 +++++++++---------- .../GraphCanvas/Utils/GraphUtils.cpp | 6 +- Gems/GraphModel/Code/Source/Model/Graph.cpp | 4 +- .../Editor/Animation/UiAnimViewAnimNode.cpp | 12 +-- .../Code/Editor/Animation/UiAnimViewTrack.cpp | 6 +- .../MicrophoneSystemComponent_Windows.cpp | 22 ++--- .../NetworkEntity/NetworkEntityManager.cpp | 4 +- .../Widgets/NodePalette/NodePaletteModel.cpp | 12 +-- .../View/Windows/ScriptCanvasContextMenus.cpp | 4 +- .../Grammar/AbstractCodeModel.cpp | 10 +-- .../Internal/Nodes/ExpressionNodeBase.cpp | 6 +- .../Code/Source/Core/WhiteBoxToolApi.cpp | 16 ++-- .../Common/MSVC/Configurations_msvc.cmake | 1 - 23 files changed, 124 insertions(+), 125 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp index 2a90509187..042f19aa70 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp @@ -139,7 +139,7 @@ namespace AzNetworking NetworkOutputSerializer networkSerializer(buffer.GetBuffer(), buffer.GetSize()); { - ISerializer& serializer = networkSerializer; // To get the default typeinfo parameters in ISerializer + ISerializer& networkISerializer = networkSerializer; // To get the default typeinfo parameters in ISerializer // First, serialize out the header if (!header.SerializePacketFlags(networkSerializer)) @@ -148,7 +148,7 @@ namespace AzNetworking return false; } - if (!serializer.Serialize(header, "Header")) + if (!networkISerializer.Serialize(header, "Header")) { AZLOG(NET_FragmentQueue, "Reconstructed fragmented packet failed header serialization"); return false; diff --git a/Code/Sandbox/Editor/CVarMenu.cpp b/Code/Sandbox/Editor/CVarMenu.cpp index f3dd4cf065..bbce0c63fc 100644 --- a/Code/Sandbox/Editor/CVarMenu.cpp +++ b/Code/Sandbox/Editor/CVarMenu.cpp @@ -118,10 +118,10 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName, // Otherwise we could have just used the action's currently checked // state and updated the CVar's value only bool cVarOn = (cVar->GetFVal() == availableCVar.m_onValue); - bool checked = !cVarOn; - SetCVar(cVar, checked ? availableCVar.m_onValue : availableCVar.m_offValue); - action->setChecked(checked); - if (checked) + bool cVarChecked = !cVarOn; + SetCVar(cVar, cVarChecked ? availableCVar.m_onValue : availableCVar.m_offValue); + action->setChecked(cVarChecked); + if (cVarChecked) { // Set the rest of the CVars in the group to their off values SetCVarsToOffValue(availableCVars, availableCVar); @@ -132,9 +132,9 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName, // Initialize the action's checked state based on its associated CVar's current value ICVar* cVar = gEnv->pConsole->GetCVar(availableCVar.m_cVarName.toUtf8().data()); - bool checked = (cVar && cVar->GetFVal() == availableCVar.m_onValue); - action->setChecked(checked); - if (checked) + bool cVarChecked = (cVar && cVar->GetFVal() == availableCVar.m_onValue); + action->setChecked(cVarChecked); + if (cVarChecked) { // Set the rest of the CVars in the group to their off values SetCVarsToOffValue(availableCVars, availableCVar); diff --git a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp index 35306b9535..2b4622f5ec 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewAnimNode.cpp @@ -205,10 +205,10 @@ CTrackViewAnimNode::CTrackViewAnimNode(IAnimSequence* pSequence, IAnimNode* anim for (int i = 0; i < nodeCount; ++i) { IAnimNode* node = pSequence->GetNode(i); - IAnimNode* pParentNode = node->GetParent(); + IAnimNode* pNodeParentNode = node->GetParent(); // If our node is the parent, then the current node is a child of it - if (animNode == pParentNode) + if (animNode == pNodeParentNode) { CTrackViewAnimNodeFactory animNodeFactory; CTrackViewAnimNode* pNewTVAnimNode = animNodeFactory.BuildAnimNode(pSequence, node, this); diff --git a/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp b/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp index 94559921c3..76a836f86c 100644 --- a/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp +++ b/Code/Sandbox/Editor/TrackView/TrackViewTrack.cpp @@ -68,12 +68,12 @@ CTrackViewTrack::CTrackViewTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackA { // Search for child tracks const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount(); - for (unsigned int subTrackIndex = 0; subTrackIndex < subTrackCount; ++subTrackIndex) + for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI) { - IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackIndex); + IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI); CTrackViewTrackFactory trackFactory; - CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackIndex); + CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI); m_childNodes.push_back(std::unique_ptr(pNewTVTrack)); } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp index 634f5a51ac..37f1e31542 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/DescriptorSet.cpp @@ -261,8 +261,8 @@ namespace AZ if (vulkanDescriptor.m_constantDataPool && constantDataSize) { m_constantDataBuffer = Buffer::Create(); - const RHI::BufferDescriptor descriptor(RHI::BufferBindFlags::Constant, constantDataSize); - RHI::BufferInitRequest request(*m_constantDataBuffer, descriptor); + const RHI::BufferDescriptor bufferDescriptor(RHI::BufferBindFlags::Constant, constantDataSize); + RHI::BufferInitRequest request(*m_constantDataBuffer, bufferDescriptor); RHI::ResultCode rhiResult = vulkanDescriptor.m_constantDataPool->InitBuffer(request); if (rhiResult != RHI::ResultCode::Success) { diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp index 6ab297bfc5..08d406884f 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp @@ -510,9 +510,9 @@ namespace ShaderManagementConsole AZStd::vector documentIdsToClose; documentIdsToClose.reserve(m_tabWidget->count()); const AZ::Uuid documentIdToKeepOpen = GetDocumentIdFromTab(tabIndex); - for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex) + for (int tabI = 0; tabI < m_tabWidget->count(); ++tabI) { - const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex); + const AZ::Uuid documentId = GetDocumentIdFromTab(tabI); if (documentId != documentIdToKeepOpen) { documentIdsToClose.push_back(documentId); diff --git a/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp b/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp index d05f7830a4..32446237a3 100644 --- a/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/QATLControlsTreeModel.cpp @@ -287,9 +287,9 @@ namespace AudioControls QDataStream stream(&encoded, QIODevice::ReadOnly); while (!stream.atEnd()) { - int row, col; + int streamRow, streamCol; QMap roleDataMap; - stream >> row >> col >> roleDataMap; + stream >> streamRow >> streamCol >> roleDataMap; if (!roleDataMap.isEmpty()) { // If dropping a folder, make sure that folder name doesn't already exist where it is being dropped @@ -341,9 +341,9 @@ namespace AudioControls { QByteArray data = mimeData->data(format); QDataStream stream(&data, QIODevice::ReadOnly); - int row, col; + int streamRow, streamCol; QMap roleDataMap; - stream >> row >> col >> roleDataMap; + stream >> streamRow >> streamCol >> roleDataMap; if (!roleDataMap.isEmpty() && roleDataMap[eDR_TYPE] != eIT_FOLDER) { return false; diff --git a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp index b2389a3086..90752c7604 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Rendering/Common/RenderUtil.cpp @@ -1628,18 +1628,18 @@ namespace MCommon } else { - const float screenWidth = static_cast(camera->GetScreenWidth()); - const float screenHeight = static_cast(camera->GetScreenHeight()); + const float cameraScreenWidth = static_cast(camera->GetScreenWidth()); + const float cameraScreenHeight = static_cast(camera->GetScreenHeight()); // find the 4 corners of the frustum AZ::Vector3 corners[4]; const AZ::Matrix4x4 inversedProjectionMatrix = MCore::InvertProjectionMatrix(camera->GetProjectionMatrix()); const AZ::Matrix4x4 inversedViewMatrix = MCore::InvertProjectionMatrix(camera->GetViewMatrix()); - corners[0] = MCore::Unproject(0.0f, 0.0f, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[1] = MCore::Unproject(screenWidth, 0.0f, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[2] = MCore::Unproject(screenWidth, screenHeight, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); - corners[3] = MCore::Unproject(0.0f, screenHeight, screenWidth, screenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[0] = MCore::Unproject(0.0f, 0.0f, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[1] = MCore::Unproject(cameraScreenWidth, 0.0f, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[2] = MCore::Unproject(cameraScreenWidth, cameraScreenHeight, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); + corners[3] = MCore::Unproject(0.0f, cameraScreenHeight, cameraScreenWidth, cameraScreenHeight, camera->GetFarClipDistance(), inversedProjectionMatrix, inversedViewMatrix); // calculate the intersection points with the ground plane and create an AABB around those // if there is no intersection point then use the ray target as point, which is the projection onto the far plane basically diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp index 327f9d27ae..1094ad72a3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MultiThreadScheduler.cpp @@ -319,9 +319,9 @@ namespace EMotionFX step.mDependencies.Clear(false); // calculate the new dependencies for this step - for (ActorInstance* actorInstance : step.mActorInstances) + for (ActorInstance* stepActorInstance : step.mActorInstances) { - AddDependenciesToStep(actorInstance, &step); + AddDependenciesToStep(stepActorInstance, &step); } // assume that there is only one of the same actor instance in the whole schedule diff --git a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp index 7597cb888d..6060a81bd0 100644 --- a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/MotionDataHandler.cpp @@ -91,10 +91,10 @@ namespace EMotionFX } else { - AZ::Outcome index = factory.FindRegisteredIndexByTypeId(instance); - if (index.IsSuccess()) + AZ::Outcome motionIndex = factory.FindRegisteredIndexByTypeId(instance); + if (motionIndex.IsSuccess()) { - GUI->setCurrentIndex(static_cast(index.GetValue() + 1)); // +1 because we inserted an 'Automatic' one as first entry. + GUI->setCurrentIndex(static_cast(motionIndex.GetValue() + 1)); // +1 because we inserted an 'Automatic' one as first entry. } else { diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp index db07c3ca8e..23d28857e1 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp @@ -725,23 +725,23 @@ namespace GraphCanvas case Attribute::LineColor: case Attribute::StripeColor: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsColorValid(value)) + if (IsColorValid(valueStr)) { - style->SetAttribute(attribute, ParseColor(value)); + style->SetAttribute(attribute, ParseColor(valueStr)); } break; } case Attribute::BackgroundImage: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (value.startsWith(QStringLiteral(":/"))) + if (valueStr.startsWith(QStringLiteral(":/"))) { - value = QString("qrc%1").arg(value); + valueStr = QString("qrc%1").arg(valueStr); } - QUrl url(value); + QUrl url(valueStr); if (url.isValid()) { style->SetAttribute(attribute, url); @@ -844,103 +844,103 @@ namespace GraphCanvas case Attribute::BorderStyle: case Attribute::LineStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsLineStyleValid(value)) + if (IsLineStyleValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseLineStyle(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseLineStyle(valueStr))); } break; } case Attribute::LineCurve: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsLineCurveValid(value)) + if (IsLineCurveValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseLineCurve(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseLineCurve(valueStr))); } break; } case Attribute::CapStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (IsCapStyleValid(value)) + if (IsCapStyleValid(valueStr)) { - style->SetAttribute(attribute, QVariant::fromValue(ParseCapStyle(value))); + style->SetAttribute(attribute, QVariant::fromValue(ParseCapStyle(valueStr))); } break; } case Attribute::FontFamily: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { - value = defaultFontInfo.family(); + valueStr = defaultFontInfo.family(); } else { - QFont font(value); + QFont font(valueStr); QFontInfo info(font); if (!info.exactMatch()) { - qWarning() << "Invalid font-family:" << value; + qWarning() << "Invalid font-family:" << valueStr; } } - style->SetAttribute(attribute, value); + style->SetAttribute(attribute, valueStr); } case Attribute::FontStyle: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFontInfo.style()); } else { - if (IsFontStyleValid(value)) + if (IsFontStyleValid(valueStr)) { - style->SetAttribute(attribute, ParseFontStyle(value)); + style->SetAttribute(attribute, ParseFontStyle(valueStr)); } } break; } case Attribute::FontWeight: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFontInfo.weight()); } else { - if (IsFontWeightValid(value)) + if (IsFontWeightValid(valueStr)) { - style->SetAttribute(attribute, ParseFontWeight(value)); + style->SetAttribute(attribute, ParseFontWeight(valueStr)); } } break; } case Attribute::FontVariant: { - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - if (QString::compare(value, QLatin1String("default"), Qt::CaseInsensitive) == 0) + if (QString::compare(valueStr, QLatin1String("default"), Qt::CaseInsensitive) == 0) { style->SetAttribute(attribute, defaultFont.capitalization()); } else { - if (IsFontVariantValid(value)) + if (IsFontVariantValid(valueStr)) { - style->SetAttribute(attribute, value); + style->SetAttribute(attribute, valueStr); } } break; @@ -965,23 +965,23 @@ namespace GraphCanvas break; case Attribute::PaletteStyle: { - QString value(member->value.GetString()); - style->SetAttribute(attribute, QVariant::fromValue(ParsePaletteStyle(value))); + QString valueStr(member->value.GetString()); + style->SetAttribute(attribute, QVariant::fromValue(ParsePaletteStyle(valueStr))); break; } case Attribute::PatternTemplate: case Attribute::PatternPalettes: { - QString value(member->value.GetString()); - style->SetAttribute(attribute, value); + QString valueStr(member->value.GetString()); + style->SetAttribute(attribute, valueStr); break; } case Attribute::Steps: { QList stepList; - QString value(member->value.GetString()); + QString valueStr(member->value.GetString()); - QStringList splitValues = value.split("|"); + QStringList splitValues = valueStr.split("|"); for (QString currentString : splitValues) { diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp index c2574a69f6..16bc3dd86a 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp @@ -1325,12 +1325,12 @@ namespace GraphCanvas AZStd::vector< Endpoint > endpoints; SlotRequestBus::EventResult(endpoints, currentEndpoint.GetSlotId(), &SlotRequests::GetRemappedModelEndpoints); - for (const Endpoint& endpoint : endpoints) + for (const Endpoint& e : endpoints) { // If we haven't already processed the node, add it to our explore set so we can recurse. - if (retVal.count(endpoint) == 0) + if (retVal.count(e) == 0) { - exploreSet.insert(endpoint); + exploreSet.insert(e); } } } diff --git a/Gems/GraphModel/Code/Source/Model/Graph.cpp b/Gems/GraphModel/Code/Source/Model/Graph.cpp index 25ad61db2c..809679e9f6 100644 --- a/Gems/GraphModel/Code/Source/Model/Graph.cpp +++ b/Gems/GraphModel/Code/Source/Model/Graph.cpp @@ -279,8 +279,8 @@ namespace GraphModel m_connections.erase(iter); #if defined(AZ_ENABLE_TRACING) - auto iter = AZStd::find(m_connections.begin(), m_connections.end(), connection); - AZ_Assert(iter == m_connections.end(), "Graph is broken. The same connection object was found multiple times."); + auto iterConnection = AZStd::find(m_connections.begin(), m_connections.end(), connection); + AZ_Assert(iterConnection == m_connections.end(), "Graph is broken. The same connection object was found multiple times."); #endif return true; diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp index f99eea5fa2..37629fac37 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewAnimNode.cpp @@ -110,10 +110,10 @@ CUiAnimViewAnimNode::CUiAnimViewAnimNode(IUiAnimSequence* pSequence, IUiAnimNode for (int i = 0; i < nodeCount; ++i) { IUiAnimNode* pNode = pSequence->GetNode(i); - IUiAnimNode* pParentNode = pNode->GetParent(); + IUiAnimNode* pNodeParentNode = pNode->GetParent(); // If our node is the parent, then the current node is a child of it - if (pAnimNode == pParentNode) + if (pAnimNode == pNodeParentNode) { CUiAnimViewAnimNodeFactory animNodeFactory; CUiAnimViewAnimNode* pNewUiAVAnimNode = animNodeFactory.BuildAnimNode(pSequence, pNode, this); @@ -510,20 +510,20 @@ bool CUiAnimViewAnimNode::BaseClassPropertyPotentiallyChanged( { for (const AZ::SerializeContext::ClassElement& baseElement : baseClassData->m_elements) { - size_t offset = baseClassOffset + baseElement.m_offset; + size_t baseOffset = baseClassOffset + baseElement.m_offset; if (baseElement.m_flags & AZ::SerializeContext::ClassElement::FLG_BASE_CLASS) { - if (BaseClassPropertyPotentiallyChanged(context, dstComponent, srcComponent, baseElement, offset)) + if (BaseClassPropertyPotentiallyChanged(context, dstComponent, srcComponent, baseElement, baseOffset)) { valueChanged = true; } } else { - if (HasComponentParamValueAzChanged(dstComponent, srcComponent, baseElement, offset)) + if (HasComponentParamValueAzChanged(dstComponent, srcComponent, baseElement, baseOffset)) { valueChanged = true; - AzEntityPropertyChanged(srcComponent, dstComponent, baseElement, offset); + AzEntityPropertyChanged(srcComponent, dstComponent, baseElement, baseOffset); } } } diff --git a/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp b/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp index 797154a50b..fc77a370d3 100644 --- a/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp +++ b/Gems/LyShine/Code/Editor/Animation/UiAnimViewTrack.cpp @@ -59,12 +59,12 @@ CUiAnimViewTrack::CUiAnimViewTrack(IUiAnimTrack* pTrack, CUiAnimViewAnimNode* pT { // Search for child tracks const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount(); - for (unsigned int subTrackIndex = 0; subTrackIndex < subTrackCount; ++subTrackIndex) + for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI) { - IUiAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackIndex); + IUiAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI); CUiAnimViewTrackFactory trackFactory; - CUiAnimViewTrack* pNewUiAVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackIndex); + CUiAnimViewTrack* pNewUiAVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI); m_childNodes.push_back(std::unique_ptr(pNewUiAVTrack)); } diff --git a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp index 2eb840b253..5a080084e1 100644 --- a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp +++ b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp @@ -422,12 +422,12 @@ namespace Audio if (stereoToMono) { // Samples are interleaved now, copy only left channel to the output - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float* outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float* bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - outputData[frame] = *inputData++; - ++inputData; + bufferOutputData[frame] = *bufferInputData++; + ++bufferInputData; } } else // monoToStereo @@ -435,21 +435,21 @@ namespace Audio // Split single samples to both left and right channels if (shouldDeinterleave) { - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float** outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float** bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - outputData[0][frame] = outputData[1][frame] = inputData[frame]; + bufferOutputData[0][frame] = bufferOutputData[1][frame] = bufferInputData[frame]; } } else { - float* inputData = reinterpret_cast(m_conversionBufferIn.m_data); - float* outputData = reinterpret_cast(m_conversionBufferOut.m_data); + float* bufferInputData = reinterpret_cast(m_conversionBufferIn.m_data); + float* bufferOutputData = reinterpret_cast(m_conversionBufferOut.m_data); for (AZ::u32 frame = 0; frame < numFrames; ++frame) { - *outputData++ = inputData[frame]; - *outputData++ = inputData[frame]; + *bufferOutputData++ = bufferInputData[frame]; + *bufferOutputData++ = bufferInputData[frame]; } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index eaf89f3489..b0f1b221e8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -263,9 +263,9 @@ namespace Multiplayer // Validate that we aren't already planning to remove this entity if (safeToExit) { - for (auto entityId : m_removeList) + for (auto remoteEntityId : m_removeList) { - if (entityId == entityId) + if (remoteEntityId == remoteEntityId) { safeToExit = false; } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp index 8b00b5b71b..a5c0287cfd 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp @@ -340,31 +340,31 @@ namespace } // Pass in the associated class data so we can do more intensive lookups? - const AZ::SerializeContext::ClassData* classData = serializeContext.FindClassData(node.first); + const AZ::SerializeContext::ClassData* nodeClassData = serializeContext.FindClassData(node.first); - if (classData == nullptr) + if (nodeClassData == nullptr) { continue; } // Detect primitive types os we avoid making nodes out of them. // Or anything that is 'pure data' and should be populated through a different mechanism. - if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } // Skip over some of our more dynamic nodes that we want to populate using different means - else if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } - else if (classData->m_azRtti && classData->m_azRtti->IsTypeOf()) + else if (nodeClassData->m_azRtti && nodeClassData->m_azRtti->IsTypeOf()) { continue; } else { - nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, classData); + nodePaletteModel.RegisterCustomNode(categoryPath, node.first, node.second, nodeClassData); } } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp index 428f5eb290..3c21701fb0 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/ScriptCanvasContextMenus.cpp @@ -563,13 +563,13 @@ namespace ScriptCanvasEditor else if (slotType == GraphCanvas::SlotTypes::DataSlot) { const AZ::EntityId& slotId2 = GetTargetId(); - const GraphCanvas::GraphId& graphId = GetGraphId(); + const GraphCanvas::GraphId& graphId2 = GetGraphId(); GraphCanvas::Endpoint endpoint; GraphCanvas::SlotRequestBus::EventResult(endpoint, slotId2, &GraphCanvas::SlotRequests::GetEndpoint); bool promotedElement = false; - GraphCanvas::GraphModelRequestBus::EventResult(promotedElement, graphId, &GraphCanvas::GraphModelRequests::PromoteToVariableAction, endpoint); + GraphCanvas::GraphModelRequestBus::EventResult(promotedElement, graphId2, &GraphCanvas::GraphModelRequests::PromoteToVariableAction, endpoint); if (promotedElement) { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index db45f6bfe3..2a485c9501 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -4090,23 +4090,23 @@ namespace ScriptCanvas auto userFunctionIter = m_userInsThatRequireTopology.find(nodeling); if (userFunctionIter != m_userInsThatRequireTopology.end()) { - auto& node = *userFunctionIter->first; - auto outSlots = node.GetSlotsByType(CombinedSlotType::ExecutionOut); + auto& userFunctionNode = *userFunctionIter->first; + auto outSlots = userFunctionNode.GetSlotsByType(CombinedSlotType::ExecutionOut); if (outSlots.empty() || !outSlots.front()) { - AddError(node.GetEntityId(), nullptr, ScriptCanvas::ParseErrors::NoOutSlotInFunctionDefinitionStart); + AddError(userFunctionNode.GetEntityId(), nullptr, ScriptCanvas::ParseErrors::NoOutSlotInFunctionDefinitionStart); return; } - if (!ExecutionContainsCyclesCheck(node, *outSlots.front())) + if (!ExecutionContainsCyclesCheck(userFunctionNode, *outSlots.front())) { auto definition = userFunctionIter->second; auto entrySlot = definition->GetId().m_slot; AZ_Assert(entrySlot, "Bad accounting in user function definition node"); AZStd::vector returnValues; UserOutCallCollector userOutCallCollector; - TraverseExecutionConnections(node, *entrySlot, userOutCallCollector); + TraverseExecutionConnections(userFunctionNode, *entrySlot, userOutCallCollector); const AZStd::unordered_set& uniqueNodelingsOut = userOutCallCollector.GetOutCalls(); for (const auto& returnCall : uniqueNodelingsOut) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp index e7edcce002..8c0f5760a8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Internal/Nodes/ExpressionNodeBase.cpp @@ -121,13 +121,13 @@ namespace ScriptCanvas { if (slotId == ExpressionNodeBaseProperty::GetInSlotId(this)) { - for (const SlotId& slotId : m_dirtyInputs) + for (const SlotId& dirtySlotId : m_dirtyInputs) { - auto variableIter = m_slotToVariableMap.find(slotId); + auto variableIter = m_slotToVariableMap.find(dirtySlotId); if (variableIter != m_slotToVariableMap.end()) { - PushVariable(variableIter->second, (*FindDatum(slotId))); + PushVariable(variableIter->second, (*FindDatum(dirtySlotId))); } } diff --git a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp index 5803f066ae..a6b9456f98 100644 --- a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp +++ b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp @@ -2060,23 +2060,23 @@ namespace WhiteBox polygonHandle.m_faceHandles.push_back(faceHandleToVisit); // for all halfedges - for (const auto halfedgeHandle : faceHalfedges) + for (const auto faceHalfedgeHandle : faceHalfedges) { - const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, halfedgeHandle); + const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, faceHalfedgeHandle); // if we haven't seen this halfedge before and we want to track it, // store it in visited halfedges - if (halfedgeHandle != oppositeHalfedgeHandle + if (faceHalfedgeHandle != oppositeHalfedgeHandle // ignore border halfedges (not inside the polygon) - && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), halfedgeHandle) == + && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), faceHalfedgeHandle) == borderHalfedgeHandles.cend() // ensure we do not visit the same halfedge again - && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), halfedgeHandle) == + && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), faceHalfedgeHandle) == visitedHalfedges.cend() // ignore the halfedge if we've already tracked it in our 'building' list && AZStd::find(buildingEdgeHandles.cbegin(), buildingEdgeHandles.cend(), edgeHandle) == buildingEdgeHandles.cend()) { - halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, halfedgeHandle)); + halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, faceHalfedgeHandle)); } } } @@ -3198,10 +3198,10 @@ namespace WhiteBox // - add bottom faces if mesh was 2d previously (reverse winding order) FaceHandles allFacesToRemove = polygonHandle.m_faceHandles; - for (const auto& polygonHandle : polygonHandlesToRemove) + for (const auto& polygonHandleToRemove : polygonHandlesToRemove) { allFacesToRemove.insert( - allFacesToRemove.end(), polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend()); + allFacesToRemove.end(), polygonHandleToRemove.m_faceHandles.cbegin(), polygonHandleToRemove.m_faceHandles.cend()); } // remove all faces that were already there diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index f53b8aa769..357d578c44 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -73,7 +73,6 @@ ly_append_configurations_options( /wd4389 # comparison, signed/unsigned mismatch /wd4436 # the result of unary operator may be unaligned /wd4450 # declaration hides global declaration - /wd4457 # declaration hides function parameter # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From 1f48985a0e325164af62561f6983ebac4a620e0c Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 4 Jun 2021 18:09:26 +0000 Subject: [PATCH 090/105] Update Blast to the latest version, eb169fe (#1076) --- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index fa1326b63d..1a2cfa4049 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -30,11 +30,11 @@ ly_associate_package(PACKAGE_NAME azslc-1.7.21-rev1-multiplatform ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) ly_associate_package(PACKAGE_NAME xxhash-0.7.4-rev1-multiplatform TARGETS xxhash PACKAGE_HASH e81f3e6c4065975833996dd1fcffe46c3cf0f9e3a4207ec5f4a1b564ba75861e) -ly_associate_package(PACKAGE_NAME Blast-1.1.7-rev1-multiplatform TARGETS Blast PACKAGE_HASH 36b8f393bcd25d0f85cfc7a831ebbdac881e6054c4f0735649966aa6aa86e6f0) ly_associate_package(PACKAGE_NAME PVRTexTool-4.24.0-rev4-multiplatform TARGETS PVRTexTool PACKAGE_HASH d0d6da61c7557de0d2c71fc35ba56c3be49555b703f0e853d4c58225537acf1e) # platform-specific: ly_associate_package(PACKAGE_NAME AWSGameLiftServerSDK-3.4.1-rev1-windows TARGETS AWSGameLiftServerSDK PACKAGE_HASH a0586b006e4def65cc25f388de17dc475e417dc1e6f9d96749777c88aa8271b0) +ly_associate_package(PACKAGE_NAME Blast-v1.1.7_rc2-9-geb169fe-rev1-windows TARGETS Blast PACKAGE_HASH 216df71f4ffaf4a6ea3f2e77e5f27d68f2325e717fbd1626b00c785b82cd1b67) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-1.6.2104-o3de-rev2-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH decc53e97c7ddda9c7f853a30af7808a7b652a912f59ad2cd4bca5d308aae2c4) ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-windows TARGETS SPIRVCross PACKAGE_HASH 7d601ea9d625b1d509d38bd132a1f433d7e895b16adab76bac6103567a7a6817) ly_associate_package(PACKAGE_NAME freetype-2.10.4.14-windows TARGETS freetype PACKAGE_HASH 88dedc86ccb8c92f14c2c033e51ee7d828fa08eafd6475c6aa963938a99f4bf3) From febf53671eacbe0f67a60feb5317ef93d70f43f5 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 11:09:44 -0700 Subject: [PATCH 091/105] Addressed PR feedback. --- .../AzCore/AzCore/Serialization/IdUtils.h | 2 +- .../Spawnable/SpawnableEntitiesInterface.h | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h index 98959fe4bd..15ce299000 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/IdUtils.h @@ -29,7 +29,7 @@ namespace AZ namespace IdUtils { /** - * \param AllowDuplicates - If true allows the same id to be registered multiple time, + * \param AllowDuplicates - If true allows the same id to be registered multiple times, with the newer value overwriting the stored value. If false, duplicates are not allowed and the first stored value is kept.The default is false. */ diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 2ad1db60a4..d2a5872fc8 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -177,8 +177,8 @@ namespace AzFramework //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. EntityPreInsertionCallback m_preInsertionCallback; - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext { nullptr }; @@ -191,8 +191,8 @@ namespace AzFramework //! Callback that's called after instances of entities have been created, but before they're spawned into the world. This //! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components. EntityPreInsertionCallback m_preInsertionCallback; - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to spawn. The returned list of entities contains all the newly created entities. EntitySpawnCallback m_completionCallback; //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. AZ::SerializeContext* m_serializeContext{ nullptr }; @@ -207,8 +207,8 @@ namespace AzFramework struct DespawnAllEntitiesOptionalArgs final { - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to despawn. The returned list of entities contains all the newly created entities. EntityDespawnCallback m_completionCallback; //! The priority at which this call will be executed. SpawnablePriority m_priority { SpawnablePriority_Default }; @@ -216,10 +216,10 @@ namespace AzFramework struct ReloadSpawnableOptionalArgs final { - //! Callback that's called when spawning entities has completed. This can be called from a different thread than the one that - //! made the function call. The returned list of entities contains all the newly created entities. + //! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that + //! made the function call to respawn. The returned list of entities contains all the newly created entities. ReloadSpawnableCallback m_completionCallback; - //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used. + //! The Serialize Context used to clone entities with. If this is not provided the global Serialize Context will be used. AZ::SerializeContext* m_serializeContext { nullptr }; //! The priority at which this call will be executed. SpawnablePriority m_priority { SpawnablePriority_Default }; @@ -268,32 +268,32 @@ namespace AzFramework //! Spawn instances of all entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs. virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Spawn instances of some entities in the spawnable. //! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them. //! @param priority The priority at which this call will be executed. //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs. virtual void SpawnEntities( EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param priority The priority at which this call will be executed. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs. virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable. //! @param ticket Holds the information on the entities to reload. //! @param priority The priority at which this call will be executed. //! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id. - //! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs + //! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs. virtual void ReloadSpawnable( EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities on. + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. virtual void ListEntities( EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! List all entities that are spawned using this ticket with their spawnable index. @@ -303,23 +303,23 @@ namespace AzFramework //! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be //! created. //! @param ticket Only the entities associated with this ticket will be listed. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to list the entities and indices on. + //! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs. virtual void ListIndicesAndEntities( EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0; //! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the //! caller through the callback. After this call the ticket will have no entities associated with it. The caller of //! this function will need to manage the entities after this call. //! @param ticket Only the entities associated with this ticket will be released. - //! @param priority The priority at which this call will be executed. //! @param listCallback Required callback that will be called to transfer the entities through. + //! @param optionalArgs Optional additional arguments, see ClaimEntitiesOptionalArgs. virtual void ClaimEntities( EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0; //! Blocks until all operations made on the provided ticket before the barrier call have completed. //! @param ticket The ticket to monitor. - //! @param priority The priority at which this call will be executed. //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. + //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; //! Register a handler for OnSpawned events. From b10ed227c0d7a59d51b6397a4f25a4b395293ae8 Mon Sep 17 00:00:00 2001 From: guthadam Date: Fri, 4 Jun 2021 13:54:45 -0500 Subject: [PATCH 092/105] Added version handling for removed fields --- .../Code/Source/Material/EditorMaterialComponentSlot.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index bdc82acda6..65fa7bc286 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -70,6 +70,12 @@ namespace AZ } } + if (classElement.GetVersion() < 5) + { + classElement.RemoveElementByName(AZ_CRC_CE("matModUvOverrides")); + classElement.RemoveElementByName(AZ_CRC_CE("propertyOverrides")); + } + return true; } From 40d90c49a378bb82c1632fb1e29fbc6b7bd76774 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 13:42:25 -0700 Subject: [PATCH 093/105] Disabled writing UserSettings.xml in Spawnable tests. --- .../Tests/Spawnable/SpawnableEntitiesManagerTests.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 484b7f46d7..8bb39449f1 100644 --- a/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -40,6 +41,10 @@ namespace UnitTest m_application = new TestApplication(); AZ::ComponentApplication::Descriptor descriptor; m_application->Start(descriptor); + // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // in the unit tests. + AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); m_spawnable = aznew AzFramework::Spawnable( AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready); From 55a46806590d04c45b3203fa27b5216536677100 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:10:30 -0700 Subject: [PATCH 094/105] Fixed Multiplayer unit tests. The multiplayer unit tests created a SpawnableSystemComponent without an application to provide the Serialize Context. This caused an assert which failed the unit tests. Since the entity spawning system doesn't seem to be directly used the component was removed. --- Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 6ace4db592..7b09b65de4 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -30,7 +30,6 @@ namespace UnitTest { SetupAllocator(); AZ::NameDictionary::Create(); - m_spawnableComponent = new AzFramework::SpawnableSystemComponent(); m_netComponent = new AzNetworking::NetworkingSystemComponent(); m_mpComponent = new Multiplayer::MultiplayerSystemComponent(); @@ -46,7 +45,6 @@ namespace UnitTest { delete m_mpComponent; delete m_netComponent; - delete m_spawnableComponent; AZ::NameDictionary::Destroy(); TeardownAllocator(); } @@ -76,7 +74,6 @@ namespace UnitTest AzNetworking::NetworkingSystemComponent* m_netComponent = nullptr; Multiplayer::MultiplayerSystemComponent* m_mpComponent = nullptr; - AzFramework::SpawnableSystemComponent* m_spawnableComponent = nullptr; }; TEST_F(MultiplayerSystemTests, TestInitEvent) From 50d6e36ccd17c9214f057e749dc73db5a2c148b8 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:36:46 -0700 Subject: [PATCH 095/105] Bug and improvements to Editor/AP debugging settings (#1146) --- Code/Sandbox/Editor/CMakeLists.txt | 4 ++-- Code/Tools/AssetProcessor/CMakeLists.txt | 4 ++-- cmake/Projects.cmake | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Code/Sandbox/Editor/CMakeLists.txt b/Code/Sandbox/Editor/CMakeLists.txt index 01b58e3f77..58d5e86a2f 100644 --- a/Code/Sandbox/Editor/CMakeLists.txt +++ b/Code/Sandbox/Editor/CMakeLists.txt @@ -191,8 +191,8 @@ ly_add_translations( ) ly_add_dependencies(Editor AssetProcessor) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() ################################################################################ diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 6c12ab6024..2db888218b 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -125,8 +125,8 @@ ly_add_target( AZ::AssetProcessorBatch.Static ) -if(LY_FIRST_PROJECT_PATH) - set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_FIRST_PROJECT_PATH}\"") +if(LY_DEFAULT_PROJECT_PATH) + set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") endif() # Adds the AssetProcessorBatch target as a C preprocessor define so that it can be used as a Settings Registry diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index adaf7ee15f..eadfa79c1b 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -167,9 +167,6 @@ endfunction() # Add the projects here so the above function is found foreach(project ${LY_PROJECTS}) file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) - if(NOT LY_FIRST_PROJECT) - ly_set(LY_FIRST_PROJECT_PATH ${full_directory_path}) - endif() string(SHA256 full_directory_hash ${full_directory_path}) # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit @@ -182,4 +179,13 @@ foreach(project ${LY_PROJECTS}) ly_generate_project_build_path_setreg(${full_directory_path}) add_project_json_external_subdirectories(${full_directory_path}) endforeach() + +# If just one project is defined we pass it as a parameter to the applications +list(LENGTH LY_PROJECTS projects_length) +if(projects_length EQUAL "1") + list(GET LY_PROJECTS 0 project) + file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + ly_set(LY_DEFAULT_PROJECT_PATH ${full_directory_path}) +endif() + ly_set(LY_PROJECTS_FOLDER_NAME ${LY_PROJECTS_FOLDER_NAME}) From 8a079da914ceadb07846d66230dd031a3b8cccc6 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Sat, 5 Jun 2021 00:04:26 +0200 Subject: [PATCH 096/105] GemCatalog: Gem cart widget and overlay window * [LYN-4174] Added icons for gem catalog summary cart * [LYN-4174] Gem Catalog: Text eliding for too long gem names and creators * [LYN-4174] Gem catalog: Resetting filters when re-initializing for another project * [LYN-4174] Gem Catalog: Fixed a bug with filters being applied/remembered after leaving gem catalog and coming back editing another project * [LYN-4174] GemCatalog: Gem cart widget and overlay window * Added cart button with dynamic label to display the number of gems to be enabled/disabled and a arrow down button to indicate some sort of pop-up/overlay window will appear on click. * Overlay gem tags update dynamically while the dialog is open based on the gem model. * Moved some styling from C++ to the style sheet. --- .../Resources/CarrotArrowDown.svg | 3 + .../Resources/ProjectManager.qrc | 3 + .../Resources/ProjectManager.qss | 40 ++- .../ProjectManager/Resources/Summary.svg | 3 + .../ProjectManager/Resources/WindowClose.svg | 4 + .../GemCatalog/GemCatalogHeaderWidget.cpp | 235 +++++++++++++++++- .../GemCatalog/GemCatalogHeaderWidget.h | 64 ++++- .../Source/GemCatalog/GemCatalogScreen.cpp | 8 +- .../Source/GemCatalog/GemCatalogScreen.h | 2 + .../Source/GemCatalog/GemItemDelegate.cpp | 8 +- .../GemCatalog/GemSortFilterProxyModel.cpp | 11 + .../GemCatalog/GemSortFilterProxyModel.h | 1 + 12 files changed, 350 insertions(+), 32 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg create mode 100644 Code/Tools/ProjectManager/Resources/Summary.svg create mode 100644 Code/Tools/ProjectManager/Resources/WindowClose.svg diff --git a/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg new file mode 100644 index 0000000000..73545968d5 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/CarrotArrowDown.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 04d5e98a10..62b7d23e9c 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -26,5 +26,8 @@ Backgrounds/FirstTimeBackgroundImage.jpg ArrowDownLine.svg ArrowUpLine.svg + CarrotArrowDown.svg + Summary.svg + WindowClose.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 224574f522..6fd4086c58 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -115,7 +115,7 @@ QTabBar::tab:pressed /************** General (Modal windows) **************/ #header { - background-color:#111111; + background-color:#111111; min-height:80px; max-height:80px; } @@ -172,8 +172,8 @@ QTabBar::tab:pressed #footer > QPushButton { qproperty-flat: true; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0095f2, stop: 1.0 #1e70eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0095f2, stop: 1.0 #1e70eb); border-radius: 3px; min-height: 28px; max-height: 28px; @@ -181,26 +181,26 @@ QTabBar::tab:pressed margin-right:30px; } #footer > QPushButton:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #10A5f2, stop: 1.0 #2e80eb); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #10A5f2, stop: 1.0 #2e80eb); } #footer > QPushButton:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #0085e2, stop: 1.0 #0e60db); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #0085e2, stop: 1.0 #0e60db); } #footer > QPushButton[secondary="true"] { margin-right: 10px; - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #888888, stop: 1.0 #555555); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); } #footer > QPushButton[secondary="true"]:hover { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #999999, stop: 1.0 #666666); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); } #footer > QPushButton[secondary="true"]:pressed { - background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #555555, stop: 1.0 #777777); + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); } /************** Project Settings **************/ @@ -356,6 +356,20 @@ QTabBar::tab:pressed font-size: 18px; } +#GemCatalogCart { + background-color: #555555; +} + +#GemCatalogCartCountLabel { + font-size: 12px; + background-color: #4285F4; + border-radius: 3px; +} + +#GemCatalogCartOverlaySectionLabel { + font-weight: 600; +} + /************** Gem Catalog (Inspector) **************/ #GemCatalogInspector { diff --git a/Code/Tools/ProjectManager/Resources/Summary.svg b/Code/Tools/ProjectManager/Resources/Summary.svg new file mode 100644 index 0000000000..fe26718aff --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Summary.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/WindowClose.svg b/Code/Tools/ProjectManager/Resources/WindowClose.svg new file mode 100644 index 0000000000..0485ff95cd --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/WindowClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 6402121e4a..a98135d3e6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -10,24 +10,229 @@ * */ -#include #include #include +#include #include +#include +#include namespace O3DE::ProjectManager { - GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) + CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + setObjectName("GemCatalogCart"); + + m_layout = new QVBoxLayout(); + m_layout->setSpacing(0); + m_layout->setMargin(0); + m_layout->setAlignment(Qt::AlignTop); + setLayout(m_layout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + + QPushButton* closeButton = new QPushButton(); + closeButton->setFlat(true); + closeButton->setFocusPolicy(Qt::NoFocus); + closeButton->setIcon(QIcon(":/WindowClose.svg")); + connect(closeButton, &QPushButton::clicked, this, [=] + { + deleteLater(); + }); + hLayout->addSpacerItem(new QSpacerItem(10, 0, QSizePolicy::Expanding)); + hLayout->addWidget(closeButton); + m_layout->addLayout(hLayout); + + // enabled + { + m_enabledWidget = new QWidget(); + m_enabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_enabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_enabledWidget->setLayout(layout); + + m_enabledLabel = new QLabel(); + m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_enabledLabel); + m_enabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_enabledTagContainer); + } + + // disabled + { + m_disabledWidget = new QWidget(); + m_disabledWidget->setFixedWidth(s_width); + m_layout->addWidget(m_disabledWidget); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + m_disabledWidget->setLayout(layout); + + m_disabledLabel = new QLabel(); + m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel"); + layout->addWidget(m_disabledLabel); + m_disabledTagContainer = new TagContainerWidget(); + layout->addWidget(m_disabledTagContainer); + } + + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + + Update(); + connect(gemModel, &GemModel::dataChanged, this, [=] + { + Update(); + }); + } + + void CartOverlayWidget::Update() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + if (toBeAdded.isEmpty()) + { + m_enabledWidget->hide(); + } + else + { + m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded)); + m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled"))); + m_enabledWidget->show(); + } + + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeRemoved.isEmpty()) + { + m_disabledWidget->hide(); + } + else + { + m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved)); + m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled"))); + m_disabledWidget->show(); + } + } + + QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const + { + QStringList gemNames; + gemNames.reserve(gems.size()); + for (const QModelIndex& modelIndex : gems) + { + gemNames.push_back(GemModel::GetName(modelIndex)); + } + return gemNames; + } + + CartButton::CartButton(GemModel* gemModel, QWidget* parent) + : QWidget(parent) + , m_gemModel(gemModel) + { + m_layout = new QHBoxLayout(); + m_layout->setMargin(0); + setLayout(m_layout); + + QPushButton* iconButton = new QPushButton(); + iconButton->setFlat(true); + iconButton->setFocusPolicy(Qt::NoFocus); + iconButton->setIcon(QIcon(":/Summary.svg")); + iconButton->setFixedSize(s_iconSize, s_iconSize); + connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(iconButton); + + m_countLabel = new QLabel(); + m_countLabel->setObjectName("GemCatalogCartCountLabel"); + m_countLabel->setFixedHeight(s_iconSize - 1); // Compensate for the empty icon space by using a slightly smaller label height. + m_layout->addWidget(m_countLabel); + + m_dropDownButton = new QPushButton(); + m_dropDownButton->setFlat(true); + m_dropDownButton->setFocusPolicy(Qt::NoFocus); + m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg")); + m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize); + connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay); + m_layout->addWidget(m_dropDownButton); + + // Adjust the label text whenever the model gets updated. + connect(gemModel, &GemModel::dataChanged, [=] + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + + const int count = toBeAdded.size() + toBeRemoved.size(); + m_countLabel->setText(QString::number(count)); + + m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty()); + + // Automatically close the overlay window in case there are no gems to be enabled or disabled anymore. + if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + m_cartOverlay->deleteLater(); + m_cartOverlay = nullptr; + } + }); + } + + void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event) + { + ShowOverlay(); + } + + void CartButton::ShowOverlay() + { + const QVector toBeAdded = m_gemModel->GatherGemsToBeAdded(); + const QVector toBeRemoved = m_gemModel->GatherGemsToBeRemoved(); + if (toBeAdded.isEmpty() && toBeRemoved.isEmpty()) + { + return; + } + + if (m_cartOverlay) + { + // Directly delete the former overlay before creating the new one. + // Don't use deleteLater() here. This might overwrite the new overlay pointer + // depending on the event queue. + delete m_cartOverlay; + } + + m_cartOverlay = new CartOverlayWidget(m_gemModel, this); + connect(m_cartOverlay, &QWidget::destroyed, this, [=] + { + // Reset the overlay pointer on destruction to prevent dangling pointers. + m_cartOverlay = nullptr; + }); + m_cartOverlay->show(); + + const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos()); + const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos()); + const QPoint offset(-4, 10); + m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(), + globalPos.y() + offset.y(), + m_cartOverlay->width(), + m_cartOverlay->height()); + } + + CartButton::~CartButton() + { + // Make sure the overlay window is automatically closed in case the gem catalog is destroyed. + if (m_cartOverlay) + { + m_cartOverlay->deleteLater(); + } + } + + GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QFrame(parent) { QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setAlignment(Qt::AlignLeft); - hLayout->setMargin(0); + hLayout->setContentsMargins(10, 7, 10, 7); setLayout(hLayout); setObjectName("GemCatalogHeaderWidget"); - - hLayout->addSpacing(7); + setFixedHeight(s_height); QLabel* titleLabel = new QLabel(tr("Gem Catalog")); titleLabel->setObjectName("GemCatalogTitle"); @@ -35,17 +240,23 @@ namespace O3DE::ProjectManager hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit(); - filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); - connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) + m_filterLineEdit = new AzQtComponents::SearchLineEdit(); + m_filterLineEdit->setStyleSheet("background-color: #DDDDDD;"); + connect(m_filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text) { filterProxyModel->SetSearchString(text); }); - hLayout->addWidget(filterLineEdit); + hLayout->addWidget(m_filterLineEdit); hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding)); - hLayout->addSpacerItem(new QSpacerItem(140, 0, QSizePolicy::Fixed)); - - setFixedHeight(60); + hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed)); + + CartButton* cartButton = new CartButton(gemModel); + hLayout->addWidget(cartButton); + } + + void GemCatalogHeaderWidget::ReinitForProject() + { + m_filterLineEdit->setText({}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 3e065edd8f..bef7555618 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -13,19 +13,81 @@ #pragma once #if !defined(Q_MOC_RUN) +#include +#include #include +#include #include +#include +#include +#include +#include #endif namespace O3DE::ProjectManager { + class CartOverlayWidget + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr); + void Update(); + + private: + QStringList ConvertFromModelIndices(const QVector& gems) const; + + QVBoxLayout* m_layout = nullptr; + GemModel* m_gemModel = nullptr; + + QWidget* m_enabledWidget = nullptr; + QLabel* m_enabledLabel = nullptr; + TagContainerWidget* m_enabledTagContainer = nullptr; + + QWidget* m_disabledWidget = nullptr; + QLabel* m_disabledLabel = nullptr; + TagContainerWidget* m_disabledTagContainer = nullptr; + + inline constexpr static int s_width = 240; + }; + + class CartButton + : public QWidget + { + Q_OBJECT // AUTOMOC + + public: + CartButton(GemModel* gemModel, QWidget* parent = nullptr); + ~CartButton(); + void ShowOverlay(); + + private: + void mousePressEvent(QMouseEvent* event) override; + + GemModel* m_gemModel = nullptr; + QHBoxLayout* m_layout = nullptr; + QLabel* m_countLabel = nullptr; + QPushButton* m_dropDownButton = nullptr; + CartOverlayWidget* m_cartOverlay = nullptr; + + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_arrowDownIconSize = 8; + }; + class GemCatalogHeaderWidget : public QFrame { Q_OBJECT // AUTOMOC public: - explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); + explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemCatalogHeaderWidget() = default; + + void ReinitForProject(); + + private: + AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr; + inline constexpr static int s_height = 60; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index aa36c1b0ab..4424767c0b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,8 @@ namespace O3DE::ProjectManager vLayout->setSpacing(0); setLayout(vLayout); - GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(m_proxModel); - vLayout->addWidget(headerWidget); + m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel); + vLayout->addWidget(m_headerWidget); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -77,10 +76,11 @@ namespace O3DE::ProjectManager m_filterWidget->deleteLater(); } + m_proxModel->ResetFilters(); m_filterWidget = new GemFilterWidget(m_proxModel); m_filterWidgetLayout->addWidget(m_filterWidget); - m_proxModel->InvalidateFilter(); + m_headerWidget->ReinitForProject(); // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 0847d9b74e..f5092e837a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -14,6 +14,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager GemListView* m_gemListView = nullptr; GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; + GemCatalogHeaderWidget* m_headerWidget = nullptr; GemSortFilterProxyModel* m_proxModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 0fc0d89fcb..8aa68fb7a2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -53,6 +53,7 @@ namespace O3DE::ProjectManager QFont standardFont(options.font); standardFont.setPixelSize(s_fontSize); + QFontMetrics standardFontMetrics(standardFont); painter->save(); painter->setClipping(true); @@ -78,8 +79,10 @@ namespace O3DE::ProjectManager } // Gem name - const QString gemName = GemModel::GetName(modelIndex); + QString gemName = GemModel::GetName(modelIndex); QFont gemNameFont(options.font); + const int firstColumnMaxTextWidth = s_summaryStartX - 30; + gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); gemNameFont.setPixelSize(s_gemNameFontSize); gemNameFont.setBold(true); QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize); @@ -90,7 +93,8 @@ namespace O3DE::ProjectManager painter->drawText(gemNameRect, Qt::TextSingleLine, gemName); // Gem creator - const QString gemCreator = GemModel::GetCreator(modelIndex); + QString gemCreator = GemModel::GetCreator(modelIndex); + gemCreator = standardFontMetrics.elidedText(gemCreator, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize); gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 33936f417e..d8f41c077e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -130,4 +130,15 @@ namespace O3DE::ProjectManager invalidate(); emit OnInvalidated(); } + + void GemSortFilterProxyModel::ResetFilters() + { + m_searchString.clear(); + m_gemOriginFilter = {}; + m_platformFilter = {}; + m_typeFilter = {}; + m_featureFilter = {}; + + InvalidateFilter(); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index e5554c020c..f24a724ecf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -51,6 +51,7 @@ namespace O3DE::ProjectManager void SetFeatures(const QSet& features) { m_featureFilter = features; InvalidateFilter(); } void InvalidateFilter(); + void ResetFilters(); signals: void OnInvalidated(); From e71a4656bc1a3a304ee124c426d0936dafc22a38 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:10:20 -0700 Subject: [PATCH 097/105] SPEC-2513 Fixes to enable w4450 (#1145) * Fix for w4457 * Nothing to fix, seems we deleted all the code that was causing this offense --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 357d578c44..d0e4d9ed73 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -72,7 +72,6 @@ ly_append_configurations_options( /wd4366 # the result of unary operator may be unaligned /wd4389 # comparison, signed/unsigned mismatch /wd4436 # the result of unary operator may be unaligned - /wd4450 # declaration hides global declaration # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From cf35585bc0d50e6e09c6e712bdb66351cf40fc94 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:25:57 -0700 Subject: [PATCH 098/105] Making incremental linking off by default (#1154) --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 2 +- scripts/build/Platform/Windows/build_config.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index d0e4d9ed73..8c22677f91 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -126,7 +126,7 @@ ly_append_configurations_options( /INCREMENTAL:NO ) -set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG TRUE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = TRUE)") +set(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG FALSE CACHE BOOL "Indicates if incremental linking is used in debug configurations (default = FALSE)") if(LY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG) ly_append_configurations_options( COMPILATION_DEBUG diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index fc4668de0a..552ef2c6fd 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -87,7 +87,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -101,7 +101,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", From 1396110f6d0edc8795ae7a439945b399b3684e5a Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 15:46:19 -0700 Subject: [PATCH 099/105] Preventing builds from cleaning on each step (#1151) --- scripts/build/Jenkins/Jenkinsfile | 14 +++++++------- scripts/build/Platform/Linux/build_linux.sh | 1 - scripts/build/Platform/Mac/build_mac.sh | 1 - scripts/build/Platform/Windows/build_windows.cmd | 1 - 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 2f6be2b060..3cf7d92ba6 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -313,13 +313,13 @@ def PreBuildCommonSteps(Map pipelineConfig, String repositoryName, String projec script: 'python/get_python.bat' } - if(env.CLEAN_OUTPUT_DIRECTORY?.toBoolean() || env.CLEAN_ASSETS?.toBoolean()) { - def command = "${pipelineConfig.PYTHON_DIR}/python" - if(env.IS_UNIX) command += '.sh' - else command += '.cmd' - command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" - palSh(command, "Running ${platform} clean") - } + // Always run the clean step, the scripts detect what variables were set, but it also cleans if + // the NODE_LABEL has changed + def command = "${pipelineConfig.PYTHON_DIR}/python" + if(env.IS_UNIX) command += '.sh' + else command += '.cmd' + command += " -u ${pipelineConfig.BUILD_ENTRY_POINT} --platform ${platform} --type clean" + palSh(command, "Running ${platform} clean") } } diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index ef1b1dcb96..f88d3ab116 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_linux.sh -source $BASEDIR/clean_linux.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index 4a61f97fe4..473a968d98 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -14,7 +14,6 @@ set -o errexit # exit on the first failure encountered BASEDIR=$(dirname "$0") source $BASEDIR/env_mac.sh -source $BASEDIR/clean_mac.sh mkdir -p ${OUTPUT_DIRECTORY} SOURCE_DIRECTORY=${PWD} diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index 474c1720df..4dcd12d008 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -13,7 +13,6 @@ REM SETLOCAL EnableDelayedExpansion CALL %~dp0env_windows.cmd -CALL %~dp0clean_windows.cmd IF NOT EXIST "%OUTPUT_DIRECTORY%" ( MKDIR %OUTPUT_DIRECTORY%. From 74f474aae2084c0489fd9dfa8b5025970b197e03 Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:02:09 -0500 Subject: [PATCH 100/105] Add unit tests for the ViewportScreen ndc <-> worldspace utility functions (#1149) Add ScreenNdcToWorld function to enable round trip testing. --- .../AzFramework/Viewport/ViewportScreen.cpp | 17 +++- .../AzFramework/Viewport/ViewportScreen.h | 8 +- .../Tests/Viewport/ViewportScreenTests.cpp | 87 ++++++++++++++++++- 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp index 5d2d02a398..8283e0b1f2 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.cpp @@ -128,12 +128,12 @@ namespace AzFramework worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize); } - AZ::Vector3 ScreenToWorld( - const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, - const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + AZ::Vector3 ScreenNDCToWorld( + const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection) { // convert screen space coordinates from <0, 1> to <-1,1> range - const auto ndcPosition = NDCFromScreenPoint(screenPosition, viewportSize) * 2.0f - AZ::Vector2::CreateOne(); + const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne(); // transform ndc space position to clip space const auto clipSpacePosition = inverseCameraProjection * Vector2ToVector4(ndcPosition, -1.0f, 1.0f); @@ -145,6 +145,15 @@ namespace AzFramework return worldPosition; } + AZ::Vector3 ScreenToWorld( + const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize) + { + const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize); + + return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection); + } + AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState) { return ScreenToWorld( diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h index a2c650465f..844f52b565 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportScreen.h @@ -42,7 +42,7 @@ namespace AzFramework const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection, const AZ::Vector2& viewportSize); - //! Unprojects a position in screen space to world space. + //! Unprojects a position in screen space pixel coordinates to world space. //! Note: The position returned will be on the near clip plane of the camera in world space. AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState); @@ -52,6 +52,12 @@ namespace AzFramework const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize); + //! Unprojects a position in screen space normalized device coordinates to world space. + //! Note: The position returned will be on the near clip plane of the camera in world space. + AZ::Vector3 ScreenNDCToWorld( + const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, + const AZ::Matrix4x4& inverseCameraProjection); + //! Returns the camera projection for the current camera state. AZ::Matrix4x4 CameraProjection(const CameraState& cameraState); diff --git a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp index 18462bc97b..d82c7ec425 100644 --- a/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Viewport/ViewportScreenTests.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,15 @@ namespace UnitTest { + // transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates + AZ::Vector2 ScreenNDCToWorldToScreenNDC( + const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState) + { + const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); + const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState)); + return AZ::Vector3ToVector2(ndcResult); + } + // transform a point from screen space to world space, and then from world space back to screen space AzFramework::ScreenPoint ScreenToWorldToScreen( const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState) @@ -30,7 +40,8 @@ namespace UnitTest const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState); return AzFramework::WorldToScreen(worldResult, cameraState); } - + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenPoint tests TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin) { using AzFramework::ScreenPoint; @@ -38,8 +49,6 @@ namespace UnitTest const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f); - // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip - // plane of the camera so use that to confirm the mapping to/from is correct const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); { const auto expectedScreenPoint = ScreenPoint{600, 450}; @@ -81,6 +90,8 @@ namespace UnitTest EXPECT_EQ(resultScreenPoint, expectedScreenPoint); } + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct TEST(ViewportScreen, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace) { using AzFramework::ScreenPoint; @@ -94,7 +105,75 @@ namespace UnitTest const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState); EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); } + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // NDC tests + TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f); + const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions); + { + const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + { + const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + } + + TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraTransform = + AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f)); + + const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f}; + const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState); + EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint)); + } + + // note: nearClip is 0.1 - the world space value returned will be aligned to the near clip + // plane of the camera so use that to confirm the mapping to/from is correct + TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace) + { + using NdcPoint = AZ::Vector2; + + const auto screenDimensions = AZ::Vector2(800.0f, 600.0f); + const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * + AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f)); + + const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions); + + const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState)); + EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f))); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // ScreenVector tests TEST(ViewportScreen, SubstractingScreenPointGivesScreenVector) { using AzFramework::ScreenPoint; @@ -220,6 +299,8 @@ namespace UnitTest EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f); } + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // Other tests TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack) { const auto screenDimensions = AZ::Vector2(1024.0f, 768.0f); From 9b1775427895177c6d0125dd6d6ed6bc95d6c823 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 4 Jun 2021 16:14:25 -0700 Subject: [PATCH 101/105] Project Template details and preview changes --- .../Resources/ArrowBack_Hover.svg | 3 + .../Resources/DefaultTemplate.png | 3 + .../Resources/ProjectManager.qrc | 2 + .../Resources/ProjectManager.qss | 67 ++++++- .../Source/CreateProjectCtrl.cpp | 183 ++++++++++++------ .../ProjectManager/Source/CreateProjectCtrl.h | 36 +++- .../Source/NewProjectSettingsScreen.cpp | 127 ++++++++++-- .../Source/NewProjectSettingsScreen.h | 14 ++ .../Source/ProjectTemplateInfo.h | 1 + .../ProjectManager/Source/PythonBindings.cpp | 9 +- .../Source/TemplateButtonWidget.cpp | 65 +++++++ .../Source/TemplateButtonWidget.h | 37 ++++ .../project_manager_files.cmake | 2 + Templates/DefaultProject/template.json | 5 +- 14 files changed, 464 insertions(+), 90 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg create mode 100644 Code/Tools/ProjectManager/Resources/DefaultTemplate.png create mode 100644 Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp create mode 100644 Code/Tools/ProjectManager/Source/TemplateButtonWidget.h diff --git a/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg new file mode 100644 index 0000000000..5b3b14f09a --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowBack_Hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/DefaultTemplate.png b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png new file mode 100644 index 0000000000..2634c383fc --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/DefaultTemplate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8358f4dad9878c662b9819b2b346622af691eb45f8eddc28fff79a50650ae6cf +size 2503 diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 62b7d23e9c..33acfa9e1b 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -7,6 +7,7 @@ AddOffset.svg AddOffset_Hover.svg ArrowBack.svg + ArrowBack_Hover.svg build.svg FolderOffset.svg FolderOffset_Hover.svg @@ -18,6 +19,7 @@ Linux.svg macOS.svg DefaultProjectImage.png + DefaultTemplate.png ArrowDownLine.svg ArrowUpLine.svg o3de.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6fd4086c58..8b7470051c 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -136,11 +136,9 @@ QTabBar::tab:pressed #header QPushButton:focus { border:none; } -#header QPushButton:hover { - background:#333333 url(:/ArrowBack.svg) no-repeat center; -} +#header QPushButton:hover, #header QPushButton:pressed { - background:#222222 url(:/ArrowBack.svg) no-repeat center; + background:transparent url(:/ArrowBack_Hover.svg) no-repeat center; } #headerTitle { @@ -210,9 +208,6 @@ QTabBar::tab:pressed #projectTemplate { margin: 55px 0 0 50px; - max-width: 780px; - min-height:200px; - max-height:200px; } #projectTemplateLabel { font-size:16px; @@ -227,11 +222,67 @@ QTabBar::tab:pressed #projectTemplateDetails { background-color:#444444; - max-width:240px; + max-width:20%; min-width:240px; margin-left:30px; } +#projectTemplateDetails #displayName, +#projectTemplateDetails #includedGemsTitle { + font-size:18px; +} + +#projectTemplateDetails #moreGems { + font-size:14px; + margin-top:20px; +} + +#projectTemplateDetails #includedGemsTitle { + margin-top:5px; + margin-bottom:5px; +} + +#projectTemplateDetails #summary { + padding-bottom:0px; + border-bottom:2px solid #555555; + min-height:80px; + qproperty-alignment: AlignTop; +} + +#projectTemplateDetails #browseCatalog { + margin:5px 0px 15px 0px; +} + +#projectTemplate QPushButton { + qproperty-flat: true; + min-width: 96px; + max-width: 96px; + min-height: 160px; + max-height: 160px; +} +#projectTemplate #templateLabel { + qproperty-alignment: AlignCenter; +} +#projectTemplate QPushButton #templateImage { + border:3px solid transparent; + border-radius: 4px; +} +#projectTemplate QPushButton[Checked="true"] #templateImage { + border:3px solid #1e70eb; +} +#projectTemplate QPushButton[Checked="true"] #templateLabel { + font-weight:bold; +} +#projectTemplate QPushButton:hover { + background-color: #444444; +} +#projectTemplate QPushButton:focus { + outline: none; + border:none; +} + + + #projectSettingsTab::tab-bar { left: 60px; } diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 85e34aeced..4498a6bc82 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -41,24 +42,33 @@ namespace O3DE::ProjectManager m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); - m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); - m_stack->addWidget(new NewProjectSettingsScreen()); - m_gemCatalog = new GemCatalogScreen(); - m_stack->addWidget(m_gemCatalog); + m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding)); + + m_newProjectSettingsScreen = new NewProjectSettingsScreen(this); + m_stack->addWidget(m_newProjectSettingsScreen); + + m_gemCatalogScreen = new GemCatalogScreen(this); + m_stack->addWidget(m_gemCatalogScreen); vLayout->addWidget(m_stack); - QDialogButtonBox* backNextButtons = new QDialogButtonBox(); - backNextButtons->setObjectName("footer"); - vLayout->addWidget(backNextButtons); + QDialogButtonBox* buttons = new QDialogButtonBox(); + buttons->setObjectName("footer"); + vLayout->addWidget(buttons); - m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole); - m_backButton->setProperty("secondary", true); - m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); - connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton); - connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton); + m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole); + m_secondaryButton->setProperty("secondary", true); + m_secondaryButton->setVisible(false); + connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton); Update(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); + connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); + setLayout(vLayout); } @@ -80,8 +90,10 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() > 0) { - m_stack->setCurrentIndex(m_stack->currentIndex() - 1); - Update(); +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } else { @@ -89,70 +101,121 @@ namespace O3DE::ProjectManager } } - void CreateProjectCtrl::HandleNextButton() +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void CreateProjectCtrl::HandleSecondaryButton() { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum(); - - if (screenEnum == ProjectManagerScreen::NewProjectSettings) + if (m_stack->currentIndex() > 0) { - auto newProjectScreen = reinterpret_cast(currentScreen); - if (newProjectScreen) - { - if (!newProjectScreen->Validate()) - { - QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings")); - return; - } - - m_projectInfo = newProjectScreen->GetProjectInfo(); - m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath(); - - // The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog. - m_gemCatalog->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/true); - } - } - - if (m_stack->currentIndex() != m_stack->count() - 1) - { - m_stack->setCurrentIndex(m_stack->currentIndex() + 1); - Update(); + // return to Project Settings page + PreviousScreen(); } else { - auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo); + // Configure Gems + NextScreen(); + } + } + + void CreateProjectCtrl::Update() + { + if (m_stack->currentWidget() == m_gemCatalogScreen) + { + m_header->setSubTitle(tr("Configure project with Gems")); + m_secondaryButton->setVisible(false); + } + else + { + m_header->setSubTitle(tr("Enter Project Details")); + m_secondaryButton->setVisible(true); + m_secondaryButton->setText(tr("Configure Gems")); + } + } + + void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) + { + if (screen == ProjectManagerScreen::GemCatalog) + { + HandleSecondaryButton(); + } + else + { + emit ChangeScreenRequest(screen); + } + } + + void CreateProjectCtrl::NextScreen() + { + if (m_stack->currentIndex() < m_stack->count()) + { + if(CurrentScreenIsValid()) + { + m_stack->setCurrentIndex(m_stack->currentIndex() + 1); + + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true); + + Update(); + } + else + { + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); + } + } + } + + void CreateProjectCtrl::PreviousScreen() + { + // we don't require the current screen to be valid when moving back + if (m_stack->currentIndex() > 0) + { + m_stack->setCurrentIndex(m_stack->currentIndex() - 1); + Update(); + } + } +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + + void CreateProjectCtrl::HandlePrimaryButton() + { + CreateProject(); + } + + bool CreateProjectCtrl::CurrentScreenIsValid() + { + if (m_stack->currentWidget() == m_newProjectSettingsScreen) + { + return m_newProjectSettingsScreen->Validate(); + } + + return true; + } + + void CreateProjectCtrl::CreateProject() + { + if (m_newProjectSettingsScreen->Validate()) + { + ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); + QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + + auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo); if (result.IsSuccess()) { // automatically register the project - PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path); + PythonBindingsInterface::Get()->AddProject(projectInfo.m_path); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - // adding gems is not implemented yet because we don't know what targets to add or how to add them emit ChangeScreenRequest(ProjectManagerScreen::Projects); } else { QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project.")); } - - // Enable/disable gems for the newly created project. - m_gemCatalog->EnableDisableGemsForProject(m_projectInfo.m_path); - } - } - - void CreateProjectCtrl::Update() - { - ScreenWidget* currentScreen = reinterpret_cast(m_stack->currentWidget()); - if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog) - { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Configure project with Gems")); - m_nextButton->setText(tr("Create Project")); } else { - m_header->setTitle(tr("Create Project")); - m_header->setSubTitle(tr("Enter Project Details")); - m_nextButton->setText(tr("Next")); + QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again.")); } } diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 89d18a9ebc..e802b6845a 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -14,9 +14,11 @@ #if !defined(Q_MOC_RUN) #include #include -#include #endif +// due to current limitations, customizing template Gems is disabled +#define TEMPLATE_GEM_CONFIGURATION_ENABLED + QT_FORWARD_DECLARE_CLASS(QStackedWidget) QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QLabel) @@ -24,6 +26,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel) namespace O3DE::ProjectManager { QT_FORWARD_DECLARE_CLASS(ScreenHeader) + QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) + QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) class CreateProjectCtrl : public ScreenWidget @@ -36,21 +40,37 @@ namespace O3DE::ProjectManager protected slots: void HandleBackButton(); - void HandleNextButton(); + void HandlePrimaryButton(); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + void OnChangeScreenRequest(ProjectManagerScreen screen); + void HandleSecondaryButton(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED private: +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED void Update(); + void NextScreen(); + void PreviousScreen(); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - QStackedWidget* m_stack; - ScreenHeader* m_header; + bool CurrentScreenIsValid(); + void CreateProject(); - QPushButton* m_backButton; - QPushButton* m_nextButton; + QStackedWidget* m_stack = nullptr; + ScreenHeader* m_header = nullptr; + + QPushButton* m_primaryButton = nullptr; + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QPushButton* m_secondaryButton = nullptr; +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED QString m_projectTemplatePath; ProjectInfo m_projectInfo; - - GemCatalogScreen* m_gemCatalog = nullptr; + + NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; + GemCatalogScreen* m_gemCatalogScreen = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index c8dc8451ae..5faa6cb8bd 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -14,8 +14,12 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include @@ -28,10 +32,12 @@ #include #include #include +#include +#include namespace O3DE::ProjectManager { - constexpr const char* k_pathProperty = "Path"; + constexpr const char* k_templateIndexProperty = "TemplateIndex"; NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent) : ProjectSettingsScreen(parent) @@ -59,30 +65,69 @@ namespace O3DE::ProjectManager projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel"); containerLayout->addWidget(projectTemplateDetailsLabel); - QHBoxLayout* templateLayout = new QHBoxLayout(this); - containerLayout->addItem(templateLayout); + + // we might have enough templates that we need to scroll + QScrollArea* templatesScrollArea = new QScrollArea(this); + QWidget* scrollWidget = new QWidget(); + + FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize); + scrollWidget->setLayout(flowLayout); + + templatesScrollArea->setWidget(scrollWidget); + templatesScrollArea->setWidgetResizable(true); m_projectTemplateButtonGroup = new QButtonGroup(this); m_projectTemplateButtonGroup->setObjectName("templateButtonGroup"); + + // QButtonGroup has overloaded buttonClicked methods so we need the QOverload + connect( + m_projectTemplateButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, + [=](QAbstractButton* button) + { + if (button && button->property(k_templateIndexProperty).isValid()) + { + int projectIndex = button->property(k_templateIndexProperty).toInt(); + UpdateTemplateDetails(m_templates.at(projectIndex)); + } + }); + auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates(); if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty()) { - for (const ProjectTemplateInfo& projectTemplate : templatesResult.GetValue()) - { - QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this); - radioButton->setProperty(k_pathProperty, projectTemplate.m_path); - m_projectTemplateButtonGroup->addButton(radioButton); + m_templates = templatesResult.GetValue(); - containerLayout->addWidget(radioButton); + // sort alphabetically by display name because they could be in any order + std::sort(m_templates.begin(), m_templates.end(), [](const ProjectTemplateInfo& arg1, const ProjectTemplateInfo& arg2) + { + return arg1.m_displayName.toLower() < arg2.m_displayName.toLower(); + }); + + for (int index = 0; index < m_templates.size(); ++index) + { + ProjectTemplateInfo projectTemplate = m_templates.at(index); + QString projectPreviewPath = projectTemplate.m_path + "/Template/preview.png"; + QFileInfo doesPreviewExist(projectPreviewPath); + if (!doesPreviewExist.exists() || !doesPreviewExist.isFile()) + { + projectPreviewPath = ":/DefaultTemplate.png"; + } + TemplateButton* templateButton = new TemplateButton(projectPreviewPath, projectTemplate.m_displayName, this); + templateButton->setCheckable(true); + templateButton->setProperty(k_templateIndexProperty, index); + + m_projectTemplateButtonGroup->addButton(templateButton); + + flowLayout->addWidget(templateButton); } m_projectTemplateButtonGroup->buttons().first()->setChecked(true); } + containerLayout->addWidget(templatesScrollArea); } projectTemplateWidget->setLayout(containerLayout); m_verticalLayout->addWidget(projectTemplateWidget); - QWidget* projectTemplateDetails = new QWidget(this); + QFrame* projectTemplateDetails = CreateTemplateDetails(s_templateDetailsContentMargin); projectTemplateDetails->setObjectName("projectTemplateDetails"); m_horizontalLayout->addWidget(projectTemplateDetails); } @@ -109,11 +154,71 @@ namespace O3DE::ProjectManager void NewProjectSettingsScreen::NotifyCurrentScreen() { + if (!m_templates.isEmpty()) + { + UpdateTemplateDetails(m_templates.first()); + } + Validate(); } QString NewProjectSettingsScreen::GetProjectTemplatePath() { - return m_projectTemplateButtonGroup->checkedButton()->property(k_pathProperty).toString(); + const int templateIndex = m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt(); + return m_templates.at(templateIndex).m_path; + } + + QFrame* NewProjectSettingsScreen::CreateTemplateDetails(int margin) + { + QFrame* projectTemplateDetails = new QFrame(this); + projectTemplateDetails->setObjectName("projectTemplateDetails"); + QVBoxLayout* templateDetailsLayout = new QVBoxLayout(); + templateDetailsLayout->setContentsMargins(margin, margin, margin, margin); + templateDetailsLayout->setAlignment(Qt::AlignTop); + { + m_templateDisplayName = new QLabel(this); + m_templateDisplayName->setObjectName("displayName"); + templateDetailsLayout->addWidget(m_templateDisplayName); + + m_templateSummary = new QLabel(this); + m_templateSummary->setObjectName("summary"); + m_templateSummary->setWordWrap(true); + templateDetailsLayout->addWidget(m_templateSummary); + + QLabel* includedGemsTitle = new QLabel(tr("Included Gems"), this); + includedGemsTitle->setObjectName("includedGemsTitle"); + templateDetailsLayout->addWidget(includedGemsTitle); + + m_templateIncludedGems = new TagContainerWidget(this); + m_templateIncludedGems->setObjectName("includedGems"); + templateDetailsLayout->addWidget(m_templateIncludedGems); + +#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED + QLabel* moreGemsLabel = new QLabel(tr("Looking for more Gems?"), this); + moreGemsLabel->setObjectName("moreGems"); + templateDetailsLayout->addWidget(moreGemsLabel); + + QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this); + browseCatalogLabel->setObjectName("browseCatalog"); + browseCatalogLabel->setWordWrap(true); + templateDetailsLayout->addWidget(browseCatalogLabel); + + QPushButton* configureGemsButton = new QPushButton(tr("Configure with more Gems"), this); + connect(configureGemsButton, &QPushButton::clicked, this, [=]() + { + emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog); + }); + templateDetailsLayout->addWidget(configureGemsButton); +#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED + } + projectTemplateDetails->setLayout(templateDetailsLayout); + return projectTemplateDetails; + } + + void NewProjectSettingsScreen::UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo) + { + m_templateDisplayName->setText(templateInfo.m_displayName); + m_templateSummary->setText(templateInfo.m_summary); + m_templateIncludedGems->Update(templateInfo.m_includedGems); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 6a4b6ec57d..ce77915404 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -13,12 +13,17 @@ #if !defined(Q_MOC_RUN) #include +#include +#include #endif QT_FORWARD_DECLARE_CLASS(QButtonGroup) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QFrame) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(TagContainerWidget) class NewProjectSettingsScreen : public ProjectSettingsScreen { @@ -33,8 +38,17 @@ namespace O3DE::ProjectManager private: QString GetDefaultProjectPath(); + QFrame* CreateTemplateDetails(int margin); + void UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo); QButtonGroup* m_projectTemplateButtonGroup; + QLabel* m_templateDisplayName; + QLabel* m_templateSummary; + TagContainerWidget* m_templateIncludedGems; + QVector m_templates; + + inline constexpr static int s_spacerSize = 20; + inline constexpr static int s_templateDetailsContentMargin = 20; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h index 0477968050..e75c64ec90 100644 --- a/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectTemplateInfo.h @@ -31,6 +31,7 @@ namespace O3DE::ProjectManager QString m_name; QString m_path; QString m_summary; + QStringList m_includedGems; QStringList m_canonicalTags; QStringList m_userTags; }; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 37e636caef..3263505f9e 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -754,7 +754,7 @@ namespace O3DE::ProjectManager ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path) { ProjectTemplateInfo templateInfo; - templateInfo.m_path = Py_To_String(path); + templateInfo.m_path = Py_To_String(pybind11::str(path)); auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path); if (pybind11::isinstance(data)) @@ -781,6 +781,13 @@ namespace O3DE::ProjectManager templateInfo.m_canonicalTags.push_back(Py_To_String(tag)); } } + if (data.contains("included_gems")) + { + for (auto gem : data["included_gems"]) + { + templateInfo.m_includedGems.push_back(Py_To_String(gem)); + } + } } catch ([[maybe_unused]] const std::exception& e) { diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp new file mode 100644 index 0000000000..41b5e51c99 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.cpp @@ -0,0 +1,65 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + + TemplateButton::TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent) + : QPushButton(parent) + { + setAutoExclusive(true); + + setObjectName("templateButton"); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); + setLayout(vLayout); + + QLabel* image = new QLabel(this); + image->setObjectName("templateImage"); + image->setPixmap( + QPixmap(imagePath).scaled(QSize(s_templateImageWidth,s_templateImageHeight) , Qt::KeepAspectRatio, Qt::SmoothTransformation)); + vLayout->addWidget(image); + + QLabel* label = new QLabel(labelText, this); + label->setObjectName("templateLabel"); + vLayout->addWidget(label); + + connect(this, &QAbstractButton::toggled, this, &TemplateButton::onToggled); + } + + void TemplateButton::onToggled() + { + setProperty("Checked", isChecked()); + + // we must unpolish/polish every child after changing a property + // or else they won't use the correct stylesheet selector + for (auto child : findChildren()) + { + child->style()->unpolish(child); + child->style()->polish(child); + } + + style()->unpolish(this); + style()->polish(this); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h new file mode 100644 index 0000000000..db0f5f39c8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h @@ -0,0 +1,37 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + class TemplateButton + : public QPushButton + { + Q_OBJECT // AUTOMOC + + public: + explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr); + ~TemplateButton() = default; + + protected slots: + void onToggled(); + + private: + inline constexpr static int s_templateImageWidth = 92; + inline constexpr static int s_templateImageHeight = 122; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index a7a36f26ab..40f450ab6f 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -60,6 +60,8 @@ set(FILES Source/LinkWidget.cpp Source/TagWidget.h Source/TagWidget.cpp + Source/TemplateButtonWidget.h + Source/TemplateButtonWidget.cpp Source/GemCatalog/GemCatalogHeaderWidget.h Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 26b868d315..6f74fb6b26 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -4,8 +4,9 @@ "restricted_platform_relative_path": "Templates", "origin": "The primary repo for DefaultProject goes here: i.e. http://www.mydomain.com", "license": "What license DefaultProject uses goes here: i.e. https://opensource.org/licenses/MIT", - "display_name": "DefaultProject", + "display_name": "Default", "summary": "A short description of DefaultProject.", + "included_gems": ["Atom","Camera","EMotionFX","UI","Maestro","Input","ImGui"], "canonical_tags": [], "user_tags": [ "DefaultProject" @@ -651,4 +652,4 @@ "origin": "Shaders" } ] -} \ No newline at end of file +} From 08db0584762545b87d09a48c897a8787852bcdbd Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 16:41:24 -0700 Subject: [PATCH 102/105] SPEC-2513 Fixes to enable w4436 and w4366 (#1157) * Fix for w4457 * Nothing to fix, seems we deleted all the code that was causing this offense * removing warning * another warning that doesnt trigger --- cmake/Platform/Common/MSVC/Configurations_msvc.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 8c22677f91..bcff2adeb7 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -69,9 +69,7 @@ ly_append_configurations_options( /wd4267 # conversion, possible loss of data /wd4310 # cast truncates constant value /wd4324 # structure was padded due to alignment specifier - /wd4366 # the result of unary operator may be unaligned /wd4389 # comparison, signed/unsigned mismatch - /wd4436 # the result of unary operator may be unaligned # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 From d9b57bce678d11ef9a2f73cf45c3bd37422c8f0b Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 19:34:22 -0500 Subject: [PATCH 103/105] Fixed configuring of cmake when a project resides on a different drive than the engine (#1153) --- cmake/Platform/Common/Install_common.cmake | 38 ++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 016c0d623d..4aeaf21e95 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -182,8 +182,20 @@ set_property(TARGET ${TARGET_NAME} endif() endif() - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/${NAME_PLACEHOLDER}_$.cmake" + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" CONTENT "${target_file_contents}") + install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$.cmake" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) @@ -235,18 +247,32 @@ function(ly_setup_subdirectory absolute_target_source_dir) endforeach() file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment) - # Write out all the agreegated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeList.txt - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt + + if(IS_ABSOLUTE ${target_source_dir}) + # This normally applies the target_source_dir is outside of the engine root + # such as when invoking ly_setup_subdirectory from the project + # Therefore the final directory component of the target source directory is used first 8 characters + # of a SHA256 hash + string(SHA256 target_source_hash ${target_source_dir}) + string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) + get_filename_component(target_source_folder_name ${target_source_dir} NAME) + set(target_source_dir "${target_source_folder_name}-${target_source_hash}") + endif() + + # Initialize the target install source directory to path underneath the current binary directory + set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) + # Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt + file(WRITE ${target_install_source_dir}/CMakeLists.txt "${cmake_copyright_comment}" "${all_configured_targets}" "\n" "${CREATE_ALIASES_PLACEHOLDER}" ) - # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}/CMakeLists.txt" + install(FILES "${target_install_source_dir}/CMakeLists.txt" DESTINATION ${target_source_dir} COMPONENT ${install_component} ) From 77f0d983c8475f24c874ccdd634a51c1ae8942ef Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Fri, 4 Jun 2021 19:34:28 -0500 Subject: [PATCH 104/105] Mac SystemFile_Apple.h build fix (#1159) --- .../AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h index 3967cafc90..2ebb79634a 100644 --- a/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h +++ b/Code/Framework/AzCore/Platform/Common/Apple/AzCore/IO/SystemFile_Apple.h @@ -14,6 +14,9 @@ #include #include #include +#include + +#include namespace AZ { From 74e5090f26f957adf75f141d70cc7d3da9c0bfdd Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:08:52 -0700 Subject: [PATCH 105/105] Adding ExternalWarningLevel to the Directory.Build.props to get the default warning level for external headers to match the one we define through compile options (#1160) --- cmake/Platform/Common/Directory.Build.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/Platform/Common/Directory.Build.props b/cmake/Platform/Common/Directory.Build.props index b74fa48471..951d3c6605 100644 --- a/cmake/Platform/Common/Directory.Build.props +++ b/cmake/Platform/Common/Directory.Build.props @@ -15,4 +15,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. true true + + + TurnOffAllWarnings + + \ No newline at end of file