Merge branch 'development' into o3de_sdk/installer_configs

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-10-28 18:29:52 -07:00
269 changed files with 28004 additions and 1586 deletions
@@ -61,8 +61,12 @@ namespace AssetBundler
{
AZStd::string absolutePath = filePath.toUtf8().data();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath.c_str()))
{
AZStd::string projectName = pathToProjectNameMap.at(absolutePath);
{
AZStd::string projectName;
if (pathToProjectNameMap.contains(absolutePath))
{
projectName = pathToProjectNameMap.at(absolutePath);
}
// If a project name is already specified, then the associated file is a default file
LoadFile(absolutePath, projectName, !projectName.empty());
@@ -10,12 +10,13 @@
#pragma once
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/AZStdContainers.inl>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/bitset.h>
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@@ -3576,23 +3576,24 @@ namespace AssetProcessor
// Absolute path, just check the 1 scan folder
if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute())
{
QString scanFolderName;
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName))
auto scanFolderInfo = m_platformConfig->GetScanFolderForFile(encodedFileData);
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, scanFolderInfo, resultDatabaseSourceName))
{
AZ_Warning(
AssetProcessor::ConsoleChannel, false,
"'%s' does not appear to be in any input folder. Use relative paths instead.",
sourceDependency.m_sourceFileDependencyPath.c_str());
}
else
{
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
QDir rooted(scanFolderInfo->ScanPath());
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName);
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
QDir rooted(scanFolderName);
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
}
}
else // Relative path, check every scan folder
{
@@ -259,6 +259,11 @@ namespace AssetProcessor
scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
AZStd::fixed_vector<AZStd::string_view, AzFramework::MaxPlatformCodeNames> platformCodes;
AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier);
AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier"
" to platform codename is required in the SettingsRegistryBuilder."
" The bootstrap.game is now only produced per build configuration and doesn't take into account"
" different platforms names");
const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier();
// Determines the suffix that will be used for the launcher based on processing server vs non-server assets
const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER)
@@ -293,9 +298,10 @@ namespace AssetProcessor
outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
SettingsExporter exporter(outputBuffer, excludes);
for (AZStd::string_view platform : platformCodes)
if (!platformCodes.empty())
{
AZ::u32 productSubID = static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}(platform)); // Deliberately ignoring half the bits.
AZStd::string_view platform = platformCodes.front();
constexpr AZ::u32 productSubID = 0;
for (size_t i = 0; i < AZStd::size(specializations); ++i)
{
const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i];
@@ -337,7 +343,7 @@ namespace AssetProcessor
// The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry
// the local SettingsRegistry. The reason this is needed is so that the call to
// `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "<gem-root>/Registry" folder
// that will be merged into the bootstrap.game.<configuration>.<platform>.setreg file
// that will be merged into the bootstrap.game.<configuration>.setreg file
// This is used by the GameLauncher applications to read from a single merged .setreg file
// containing the settings needed to run a game/simulation without have access to the source code base registry
AZStd::vector<AzFramework::GemInfo> gemInfos;
@@ -408,8 +414,6 @@ namespace AssetProcessor
}
outputPath += specialization.GetSpecialization(0); // Append configuration
outputPath += '.';
outputPath += platform;
outputPath += ".setreg";
AZ::IO::SystemFile file;
@@ -281,7 +281,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
}
@@ -88,6 +88,7 @@ public:
friend struct DuplicateProductsTest;
friend struct DuplicateProcessTest;
friend struct AbsolutePathProductDependencyTest;
friend struct WildcardSourceDependencyTest;
explicit AssetProcessorManager_Test(PlatformConfiguration* config, QObject* parent = nullptr);
~AssetProcessorManager_Test() override;
@@ -4446,7 +4447,9 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons
void FingerprintTest::SetUp()
{
AZ_Printf("FingerprintTest", "SetUp start");
AssetProcessorManagerTest::SetUp();
AZ_Printf("FingerprintTest", "SetUp self");
// We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own
m_mockApplicationManager->BusDisconnect();
@@ -4465,18 +4468,23 @@ void FingerprintTest::SetUp()
});
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, ""));
AZ_Printf("FingerprintTest", "SetUp end");
}
void FingerprintTest::TearDown()
{
AZ_Printf("FingerprintTest", "TearDown start");
m_jobResults = AZStd::vector<AssetProcessor::JobDetails>{};
m_mockBuilderInfoHandler = {};
AZ_Printf("FingerprintTest", "TearDown parent");
AssetProcessorManagerTest::TearDown();
AZ_Printf("FingerprintTest", "TearDown end");
}
void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult)
{
AZ_Printf("FingerprintTest", "Fingerprint Test Start");
m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data();
m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint;
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath));
@@ -4485,6 +4493,7 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job
ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1);
ASSERT_EQ(m_jobResults.size(), 1);
ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult);
AZ_Printf("FingerprintTest", "Fingerprint Test End");
}
TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint)
@@ -5308,3 +5317,141 @@ TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase)
ASSERT_TRUE(BlockUntilIdle(5000));
ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName);
}
bool WildcardSourceDependencyTest::Test(
const AZStd::string& dependencyPath, AZStd::vector<AZStd::string>& resolvedPaths)
{
[[maybe_unused]] QString resolvedName;
QStringList stringlistPaths;
AssetBuilderSDK::SourceFileDependency dependency(dependencyPath, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards);
bool result = m_assetProcessorManager->ResolveSourceFileDependencyPath(dependency, resolvedName, stringlistPaths);
// Convert to a vector of AZStd::strings because GTest handles this type better when displaying errors
for (const QString& resolvedPath : stringlistPaths)
{
resolvedPaths.emplace_back(resolvedPath.toUtf8().constData());
}
return result;
}
void WildcardSourceDependencyTest::SetUp()
{
AssetProcessorManagerTest::SetUp();
QDir tempPath(m_tempDir.path());
// Add a non-recursive scan folder. Only files directly inside of this folder should be picked up, subfolders are ignored
m_config->AddScanFolder(ScanFolderInfo(tempPath.filePath("no_recurse"), "no_recurse",
"no_recurse", false, false, m_config->GetEnabledPlatforms(), 1));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1a.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1b.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/a.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/b.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/c.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/d.foo"));
// Add a file that is not in a scanfolder. Should always be ignored
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("not/a/scanfolder/e.foo"));
// Add a file in the non-recursive scanfolder. Since its not directly in the scan folder, it should always be ignored
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("no_recurse/one/two/three/f.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_Broad)
{
// Expect all files except for the 2 invalid ones (e and f)
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("*.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo", "1a.foo", "1b.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_WithFolder)
{
// Make sure we can filter to files under a folder
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("folder/*.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("folder/one/c.foo", "folder/one/d.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_WildcardPath)
{
// Make sure the * wildcard works even if the full filename is given
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("*a.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "1a.foo"));
}
TEST_F(WildcardSourceDependencyTest, Absolute_WithFolder)
{
// Make sure we can use absolute paths to filter to files under a folder
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("subfolder2/redirected/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo"));
}
TEST_F(WildcardSourceDependencyTest, Absolute_NotInScanfolder)
{
// Files outside a scanfolder should not be returned even with an absolute path
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("not/a/scanfolder/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_NotInScanfolder)
{
// Files outside a scanfolder should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test("*/e.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_InNonRecursiveScanfolder)
{
// Files deep inside non-recursive scanfolders should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test("*/f.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Absolute_InNonRecursiveScanfolder)
{
// Absolute paths to files deep inside non-recursive scanfolders should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("one/two/three/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_NoWildcard)
{
// No wildcard results in a failure
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_FALSE(Test("subfolder1/1a.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Absolute_NoWildcard)
{
// No wildcard results in a failure
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_FALSE(Test(tempPath.absoluteFilePath("subfolder1/1a.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
@@ -131,6 +131,14 @@ struct MultiplatformPathDependencyTest
void SetUp() override;
};
struct WildcardSourceDependencyTest
: AssetProcessorManagerTest
{
bool Test(const AZStd::string& dependencyPath, AZStd::vector<AZStd::string>& resolvedPaths);
void SetUp() override;
};
struct MockBuilderInfoHandler
: public AssetProcessor::AssetBuilderInfoBus::Handler
{
@@ -123,7 +123,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding all the files in the folder but not the folder itself
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
@@ -144,7 +144,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding the complete folder here
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
@@ -405,6 +405,8 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config;
config.AddScanFolder(ScanFolderInfo("blahblah", "Blah ScanFolder", "sf2", true, true), true);
m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
@@ -347,7 +347,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
AssetProcessorManager_Test apm(&config); // note, this will 'push' the scan folders in to the db.
@@ -1791,13 +1791,8 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different
//------Test for Files which are excluded
processResults.clear();
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/savebackup/test.txt"));
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
UNIT_TEST_EXPECT_FALSE(BlockUntil(idling, 3000)); //Processing a file that will be excluded should not cause assetprocessor manager to emit the onBecameIdle signal because its state should not change
UNIT_TEST_EXPECT_TRUE(processResults.size() == 0);
// ------------- Test querying asset status -------------------
{
absolutePath = tempPath.absoluteFilePath("subfolder2/folder/ship.tiff");
@@ -21,6 +21,7 @@
#endif
#include <gtest/gtest.h>
#include <AzCore/UnitTest/UnitTest.h>
//! These macros can be used for checking your unit tests,
//! you can check AssetScannerUnitTest.cpp for usage
@@ -155,6 +156,7 @@ namespace UnitTestUtils
bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numWarningsAbsorbed;
if (m_debugMessages)
{
@@ -165,6 +167,7 @@ namespace UnitTestUtils
bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numAssertsAbsorbed;
if (m_debugMessages)
{
@@ -175,6 +178,7 @@ namespace UnitTestUtils
bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
{
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numErrorsAbsorbed;
if (m_debugMessages)
{
@@ -183,8 +187,9 @@ namespace UnitTestUtils
return true; // I handled this, do not forward it
}
bool OnPrintf(const char* /*window*/, const char* /*message*/) override
bool OnPrintf(const char* /*window*/, const char* message) override
{
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
++m_numMessagesAbsorbed;
return true;
}
@@ -1633,13 +1633,18 @@ namespace AssetProcessor
bool AssetProcessor::PlatformConfiguration::IsFileExcluded(QString fileName) const
{
for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
QString relPath, scanFolderName;
if (ConvertToRelativePath(fileName, relPath, scanFolderName))
{
if (excludeRecognizer.m_patternMatcher.MatchesPath(fileName.toUtf8().constData()))
for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
{
return true;
if (excludeRecognizer.m_patternMatcher.MatchesPath(relPath.toUtf8().constData()))
{
return true;
}
}
}
return false;
}
@@ -50,10 +50,10 @@
"order": 6000
},
"Exclude HoldFiles": {
"pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
"pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
"pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
"pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
@@ -74,10 +74,10 @@
"include": "test"
},
"Exclude HoldFiles": {
"pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
"pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
"pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
"pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
+1
View File
@@ -34,6 +34,7 @@ ly_add_target(
INCLUDE_DIRECTORIES
PRIVATE
Source
Platform/${PAL_PLATFORM_NAME}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_linux.cpp
ProjectUtils_linux.cpp
ProjectManagerDefs_linux.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Linux.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Linux.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_mac.cpp
ProjectUtils_mac.cpp
ProjectManagerDefs_mac.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Mac.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Mac.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_windows.cpp
ProjectUtils_windows.cpp
ProjectManagerDefs_windows.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Windows.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Windows.h>
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true
@@ -9,7 +9,7 @@ QMainWindow {
#ScreensCtrl {
min-width:1200px;
min-height:800px;
min-height:700px;
}
QPushButton:focus {
@@ -691,6 +691,12 @@ QProgressBar::chunk {
#gemRepoAddDialogInstructionTitleLabel {
font-size:14px;
font-weight:bold;
}
#gemRepoAddDialogWarningLabel {
font-size:12px;
font-style:italic;
}
#addGemRepoDialog #formFrame {
@@ -16,6 +16,7 @@
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <ProjectManager_Traits_Platform.h>
#include <QApplication>
#include <QDir>
@@ -194,8 +195,12 @@ namespace O3DE::ProjectManager
// 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
// the decoration wrapper is intended to remember window positioning and sizing
#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
#else
auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled);
#endif
wrapper->setGuest(m_mainWindow.data());
// show the main window here to apply the stylesheet before restoring geometry or we
@@ -42,7 +42,9 @@ namespace O3DE::ProjectManager
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController);
vLayout->addWidget(m_headerWidget);
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
@@ -73,6 +75,7 @@ namespace O3DE::ProjectManager
m_notificationsView = AZStd::make_unique<AzToolsFramework::ToastNotificationsView>(this, AZ_CRC("GemCatalogNotificationsView"));
m_notificationsView->SetOffset(QPoint(10, 70));
m_notificationsView->SetMaxQueuedNotifications(1);
}
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
@@ -94,48 +97,6 @@ namespace O3DE::ProjectManager
m_headerWidget->ReinitForProject();
connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(
m_headerWidget, &GemCatalogHeaderWidget::AddGem,
[&]()
{
EngineInfo engineInfo;
QString defaultPath;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
defaultPath = engineInfo.m_defaultGemsFolder;
}
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
if (!directory.isEmpty())
{
// register the gem to the o3de_manifest.json and to the project after the user confirms
// project creation/update
auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
if(!registerResult)
{
QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
}
else
{
m_gemsToRegisterWithProject.insert(directory);
AZ::Outcome<GemInfo, void> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
if (gemInfoResult)
{
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
m_gemModel->UpdateGemDependencies();
}
}
}
});
// Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{
@@ -144,6 +105,46 @@ namespace O3DE::ProjectManager
});
}
void GemCatalogScreen::OnAddGemClicked()
{
EngineInfo engineInfo;
QString defaultPath;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
defaultPath = engineInfo.m_defaultGemsFolder;
}
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
if (!directory.isEmpty())
{
// register the gem to the o3de_manifest.json and to the project after the user confirms
// project creation/update
auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
if(!registerResult)
{
QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
}
else
{
m_gemsToRegisterWithProject.insert(directory);
AZ::Outcome<GemInfo, void> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
if (gemInfoResult)
{
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
m_gemModel->UpdateGemDependencies();
}
}
}
}
void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies)
{
if (m_notificationsEnabled)
@@ -166,6 +167,10 @@ namespace O3DE::ProjectManager
{
notification += " " + tr("and") + " ";
}
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
{
m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
}
}
if (numChangedDependencies == 1 )
@@ -174,7 +179,7 @@ namespace O3DE::ProjectManager
}
else if (numChangedDependencies > 1)
{
notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies");
notification += QString("%1 Gem ").arg(numChangedDependencies) + tr("dependencies");
}
notification += " " + (added ? tr("activated") : tr("deactivated"));
@@ -47,6 +47,7 @@ namespace O3DE::ProjectManager
public slots:
void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies);
void OnAddGemClicked();
protected:
void hideEvent(QHideEvent* event) override;
@@ -225,21 +225,22 @@ namespace O3DE::ProjectManager
QVector<QString> elementNames;
QVector<int> elementCounts;
const int totalGems = m_gemModel->rowCount();
const int selectedGemTotal = m_gemModel->TotalAddedGems();
const int selectedGemTotal = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true).size();
const int unselectedGemTotal = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true).size();
const int enabledGemTotal = m_gemModel->TotalAddedGems(/*includeDependencies=*/true);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
elementCounts.push_back(totalGems - selectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Selected));
elementCounts.push_back(selectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
elementCounts.push_back(totalGems - enabledGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
elementCounts.push_back(unselectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Active));
elementCounts.push_back(enabledGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
elementCounts.push_back(totalGems - enabledGemTotal);
bool wasCollapsed = false;
if (m_statusFilter)
{
@@ -262,44 +263,51 @@ namespace O3DE::ProjectManager
const QList<QAbstractButton*> buttons = m_statusFilter->GetButtonGroup()->buttons();
QAbstractButton* unselectedButton = buttons[0];
QAbstractButton* selectedButton = buttons[1];
unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
QAbstractButton* selectedButton = buttons[0];
QAbstractButton* unselectedButton = buttons[1];
selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
auto updateGemSelection = [=]([[maybe_unused]] bool checked)
{
if (unselectedButton->isChecked() && !selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
}
else if (!unselectedButton->isChecked() && selectedButton->isChecked())
if (!unselectedButton->isChecked() && selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected);
}
else if (unselectedButton->isChecked() && !selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
}
else
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
if (unselectedButton->isChecked() && selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Both);
}
else
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
}
}
};
connect(unselectedButton, &QAbstractButton::toggled, this, updateGemSelection);
connect(selectedButton, &QAbstractButton::toggled, this, updateGemSelection);
QAbstractButton* inactiveButton = buttons[2];
QAbstractButton* activeButton = buttons[3];
inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
QAbstractButton* activeButton = buttons[2];
QAbstractButton* inactiveButton = buttons[3];
activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
auto updateGemActive = [=]([[maybe_unused]] bool checked)
{
if (inactiveButton->isChecked() && !activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
}
else if (!inactiveButton->isChecked() && activeButton->isChecked())
if (!inactiveButton->isChecked() && activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active);
}
else if (inactiveButton->isChecked() && !activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
}
else
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::NoFilter);
@@ -50,11 +50,26 @@ namespace O3DE::ProjectManager
}
}
// Gem selected
if (m_gemSelectedFilter != GemSelected::NoFilter)
// Gem selected
if (m_gemSelectedFilter == GemSelected::Selected)
{
const GemSelected sourceGemStatus = static_cast<GemSelected>(GemModel::IsAdded(sourceIndex));
if (m_gemSelectedFilter != sourceGemStatus)
if (!GemModel::NeedsToBeAdded(sourceIndex, true))
{
return false;
}
}
// Gem unselected
else if (m_gemSelectedFilter == GemSelected::Unselected)
{
if (!GemModel::NeedsToBeRemoved(sourceIndex, true))
{
return false;
}
}
// Gem selected or unselected
else if (m_gemSelectedFilter == GemSelected::Both)
{
if (!GemModel::NeedsToBeAdded(sourceIndex, true) && !GemModel::NeedsToBeRemoved(sourceIndex, true))
{
return false;
}
@@ -29,7 +29,8 @@ namespace O3DE::ProjectManager
{
NoFilter = -1,
Unselected,
Selected
Selected,
Both
};
enum class GemActive
{
@@ -27,6 +27,7 @@ namespace O3DE::ProjectManager
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setContentsMargins(30, 30, 25, 10);
vLayout->setSpacing(0);
vLayout->setAlignment(Qt::AlignTop);
setLayout(vLayout);
QLabel* instructionTitleLabel = new QLabel(tr("Enter a valid path to add a new user repository"));
@@ -41,9 +42,18 @@ namespace O3DE::ProjectManager
vLayout->addWidget(instructionContextLabel);
m_repoPath = new FormFolderBrowseEditWidget(tr("Repository Path"), "", this);
m_repoPath->setFixedWidth(600);
m_repoPath->setFixedSize(QSize(600, 100));
vLayout->addWidget(m_repoPath);
vLayout->addSpacing(10);
QLabel* warningLabel = new QLabel(tr("Online repositories may contain files that could potentially harm your computer,"
" please ensure you understand the risks before downloading Gems from third-party sources."));
warningLabel->setObjectName("gemRepoAddDialogWarningLabel");
warningLabel->setWordWrap(true);
warningLabel->setAlignment(Qt::AlignLeft);
vLayout->addWidget(warningLabel);
vLayout->addSpacing(40);
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
@@ -133,6 +133,11 @@ namespace O3DE::ProjectManager
return true;
}
else
{
// If we are already on this screen still notify we are on this screen to refresh it
newScreen->NotifyCurrentScreen();
}
}
return false;
@@ -187,6 +187,7 @@ namespace AZ
// Register utilities
AZ::SceneAPI::SceneCore::PatternMatcher::Reflect(context);
AZ::SceneAPI::Utilities::DebugSceneGraph::Reflect(context);
}
}
@@ -10,6 +10,7 @@
#include <AzCore/std/optional.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
@@ -20,6 +21,36 @@
namespace AZ::SceneAPI::Utilities
{
void DebugNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DebugNode>()
->Field("Name", &DebugNode::m_name)
->Field("Path", &DebugNode::m_path)
->Field("Type", &DebugNode::m_type)
->Field("Data", &DebugNode::m_data);
}
}
void DebugSceneGraph::Reflect(AZ::ReflectContext* context)
{
DebugNode::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DebugSceneGraph>()
->Field("Version", &DebugSceneGraph::m_version)
->Field("ProductName", &DebugSceneGraph::m_productName)
->Field("SceneName", &DebugSceneGraph::m_sceneName)
->Field("Nodes", &DebugSceneGraph::m_nodes);
}
}
void DebugOutput::Write(const char* name, const char* data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data);
@@ -38,21 +69,29 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::Write(const char* name, const AZStd::string& data)
{
Write(name, data.c_str());
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, double data)
{
m_output += AZStd::string::format("\t%s: %f\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, uint64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRIu64 "\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, int64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRId64 "\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, const DataTypes::MatrixType& data)
@@ -63,6 +102,7 @@ namespace AZ::SceneAPI::Utilities
AZ::Vector3 translation{};
data.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
m_pauseNodeData = true;
m_output += AZStd::string::format("\t%s:\n", name);
m_output += "\t";
@@ -76,16 +116,23 @@ namespace AZ::SceneAPI::Utilities
m_output += "\t";
Write("Transl", translation);
m_pauseNodeData = false;
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, bool data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data ? "true" : "false");
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, Vector3 data)
{
m_output += AZStd::string::format("\t%s: <% f, % f, % f>\n", name, data.GetX(), data.GetY(), data.GetZ());
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, AZStd::optional<bool> data)
@@ -128,6 +175,11 @@ namespace AZ::SceneAPI::Utilities
return m_output;
}
DebugNode DebugOutput::GetDebugNode() const
{
return m_currentNode;
}
void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite);
@@ -137,7 +189,6 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName)
{
const int debugSceneGraphVersion = 1;
AZStd::string debugSceneFile;
AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
@@ -147,7 +198,7 @@ namespace AZ::SceneAPI::Utilities
if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", SceneGraphVersion).c_str());
WriteAndLog(dbgFile, scene->GetName().c_str());
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
@@ -158,6 +209,11 @@ namespace AZ::SceneAPI::Utilities
AZ::SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
DebugSceneGraph debugSceneGraph;
debugSceneGraph.m_version = SceneGraphVersion;
debugSceneGraph.m_productName = productName;
debugSceneGraph.m_sceneName = scene->GetName().c_str();
for (auto&& viewIt : view)
{
if (viewIt.second == nullptr)
@@ -170,20 +226,31 @@ namespace AZ::SceneAPI::Utilities
WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
AZ::SceneAPI::Utilities::DebugOutput debugOutput;
AZ::SceneAPI::Utilities::DebugOutput debugOutput(
DebugNode(viewIt.first.GetName(), viewIt.first.GetPath(), graphObject->RTTI_GetTypeName()));
viewIt.second->GetDebugOutput(debugOutput);
if (!debugOutput.GetOutput().empty())
{
WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
}
debugSceneGraph.m_nodes.push_back(debugOutput.GetDebugNode());
}
dbgFile.Close();
Utils::SaveObjectToFile((debugSceneFile + ".xml").c_str(), DataStream::StreamType::ST_XML, &debugSceneGraph);
static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
AZStd::nullopt, AZStd::nullopt);
static const AZ::Data::AssetType dbgSceneGraphXmlAssetType("{51F37614-0D77-4F36-9AC6-7ED70A0AC868}");
productList.AddProduct(
(productName + ".xml"), AZ::Uuid::CreateName((productName + ".xml").c_str()), dbgSceneGraphXmlAssetType,
AZStd::nullopt, AZStd::nullopt);
}
}
}
@@ -15,6 +15,7 @@
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <cinttypes>
#include <AzCore/std/any.h>
namespace AZ
{
@@ -34,15 +35,63 @@ namespace AZ
namespace AZ::SceneAPI::Utilities
{
constexpr int SceneGraphVersion = 1;
struct DebugNode
{
AZ_TYPE_INFO(DebugNode, "{490B9D4C-1847-46EB-BEBC-49812E104626}");
AZStd::string m_name;
AZStd::string m_path;
AZStd::string m_type;
DebugNode() = default;
DebugNode(AZStd::string name, AZStd::string path, AZStd::string type)
: m_name(AZStd::move(name)),
m_path(AZStd::move(path)),
m_type(AZStd::move(type))
{
}
static void Reflect(AZ::ReflectContext* context);
using DataItem = AZStd::pair<AZStd::string, AZStd::any>;
AZStd::vector<DataItem> m_data;
};
struct DebugSceneGraph
{
AZ_TYPE_INFO(DebugSceneGraph, "{375F6558-5709-409F-881E-8ED575D56C92}");
int m_version = SceneGraphVersion;
AZStd::string m_productName;
AZStd::string m_sceneName;
AZStd::vector<DebugNode> m_nodes;
static void Reflect(AZ::ReflectContext* context);
};
class DebugOutput
{
public:
DebugOutput(DebugNode node) : m_currentNode(AZStd::move(node)){}
template<typename T>
void Write(const char* name, const AZStd::vector<T>& data);
template<typename T>
void Write(const char* name, const AZStd::vector<AZStd::vector<T>>& data);
template<typename T>
void AddToNode(const char* name, const T& data)
{
if (!m_pauseNodeData)
{
m_currentNode.m_data.emplace_back(name, AZStd::make_any<AZStd::decay_t<T>>(data));
}
}
SCENE_CORE_API void Write(const char* name, const char* data);
SCENE_CORE_API void WriteArray(const char* name, const unsigned int* data, int size);
SCENE_CORE_API void Write(const char* name, const AZStd::string& data);
@@ -57,11 +106,15 @@ namespace AZ::SceneAPI::Utilities
SCENE_CORE_API void Write(const char* name, AZStd::optional<AZ::Vector3> data);
SCENE_CORE_API const AZStd::string& GetOutput() const;
SCENE_CORE_API DebugNode GetDebugNode() const;
SCENE_CORE_API static void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName);
protected:
AZStd::string m_output;
DebugSceneGraph m_graph;
DebugNode m_currentNode;
bool m_pauseNodeData = false; // If true, don't append any data to the DebugNode. Useful when a Write function calls other Write functions
};
}
@@ -13,7 +13,11 @@ namespace AZ::SceneAPI::Utilities
template <typename T>
void DebugOutput::Write(const char* name, const AZStd::vector<T>& data)
{
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), AZStd::hash_range(data.begin(), data.end()));
size_t hash = AZStd::hash_range(data.begin(), data.end());
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
template <typename T>
@@ -27,5 +31,9 @@ namespace AZ::SceneAPI::Utilities
}
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
}
@@ -81,8 +81,6 @@ namespace AZ
// Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus
// so that the OnCatalogLoaded event gets processed now, instead of during application shutdown.
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml");
application.Tick();
AZStd::string logggingScratchBuffer;