diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h index c1beea08f0..39aab4d3fb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetUtils.h @@ -17,6 +17,9 @@ class QString; namespace AzToolsFramework::AssetUtils { + static constexpr const char* AssetImporterSettingsKey{ "/O3DE/SceneAPI/AssetImporter" }; + static constexpr const char* AssetImporterSupportedFileTypeKey{ "SupportedFileTypeExtensions" }; + //! Reads the "/Amazon/AssetProcessor/Settings/Platforms" entry from the settings registry //! to retrieve all enabled platforms void ReadEnabledPlatformsFromSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry, diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 1d51b2e775..f10e4a9e05 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -238,6 +238,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) OUTPUT_SUBDIRECTORY testdata/DummyProject ) + ly_add_target_files( + TARGETS + AssetProcessor.Tests + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/testdata/config_metadata/AssetProcessorPlatformConfig.setreg + OUTPUT_SUBDIRECTORY + testdata/config_metadata + ) # Have the AssetProcessorTest use the LY_CMAKE_TARGET define of AssetProcessorBatch for the purpose # of looking up the generated cmake build dependencies settings registry .setreg file diff --git a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake index 747413d6cc..6aebc8bc25 100644 --- a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake +++ b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake @@ -12,6 +12,7 @@ set(FILES testdata/config_broken_noscans/AssetProcessorPlatformConfig.setreg testdata/config_broken_recognizers/AssetProcessorPlatformConfig.setreg testdata/config_regular/AssetProcessorPlatformConfig.setreg + testdata/config_metadata/AssetProcessorPlatformConfig.setreg testdata/config_regular_platform_scanfolder/AssetProcessorPlatformConfig.setreg testdata/EmptyDummyProject/AssetProcessorGamePlatformConfig.setreg testdata/DummyProject/AssetProcessorGamePlatformConfig.setreg diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index f0cf8aaf19..10ecc411ab 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -76,6 +76,7 @@ public: friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyMetadataFile); friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_DeleteFile); friend class GTEST_TEST_CLASS_NAME_(DeleteTest, DeleteFolderSharedAcrossTwoScanFolders_CorrectFileAndFolderAreDeletedFromCache); + friend class GTEST_TEST_CLASS_NAME_(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase); friend class AssetProcessorManagerTest; friend struct ModtimeScanningTest; @@ -5241,3 +5242,59 @@ void DuplicateProcessTest::SetUp() m_sharedConnection = m_assetProcessorManager->m_stateData.get(); ASSERT_TRUE(m_sharedConnection); } + +void MetadataFileTest::SetUp() +{ + AssetProcessorManagerTest::SetUp(); + m_config->AddMetaDataType("foo", "txt"); +} + +TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase) +{ + + using namespace AzToolsFramework::AssetSystem; + using namespace AssetProcessor; + + QDir tempPath(m_tempDir.path()); + + QString relFileName("Dummy.TXT"); + QString absPath(tempPath.absoluteFilePath("subfolder1/Dummy.TXT")); + QString watchFolder = tempPath.absoluteFilePath("subfolder1"); + UnitTestUtils::CreateDummyFile(absPath, "dummy"); + + JobEntry entry; + entry.m_watchFolderPath = watchFolder; + entry.m_databaseSourceName = entry.m_pathRelativeToWatchFolder = relFileName; + entry.m_jobKey = "txt"; + entry.m_platformInfo = { "pc", {"host", "renderer", "desktop"} }; + entry.m_jobRunKey = 1; + + QString productPath(m_normalizedCacheRootDir.absoluteFilePath("outputfile.TXT")); + UnitTestUtils::CreateDummyFile(productPath); + + AssetBuilderSDK::ProcessJobResponse jobResponse; + jobResponse.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; + jobResponse.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(productPath.toUtf8().data())); + + QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, entry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, jobResponse)); + + ASSERT_TRUE(BlockUntilIdle(5000)); + + // Creating a metadata file for the source assets + // APM should process the source asset if a metadafile is detected + // We are intentionally having a source file with a different file extension casing than the one specified in the metadata rule. + QString metadataFile(tempPath.absoluteFilePath("subfolder1/Dummy.foo")); + UnitTestUtils::CreateDummyFile(metadataFile, "dummy"); + + // Capture the job details as the APM inspects the file. + JobDetails jobDetails; + auto connection = QObject::connect(m_assetProcessorManager.get(), &AssetProcessorManager::AssetToProcess, [&jobDetails](JobDetails job) + { + jobDetails = job; + }); + + m_assetProcessorManager->AssessAddedFile(tempPath.absoluteFilePath(metadataFile)); + + ASSERT_TRUE(BlockUntilIdle(5000)); + ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName); +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h index 94ba8e864f..3443a4c519 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h @@ -179,6 +179,13 @@ struct ModtimeScanningTest AZStd::unique_ptr m_data; }; + +struct MetadataFileTest + : public AssetProcessorManagerTest +{ + void SetUp() override; +}; + struct FingerprintTest : public AssetProcessorManagerTest { diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 5654875b30..6f07901e27 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -21,6 +21,7 @@ class UnitTestPlatformConfiguration : public AssetProcessor::PlatformConfigurati { friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_GemHandling); friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_MetaFileTypes); + friend class GTEST_TEST_CLASS_NAME_(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtensions); protected: }; @@ -665,3 +666,24 @@ TEST_F(PlatformConfigurationUnitTests, PlatformConfigFile_IsPresent_Found) ASSERT_TRUE(config.AddPlatformConfigFilePaths(platformConfigList)); ASSERT_EQ(platformConfigList.size(), 1); } + +TEST_F(PlatformConfigurationUnitTests, Test_MetaFileTypes_AssetImporterExtensions) +{ + using namespace AssetProcessor; + + const auto testExeFolder = AZ::IO::FileIOBase::GetInstance()->ResolvePath(TestAppRoot); + auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_metadata"); + ASSERT_TRUE(configRoot); + UnitTestPlatformConfiguration config; + m_absorber.Clear(); + ASSERT_FALSE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false)); + ASSERT_GT(m_absorber.m_numErrorsAbsorbed, 0); + ASSERT_TRUE(config.MetaDataFileTypesCount() == 2); + + QStringList entriesToTest{ "aaa", "bbb" }; + for (int idx = 0; idx < entriesToTest.size(); idx++) + { + ASSERT_EQ(config.GetMetaDataFileTypeAt(idx).first, QString("%1.assetinfo").arg(entriesToTest[idx])); + ASSERT_EQ(config.GetMetaDataFileTypeAt(idx).second, QString("%1").arg(entriesToTest[idx])); + } +} diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp index 14ded4f98e..782e145ad6 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace { @@ -23,6 +24,21 @@ namespace namespace AssetProcessor { + + void AssetImporterPathsVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, + AZStd::string_view value) + { + auto found = value.find('.'); + if (found != AZStd::string::npos) + { + m_supportedFileExtensions.emplace_back(value.substr(found + 1)); + } + else + { + m_supportedFileExtensions.emplace_back(value); + } + } + struct PlatformsInfoVisitor : AZ::SettingsRegistryInterface::Visitor { @@ -1126,6 +1142,17 @@ namespace AssetProcessor MetaDataTypesVisitor visitor; settingsRegistry->Visit(visitor, AZ::SettingsRegistryInterface::FixedValueString(AssetProcessorSettingsKey) + "/MetaDataTypes"); + + using namespace AzToolsFramework::AssetUtils; + AZStd::vector supportedFileExtensions; + AssetImporterPathsVisitor assetImporterVisitor{ settingsRegistry, supportedFileExtensions }; + settingsRegistry->Visit(assetImporterVisitor, AZ::SettingsRegistryInterface::FixedValueString(AssetImporterSettingsKey) + "/" + AssetImporterSupportedFileTypeKey); + + for (auto& entry : assetImporterVisitor.m_supportedFileExtensions) + { + visitor.m_metaDataTypes.push_back({ AZStd::string::format("%s.assetinfo", entry.c_str()), entry }); + } + for (const auto& metaDataType : visitor.m_metaDataTypes) { QString fileType = AssetUtilities::NormalizeFilePath(QString::fromUtf8(metaDataType.m_fileType.c_str(), diff --git a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h index e70633202a..2c3615c4fc 100644 --- a/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h +++ b/Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h @@ -40,6 +40,21 @@ namespace AssetProcessor extern const char AssetConfigPlatformDir[]; extern const char AssetProcessorPlatformConfigFileName[]; + struct AssetImporterPathsVisitor + : AZ::SettingsRegistryInterface::Visitor + { + AssetImporterPathsVisitor(AZ::SettingsRegistryInterface* settingsRegistry, AZStd::vector& supportedExtension) + : m_settingsRegistry(settingsRegistry) + , m_supportedFileExtensions(supportedExtension) + { + } + + void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override; + + AZ::SettingsRegistryInterface* m_settingsRegistry; + AZStd::vector m_supportedFileExtensions; + }; + //! Information for a given recognizer, on a specific platform //! essentially a plain data holder, but with helper funcs class AssetPlatformSpec diff --git a/Code/Tools/AssetProcessor/testdata/config_metadata/AssetProcessorPlatformConfig.setreg b/Code/Tools/AssetProcessor/testdata/config_metadata/AssetProcessorPlatformConfig.setreg new file mode 100644 index 0000000000..9623b6268a --- /dev/null +++ b/Code/Tools/AssetProcessor/testdata/config_metadata/AssetProcessorPlatformConfig.setreg @@ -0,0 +1,16 @@ +{ + "O3DE": + { + "SceneAPI": + { + "AssetImporter": + { + "SupportedFileTypeExtensions": + [ + ".aaa", + ".bbb" + ] + } + } + } +} \ No newline at end of file diff --git a/Code/Tools/SceneAPI/SceneBuilder/SceneImportRequestHandler.cpp b/Code/Tools/SceneAPI/SceneBuilder/SceneImportRequestHandler.cpp index 65c2585baa..637fb47275 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/SceneImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/SceneImportRequestHandler.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,19 +24,21 @@ namespace AZ { void SceneImporterSettings::Reflect(AZ::ReflectContext* context) { + using namespace AzToolsFramework::AssetUtils; if (auto serializeContext = azrtti_cast(context); serializeContext) { serializeContext->Class() ->Version(2) - ->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions); + ->Field(AssetImporterSupportedFileTypeKey, &SceneImporterSettings::m_supportedFileTypeExtensions); } } void SceneImportRequestHandler::Activate() { + using namespace AzToolsFramework::AssetUtils; if (auto* settingsRegistry = AZ::SettingsRegistry::Get()) { - settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter"); + settingsRegistry->GetObject(m_settings, AssetImporterSettingsKey); } BusConnect();