From 00a529ad74d139419ea2e4f331b21509a01dcf85 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri, 28 May 2021 14:56:52 -0500 Subject: [PATCH 01/13] Fix AssImpTransformImporter logic for bone nodes For bone nodes, the Transform is computed by multiplying the parent offsetMatrix by the inverse of the node's offsetMatrix Note that this currently disables the LimitBoneWeights option since that results in the removal of bone nodes that are not attached to a mesh. Without the bones there is no way to retrieve the offsetMatrix, so the Transform cannot be computed correctly Fixes LYN-3755 --- .../Importers/AssImpTransformImporter.cpp | 59 ++++++++++++++++--- .../SDKWrapper/AssImpSceneWrapper.cpp | 5 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp index 5357c32fa9..bcc007e3a7 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp @@ -46,22 +46,69 @@ namespace AZ serializeContext->Class()->Version(1); } } - + + void GetAllBones(const aiScene* scene, AZStd::unordered_map& boneLookup) + { + for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) + { + const aiMesh* mesh = scene->mMeshes[meshIndex]; + + for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) + { + const aiBone* bone = mesh->mBones[boneIndex]; + + boneLookup[bone->mName.C_Str()] = bone; + } + } + } + Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context) { AZ_TraceContext("Importer", "transform"); const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - + if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName)) { return Events::ProcessingResult::Ignored; } - aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode); + AZStd::unordered_map boneLookup; + GetAllBones(scene, boneLookup); + + auto boneIterator = boneLookup.find(currentNode->mName.C_Str()); + const bool isBone = boneIterator != boneLookup.end(); + + aiMatrix4x4 combinedTransform; + + if (isBone) + { + auto parentNode = currentNode->mParent; + + aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix; + aiMatrix4x4 parentOffset {}; + + auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str()); + + if (parentNode && parentBoneIterator != boneLookup.end()) + { + const auto& parentBone = parentBoneIterator->second; + + parentOffset = parentBone->mOffsetMatrix; + } + + auto inverseOffset = offsetMatrix; + inverseOffset.Inverse(); + + combinedTransform = parentOffset * inverseOffset; + } + else + { + combinedTransform = GetConcatenatedLocalTransform(currentNode); + } DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform); - + context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform); context.m_sourceSceneSystem.ConvertUnit(localTransform); @@ -105,9 +152,7 @@ namespace AZ } else { - bool addedData = context.m_scene.GetGraph().SetContent( - context.m_currentGraphPosition, - transformData); + bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, transformData); AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data"); return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure; diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp index 791af4bf68..9186a2fb7a 100644 --- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp +++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp @@ -69,13 +69,14 @@ namespace AZ // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this, // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release. // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph. + + // aiProcess_LimitBoneWeights is not enabled because it will remove bones which are not associated with a mesh. + // This results in the loss of the offset matrix data for nodes without a mesh which is required for the Transform Importer. m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false); m_sceneFileName = fileName; m_assImpScene = m_importer.ReadFile(fileName, aiProcess_Triangulate //Triangulates all faces of all meshes - | aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value - //dropping the least important and re-normalizing | aiProcess_GenNormals); //Generate normals for meshes #if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL From dda6fee2276f5e2418159a40dc6f978ddf789ab6 Mon Sep 17 00:00:00 2001 From: gallowj Date: Wed, 9 Jun 2021 09:58:38 -0500 Subject: [PATCH 02/13] fixed a type-o --- Gems/AtomContent/gem.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 941e7dea20..b043cbbaca 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -8,7 +8,7 @@ "Gem" ], "user_tags": [ - "AtomConent" + "AtomContent" ], "icon_path": "preview.png" } From d5916630b88fc19bd017c9fff6591a3db2a7818a Mon Sep 17 00:00:00 2001 From: gallowj Date: Wed, 9 Jun 2021 14:27:59 -0500 Subject: [PATCH 03/13] Changes related to ATOM-15021, this allows maya on py2.7 to work with .o3de\boostrap changes --- .../Solutions/.wing/DCCsi_7x.wpr | 6 ++++++ .../DccScriptingInterface/azpy/config_utils.py | 8 +++++--- .../DccScriptingInterface/azpy/constants.py | 13 ++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr index d57e91f35d..aa7a8c4bd9 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Solutions/.wing/DCCsi_7x.wpr @@ -69,10 +69,16 @@ proj.launch-config = {loc('../../SDK/Atom/Scripts/Python/DCC_Materials/maya_mate loc('../../azpy/__init__.py'): ('custom', (u'', 'launch-oobMrvXFf1SwtYBg')), + loc('../../azpy/constants.py'): ('custom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/env_base.py'): ('project', (u'', 'launch-GeaM41WYMGA1sEfm')), loc('../../azpy/maya/callbacks/node_message_callback_handler.py'): ('c'\ 'ustom', + (u'', + 'launch-GeaM41WYMGA1sEfm')), + loc('../../config.py'): ('custom', (u'', 'launch-GeaM41WYMGA1sEfm'))} diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py index 0a0c6b8337..5b5c5aa079 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/config_utils.py @@ -165,12 +165,14 @@ def get_current_project(): bootstrap_box = None try: - bootstrap_box = Box.from_json(filename=PATH_USER_O3DE_BOOTSTRAP, + bootstrap_box = Box.from_json(filename=str(Path(PATH_USER_O3DE_BOOTSTRAP).resolve()), encoding="utf-8", errors="strict", object_pairs_hook=OrderedDict) - except FileExistsError as e: - _LOGGER.error('File does not exist: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + except Exception as e: + # this file runs in py2.7 for Maya 2020, FileExistsError is not defined + _LOGGER.error('FileExistsError: {}'.format(PATH_USER_O3DE_BOOTSTRAP)) + _LOGGER.error('exception is: {}'.format(e)) if bootstrap_box: # this seems fairly hard coded - what if the data changes? diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py index e10221d324..7f6b0e4523 100755 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/constants.py @@ -226,7 +226,18 @@ TAG_DEFAULT_PY = str('Launch_pyBASE.bat') FILENAME_DEFAULT_CONFIG = str('DCCSI_config.json') # new o3de related paths -PATH_USER_O3DE = str('{home}\\{o3de}').format(home=expanduser("~"), +# os.path.expanduser("~") returns different values in py2.7 vs 3 +PATH_USER_HOME = expanduser("~") +_LOGGER.debug('user home: {}'.format(PATH_USER_HOME)) + +# special case, make sure didn't return \documents +parts = os.path.split(PATH_USER_HOME) + +if str(parts[1].lower()) == 'documents': + PATH_USER_HOME = parts[0] + _LOGGER.debug('user home CORRECTED: {}'.format(PATH_USER_HOME)) + +PATH_USER_O3DE = str('{home}\\{o3de}').format(home=PATH_USER_HOME, o3de=TAG_O3DE_FOLDER) PATH_USER_O3DE_REGISTRY = str('{0}\\Registry').format(PATH_USER_O3DE) PATH_USER_O3DE_BOOTSTRAP = str('{reg}\\{file}').format(reg=PATH_USER_O3DE_REGISTRY, From 886601ad947142cfeff23c6cb810c1798aeb43dc Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Sat, 12 Jun 2021 03:20:31 -0700 Subject: [PATCH 04/13] Created a new ReflectionScreenSpaceCompositePass to set the maximum roughness mip in the pass Srg. Changed the ReflectionScreenSpaceBlurPass to auto-generate the mip chain, which will compute the appropriate number of mips. --- .../Passes/ReflectionScreenSpaceBlur.pass | 4 +- .../ReflectionScreenSpaceComposite.pass | 2 +- .../ReflectionScreenSpaceComposite.azsl | 10 ++-- .../Code/Source/CommonSystemComponent.cpp | 2 + .../ReflectionScreenSpaceBlurPass.h | 3 + .../ReflectionScreenSpaceCompositePass.cpp | 55 +++++++++++++++++++ .../ReflectionScreenSpaceCompositePass.h | 43 +++++++++++++++ .../Code/atom_feature_common_files.cmake | 2 + 8 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp create mode 100644 Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass index 1d20382408..e2fde2d4ef 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceBlur.pass @@ -24,9 +24,9 @@ }, "ImageDescriptor": { "Format": "R16G16B16A16_FLOAT", - "MipLevels": "8", "SharedQueueMask": "Graphics" - } + }, + "GenerateFullMipChain": true } ], "Connections": [ diff --git a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass index 80c4e8987b..5443c32406 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/ReflectionScreenSpaceComposite.pass @@ -5,7 +5,7 @@ "ClassData": { "PassTemplate": { "Name": "ReflectionScreenSpaceCompositePassTemplate", - "PassClass": "FullScreenTriangle", + "PassClass": "ReflectionScreenSpaceCompositePass", "Slots": [ { "Name": "TraceInput", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl index fa3885f180..c5c724e106 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionScreenSpaceComposite.azsl @@ -37,6 +37,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass AddressV = Clamp; AddressW = Clamp; }; + + // the max roughness mip level for sampling the previous frame image + uint m_maxMipLevel; } #include @@ -69,10 +72,6 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) float4 positionWS = mul(ViewSrg::m_viewProjectionInverseMatrix, projectedPos); positionWS /= positionWS.w; - //float4 positionVS = mul(ViewSrg::m_projectionMatrixInverse, projectedPos); - //positionVS /= positionVS.w; - //float4 positionWS = mul(ViewSrg::m_viewMatrixInverse, positionVS); - // compute ray from camera to surface position float3 cameraToPositionWS = normalize(positionWS.xyz - ViewSrg::m_worldPosition); @@ -103,8 +102,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute the roughness mip to use in the previous frame image // remap the roughness mip into a lower range to more closely match the material roughness values const float MaxRoughness = 0.5f; - const float MaxRoughnessMip = 7; - float mip = saturate(roughness / MaxRoughness) * MaxRoughnessMip; + float mip = saturate(roughness / MaxRoughness) * PassSrg::m_maxMipLevel; // sample reflection value from the roughness mip float4 reflectionColor = float4(PassSrg::m_previousFrame.SampleLevel(PassSrg::LinearSampler, tracePrevUV, mip).rgb, 1.0f); diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index af28624357..1866da63e5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -103,6 +103,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,7 @@ namespace AZ // Add Reflection passes passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurPass"), &Render::ReflectionScreenSpaceBlurPass::Create); passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create); + passSystem->AddPassCreator(Name("ReflectionScreenSpaceCompositePass"), &Render::ReflectionScreenSpaceCompositePass::Create); passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create); // Add RayTracing pas diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h index 4a2ccce1d4..be8dc6d596 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h @@ -37,6 +37,9 @@ namespace AZ //! to store the previous frame image Data::Instance& GetFrameBufferImageAttachment() { return m_frameBufferImageAttachment; } + //! Returns the number of mip levels in the blur + uint32_t GetNumBlurMips() const { return m_numBlurMips; } + private: explicit ReflectionScreenSpaceBlurPass(const RPI::PassDescriptor& descriptor); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp new file mode 100644 index 0000000000..fe2030ca7e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -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. +* +*/ + +#include "ReflectionScreenSpaceCompositePass.h" +#include "ReflectionScreenSpaceBlurPass.h" +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr ReflectionScreenSpaceCompositePass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew ReflectionScreenSpaceCompositePass(descriptor); + return AZStd::move(pass); + } + + ReflectionScreenSpaceCompositePass::ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor) + : RPI::FullscreenTrianglePass(descriptor) + { + } + + void ReflectionScreenSpaceCompositePass::CompileResources([[maybe_unused]] const RHI::FrameGraphCompileContext& context) + { + if (!m_shaderResourceGroup) + { + return; + } + + RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); + const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); + if (!passes.empty()) + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + } + + FullscreenTrianglePass::CompileResources(context); + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h new file mode 100644 index 0000000000..110673541e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.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 + +namespace AZ +{ + namespace Render + { + //! This pass composites the screenspace reflection trace onto the reflection buffer. + class ReflectionScreenSpaceCompositePass + : public RPI::FullscreenTrianglePass + { + AZ_RPI_PASS(ReflectionScreenSpaceCompositePass); + + public: + AZ_RTTI(Render::ReflectionScreenSpaceCompositePass, "{88739CC9-C3F1-413A-A527-9916C697D93A}", FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(Render::ReflectionScreenSpaceCompositePass, SystemAllocator, 0); + + //! Creates a new pass without a PassTemplate + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + private: + explicit ReflectionScreenSpaceCompositePass(const RPI::PassDescriptor& descriptor); + + // Pass Overrides... + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + }; + } // namespace RPI +} // 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 a759de77fa..a656558abf 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -267,6 +267,8 @@ set(FILES Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.h + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp + Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h Source/ScreenSpace/DeferredFogSettings.cpp From 00062430a2788e21f61186f78d199b56aae5f316 Mon Sep 17 00:00:00 2001 From: Santora Date: Sat, 12 Jun 2021 11:52:46 -0700 Subject: [PATCH 05/13] Updated the usage of ShaderReloadDebugTracker to include the address of the object. This is necessary for debugging where multiple assets may be reloading at the same time. Also added a Printf function for generic messages at the current indent level. This is specifically in support of: ATOM-14613 Baseviewer MatertialHotReloadTest fails to change the color after turning blending on and off ATOM-15728 Shader Hot Reload Fails in Debug Build --- .../RPI.Public/Shader/ShaderReloadDebugTracker.h | 14 ++++++++++++++ .../Code/Source/RPI.Public/Material/Material.cpp | 9 +++++---- .../RPI/Code/Source/RPI.Public/Shader/Shader.cpp | 2 +- .../Source/RPI.Reflect/Material/MaterialAsset.cpp | 2 +- .../RPI.Reflect/Material/MaterialTypeAsset.cpp | 2 +- .../Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp | 4 ++-- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h index dfda93dca3..8220d25eb8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h @@ -57,6 +57,20 @@ namespace AZ } #endif } + + //! Prints a generic message at the appropriate indent level. + template + static void Printf([[maybe_unused]] const char* format, [[maybe_unused]] Args... args) + { +#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER + if (IsEnabled()) + { + const AZStd::string message = AZStd::string::format(format, args...); + + AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", s_indent, "", message.c_str()); + } +#endif + } //! Use this utility to call BeginSection(), and automatically call EndSection() when the object goes out of scope. class ScopedSection final diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 4a2718b9d5..eac1ee42e5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -97,6 +97,7 @@ namespace AZ ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); for (auto& shaderItem : m_shaderCollection) { + ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get()); ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId()); } @@ -226,7 +227,7 @@ namespace AZ // AssetBus overrides... void Material::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; @@ -241,7 +242,7 @@ namespace AZ // MaterialReloadNotificationBus overrides... void Material::OnMaterialAssetReinitialized(const Data::Asset& materialAsset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnMaterialAssetReinitialized %s", materialAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnMaterialAssetReinitialized %s", this, materialAsset.GetHint().c_str()); OnAssetReloaded(materialAsset); } @@ -249,7 +250,7 @@ namespace AZ // ShaderReloadNotificationBus overrides... void Material::OnShaderReinitialized([[maybe_unused]] const Shader& shader) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderReinitialized %s", shader.GetAsset().GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderReinitialized %s", this, shader.GetAsset().GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. @@ -269,7 +270,7 @@ namespace AZ void Material::OnShaderVariantReinitialized(const Shader& shader, const ShaderVariantId& /*shaderVariantId*/, ShaderVariantStableId shaderVariantStableId) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderVariantReinitialized %s variant %u", shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderVariantReinitialized %s variant %u", this, shader.GetAsset().GetHint().c_str(), shaderVariantStableId.GetIndex()); // Note that it would be better to check the shaderVariantId to see if that variant is relevant to this particular material before reinitializing it. // There could be hundreds or even thousands of variants for a shader, but only one of those variants will be used by any given material. So we could diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp index 7194c524ae..147f24d4f8 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp @@ -132,7 +132,7 @@ namespace AZ // AssetBus overrides void Shader::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("Shader::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnAssetReloaded %s", this, asset.GetHint().c_str()); if (asset->GetId() == m_asset->GetId()) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 7e9e2ddb10..2e56c30652 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -119,7 +119,7 @@ namespace AZ void MaterialAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 20adb63cb8..f7d0a83ac9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -130,7 +130,7 @@ namespace AZ void MaterialTypeAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("MaterialTypeAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialTypeAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); // The order of asset reloads is non-deterministic. If the MaterialTypeAsset reloads before these // dependency assets, this will make sure the MaterialTypeAsset gets the latest ones when they reload. 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 cf655d43f7..75d4a47bc0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -381,7 +381,7 @@ namespace AZ // AssetBus overrides... void ShaderAsset::OnAssetReloaded(Data::Asset asset) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnAssetReloaded %s", asset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); Data::Asset shaderVariantAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, @@ -396,7 +396,7 @@ namespace AZ /// ShaderVariantFinderNotificationBus overrides void ShaderAsset::OnShaderVariantTreeAssetReady(Data::Asset shaderVariantTreeAsset, bool isError) { - ShaderReloadDebugTracker::ScopedSection reloadSection("ShaderAsset::OnShaderVariantTreeAssetReady %s", shaderVariantTreeAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnShaderVariantTreeAssetReady %s", this, shaderVariantTreeAsset.GetHint().c_str()); AZStd::unique_lock lock(m_variantTreeMutex); if (isError) From 406792606b1c335e138d4caf3770b6b19be737b7 Mon Sep 17 00:00:00 2001 From: Santora Date: Mon, 14 Jun 2021 13:04:15 -0700 Subject: [PATCH 06/13] I missed one spot that needs to print the address. --- Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index eac1ee42e5..d9691ca175 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -261,7 +261,7 @@ namespace AZ { // TODO: I think we should make Shader handle OnShaderAssetReinitialized and treat it just like the shader reloaded. - ShaderReloadDebugTracker::ScopedSection reloadSection("Material::OnShaderAssetReinitialized %s", shaderAsset.GetHint().c_str()); + ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str()); // Note that it might not be strictly necessary to reinitialize the entire material, we might be able to get away with // just bumping the m_currentChangeId or some other minor updates. But it's pretty hard to know what exactly needs to be // updated to correctly handle the reload, so it's safer to just reinitialize the whole material. From 6c05adddec4a1c056fd7e52a28aeccd7f289cec1 Mon Sep 17 00:00:00 2001 From: gallowj Date: Mon, 14 Jun 2021 15:41:50 -0500 Subject: [PATCH 07/13] This material did not look correct, sneaking it in --- .../Objects/PlayfulTeapot_playfulteapot.material | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material index 27540c587e..da35fda141 100644 --- a/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material +++ b/Gems/AtomContent/LookDevelopmentStudioPixar/Assets/Objects/PlayfulTeapot_playfulteapot.material @@ -14,13 +14,14 @@ }, "clearCoat": { "enable": true, - "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif" + "normalMap": "EngineAssets/Textures/perlinNoiseNormal_ddn.tif", + "normalStrength": 0.10000000149011612 }, "general": { "applySpecularAA": true }, "metallic": { - "factor": 0.5 + "factor": 0.10000000149011612 }, "normal": { "factor": 0.05000000074505806, @@ -31,6 +32,9 @@ }, "roughness": { "factor": 0.0 + }, + "specularF0": { + "enableMultiScatterCompensation": true } } -} +} \ No newline at end of file From 948075fa61a9a08f1ef36b059602eeaabce69a94 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Mon, 14 Jun 2021 15:56:54 -0700 Subject: [PATCH 08/13] Gem Catalog Allows Filtering by Gem Selected Status Live (#1274) --- .../Source/GemCatalog/GemCatalogScreen.cpp | 2 + .../Source/GemCatalog/GemFilterWidget.cpp | 111 ++++++++++++++++-- .../Source/GemCatalog/GemFilterWidget.h | 9 +- .../Source/GemCatalog/GemItemDelegate.cpp | 20 ---- .../Source/GemCatalog/GemListHeaderWidget.cpp | 10 ++ .../Source/GemCatalog/GemModel.cpp | 15 +++ .../Source/GemCatalog/GemModel.h | 2 + .../GemCatalog/GemSortFilterProxyModel.cpp | 23 ++++ .../GemCatalog/GemSortFilterProxyModel.h | 13 ++ .../Source/UpdateProjectCtrl.cpp | 6 +- 10 files changed, 179 insertions(+), 32 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 4424767c0b..2c92af4e51 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -82,6 +82,8 @@ namespace O3DE::ProjectManager m_headerWidget->ReinitForProject(); + connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter); + // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ QModelIndex firstModelIndex = m_gemListView->model()->index(0,0); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index a6a4e95ff9..7c6150ff99 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -26,6 +26,7 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton, + bool collapsed, int defaultShowCount, QWidget* parent) : QWidget(parent) @@ -40,6 +41,7 @@ namespace O3DE::ProjectManager QHBoxLayout* collapseLayout = new QHBoxLayout(); m_collapseButton = new QPushButton(); m_collapseButton->setCheckable(true); + m_collapseButton->setChecked(collapsed); m_collapseButton->setFlat(true); m_collapseButton->setFocusPolicy(Qt::NoFocus); m_collapseButton->setFixedWidth(s_collapseButtonSize); @@ -178,6 +180,11 @@ namespace O3DE::ProjectManager return m_buttonGroup; } + bool FilterCategoryWidget::IsCollapsed() + { + return m_collapseButton->isChecked(); + } + GemFilterWidget::GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent) : QScrollArea(parent) , m_filterProxyModel(filterProxyModel) @@ -193,20 +200,106 @@ namespace O3DE::ProjectManager QWidget* mainWidget = new QWidget(); setWidget(mainWidget); - m_mainLayout = new QVBoxLayout(); - m_mainLayout->setAlignment(Qt::AlignTop); - mainWidget->setLayout(m_mainLayout); + QVBoxLayout* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignTop); + mainWidget->setLayout(mainLayout); QLabel* filterByLabel = new QLabel("Filter by"); filterByLabel->setStyleSheet("font-size: 16px;"); - m_mainLayout->addWidget(filterByLabel); + mainLayout->addWidget(filterByLabel); + QWidget* filterSection = new QWidget(this); + mainLayout->addWidget(filterSection); + + m_filterLayout = new QVBoxLayout(); + m_filterLayout->setAlignment(Qt::AlignTop); + m_filterLayout->setContentsMargins(0, 0, 0, 0); + filterSection->setLayout(m_filterLayout); + + ResetGemStatusFilter(); AddGemOriginFilter(); AddTypeFilter(); AddPlatformFilter(); AddFeatureFilter(); } + void GemFilterWidget::ResetGemStatusFilter() + { + QVector elementNames; + QVector elementCounts; + const int totalGems = m_gemModel->rowCount(); + const int selectedGemTotal = m_gemModel->TotalAddedGems(); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected)); + elementCounts.push_back(totalGems - selectedGemTotal); + + elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected)); + elementCounts.push_back(selectedGemTotal); + + bool wasCollapsed = false; + if (m_statusFilter) + { + wasCollapsed = m_statusFilter->IsCollapsed(); + } + + FilterCategoryWidget* filterWidget = + new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed); + if (m_statusFilter) + { + m_filterLayout->replaceWidget(m_statusFilter, filterWidget); + } + else + { + m_filterLayout->addWidget(filterWidget); + } + + m_statusFilter->deleteLater(); + m_statusFilter = filterWidget; + + const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus(); + const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); + for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex) + { + const GemSortFilterProxyModel::GemStatus gemStatus = static_cast(statusFilterIndex); + QAbstractButton* button = buttons[statusFilterIndex]; + + if (static_cast(statusFilterIndex) == currentFilterState) + { + button->setChecked(true); + } + + connect( + button, &QAbstractButton::toggled, this, + [=](bool checked) + { + GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus(); + if (checked) + { + if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter) + { + filterStatus = gemStatus; + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + else + { + if (filterStatus != gemStatus) + { + filterStatus = static_cast(!gemStatus); + } + else + { + filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter; + } + } + m_filterProxyModel->SetGemStatus(filterStatus); + }); + } + } + void GemFilterWidget::AddGemOriginFilter() { QVector elementNames; @@ -233,7 +326,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -283,7 +376,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -333,7 +426,7 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false); - m_mainLayout->addWidget(filterWidget); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) @@ -388,8 +481,8 @@ namespace O3DE::ProjectManager } FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts, - /*showAllLessButton=*/true, /*defaultShowCount=*/5); - m_mainLayout->addWidget(filterWidget); + /*showAllLessButton=*/true, false, /*defaultShowCount=*/5); + m_filterLayout->addWidget(filterWidget); const QList buttons = filterWidget->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h index 017eadc020..520370eb44 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h @@ -37,11 +37,14 @@ namespace O3DE::ProjectManager const QVector& elementNames, const QVector& elementCounts, bool showAllLessButton = true, + bool collapsed = false, int defaultShowCount = 4, QWidget* parent = nullptr); QButtonGroup* GetButtonGroup(); + bool IsCollapsed(); + private: void UpdateCollapseState(); void UpdateSeeMoreLess(); @@ -66,14 +69,18 @@ namespace O3DE::ProjectManager explicit GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr); ~GemFilterWidget() = default; + public slots: + void ResetGemStatusFilter(); + private: void AddGemOriginFilter(); void AddTypeFilter(); void AddPlatformFilter(); void AddFeatureFilter(); - QVBoxLayout* m_mainLayout = nullptr; + QVBoxLayout* m_filterLayout = nullptr; GemModel* m_gemModel = nullptr; GemSortFilterProxyModel* m_filterProxyModel = nullptr; + FilterCategoryWidget* m_statusFilter = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index 8aa68fb7a2..03787de7e8 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -204,7 +204,6 @@ namespace O3DE::ProjectManager painter->save(); const QRect buttonRect = CalcButtonRect(contentRect); QPoint circleCenter; - QString buttonText; const bool isAdded = GemModel::IsAdded(modelIndex); if (isAdded) @@ -213,34 +212,15 @@ namespace O3DE::ProjectManager painter->setPen(m_buttonEnabledColor); circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); - buttonText = "Added"; } else { circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1); - buttonText = "Get"; } // Rounded rect painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); - // Text - QFont font; - QRect textRect = GetTextRect(font, buttonText, s_buttonFontSize); - if (isAdded) - { - textRect = QRect(buttonRect.left(), buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - else - { - textRect = QRect(buttonRect.left() + s_buttonCircleRadius * 2.0, buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); - } - - font.setPixelSize(s_buttonFontSize); - painter->setFont(font); - painter->setPen(m_textColor); - painter->drawText(textRect, Qt::AlignCenter, buttonText); - // Circle painter->setBrush(m_textColor); painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index bc287e3c61..ad1b57a27c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -74,6 +75,15 @@ namespace O3DE::ProjectManager gemSummaryLabel->setStyleSheet("font-size: 12px;"); columnHeaderLayout->addWidget(gemSummaryLabel); + QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + columnHeaderLayout->addSpacerItem(horizontalSpacer); + + QLabel* gemSelectedLabel = new QLabel(tr("Selected")); + gemSelectedLabel->setStyleSheet("font-size: 12px;"); + columnHeaderLayout->addWidget(gemSelectedLabel); + + columnHeaderLayout->addSpacing(60); + vLayout->addLayout(columnHeaderLayout); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 6c09c95572..5dc40723c9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -235,4 +235,19 @@ namespace O3DE::ProjectManager } return result; } + + int GemModel::TotalAddedGems() const + { + int result = 0; + for (int row = 0; row < rowCount(); ++row) + { + const QModelIndex modelIndex = index(row, 0); + if (IsAdded(modelIndex)) + { + ++result; + } + } + return result; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 77f973a91c..2e05472cdf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -63,6 +63,8 @@ namespace O3DE::ProjectManager QVector GatherGemsToBeAdded() const; QVector GatherGemsToBeRemoved() const; + int TotalAddedGems() const; + private: enum UserRole { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index d8f41c077e..c1360cabc6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -37,6 +37,16 @@ namespace O3DE::ProjectManager return false; } + // Gem status + if (m_gemStatusFilter != GemStatus::NoFilter) + { + const GemStatus sourceGemStatus = static_cast(GemModel::IsAdded(sourceIndex)); + if (m_gemStatusFilter != sourceGemStatus) + { + return false; + } + } + // Gem origins if (m_gemOriginFilter) { @@ -125,6 +135,19 @@ namespace O3DE::ProjectManager return true; } + QString GemSortFilterProxyModel::GetGemStatusString(GemStatus status) + { + switch (status) + { + case Unselected: + return "Unselected"; + case Selected: + return "Selected"; + default: + return ""; + } + } + void GemSortFilterProxyModel::InvalidateFilter() { invalidate(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index f24a724ecf..fcde226f40 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -29,8 +29,17 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: + enum GemStatus + { + NoFilter = -1, + Unselected, + Selected + }; + GemSortFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); + static QString GetGemStatusString(GemStatus status); + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; GemModel* GetSourceModel() const { return m_sourceModel; } @@ -38,6 +47,9 @@ namespace O3DE::ProjectManager void SetSearchString(const QString& searchString) { m_searchString = searchString; InvalidateFilter(); } + GemStatus GetGemStatus() const { return m_gemStatusFilter; } + void SetGemStatus(GemStatus gemStatus) { m_gemStatusFilter = gemStatus; InvalidateFilter(); } + GemInfo::GemOrigins GetGemOrigins() const { return m_gemOriginFilter; } void SetGemOrigins(const GemInfo::GemOrigins& gemOrigins) { m_gemOriginFilter = gemOrigins; InvalidateFilter(); } @@ -61,6 +73,7 @@ namespace O3DE::ProjectManager AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr; QString m_searchString; + GemStatus m_gemStatusFilter = GemStatus::NoFilter; GemInfo::GemOrigins m_gemOriginFilter = {}; GemInfo::Platforms m_platformFilter = {}; GemInfo::Types m_typeFilter = {}; diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index be1f0e5529..65f73accd1 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -189,11 +189,13 @@ namespace O3DE::ProjectManager { if (m_stack->currentIndex() == ScreenOrder::Gems) { - m_header->setSubTitle(QString(tr("Configure Gems for \"%1\"")).arg(m_projectInfo.m_projectName)); - m_nextButton->setText(tr("Confirm")); + m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); + m_header->setSubTitle(QString(tr("Configure Gems"))); + m_nextButton->setText(tr("Finalize")); } else { + m_header->setTitle(""); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName)); m_nextButton->setText(tr("Save")); } From ecded991b57be2db905ce4c15d7af9d2fb9c297f Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:02:22 -0700 Subject: [PATCH 09/13] Display error when unable to start Python Added AzFramework Application, logging, unit tests --- Code/Framework/AzCore/AzCore/Utils/Utils.cpp | 7 + Code/Framework/AzCore/AzCore/Utils/Utils.h | 3 + Code/Sandbox/Editor/CryEdit.cpp | 2 +- Code/Tools/ProjectManager/CMakeLists.txt | 63 +++++- .../Linux/PAL_linux_tests_files.cmake | 15 ++ .../Linux/ProjectManager_Test_Traits_Linux.h | 15 ++ .../ProjectManager_Test_Traits_Platform.h | 15 ++ .../Platform/Mac/PAL_mac_tests_files.cmake | 15 ++ .../Mac/ProjectManager_Test_Traits_Mac.h | 15 ++ .../Mac/ProjectManager_Test_Traits_Platform.h | 15 ++ .../Windows/PAL_windows_tests_files.cmake | 15 ++ .../ProjectManager_Test_Traits_Platform.h | 15 ++ .../ProjectManager_Test_Traits_Windows.h | 15 ++ .../Resources/ProjectManager.qss | 5 + .../ProjectManager/Source/Application.cpp | 186 ++++++++++++++++++ .../Tools/ProjectManager/Source/Application.h | 48 +++++ .../Source/EngineSettingsScreen.cpp | 2 +- .../Source/ProjectButtonWidget.cpp | 2 +- .../Source/ProjectManagerWindow.cpp | 27 +-- .../Source/ProjectManagerWindow.h | 8 +- .../Source/ProjectSettingsScreen.cpp | 2 +- .../ProjectManager/Source/ProjectsScreen.cpp | 4 +- .../ProjectManager/Source/PythonBindings.cpp | 14 +- .../ProjectManager/Source/PythonBindings.h | 4 + .../Source/PythonBindingsInterface.h | 6 + Code/Tools/ProjectManager/Source/main.cpp | 87 ++------ .../project_manager_app_files.cmake | 17 ++ .../project_manager_files.cmake | 7 +- .../project_manager_tests_files.cmake | 17 ++ .../ProjectManager/tests/ApplicationTests.cpp | 47 +++++ Code/Tools/ProjectManager/tests/main.cpp | 35 ++++ 31 files changed, 606 insertions(+), 122 deletions(-) create mode 100644 Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h create mode 100644 Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h create mode 100644 Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h create mode 100644 Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h create mode 100644 Code/Tools/ProjectManager/Source/Application.cpp create mode 100644 Code/Tools/ProjectManager/Source/Application.h create mode 100644 Code/Tools/ProjectManager/project_manager_app_files.cmake create mode 100644 Code/Tools/ProjectManager/project_manager_tests_files.cmake create mode 100644 Code/Tools/ProjectManager/tests/ApplicationTests.cpp create mode 100644 Code/Tools/ProjectManager/tests/main.cpp diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index 7025b3977e..e3647ec2cb 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -175,4 +175,11 @@ namespace AZ::Utils path /= ".o3de"; return path.Native(); } + + AZ::IO::FixedMaxPathString GetO3deLogsDirectory() + { + AZ::IO::FixedMaxPath path = GetO3deManifestDirectory(); + path /= "Logs"; + return path.Native(); + } } diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.h b/Code/Framework/AzCore/AzCore/Utils/Utils.h index d082a3ebe0..8fb6f05742 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.h +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.h @@ -97,6 +97,9 @@ namespace AZ //! Retrieves the full path where the manifest file lives, i.e. "/.o3de/o3de_manifest.json" AZ::IO::FixedMaxPathString GetEngineManifestPath(); + //! Retrieves the full directory to the O3DE logs directory, i.e. "/.o3de/Logs" + AZ::IO::FixedMaxPathString GetO3deLogsDirectory(); + //! Retrieves the App root path to use on the current platform //! If the optional is not engaged the AppRootPath should be calculated based //! on the location of the bootstrap.cfg file diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index a972a4bd9b..67e228a3ce 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -2891,7 +2891,7 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen) { // provide the current project path for in case we want to update the project AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); - const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project_path %s", screen.c_str(), projectPath.c_str()); + const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str()); bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions); if (!launchSuccess) { diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index aeb7be9793..434ce1424e 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -20,12 +20,11 @@ if (NOT python_package_name) message(WARNING "Python was not found in the package assocation list. Did someone call ly_associate_package(xxxxxxx Python) ?") endif() + ly_add_target( - NAME ProjectManager APPLICATION - OUTPUT_NAME o3de + NAME ProjectManager.Static STATIC NAMESPACE AZ AUTOMOC - AUTORCC FILES_CMAKE project_manager_files.cmake Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake @@ -47,6 +46,60 @@ ly_add_target( 3rdParty::pybind11 AZ::AzCore AZ::AzFramework - AZ::AzToolsFramework AZ::AzQtComponents -) \ No newline at end of file +) + +ly_add_target( + NAME ProjectManager APPLICATION + OUTPUT_NAME o3de + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_app_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzCore + AZ::AzFramework + AZ::AzQtComponents + AZ::ProjectManager.Static +) + +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + ly_add_target( + NAME ProjectManager.Tests EXECUTABLE + NAMESPACE AZ + AUTORCC + FILES_CMAKE + project_manager_tests_files.cmake + Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + Platform/${PAL_PLATFORM_NAME} + BUILD_DEPENDENCIES + PRIVATE + 3rdParty::Qt::Core + 3rdParty::Qt::Concurrent + 3rdParty::Qt::Widgets + 3rdParty::Python + 3rdParty::pybind11 + AZ::AzTest + AZ::AzFramework + AZ::AzFrameworkTestShared + AZ::ProjectManager.Static + ) + + ly_add_googletest( + NAME AZ::ProjectManager.Tests + TEST_COMMAND $ --unittest + ) + +endif() diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_files.cmake new file mode 100644 index 0000000000..e79da7183d --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_tests_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 + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Linux.h +) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h new file mode 100644 index 0000000000..c8b428a1c2 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Linux.h @@ -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. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS true diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..639ef8a387 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Test_Traits_Platform.h @@ -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. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_files.cmake new file mode 100644 index 0000000000..a2d480de40 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_tests_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 + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Mac.h +) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Mac.h @@ -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. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..af8817998f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Test_Traits_Platform.h @@ -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. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_files.cmake new file mode 100644 index 0000000000..00d9da3db3 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_tests_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 + ProjectManager_Test_Traits_Platform.h + ProjectManager_Test_Traits_Windows.h +) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h new file mode 100644 index 0000000000..915a86644a --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Platform.h @@ -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. + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h new file mode 100644 index 0000000000..053db745ea --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Test_Traits_Windows.h @@ -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. + * + */ + +#pragma once + +#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 80470591a8..efc01802b7 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -7,6 +7,11 @@ QMainWindow { margin:0; } +#ScreensCtrl { + min-width:1200px; + min-height:800px; +} + QPushButton:focus { outline: none; border:1px solid #1e70eb; diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp new file mode 100644 index 0000000000..c566e76ef1 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -0,0 +1,186 @@ +/* + * 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 + +namespace O3DE::ProjectManager +{ + Application::~Application() + { + TearDown(); + } + + bool Application::Init(bool interactive) + { + constexpr const char* applicationName { "O3DE" }; + + QApplication::setOrganizationName(applicationName); + QApplication::setOrganizationDomain("o3de.org"); + + QCoreApplication::setApplicationName(applicationName); + QCoreApplication::setApplicationVersion("1.0"); + + // Use the LogComponent for non-dev logging log + RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor()); + + // set the log alias to .o3de/Logs instead of the default user/logs + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + + // DevWriteStorage is where the event log is written during development + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage, path.LexicallyNormal().Native()); + + // Save event logs to .o3de/Logs/eventlogger/EventLogO3DE.azsl + m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::BuildTargetNameKey, applicationName); + + Start(AzFramework::Application::Descriptor()); + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + + QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); + + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); + + // Create the actual Qt Application - this needs to happen before using QMessageBox + m_app.reset(new QApplication(*GetArgC(), *GetArgV())); + + if(!InitLog(applicationName)) + { + AZ_Warning("ProjectManager", false, "Failed to init logging"); + } + + m_pythonBindings = AZStd::make_unique(GetEngineRoot()); + if (!m_pythonBindings || !m_pythonBindings->PythonStarted()) + { + if (interactive) + { + QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"), + QObject::tr("This tool requires an O3DE engine with a Python runtime, " + "but either Python is missing or mis-configured. Please rename " + "your python/runtime folder to python/runtime_bak, then run " + "python/get_python.bat to restore the Python runtime folder.")); + } + return false; + } + + const AZ::CommandLine* commandLine = GetCommandLine(); + AZ_Assert(commandLine, "Failed to get command line"); + + ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; + if (size_t screenSwitchCount = commandLine->GetNumSwitchValues("screen"); screenSwitchCount > 0) + { + QString screenOption = commandLine->GetSwitchValue("screen", screenSwitchCount - 1).c_str(); + ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); + if (screen != ProjectManagerScreen::Invalid) + { + startScreen = screen; + } + } + + AZ::IO::FixedMaxPath projectPath; + if (size_t projectSwitchCount = commandLine->GetNumSwitchValues("project-path"); projectSwitchCount > 0) + { + projectPath = commandLine->GetSwitchValue("project-path", projectSwitchCount - 1).c_str(); + } + + m_mainWindow.reset(new ProjectManagerWindow(nullptr, projectPath, startScreen)); + + return true; + } + + bool Application::InitLog(const char* logName) + { + if (!m_entity) + { + // override the log alias to the O3de Logs directory instead of the default project user/Logs folder + AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory(); + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ_Assert(fileIO, "Failed to get FileIOBase instance"); + + fileIO->SetAlias("@log@", path.LexicallyNormal().Native().c_str()); + + // this entity exists because we need a home for LogComponent + // and cannot use the system entity because we need to be able to call SetLogFileBaseName + // so the log will be named O3DE.log + m_entity = aznew AZ::Entity("Application Entity"); + if (m_entity) + { + AzFramework::LogComponent* logger = aznew AzFramework::LogComponent(); + AZ_Assert(logger, "Failed to create LogComponent"); + logger->SetLogFileBaseName(logName); + m_entity->AddComponent(logger); + m_entity->Init(); + m_entity->Activate(); + } + } + + return m_entity != nullptr; + } + + void Application::TearDown() + { + if (m_entity) + { + m_entity->Deactivate(); + delete m_entity; + m_entity = nullptr; + } + + m_pythonBindings.reset(); + m_mainWindow.reset(); + m_app.reset(); + } + + bool Application::Run() + { + // Set up the Style Manager + AzQtComponents::StyleManager styleManager(qApp); + styleManager.initialize(qApp, GetEngineRoot()); + + // setup stylesheets and hot reloading + AZ::IO::FixedMaxPath engineRoot(GetEngineRoot()); + QDir rootDir(engineRoot.c_str()); + const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); + const auto qrcPath = QStringLiteral(":/ProjectManager/style"); + AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRoot); + + // set stylesheet after creating the main window or their styles won't get updated + AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss")); + + // the decoration wrapper is intended to remember window positioning and sizing + auto wrapper = new AzQtComponents::WindowDecorationWrapper(); + wrapper->setGuest(m_mainWindow.data()); + wrapper->show(); + m_mainWindow->show(); + + qApp->setQuitOnLastWindowClosed(true); + + // Run the application + return qApp->exec(); + } + +} diff --git a/Code/Tools/ProjectManager/Source/Application.h b/Code/Tools/ProjectManager/Source/Application.h new file mode 100644 index 0000000000..d4e94e8dd4 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/Application.h @@ -0,0 +1,48 @@ +/* + * 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 +#include +#include +#include +#endif + +namespace AZ +{ + class Entity; +} + +namespace O3DE::ProjectManager +{ + class Application + : public AzFramework::Application + { + public: + using AzFramework::Application::Application; + virtual ~Application(); + + bool Init(bool interactive = true); + bool Run(); + void TearDown(); + + private: + bool InitLog(const char* logName); + + AZStd::unique_ptr m_pythonBindings; + QSharedPointer m_app; + QSharedPointer m_mainWindow; + + AZ::Entity* m_entity = nullptr; + }; +} diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index 6342041da4..cf597745ea 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -25,7 +25,7 @@ namespace O3DE::ProjectManager EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) : ScreenWidget(parent) { - auto* layout = new QVBoxLayout(this); + auto* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); setObjectName("engineSettingsScreen"); diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index 761572ffa5..3bde0a310d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager { setObjectName("labelButton"); - QVBoxLayout* vLayout = new QVBoxLayout(this); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setContentsMargins(0, 0, 0, 0); vLayout->setSpacing(5); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index cb1398cc61..59be0aaa35 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -13,21 +13,11 @@ #include #include -#include -#include -#include -#include -#include - -#include - namespace O3DE::ProjectManager { - ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) + ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen) : QMainWindow(parent) { - m_pythonBindings = AZStd::make_unique(engineRootPath); - setWindowTitle(tr("O3DE Project Manager")); ScreensCtrl* screensCtrl = new ScreensCtrl(); @@ -44,15 +34,6 @@ namespace O3DE::ProjectManager setCentralWidget(screensCtrl); - // setup stylesheets and hot reloading - QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast(engineRootPath.Native().size())); - const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources"); - const auto qrcPath = QStringLiteral(":/ProjectManager/style"); - AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath); - - // set stylesheet after creating the screens or their styles won't get updated - AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss")); - // always push the projects screen first so we have something to come back to if (startScreen != ProjectManagerScreen::Projects) { @@ -66,10 +47,4 @@ namespace O3DE::ProjectManager emit screensCtrl->NotifyCurrentProject(path); } } - - ProjectManagerWindow::~ProjectManagerWindow() - { - m_pythonBindings.reset(); - } - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h index 758af8fc00..db2b1fd304 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.h @@ -13,7 +13,7 @@ #if !defined(Q_MOC_RUN) #include -#include +#include #include #endif @@ -25,12 +25,8 @@ namespace O3DE::ProjectManager Q_OBJECT public: - explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, + explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen = ProjectManagerScreen::Projects); - ~ProjectManagerWindow(); - - private: - AZStd::unique_ptr m_pythonBindings; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp index 26711753d4..b198724353 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp @@ -37,7 +37,7 @@ namespace O3DE::ProjectManager // if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally QFrame* projectSettingsFrame = new QFrame(this); projectSettingsFrame->setObjectName("projectSettings"); - m_verticalLayout = new QVBoxLayout(this); + m_verticalLayout = new QVBoxLayout(); // you cannot remove content margins in qss m_verticalLayout->setContentsMargins(0, 0, 0, 0); diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 8e41e52643..6633558406 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -85,7 +85,7 @@ namespace O3DE::ProjectManager QFrame* frame = new QFrame(this); frame->setObjectName("firstTimeContent"); { - QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setAlignment(Qt::AlignTop); frame->setLayout(layout); @@ -100,7 +100,7 @@ namespace O3DE::ProjectManager "available by downloading our sample project.")); layout->addWidget(introLabel); - QHBoxLayout* buttonLayout = new QHBoxLayout(this); + QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->setAlignment(Qt::AlignLeft); buttonLayout->setSpacing(s_spacerSize); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index bb6c05a472..0e00319b6b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -226,7 +226,7 @@ namespace O3DE::ProjectManager PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath) : m_enginePath(enginePath) { - StartPython(); + m_pythonStarted = StartPython(); } PythonBindings::~PythonBindings() @@ -234,6 +234,11 @@ namespace O3DE::ProjectManager StopPython(); } + bool PythonBindings::PythonStarted() + { + return m_pythonStarted && Py_IsInitialized(); + } + bool PythonBindings::StartPython() { if (Py_IsInitialized()) @@ -246,7 +251,7 @@ namespace O3DE::ProjectManager AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str()); if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str())) { - AZ_Assert(false, "Python home path must exist. path:%s", pyBasePath.c_str()); + AZ_Error("python", false, "Python home path does not exist: %s", pyBasePath.c_str()); return false; } @@ -351,6 +356,11 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::ExecuteWithLockErrorHandling(AZStd::function executionCallback) { + if (!Py_IsInitialized()) + { + return AZ::Failure("Python is not initialized"); + } + AZStd::lock_guard lock(m_lock); pybind11::gil_scoped_release release; pybind11::gil_scoped_acquire acquire; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 5700ede850..065867a130 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -34,6 +34,8 @@ namespace O3DE::ProjectManager ~PythonBindings() override; // PythonBindings overrides + bool PythonStarted() override; + // Engine AZ::Outcome GetEngineInfo() override; bool SetEngineInfo(const EngineInfo& engineInfo) override; @@ -70,6 +72,8 @@ namespace O3DE::ProjectManager bool StopPython(); + bool m_pythonStarted = false; + AZ::IO::FixedMaxPath m_enginePath; pybind11::handle m_engineTemplate; AZStd::recursive_mutex m_lock; diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index bc20d8e3f0..6d72bfee0c 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -34,6 +34,12 @@ namespace O3DE::ProjectManager IPythonBindings() = default; virtual ~IPythonBindings() = default; + /** + * Get whether Python was started or not. All Python functionality will fail if Python + * failed to start. + * @return true if Python was started successfully, false on failure + */ + virtual bool PythonStarted() = 0; // Engine diff --git a/Code/Tools/ProjectManager/Source/main.cpp b/Code/Tools/ProjectManager/Source/main.cpp index c597b8a729..1f4cadfb14 100644 --- a/Code/Tools/ProjectManager/Source/main.cpp +++ b/Code/Tools/ProjectManager/Source/main.cpp @@ -10,85 +10,26 @@ * */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -using namespace O3DE::ProjectManager; +#include +#include int main(int argc, char* argv[]) { - QApplication::setOrganizationName("O3DE"); - QApplication::setOrganizationDomain("o3de.org"); - QCoreApplication::setApplicationName("ProjectManager"); - QCoreApplication::setApplicationVersion("1.0"); - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware); - - AZ::AllocatorInstance::Create(); int runSuccess = 0; + + // Call before using any Qt, or the app may not be able to locate Qt libs + AzQtComponents::PrepareQtPaths(); + + O3DE::ProjectManager::Application application(&argc, &argv); + if (!application.Init()) { - QApplication app(argc, argv); - - // Need to use settings registry to get EngineRootFolder - AZ::IO::FixedMaxPath engineRootPath; - { - AZ::ComponentApplication componentApplication; - auto settingsRegistry = AZ::SettingsRegistry::Get(); - settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); - } - - AzQtComponents::StyleManager styleManager(&app); - styleManager.initialize(&app, engineRootPath); - - // Get the initial start screen if one is provided via command line - constexpr char optionPrefix[] = "--"; - AZ::CommandLine commandLine(optionPrefix); - commandLine.Parse(argc, argv); - - ProjectManagerScreen startScreen = ProjectManagerScreen::Projects; - if(commandLine.HasSwitch("screen")) - { - QString screenOption = commandLine.GetSwitchValue("screen", 0).c_str(); - ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption); - if (screen != ProjectManagerScreen::Invalid) - { - startScreen = screen; - } - } - - AZ::IO::FixedMaxPath projectPath; - if (commandLine.HasSwitch("project-path")) - { - projectPath = commandLine.GetSwitchValue("project-path", 0).c_str(); - } - - ProjectManagerWindow window(nullptr, engineRootPath, projectPath, startScreen); - window.show(); - - // somethings is preventing us from moving the window to the center of the - // primary screen - likely an Az style or component helper - constexpr int width = 1200; - constexpr int height = 800; - window.resize(width, height); - - runSuccess = app.exec(); + AZ_Error("ProjectManager", false, "Failed to initialize"); + runSuccess = 1; + } + else + { + runSuccess = application.Run() ? 0 : 1; } - AZ::AllocatorInstance::Destroy(); return runSuccess; } diff --git a/Code/Tools/ProjectManager/project_manager_app_files.cmake b/Code/Tools/ProjectManager/project_manager_app_files.cmake new file mode 100644 index 0000000000..223683ddd9 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_app_files.cmake @@ -0,0 +1,17 @@ +# +# 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 + Resources/ProjectManager.rc + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + Source/main.cpp +) diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 633824f995..587b5907bb 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -1,4 +1,5 @@ # +# # All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or # its licensors. # @@ -10,10 +11,8 @@ # set(FILES - Resources/ProjectManager.rc - Resources/ProjectManager.qrc - Resources/ProjectManager.qss - Source/main.cpp + Source/Application.h + Source/Application.cpp Source/ScreenDefs.h Source/ScreenFactory.h Source/ScreenFactory.cpp diff --git a/Code/Tools/ProjectManager/project_manager_tests_files.cmake b/Code/Tools/ProjectManager/project_manager_tests_files.cmake new file mode 100644 index 0000000000..e1e84a43a7 --- /dev/null +++ b/Code/Tools/ProjectManager/project_manager_tests_files.cmake @@ -0,0 +1,17 @@ +# +# 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 + Resources/ProjectManager.qrc + Resources/ProjectManager.qss + tests/ApplicationTests.cpp + tests/main.cpp +) diff --git a/Code/Tools/ProjectManager/tests/ApplicationTests.cpp b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp new file mode 100644 index 0000000000..c98b1a3a6f --- /dev/null +++ b/Code/Tools/ProjectManager/tests/ApplicationTests.cpp @@ -0,0 +1,47 @@ +/* +* 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 O3DE::ProjectManager +{ + class ProjectManagerApplicationTests + : public ::UnitTest::ScopedAllocatorSetupFixture + { + public: + + ProjectManagerApplicationTests() + { + m_application = AZStd::make_unique(); + } + + ~ProjectManagerApplicationTests() + { + m_application.reset(); + } + + AZStd::unique_ptr m_application; + }; + +#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + TEST_F(ProjectManagerApplicationTests, DISABLED_Application_Init_Succeeds) +#else + TEST_F(ProjectManagerApplicationTests, Application_Init_Succeeds) +#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS + { + // we don't want to interact with actual GUI or display it + EXPECT_TRUE(m_application->Init(/*interactive=*/false)); + } +} diff --git a/Code/Tools/ProjectManager/tests/main.cpp b/Code/Tools/ProjectManager/tests/main.cpp new file mode 100644 index 0000000000..191bef846a --- /dev/null +++ b/Code/Tools/ProjectManager/tests/main.cpp @@ -0,0 +1,35 @@ +/* +* 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 + +DECLARE_AZ_UNIT_TEST_MAIN(); + +int runDefaultRunner(int argc, char* argv[]) +{ + INVOKE_AZ_UNIT_TEST_MAIN(nullptr) + return 0; +} + +int main(int argc, char* argv[]) +{ + if (argc == 1) + { + // if no parameters are provided, add the --unittests parameter + constexpr int defaultArgc = 2; + char unittest_arg[] = "--unittests"; // Conversion from string literal to char* is not allowed per ISO C++11 + char* defaultArgv[defaultArgc] = { argv[0], unittest_arg }; + return runDefaultRunner(defaultArgc, defaultArgv); + } + INVOKE_AZ_UNIT_TEST_MAIN(nullptr); + return 0; +} From 5165f6ad0495cbc310f64c6294a2fe797f84f578 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Mon, 14 Jun 2021 19:09:27 -0500 Subject: [PATCH 10/13] [ATOM-15769] Setting minimum angle to 0.5 degrees on disk lights to avoid visual artifacts that occur at values closer to 0. Also updating the outer cone angle max to be 90 degrees since it's measured from the center point, not all the way across. (#1260) --- .../Code/Source/CoreLights/EditorAreaLightComponent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index 69bec21a6c..992b6319d8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -112,13 +112,13 @@ namespace AZ ->DataElement(Edit::UIHandlers::Default, &AreaLightComponentConfig::m_enableShutters, "Enable shutters", "Restrict the light to a specific beam angle depending on shape.") ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::ShuttersMustBeEnabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_innerShutterAngleDegrees, "Inner angle", "The inner angle of the shutters where the light beam begins to be occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_outerShutterAngleDegrees, "Outer angle", "The outer angle of the shutters where the light beam is completely occluded.") - ->Attribute(Edit::Attributes::Min, 0.0f) - ->Attribute(Edit::Attributes::Max, 180.0f) + ->Attribute(Edit::Attributes::Min, 0.5f) + ->Attribute(Edit::Attributes::Max, 90.0f) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShutters) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShuttersDisabled) From 0198f6121ba871022667cfb31e00ca6a1fef864b Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Mon, 14 Jun 2021 20:29:37 -0500 Subject: [PATCH 11/13] Rebind the DebugDisplayRequestBus Instance to handle drawing in GameMode. (#1275) --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 7e087d452b..e627d7d7c0 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -514,18 +514,28 @@ void EditorViewportWidget::Update() // Disable rendering to avoid recursion into Update() PushDisableRendering(); + + //get debug display interface for the viewport + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, GetViewportId()); + AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus."); + + AzFramework::DebugDisplayRequests* debugDisplay = + AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + + // draw debug visualizations - if (m_debugDisplay) + if (debugDisplay) { - const AZ::u32 prevState = m_debugDisplay->GetState(); - m_debugDisplay->SetState( + const AZ::u32 prevState = debugDisplay->GetState(); + debugDisplay->SetState( e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOn | e_DepthTestOn); AzFramework::EntityDebugDisplayEventBus::Broadcast( &AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport, - AzFramework::ViewportInfo{ GetViewportId() }, *m_debugDisplay); + AzFramework::ViewportInfo{ GetViewportId() }, *debugDisplay); - m_debugDisplay->SetState(prevState); + debugDisplay->SetState(prevState); } QtViewport::Update(); From de4605d6b72cd3729ae5dfab9880f70c89657c7b Mon Sep 17 00:00:00 2001 From: Doug McDiarmid Date: Mon, 14 Jun 2021 18:31:43 -0700 Subject: [PATCH 12/13] Added comment. --- .../ReflectionScreenSpaceCompositePass.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index fe2030ca7e..ab09d7175a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -42,6 +42,9 @@ namespace AZ if (!passes.empty()) { Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data const uint32_t MaxNumRoughnessMips = 8; uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; From 1f6bb14ed3680cb721ec7c276b754073cc1a7498 Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Mon, 14 Jun 2021 20:38:37 -0700 Subject: [PATCH 13/13] [EMFX][ATOM] crash during mesh reload (#1301) * Fixed a crash when entering game mode, removing a mesh and re-adding mesh. --- .../Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp | 5 +++++ .../EMotionFXAtom/Code/Source/ActorAsset.cpp | 3 ++- .../EMotionFXAtom/Code/Source/AtomActorInstance.cpp | 3 +-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index 343370ae35..a87d272448 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -629,6 +629,11 @@ namespace AZ Data::Asset lodAsset; modelLodCreator.End(lodAsset); + if (!lodAsset.IsReady()) + { + // [GFX TODO] During mesh reload the modelLodCreator could report errors and result in the lodAsset not ready. + return nullptr; + } modelCreator.AddLodAsset(AZStd::move(lodAsset)); lodIndex++; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 9f68a7d12c..1c4657d8df 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -311,7 +311,8 @@ namespace AZ Data::Asset modelAsset = actor->GetMeshAsset(); if (!modelAsset.IsReady()) { - AZ_Error("CreateSkinnedMeshInputFromActor", false, "Attempting to create skinned mesh input buffers for an actor that doesn't have a loaded model."); + AZ_Warning("CreateSkinnedMeshInputFromActor", false, "Check if the actor has a mesh added. Right click the source file in the asset browser, click edit settings, " + "and navigate to the Meshes tab. Add a mesh if it's missing."); return nullptr; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 532c8720b5..18a21c93a6 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -456,9 +456,8 @@ namespace AZ void AtomActorInstance::Create() { Destroy(); - m_skinnedMeshInputBuffers = GetRenderActor()->FindOrCreateSkinnedMeshInputBuffers(); - AZ_Error("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to get SkinnedMeshInputBuffers from Actor."); + AZ_Warning("AtomActorInstance", m_skinnedMeshInputBuffers, "Failed to create SkinnedMeshInputBuffers from Actor. It is likely that this actor doesn't have any meshes"); if (m_skinnedMeshInputBuffers) { m_boneTransforms = CreateBoneTransformBufferFromActorInstance(m_actorInstance, GetSkinningMethod());