diff --git a/AutomatedTesting/surfacetypemateriallibrary.physmaterial b/AutomatedTesting/surfacetypemateriallibrary.physmaterial index 434d673998..481cd2fbfa 100644 --- a/AutomatedTesting/surfacetypemateriallibrary.physmaterial +++ b/AutomatedTesting/surfacetypemateriallibrary.physmaterial @@ -4,17 +4,152 @@ - - - - - - - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index 1010ae3473..7b33359023 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -77,6 +77,27 @@ #endif // defined(AZ_ENABLE_DEBUG_TOOLS) #include +#include + +static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments) +{ + if (arguments.empty()) + { + return; + } + + const auto entityIdStr = AZStd::string(arguments.front()); + const auto entityIdValue = AZStd::stoull(entityIdStr); + + AZStd::string entityName; + AZ::ComponentApplicationBus::BroadcastResult( + entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, AZ::EntityId(entityIdValue)); + + AZ_Printf("Entity Debug", "EntityId: %" PRIu64 ", Entity Name: %s", entityIdValue, entityName.c_str()); +} + +AZ_CONSOLEFREEFUNC( + PrintEntityName, AZ::ConsoleFunctorFlags::Null, "Parameter: EntityId value, Prints the name of the entity to the console"); namespace AZ { diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index d5f02c957c..559f7ce460 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -29,7 +29,7 @@ namespace AzFramework AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); - AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 6.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); @@ -37,7 +37,6 @@ namespace AzFramework AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, ""); - AZ_CVAR(float, ed_cameraSystemLookDeadzone, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); AZ_CVAR( AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, ""); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index e4507227b5..d7de634c11 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -303,6 +303,45 @@ namespace AzToolsFramework return true; } + bool PrefabLoader::SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) + { + AZ_Assert(absolutePath.IsAbsolute(), "SaveTemplateToFile requires an absolute path for saving the initial prefab file."); + + const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId); + if (!domAndFilepath) + { + return false; + } + + // Verify that the absolute path provided to this matches the relative path saved in the template. + // Otherwise, the saved prefab won't be able to be loaded. + auto relativePath = GenerateRelativePath(absolutePath); + if (relativePath != domAndFilepath->second) + { + AZ_Error( + "Prefab", false, + "PrefabLoader::SaveTemplateToFile - " + "Failed to save template '%s' to location '%.*s'." + "Error: Relative path '%.*s' for location didn't match template name.", + domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), AZ_STRING_ARG(relativePath.Native())); + return false; + } + + auto outcome = AzFramework::FileFunc::WriteJsonFile(domAndFilepath->first, absolutePath); + if (!outcome.IsSuccess()) + { + AZ_Error( + "Prefab", false, + "PrefabLoader::SaveTemplateToFile - " + "Failed to save template '%s' to location '%.*s'." + "Error: %s", + domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), outcome.GetError().c_str()); + return false; + } + m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, false); + return true; + } + bool PrefabLoader::SaveTemplateToString(TemplateId templateId, AZStd::string& output) { const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h index aed24e153e..3722e14a97 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h @@ -72,6 +72,16 @@ namespace AzToolsFramework */ bool SaveTemplate(TemplateId templateId) override; + /** + * Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @param absolutePath Absolute path to save the file to + * @return bool on whether the operation succeeded or not + */ + bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) override; + /** * Saves a Prefab Template into the provided output string. * Converts Prefab Template form into .prefab form by collapsing nested Template info diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h index d71fbff80f..0e551cee6b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h @@ -60,6 +60,16 @@ namespace AzToolsFramework */ virtual bool SaveTemplate(TemplateId templateId) = 0; + /** + * Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template. + * Converts Prefab Template form into .prefab form by collapsing nested Template info + * into a source path and patches. + * @param templateId Id of the template to be saved + * @param absolutePath Absolute path to save the file to + * @return bool on whether the operation succeeded or not + */ + virtual bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) = 0; + /** * Saves a Prefab Template into the provided output string. * Converts Prefab Template form into .prefab form by collapsing nested Template info diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 9dd5199ea2..fadcc1b81f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -64,7 +64,7 @@ namespace AzToolsFramework m_prefabUndoCache.Destroy(); } - PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) + PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) { EntityList inputEntityList, topLevelEntities; AZ::EntityId commonRootEntityId; @@ -76,6 +76,8 @@ namespace AzToolsFramework return findCommonRootOutcome; } + AZ_Assert(absolutePath.IsAbsolute(), "CreatePrefab requires an absolute path for saving the initial prefab file."); + InstanceOptionalReference instanceToCreate; { // Initialize Undo Batch object @@ -144,7 +146,8 @@ namespace AzToolsFramework // Create the Prefab instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab( - entities, AZStd::move(instancePtrs), filePath, commonRootEntityOwningInstance); + entities, AZStd::move(instancePtrs), m_prefabLoaderInterface->GenerateRelativePath(absolutePath), + commonRootEntityOwningInstance); if (!instanceToCreate) { @@ -254,7 +257,7 @@ namespace AzToolsFramework } // Save Template to file - m_prefabLoaderInterface->SaveTemplate(instanceToCreate->get().GetTemplateId()); + m_prefabLoaderInterface->SaveTemplateToFile(instanceToCreate->get().GetTemplateId(), absolutePath); return AZ::Success(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 7e2357dd44..e68a3e0b1e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -46,7 +46,7 @@ namespace AzToolsFramework void UnregisterPrefabPublicHandlerInterface(); // PrefabPublicInterface... - PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) override; + PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) override; PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override; PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 0750c4d264..2e9152fd1b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -46,10 +46,10 @@ namespace AzToolsFramework * Create a prefab out of the entities provided, at the path provided. * Automatically detects descendants of entities, and discerns between entities and child instances. * @param entityIds The entities that should form the new prefab (along with their descendants). - * @param filePath The path for the new prefab file. + * @param filePath The absolute path for the new prefab file. * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. */ - virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView filePath) = 0; + virtual PrefabOperationResult CreatePrefab(const AZStd::vector& entityIds, AZ::IO::PathView absolutePath) = 0; /** * Instantiate a prefab from a prefab file. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 61d4433c0e..021b97a7dd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -333,8 +333,7 @@ namespace AzToolsFramework } } - auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab( - selectedEntities, s_prefabLoaderInterface->GenerateRelativePath(prefabFilePath.data())); + auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath.data()); if (!createPrefabOutcome.IsSuccess()) { diff --git a/Code/Tools/Android/ProjectBuilder/build.gradle.in b/Code/Tools/Android/ProjectBuilder/build.gradle.in index 66f58294ab..5980984516 100644 --- a/Code/Tools/Android/ProjectBuilder/build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/build.gradle.in @@ -15,14 +15,14 @@ android { ${SIGNING_CONFIGS} compileSdkVersion sdkVer buildToolsVersion buildToolsVer - + ndkVersion ndkPlatformVer lintOptions { abortOnError false checkReleaseBuilds false } defaultConfig { - minSdkVersion ndkPlatformVer + minSdkVersion minSdkVer targetSdkVersion sdkVer ${NATIVE_CMAKE_SECTION_DEFAULT_CONFIG} } diff --git a/Code/Tools/Android/ProjectBuilder/local.properties.in b/Code/Tools/Android/ProjectBuilder/local.properties.in index 559ea67bcb..4e82cb2940 100644 --- a/Code/Tools/Android/ProjectBuilder/local.properties.in +++ b/Code/Tools/Android/ProjectBuilder/local.properties.in @@ -16,6 +16,5 @@ # For customization when using a Version Control System, please read the # header note. # ${GENERATION_TIMESTAMP} -ndk.dir=${ANDROID_NDK_PATH} sdk.dir=${ANDROID_SDK_PATH} ${CMAKE_DIR_LINE} diff --git a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in index 782a1f26b5..dfce99c3c7 100644 --- a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in @@ -12,10 +12,9 @@ buildscript { repositories { google() jcenter() - } dependencies { - classpath 'com.android.tools.build:gradle:3.6.4' + classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -26,14 +25,14 @@ allprojects { repositories { google() jcenter() - } } subprojects { ext { + minSdkVer = ${MIN_SDK_VER} sdkVer = ${SDK_VER} - ndkPlatformVer = ${NDK_PLATFORM_VER} + ndkPlatformVer = '${NDK_VERSION}' buildToolsVer = '${SDK_BUILD_TOOL_VER}' lyEngineRoot = '${LY_ENGINE_ROOT}' } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index 3696188514..79c4ca3cc3 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -13,11 +13,6 @@ "displayName": "Metallic", "description": "Properties for configuring whether the surface is metallic or not." }, - { - "id": "anisotropy", - "displayName": "Anisotropic Material Response", - "description": "How much is this material response anisotropic." - }, { "id": "roughness", "displayName": "Roughness", @@ -28,46 +23,11 @@ "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, - { - "id": "clearCoat", - "displayName": "Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "opacity", - "displayName": "Opacity", - "description": "Properties for configuring the materials transparency." - }, - { - "id": "uv", - "displayName": "UVs", - "description": "Properties for configuring UV transforms." - }, - { - "id": "occlusion", - "displayName": "Occlusion", - "description": "Properties for baked textures that represent geometric occlusion of light." - }, - { - "id": "emissive", - "displayName": "Emissive", - "description": "Properties to add light emission, independent of other lights in the scene." - }, - { - "id": "parallax", - "displayName": "Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." - }, - { - "id": "subsurfaceScattering", - "displayName": "Subsurface Scattering", - "description": "Properties for configuring subsurface scattering effects." - }, { "id": "detailLayerGroup", "displayName": "Detail Layer", @@ -78,6 +38,46 @@ "displayName": "Detail Layer UV", "description": "Properties for modifying detail layer UV." }, + { + "id": "anisotropy", + "displayName": "Anisotropic Material Response", + "description": "How much is this material response anisotropic." + }, + { + "id": "occlusion", + "displayName": "Occlusion", + "description": "Properties for baked textures that represent geometric occlusion of light." + }, + { + "id": "emissive", + "displayName": "Emissive", + "description": "Properties to add light emission, independent of other lights in the scene." + }, + { + "id": "subsurfaceScattering", + "displayName": "Subsurface Scattering", + "description": "Properties for configuring subsurface scattering effects." + }, + { + "id": "clearCoat", + "displayName": "Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, + { + "id": "parallax", + "displayName": "Displacement", + "description": "Properties for parallax effect produced by a height map." + }, + { + "id": "opacity", + "displayName": "Opacity", + "description": "Properties for configuring the materials transparency." + }, + { + "id": "uv", + "displayName": "UVs", + "description": "Properties for configuring UV transforms." + }, { // Note: this property group is used in the DiffuseGlobalIllumination pass and not by the main forward shader "id": "irradiance", @@ -86,7 +86,7 @@ }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -197,7 +197,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -208,14 +208,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -227,7 +227,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -253,7 +253,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -264,14 +264,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -284,8 +284,8 @@ "roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -295,14 +295,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -315,7 +315,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -329,7 +329,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -409,8 +409,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -420,14 +420,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -472,7 +472,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -482,14 +482,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -514,7 +514,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -524,14 +524,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -573,7 +573,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -586,8 +586,8 @@ "normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -597,14 +597,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -653,7 +653,7 @@ { "id": "mode", "displayName": "Opacity Mode", - "description": "Opacity mode for this texture.", + "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", "enumValues": [ "Opaque", "Cutout", "Blended", "TintedTransparent" ], "defaultValue": "Opaque", @@ -665,7 +665,7 @@ { "id": "alphaSource", "displayName": "Alpha Source", - "description": "Source texture of alpha value.", + "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", "enumValues": [ "Packed", "Split", "None" ], "defaultValue": "Packed", @@ -676,8 +676,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface opacity.", + "displayName": "Texture", + "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", @@ -687,7 +687,7 @@ { "id": "textureMapUv", "displayName": "UV", - "description": "Opacity texture map UV set", + "description": "Opacity map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -782,7 +782,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -792,14 +792,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -824,7 +824,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -834,14 +834,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -904,8 +904,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -915,14 +915,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -935,8 +935,8 @@ "parallax": [ { "id": "textureMap", - "displayName": "Heightmap", - "description": "Displacement heightmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", @@ -946,14 +946,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the heightmap.", + "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Heightmap UV set", + "description": "Height map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -964,8 +964,8 @@ }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Height Map Scale", + "description": "The total height of the height map in local model units.", "type": "Float", "defaultValue": 0.05, "min": 0.0, @@ -1026,7 +1026,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -1063,7 +1063,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", + "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", @@ -1073,7 +1073,7 @@ { "id": "useInfluenceMap", "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", + "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, @@ -1142,7 +1142,7 @@ { "id": "thicknessMap", "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", + "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", @@ -1171,7 +1171,7 @@ { "id": "transmissionTint", "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", + "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, @@ -1246,7 +1246,7 @@ { "id": "enableDetailMaskTexture", "displayName": " Use Texture", - "description": "Enable detail mask texture", + "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, @@ -1265,7 +1265,7 @@ { "id": "textureMapUv", "displayName": "Detail Map UVs", - "description": "Which UV set to use for detail map texture sampling", + "description": "Which UV set to use for detail map sampling", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1283,8 +1283,8 @@ }, { "id": "baseColorDetailMap", - "displayName": " Texture Map", - "description": "Detailed Base Color Texture map", + "displayName": " Texture", + "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -1307,7 +1307,7 @@ { "id": "enableNormals", "displayName": "Enable Normal", - "description": "Enable detail normal texture to be used for fine detail normal such as scratches and small dents", + "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, @@ -1326,8 +1326,8 @@ }, { "id": "normalDetailMap", - "displayName": " Texture Map", - "description": "Detailed Normal Texture map", + "displayName": " Texture", + "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index f8c49d579c..9ead0376bb 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -23,6 +23,16 @@ "displayName": "Normal", "description": "Properties related to configuring surface normal." }, + { + "id": "detailLayerGroup", + "displayName": "Detail Layer", + "description": "Properties for Fine Details Layer." + }, + { + "id": "detailUV", + "displayName": "Detail Layer UV", + "description": "Properties for modifying detail layer UV." + }, { "id": "occlusion", "displayName": "Occlusion", @@ -38,19 +48,9 @@ "displayName": "Wrinkle Layers", "description": "Properties for wrinkle maps to support morph animation, using vertex color blend weights." }, - { - "id": "detailLayerGroup", - "displayName": "Detail Layer", - "description": "Properties for Fine Details Layer." - }, - { - "id": "detailUV", - "displayName": "Detail Layer UV", - "description": "Properties for modifying detail layer UV." - }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -150,7 +150,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -161,14 +161,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -180,7 +180,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -193,8 +193,8 @@ "roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -204,14 +204,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -224,7 +224,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -238,7 +238,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -279,8 +279,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -290,14 +290,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -321,8 +321,8 @@ "normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -332,14 +332,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -388,7 +388,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -398,14 +398,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -430,7 +430,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -440,14 +440,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -498,7 +498,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", + "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", @@ -508,7 +508,7 @@ { "id": "useInfluenceMap", "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", + "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, @@ -577,7 +577,7 @@ { "id": "thicknessMap", "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", + "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", @@ -606,7 +606,7 @@ { "id": "transmissionTint", "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", + "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, @@ -792,7 +792,7 @@ { "id": "enableDetailMaskTexture", "displayName": " Use Texture", - "description": "Enable detail mask texture", + "description": "Enable detail blend mask", "type": "Bool", "defaultValue": true }, @@ -811,7 +811,7 @@ { "id": "textureMapUv", "displayName": "Detail Map UVs", - "description": "Which UV set to use for detail map texture sampling", + "description": "Which UV set to use for detail map sampling", "type": "Enum", "enumIsUv": true, "defaultValue": "Unwrapped", @@ -829,8 +829,8 @@ }, { "id": "baseColorDetailMap", - "displayName": " Texture Map", - "description": "Detailed Base Color Texture map", + "displayName": " Texture", + "description": "Detailed Base Color Texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -853,7 +853,7 @@ { "id": "enableNormals", "displayName": "Enable Normal", - "description": "Enable detail normal texture to be used for fine detail normal such as scratches and small dents", + "description": "Enable detail normal map to be used for fine detail normal such as scratches and small dents", "type": "Bool", "defaultValue": false }, @@ -872,8 +872,8 @@ }, { "id": "normalDetailMap", - "displayName": " Texture Map", - "description": "Detailed Normal Texture map", + "displayName": " Texture", + "description": "Detailed Normal map", "type": "Image", "connection": { "type": "ShaderInput", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index ca6cb77b0a..c07eac3d47 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -26,7 +26,7 @@ }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." }, //############################################################################################## @@ -57,11 +57,6 @@ "displayName": "Layer 1: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer1_clearCoat", - "displayName": "Layer 1: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer1_occlusion", "displayName": "Layer 1: Occlusion", @@ -72,6 +67,11 @@ "displayName": "Layer 1: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer1_clearCoat", + "displayName": "Layer 1: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer1_parallax", "displayName": "Layer 1: Displacement", @@ -110,11 +110,6 @@ "displayName": "Layer 2: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer2_clearCoat", - "displayName": "Layer 2: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer2_occlusion", "displayName": "Layer 2: Occlusion", @@ -125,6 +120,11 @@ "displayName": "Layer 2: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer2_clearCoat", + "displayName": "Layer 2: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer2_parallax", "displayName": "Layer 2: Displacement", @@ -163,11 +163,6 @@ "displayName": "Layer 3: Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "layer3_clearCoat", - "displayName": "Layer 3: Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "layer3_occlusion", "displayName": "Layer 3: Occlusion", @@ -178,6 +173,11 @@ "displayName": "Layer 3: Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, + { + "id": "layer3_clearCoat", + "displayName": "Layer 3: Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, { "id": "layer3_parallax", "displayName": "Layer 3: Displacement", @@ -428,7 +428,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -550,7 +550,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -561,14 +561,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -580,7 +580,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -606,7 +606,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -617,14 +617,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -637,8 +637,8 @@ "layer1_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -648,14 +648,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -668,7 +668,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -682,7 +682,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -723,8 +723,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -734,14 +734,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -754,8 +754,8 @@ "layer1_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -765,14 +765,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -841,7 +841,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -851,14 +851,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -883,7 +883,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -893,14 +893,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -942,7 +942,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -956,7 +956,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -966,14 +966,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -998,7 +998,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1008,14 +1008,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1078,8 +1078,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1089,14 +1089,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1109,8 +1109,8 @@ "layer1_parallax": [ { "id": "textureMap", - "displayName": "Heightmap", - "description": "Displacement heightmap, which can be used for layer blending and/or a parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1120,14 +1120,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the heightmap.", + "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { "id": "factor", "displayName": "Scale", - "description": "The total height of the heightmap in local model units.", + "description": "The total height of the height map in local model units.", "type": "Float", "defaultValue": 0.05, "min": 0.0, @@ -1245,7 +1245,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -1256,14 +1256,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1275,7 +1275,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -1301,7 +1301,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -1312,14 +1312,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1332,8 +1332,8 @@ "layer2_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1343,14 +1343,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1363,7 +1363,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -1377,7 +1377,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -1418,8 +1418,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1429,14 +1429,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1449,8 +1449,8 @@ "layer2_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1460,14 +1460,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1536,7 +1536,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -1546,14 +1546,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1578,7 +1578,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -1588,14 +1588,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1637,7 +1637,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1651,7 +1651,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1661,14 +1661,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1693,7 +1693,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1703,14 +1703,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1773,8 +1773,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1784,14 +1784,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1804,8 +1804,8 @@ "layer2_parallax": [ { "id": "textureMap", - "displayName": "Heightmap", - "description": "Displacement heightmap, which can be used for layer blending and/or a parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", @@ -1815,14 +1815,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the heightmap.", + "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { "id": "factor", "displayName": "Scale", - "description": "The total height of the heightmap in local model units.", + "description": "The total height of the height map in local model units.", "type": "Float", "defaultValue": 0.05, "min": 0.0, @@ -1940,7 +1940,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -1951,14 +1951,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -1970,7 +1970,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -1996,7 +1996,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -2007,14 +2007,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2027,8 +2027,8 @@ "layer3_roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2038,14 +2038,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2058,7 +2058,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -2072,7 +2072,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -2113,8 +2113,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2124,14 +2124,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2144,8 +2144,8 @@ "layer3_normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2155,14 +2155,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2231,7 +2231,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -2241,14 +2241,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2273,7 +2273,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -2283,14 +2283,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2332,7 +2332,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2346,7 +2346,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2356,14 +2356,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2388,7 +2388,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2398,14 +2398,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2468,8 +2468,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2479,14 +2479,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -2499,8 +2499,8 @@ "layer3_parallax": [ { "id": "textureMap", - "displayName": "Heightmap", - "description": "Displacement heightmap, which can be used for layer blending and/or a parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map, which can be used for layer blending and/or a parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", @@ -2510,14 +2510,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the heightmap.", + "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { "id": "factor", "displayName": "Scale", - "description": "The total height of the heightmap in local model units.", + "description": "The total height of the height map in local model units.", "type": "Float", "defaultValue": 0.05, "min": 0.0, diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index 183cddd4cb..658aaeeee9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -23,26 +23,11 @@ "displayName": "Specular Reflectance f0", "description": "The constant f0 represents the specular reflectance at normal incidence (Fresnel 0 Angle). Used to adjust reflectance of non-metal surfaces." }, - { - "id": "clearCoat", - "displayName": "Clear Coat", - "description": "Properties for configuring gloss clear coat" - }, { "id": "normal", "displayName": "Normal", "description": "Properties related to configuring surface normal." }, - { - "id": "opacity", - "displayName": "Opacity", - "description": "Properties for configuring the materials transparency." - }, - { - "id": "uv", - "displayName": "UVs", - "description": "Properties for configuring UV transforms." - }, { "id": "occlusion", "displayName": "Occlusion", @@ -53,16 +38,31 @@ "displayName": "Emissive", "description": "Properties to add light emission, independent of other lights in the scene." }, - { - "id": "parallax", - "displayName": "Parallax Mapping", - "description": "Properties for parallax effect produced by depthmap." - }, { "id": "subsurfaceScattering", "displayName": "Subsurface Scattering", "description": "Properties for configuring subsurface scattering effects." }, + { + "id": "clearCoat", + "displayName": "Clear Coat", + "description": "Properties for configuring gloss clear coat" + }, + { + "id": "parallax", + "displayName": "Displacement", + "description": "Properties for parallax effect produced by a height map." + }, + { + "id": "opacity", + "displayName": "Opacity", + "description": "Properties for configuring the materials transparency." + }, + { + "id": "uv", + "displayName": "UVs", + "description": "Properties for configuring UV transforms." + }, { // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader "id": "irradiance", @@ -71,7 +71,7 @@ }, { "id": "general", - "displayName": "General", + "displayName": "General Settings", "description": "General settings." } ], @@ -182,7 +182,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "Base color texture map", "type": "Image", "connection": { @@ -193,14 +193,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Base color texture map UV set", + "description": "Base color map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -212,7 +212,7 @@ { "id": "textureBlendMode", "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture Map.", + "description": "Selects the equation to use when combining Color, Factor, and Texture.", "type": "Enum", "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], "defaultValue": "Multiply", @@ -238,7 +238,7 @@ }, { "id": "textureMap", - "displayName": "Texture Map", + "displayName": "Texture", "description": "", "type": "Image", "connection": { @@ -249,14 +249,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Metallic texture map UV set", + "description": "Metallic map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -269,8 +269,8 @@ "roughness": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface roughness.", + "displayName": "Texture", + "description": "Texture for defining surface roughness.", "type": "Image", "connection": { "type": "ShaderInput", @@ -280,14 +280,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -300,7 +300,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "lowerBound", "displayName": "Lower Bound", - "description": "The roughness value that corresponds to black in the texture map.", + "description": "The roughness value that corresponds to black in the texture.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -314,7 +314,7 @@ // Note that "factor" is mutually exclusive with "lowerBound"/"upperBound". These are swapped by a lua functor. "id": "upperBound", "displayName": "Upper Bound", - "description": "The roughness value that corresponds to white in the texture map.", + "description": "The roughness value that corresponds to white in the texture.", "type": "Float", "defaultValue": 1.0, "min": 0.0, @@ -355,8 +355,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface reflectance.", + "displayName": "Texture", + "description": "Texture for defining surface reflectance.", "type": "Image", "connection": { "type": "ShaderInput", @@ -366,14 +366,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Specular reflection texture map UV set", + "description": "Specular reflection map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -418,7 +418,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Strength factor texture map", + "description": "Strength factor texture", "type": "Image", "connection": { "type": "ShaderInput", @@ -428,14 +428,14 @@ { "id": "useInfluenceMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the Factor value.", + "description": "Whether to use the texture, or just default to the Factor value.", "type": "Bool", "defaultValue": true }, { "id": "influenceMapUv", "displayName": " UV", - "description": "Strength factor texture map UV set", + "description": "Strength factor map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -460,7 +460,7 @@ { "id": "roughnessMap", "displayName": " Roughness Map", - "description": "Roughness texture map", + "description": "Texture for defining surface roughness", "type": "Image", "connection": { "type": "ShaderInput", @@ -470,14 +470,14 @@ { "id": "useRoughnessMap", "displayName": " Use Texture", - "description": "Whether to use the texture map, or just default to the roughness value.", + "description": "Whether to use the texture, or just default to the roughness value.", "type": "Bool", "defaultValue": true }, { "id": "roughnessMapUv", "displayName": " UV", - "description": "Roughness texture map UV set", + "description": "Roughness map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -519,7 +519,7 @@ { "id": "normalMapUv", "displayName": " UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -532,8 +532,8 @@ "normal": [ { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface normal direction.", + "displayName": "Texture", + "description": "Texture for defining surface normal direction.", "type": "Image", "connection": { "type": "ShaderInput", @@ -543,14 +543,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map, or just rely on vertex normals.", + "description": "Whether to use the texture, or just rely on vertex normals.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Normal texture map UV set", + "description": "Normal map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -599,7 +599,7 @@ { "id": "mode", "displayName": "Opacity Mode", - "description": "Opacity mode for this texture.", + "description": "Indicates the general approach how transparency is to be applied.", "type": "Enum", "enumValues": [ "Opaque", "Cutout", "Blended", "TintedTransparent" ], "defaultValue": "Opaque", @@ -611,7 +611,7 @@ { "id": "alphaSource", "displayName": "Alpha Source", - "description": "Source texture of alpha value.", + "description": "Indicates whether to get the opacity texture from the Base Color map (Packed) or from a separate greyscale texture (Split).", "type": "Enum", "enumValues": [ "Packed", "Split", "None" ], "defaultValue": "Packed", @@ -622,8 +622,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface opacity.", + "displayName": "Texture", + "description": "Texture for defining surface opacity.", "type": "Image", "connection": { "type": "ShaderInput", @@ -633,7 +633,7 @@ { "id": "textureMapUv", "displayName": "UV", - "description": "Opacity texture map UV set", + "description": "Opacity map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -728,7 +728,7 @@ { "id": "diffuseTextureMap", "displayName": "Diffuse AO", - "description": "Texture map for defining occlusion area for diffuse ambient lighting.", + "description": "Texture for defining occlusion area for diffuse ambient lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -738,14 +738,14 @@ { "id": "diffuseUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Diffuse AO texture map.", + "description": "Whether to use the Diffuse AO map.", "type": "Bool", "defaultValue": true }, { "id": "diffuseTextureMapUv", "displayName": " UV", - "description": "Diffuse AO texture map UV set.", + "description": "Diffuse AO map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -770,7 +770,7 @@ { "id": "specularTextureMap", "displayName": "Specular Cavity", - "description": "Texture map for defining occlusion area for specular lighting.", + "description": "Texture for defining occlusion area for specular lighting.", "type": "Image", "connection": { "type": "ShaderInput", @@ -780,14 +780,14 @@ { "id": "specularUseTexture", "displayName": " Use Texture", - "description": "Whether to use the Specular Cavity texture map.", + "description": "Whether to use the Specular Cavity map.", "type": "Bool", "defaultValue": true }, { "id": "specularTextureMapUv", "displayName": " UV", - "description": "Specular Cavity texture map UV set.", + "description": "Specular Cavity map UV set.", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -850,8 +850,8 @@ }, { "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining emissive area.", + "displayName": "Texture", + "description": "Texture for defining emissive area.", "type": "Image", "connection": { "type": "ShaderInput", @@ -861,14 +861,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the texture map.", + "description": "Whether to use the texture.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Emissive texture map UV set", + "description": "Emissive map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -881,8 +881,8 @@ "parallax": [ { "id": "textureMap", - "displayName": "Heightmap", - "description": "Displacement heightmap to create parallax effect.", + "displayName": "Height Map", + "description": "Displacement height map to create parallax effect.", "type": "Image", "connection": { "type": "ShaderInput", @@ -892,14 +892,14 @@ { "id": "useTexture", "displayName": "Use Texture", - "description": "Whether to use the heightmap.", + "description": "Whether to use the height map.", "type": "Bool", "defaultValue": true }, { "id": "textureMapUv", "displayName": "UV", - "description": "Heightmap UV set", + "description": "Height map UV set", "type": "Enum", "enumIsUv": true, "defaultValue": "Tiled", @@ -910,8 +910,8 @@ }, { "id": "factor", - "displayName": "Heightmap Scale", - "description": "The total height of the heightmap in local model units.", + "displayName": "Height Map Scale", + "description": "The total height of the height map in local model units.", "type": "Float", "defaultValue": 0.05, "min": 0.0, @@ -972,7 +972,7 @@ { "id": "showClipping", "displayName": "Show Clipping", - "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "description": "Highlight areas where the height map is clipped by the mesh surface.", "type": "Bool", "defaultValue": false, "connection": { @@ -1009,7 +1009,7 @@ { "id": "influenceMap", "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", + "description": "Texture for controlling the strength of subsurface scattering", "type": "Image", "connection": { "type": "ShaderInput", @@ -1019,7 +1019,7 @@ { "id": "useInfluenceMap", "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", + "description": "Whether to use the influence map.", "type": "Bool", "defaultValue": true }, @@ -1088,7 +1088,7 @@ { "id": "thicknessMap", "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", + "description": "Texture for controlling per pixel thickness", "type": "Image", "connection": { "type": "ShaderInput", @@ -1117,7 +1117,7 @@ { "id": "transmissionTint", "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", + "description": "Color of the volume light traveling through", "type": "Color", "defaultValue": [ 1.0, 0.8, 0.6 ] }, diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli new file mode 100644 index 0000000000..40faba24f9 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialSrg.azsli @@ -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. +* +*/ + +#include + +ShaderResourceGroup RayTracingMaterialSrg : SRG_RayTracingMaterial +{ + Sampler LinearSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + MaxAnisotropy = 16; + }; + + // material info structured buffer + struct MaterialInfo + { + float4 m_baseColor; + float m_metallicFactor; + float m_roughnessFactor; + uint m_textureFlags; + uint m_textureStartIndex; + }; + + // hit shaders can retrieve the MaterialInfo for a mesh hit using: RayTracingMaterialSrg::m_materialInfo[InstanceIndex()] + StructuredBuffer m_materialInfo; + + // texture flag bits indicating if optional textures are present + #define TEXTURE_FLAG_BASECOLOR 1 + #define TEXTURE_FLAG_NORMAL 2 + #define TEXTURE_FLAG_METALLIC 4 + #define TEXTURE_FLAG_ROUGHNESS 8 + + // unbounded array of Material textures + Texture2D m_materialTextures[]; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli new file mode 100644 index 0000000000..d6dd77f4fa --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingMaterialUtils.azsli @@ -0,0 +1,69 @@ +/* +* 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. +* +*/ + +struct TextureData +{ + float4 m_baseColor; + float3 m_normal; + float m_metallic; + float m_roughness; +}; + +TextureData GetHitTextureData(RayTracingMaterialSrg::MaterialInfo materialInfo, float2 uv) +{ + TextureData textureData = (TextureData)0; + + uint textureIndex = materialInfo.m_textureStartIndex; + + // base color + if (materialInfo.m_textureFlags & TEXTURE_FLAG_BASECOLOR) + { + textureData.m_baseColor = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_baseColor = materialInfo.m_baseColor; + } + + // normal + if (materialInfo.m_textureFlags & TEXTURE_FLAG_NORMAL) + { + textureData.m_normal = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_normal = float3(0.0f, 0.0f, 1.0f); + } + + // metallic + if (materialInfo.m_textureFlags & TEXTURE_FLAG_METALLIC) + { + textureData.m_metallic = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_metallic = materialInfo.m_metallicFactor; + } + + // roughness + if (materialInfo.m_textureFlags & TEXTURE_FLAG_ROUGHNESS) + { + textureData.m_roughness = RayTracingMaterialSrg::m_materialTextures[textureIndex++].SampleLevel(RayTracingMaterialSrg::LinearSampler, uv, 0); + } + else + { + textureData.m_roughness = materialInfo.m_roughnessFactor; + } + + return textureData; +} + \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli index fdf7ba92de..b8c97ef421 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli @@ -136,18 +136,35 @@ ShaderResourceGroup RayTracingSceneSrg : SRG_RayTracingScene uint m_indexOffset; uint m_positionOffset; uint m_normalOffset; + uint m_tangentOffset; + uint m_bitangentOffset; + uint m_uvOffset; + float m_padding0[2]; + float4 m_irradianceColor; float3x3 m_worldInvTranspose; + float m_padding1; + + uint m_bufferFlags; + uint m_bufferStartIndex; }; - + + // hit shaders can retrieve the MeshInfo for a mesh hit using: RayTracingSceneSrg::m_meshInfo[InstanceIndex()] StructuredBuffer m_meshInfo; - // unbounded array of Index, VertexPosition, and VertexNormal buffers - // each mesh has three entries in this array starting at its InstanceIndex() * BUFFER_COUNT_PER_MESH - #define BUFFER_COUNT_PER_MESH 3 - #define MESH_INDEX_BUFFER_OFFSET 0 - #define MESH_POSITION_BUFFER_OFFSET 1 - #define MESH_NORMAL_BUFFER_OFFSET 2 - + // buffer array index offsets for buffers that are always present for each mesh + #define MESH_INDEX_BUFFER_OFFSET 0 + #define MESH_POSITION_BUFFER_OFFSET 1 + #define MESH_NORMAL_BUFFER_OFFSET 2 + #define MESH_TANGENT_BUFFER_OFFSET 3 + #define MESH_BITANGENT_BUFFER_OFFSET 4 + + // buffer flag bits indicating if optional buffers are present + #define MESH_BUFFER_FLAG_UV 1 + + // Unbounded array of mesh stream buffers: + // - Index, Position, Normal, Tangent, and Bitangent stream buffers are always present + // - Optional stream buffers such as UV are indicated in the MeshInfo.m_bufferFlags field + // - Buffers for a particular mesh start at MeshInfo.m_bufferStartIndex ByteAddressBuffer m_meshBuffers[]; } \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli new file mode 100644 index 0000000000..f858b9a11a --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/RayTracing/RayTracingSceneUtils.azsli @@ -0,0 +1,126 @@ +/* +* 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. +* +*/ + +// returns the normalized camera view ray into the scene for this raytracing dispatch thread +float3 GetViewRayDirection(float4x4 viewProjectionInverseMatrix) +{ + float2 pixel = ((float2)DispatchRaysIndex().xy + float2(0.5f, 0.5f)) / (float2)DispatchRaysDimensions(); + float2 ndc = pixel * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f); + return normalize(mul(viewProjectionInverseMatrix, float4(ndc, 0.0f, 1.0f)).xyz); +} + +// returns the vertex indices for the primitive hit by the ray +// Note: usable only in a raytracing Hit shader +uint3 GetHitIndices(RayTracingSceneSrg::MeshInfo meshInfo) +{ + // compute the array index of the index buffer for this mesh in the m_meshBuffers unbounded array + uint meshIndexBufferArrayIndex = meshInfo.m_bufferStartIndex + MESH_INDEX_BUFFER_OFFSET; + + // compute the offset into the index buffer for this primitve of the mesh + uint offsetBytes = meshInfo.m_indexOffset + (PrimitiveIndex() * 12); + + // load the indices for this primitive from the index buffer + return RayTracingSceneSrg::m_meshBuffers[meshIndexBufferArrayIndex].Load3(offsetBytes); +} + +// returns the interpolated vertex data for the primitive hit by the ray +// Note: usable only in a raytracing hit shader +struct VertexData +{ + float3 m_position; + float3 m_normal; + float3 m_tangent; + float3 m_bitangent; + float2 m_uv; +}; + +VertexData GetHitInterpolatedVertexData(RayTracingSceneSrg::MeshInfo meshInfo, float2 builtInBarycentrics) +{ + // retrieve the poly indices + uint3 indices = GetHitIndices(meshInfo); + + // compute barycentrics + float3 barycentrics = float3((1.0f - builtInBarycentrics.x - builtInBarycentrics.y), builtInBarycentrics.x, builtInBarycentrics.y); + + // compute the vertex data using barycentric interpolation + VertexData vertexData = (VertexData)0; + for (uint i = 0; i < 3; ++i) + { + // position + { + // array index of the position buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexPositionArrayIndex = meshInfo.m_bufferStartIndex + MESH_POSITION_BUFFER_OFFSET; + + // offset into the position buffer for this vertex + uint positionOffset = meshInfo.m_positionOffset + (indices[i] * 12); + + // load the position data + vertexData.m_position += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexPositionArrayIndex].Load3(positionOffset)) * barycentrics[i]; + } + + // normal + { + // array index of the normal buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexNormalArrayIndex = meshInfo.m_bufferStartIndex + MESH_NORMAL_BUFFER_OFFSET; + + // offset into the normal buffer for this vertex + uint normalOffset = meshInfo.m_normalOffset + (indices[i] * 12); + + // load the normal data + vertexData.m_normal += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexNormalArrayIndex].Load3(normalOffset)) * barycentrics[i]; + } + + // tangent + { + // array index of the tangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexTangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_TANGENT_BUFFER_OFFSET; + + // offset into the tangent buffer for this vertex + uint tangentOffset = meshInfo.m_tangentOffset + (indices[i] * 12); + + // load the tangent data + vertexData.m_tangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexTangentArrayIndex].Load3(tangentOffset)) * barycentrics[i]; + } + + // bitangent + { + // array index of the bitangent buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexBitangentArrayIndex = meshInfo.m_bufferStartIndex + MESH_BITANGENT_BUFFER_OFFSET; + + // offset into the bitangent buffer for this vertex + uint bitangentOffset = meshInfo.m_bitangentOffset + (indices[i] * 12); + + // load the bitangent data + vertexData.m_bitangent += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexBitangentArrayIndex].Load3(bitangentOffset)) * barycentrics[i]; + } + + // optional streams begin after MESH_BITANGENT_BUFFER_OFFSET + uint optionalBufferOffset = MESH_BITANGENT_BUFFER_OFFSET + 1; + + // UV + if (meshInfo.m_bufferFlags & MESH_BUFFER_FLAG_UV) + { + // array index of the UV buffer for this mesh in the m_meshBuffers unbounded array + uint meshVertexUVArrayIndex = meshInfo.m_bufferStartIndex + optionalBufferOffset++; + + // offset into the UV buffer for this vertex + uint uvOffset = meshInfo.m_uvOffset + (indices[i] * 8); + + // load the UV data + vertexData.m_uv += asfloat(RayTracingSceneSrg::m_meshBuffers[meshVertexUVArrayIndex].Load2(uvOffset)) * barycentrics[i]; + } + } + + vertexData.m_normal = normalize(vertexData.m_normal); + + return vertexData; +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli index c134a9d293..6d1b05d797 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/SrgSemantics.azsli @@ -64,3 +64,8 @@ ShaderResourceGroupSemantic SRG_RayTracingScene { FrequencyId = 1; }; + +ShaderResourceGroupSemantic SRG_RayTracingMaterial +{ + FrequencyId = 2; +}; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader index 804b534277..746ae357ee 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant index b3bee56bfd..841c5defe3 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant index d9b2f17848..39eacb4ade 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant index cf39160d3f..597b239218 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracing_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader index c4a83a26dc..0f42cd04d6 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant index fb5b740f04..49bc94767a 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant index b7bdabeb4e..d9d30c99f7 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant index 1eeeffa8be..e2f2a13ca3 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingclosesthit_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg index c9fa0f4e1c..35d7050294 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingcommon_raytracingglobalsrg.azsrg @@ -44,8 +44,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", @@ -3338,8 +3344,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", @@ -6632,8 +6644,14 @@ "field": "element", "typeName": "ShaderResourceGroupLayout", "typeId": "{1F92C651-9B83-4379-AB5C-5201F1B2C278}", - "version": 6, + "version": 7, "Objects": [ + { + "field": "m_name", + "typeName": "Name", + "typeId": "{3D2B920C-9EFD-40D5-AAE0-DF131C3D4931}", + "value": "" + }, { "field": "m_staticSamplers", "typeName": "AZStd::vector", diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader index 4f0cea1bfe..77a0f94f97 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss.azshader differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant index 839efa7278..c144350c7f 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_dx12_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant index 5f08825487..c369381fe9 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_null_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant index a5b0e842ef..13fd579d9b 100644 Binary files a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant and b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/diffuseprobegridraytracingmiss_vulkan_0.azshadervariant differ diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr deleted file mode 100644 index 0fcbcc4746..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bc9981393c88c6d30a0a5a6837e6f6246a9f550042b3c4be39dd34e479b90569 -size 16793931 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo deleted file mode 100644 index 16cb0dd668..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/PaperMill_E_3k.exr.assetinfo +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds deleted file mode 100644 index f2b2ce550d..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleBrdf_lut.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:440bcb1579d4ad667c040bda914ed3121980526a94f23879a0c482c799fd5132 -size 1310848 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds deleted file mode 100644 index 9585c25dd1..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleDiffuseHDR_cm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf4b22481726214c062ac27fb2d9d8a49e760a76d8fff03330ff9c5f9275a8da -size 1966208 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds deleted file mode 100644 index a35fac45fb..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/exampleSpecularHDR_cm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b4c99faffc34988c268613948f2004f40e9bd51de915461a4cb74edc5e8bae6 -size 134217920 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds deleted file mode 100644 index a89dfdbd3d..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/example_iblskyboxcm.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f7edea42ded8143764654f12c19e0c9b74c74afb21f435ebb59c0a4d203892a3 -size 536871104 diff --git a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt b/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt deleted file mode 100644 index 83cfe08ab9..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Textures/sampleEnvironment/papermill_license.txt +++ /dev/null @@ -1,10 +0,0 @@ -The papermill 'Image base lighting' (IBL) images are modified from the following: - -http://www.hdrlabs.com/sibl/archive.html -'Papermill Ruins E' - -All sIBL-sets on this page, including the images within, are licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. - -Creative Commons License: http://creativecommons.org/licenses/by-nc-sa/3.0/us/ - -Remember: Do what you want with them, but always mention where you got them from... \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 16f20d7178..00c55cbade 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -89,6 +89,8 @@ #include #include +#include +#include #include #include #include @@ -128,6 +130,7 @@ namespace AZ SMAAFeatureProcessor::Reflect(context); PostProcessFeatureProcessor::Reflect(context); ImGuiPassData::Reflect(context); + RayTracingPassData::Reflect(context); LightingPreset::Reflect(context); ModelPreset::Reflect(context); @@ -271,6 +274,9 @@ namespace AZ passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create); passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create); + // Add RayTracing pas + passSystem->AddPassCreator(Name("RayTracingPass"), &Render::RayTracingPass::Create); + // setup handler for load pass template mappings m_loadTemplatesHandler = RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); }); RPI::PassSystemInterface::Get()->ConnectEvent(m_loadTemplatesHandler); diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 90499be8b8..f8c1258fc0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include @@ -55,6 +57,43 @@ namespace AZ FrameCaptureOutputResult PngFrameCaptureOutput( const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) { + AZStd::shared_ptr> buffer = readbackResult.m_dataBuffer; + + // convert bgra to rgba by swapping channels + const int numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + buffer = AZStd::make_shared>(readbackResult.m_dataBuffer->size()); + AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin()); + + AZ::JobCompletion jobCompletion; + const int numThreads = 8; + const int numPixelsPerThread = buffer->size() / numChannels / numThreads; + for (int i = 0; i < numThreads; ++i) + { + int startPixel = i * numPixelsPerThread; + + AZ::Job* job = AZ::CreateJobFunction( + [&, startPixel, numPixelsPerThread]() + { + for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset) + { + if (startPixel * numChannels + numChannels < buffer->size()) + { + AZStd::swap( + buffer->data()[(startPixel + pixelOffset) * numChannels], + buffer->data()[(startPixel + pixelOffset) * numChannels + 2] + ); + } + } + }, true, nullptr); + + job->SetDependent(&jobCompletion); + job->Start(); + } + jobCompletion.StartAndWaitForCompletion(); + } + using namespace OIIO; AZStd::unique_ptr out = ImageOutput::create(outputFilePath.c_str()); if (out) @@ -62,13 +101,13 @@ namespace AZ ImageSpec spec( readbackResult.m_imageDescriptor.m_size.m_width, readbackResult.m_imageDescriptor.m_size.m_height, - AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format) + numChannels ); spec.attribute("png:compressionLevel", r_pngCompressionLevel); if (out->open(outputFilePath.c_str(), spec)) { - out->write_image(TypeDesc::UINT8, readbackResult.m_dataBuffer->data()); + out->write_image(TypeDesc::UINT8, buffer->data()); out->close(); return FrameCaptureOutputResult{FrameCaptureResult::Success, AZStd::nullopt}; } @@ -460,13 +499,23 @@ namespace AZ #if defined(OPEN_IMAGE_IO_ENABLED) else if (extension == "png") { - AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); - AZ::IO::SystemFile::CreateDir(folderPath.c_str()); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM || + readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + AZStd::string folderPath; + AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); + AZ::IO::SystemFile::CreateDir(folderPath.c_str()); - const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); - m_result = frameCaptureResult.m_result; - m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); + m_result = frameCaptureResult.m_result; + m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + } + else + { + m_latestCaptureInfo = AZStd::string::format( + "Can't save image with format %s to a png file", RHI::ToString(readbackResult.m_imageDescriptor.m_format)); + m_result = FrameCaptureResult::UnsupportedFormat; + } } #endif else diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 117b177f05..4aaa0be132 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -710,27 +710,53 @@ namespace AZ uint32_t rayTracingLod = aznumeric_cast(modelLods.size() - 1); const Data::Instance& modelLod = modelLods[rayTracingLod]; - // setup a stream layout and shader input contract for the position and normal streams + // setup a stream layout and shader input contract for the vertex streams static const char* PositionSemantic = "POSITION"; static const char* NormalSemantic = "NORMAL"; - static const RHI::Format StreamFormat = RHI::Format::R32G32B32_FLOAT; + static const char* TangentSemantic = "TANGENT"; + static const char* BitangentSemantic = "BITANGENT"; + static const char* UVSemantic = "UV"; + static const RHI::Format PositionStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format NormalStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format TangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format BitangentStreamFormat = RHI::Format::R32G32B32_FLOAT; + static const RHI::Format UVStreamFormat = RHI::Format::R32G32_FLOAT; RHI::InputStreamLayoutBuilder layoutBuilder; - layoutBuilder.AddBuffer()->Channel(PositionSemantic, StreamFormat); - layoutBuilder.AddBuffer()->Channel(NormalSemantic, StreamFormat); + layoutBuilder.AddBuffer()->Channel(PositionSemantic, PositionStreamFormat); + layoutBuilder.AddBuffer()->Channel(NormalSemantic, NormalStreamFormat); + layoutBuilder.AddBuffer()->Channel(UVSemantic, UVStreamFormat); + layoutBuilder.AddBuffer()->Channel(TangentSemantic, TangentStreamFormat); + layoutBuilder.AddBuffer()->Channel(BitangentSemantic, BitangentStreamFormat); RHI::InputStreamLayout inputStreamLayout = layoutBuilder.End(); RPI::ShaderInputContract::StreamChannelInfo positionStreamChannelInfo; positionStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(PositionSemantic)); - positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + positionStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(PositionStreamFormat); RPI::ShaderInputContract::StreamChannelInfo normalStreamChannelInfo; normalStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(NormalSemantic)); - normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(StreamFormat); + normalStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(NormalStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo tangentStreamChannelInfo; + tangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(TangentSemantic)); + tangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(TangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo bitangentStreamChannelInfo; + bitangentStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(BitangentSemantic)); + bitangentStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(BitangentStreamFormat); + + RPI::ShaderInputContract::StreamChannelInfo uvStreamChannelInfo; + uvStreamChannelInfo.m_semantic = RHI::ShaderSemantic(AZ::Name(UVSemantic)); + uvStreamChannelInfo.m_componentCount = RHI::GetFormatComponentCount(UVStreamFormat); + uvStreamChannelInfo.m_isOptional = true; RPI::ShaderInputContract shaderInputContract; shaderInputContract.m_streamChannels.emplace_back(positionStreamChannelInfo); shaderInputContract.m_streamChannels.emplace_back(normalStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(tangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(bitangentStreamChannelInfo); + shaderInputContract.m_streamChannels.emplace_back(uvStreamChannelInfo); // setup the raytracing data for each sub-mesh const size_t meshCount = modelLod->GetMeshes().size(); @@ -739,26 +765,6 @@ namespace AZ { const RPI::ModelLod::Mesh& mesh = modelLod->GetMeshes()[meshIndex]; - // retrieve vertex/index buffers - RPI::ModelLod::StreamBufferViewList streamBufferViews; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, nullptr, shaderInputContract, meshIndex); - AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); - - // note that the element count is the size of the entire buffer, even though this mesh may only - // occupy a portion of the vertex buffer. This is necessary since we are accessing it using - // a ByteAddressBuffer in the raytracing shaders and passing the byte offset to the shader in a constant buffer. - uint32_t vertexBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; - RHI::BufferViewDescriptor vertexBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, vertexBufferByteCount); - - const RHI::IndexBufferView& indexBufferView = mesh.m_indexBufferView; - uint32_t indexElementSize = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? 2 : 4; - uint32_t indexElementCount = (uint32_t)indexBufferView.GetBuffer()->GetDescriptor().m_byteCount / indexElementSize; - RHI::BufferViewDescriptor indexBufferDescriptor; - indexBufferDescriptor.m_elementOffset = 0; - indexBufferDescriptor.m_elementCount = indexElementCount; - indexBufferDescriptor.m_elementSize = indexElementSize; - indexBufferDescriptor.m_elementFormat = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? RHI::Format::R16_UINT : RHI::Format::R32_UINT; - // retrieve the material Data::Instance material = mesh.m_material; @@ -769,31 +775,163 @@ namespace AZ material = materialAssignment.m_materialInstance; } - AZ::Color irradianceColor(1.0f, 1.0f, 1.0f, 1.0f); + // retrieve vertex/index buffers + RPI::ModelLod::StreamBufferViewList streamBufferViews; + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh( + inputStreamLayout, + streamBufferViews, + nullptr, + shaderInputContract, + meshIndex, + materialAssignment.m_matModUvOverrides, + material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap()); + AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); + + // note that the element count is the size of the entire buffer, even though this mesh may only + // occupy a portion of the vertex buffer. This is necessary since we are accessing it using + // a ByteAddressBuffer in the raytracing shaders and passing the byte offset to the shader in a constant buffer. + uint32_t positionBufferByteCount = const_cast(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor positionBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, positionBufferByteCount); + + uint32_t normalBufferByteCount = const_cast(streamBufferViews[1].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor normalBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, normalBufferByteCount); + + uint32_t tangentBufferByteCount = const_cast(streamBufferViews[2].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor tangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tangentBufferByteCount); + + uint32_t bitangentBufferByteCount = const_cast(streamBufferViews[3].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor bitangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bitangentBufferByteCount); + + uint32_t uvBufferByteCount = const_cast(streamBufferViews[4].GetBuffer())->GetDescriptor().m_byteCount; + RHI::BufferViewDescriptor uvBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, uvBufferByteCount); + + const RHI::IndexBufferView& indexBufferView = mesh.m_indexBufferView; + uint32_t indexElementSize = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? 2 : 4; + uint32_t indexElementCount = (uint32_t)indexBufferView.GetBuffer()->GetDescriptor().m_byteCount / indexElementSize; + RHI::BufferViewDescriptor indexBufferDescriptor; + indexBufferDescriptor.m_elementOffset = 0; + indexBufferDescriptor.m_elementCount = indexElementCount; + indexBufferDescriptor.m_elementSize = indexElementSize; + indexBufferDescriptor.m_elementFormat = indexBufferView.GetIndexFormat() == RHI::IndexFormat::Uint16 ? RHI::Format::R16_UINT : RHI::Format::R32_UINT; + + // set the SubMesh data to pass to the RayTracingFeatureProcessor, starting with vertex/index data + RayTracingFeatureProcessor::SubMesh subMesh; + subMesh.m_positionFormat = PositionStreamFormat; + subMesh.m_positionVertexBufferView = streamBufferViews[0]; + subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(positionBufferDescriptor); + + subMesh.m_normalFormat = NormalStreamFormat; + subMesh.m_normalVertexBufferView = streamBufferViews[1]; + subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(normalBufferDescriptor); + + subMesh.m_tangentFormat = TangentStreamFormat; + subMesh.m_tangentVertexBufferView = streamBufferViews[2]; + subMesh.m_tangentShaderBufferView = const_cast(streamBufferViews[2].GetBuffer())->GetBufferView(tangentBufferDescriptor); + + subMesh.m_bitangentFormat = BitangentStreamFormat; + subMesh.m_bitangentVertexBufferView = streamBufferViews[3]; + subMesh.m_bitangentShaderBufferView = const_cast(streamBufferViews[3].GetBuffer())->GetBufferView(bitangentBufferDescriptor); + + if (uvBufferByteCount > 0) + { + subMesh.m_bufferFlags |= RayTracingSubMeshBufferFlags::UV; + subMesh.m_uvFormat = UVStreamFormat; + subMesh.m_uvVertexBufferView = streamBufferViews[4]; + subMesh.m_uvShaderBufferView = const_cast(streamBufferViews[4].GetBuffer())->GetBufferView(uvBufferDescriptor); + } + + subMesh.m_indexBufferView = mesh.m_indexBufferView; + subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); + + // add material data if (material) { + // irradiance color RPI::MaterialPropertyIndex propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.color")); if (propertyIndex.IsValid()) { - irradianceColor = material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor = material->GetPropertyValue(propertyIndex); } propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.factor")); if (propertyIndex.IsValid()) { - irradianceColor *= material->GetPropertyValue(propertyIndex); + subMesh.m_irradianceColor *= material->GetPropertyValue(propertyIndex); + } + + // base color + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.color")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor = material->GetPropertyValue(propertyIndex); + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_baseColor *= material->GetPropertyValue(propertyIndex); + } + + // metallic + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_metallicFactor = material->GetPropertyValue(propertyIndex); + } + + // roughness + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.factor")); + if (propertyIndex.IsValid()) + { + subMesh.m_roughnessFactor = material->GetPropertyValue(propertyIndex); + } + + // textures + propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::BaseColor; + subMesh.m_baseColorImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("normal.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Normal; + subMesh.m_normalImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Metallic; + subMesh.m_metallicImageView = image->GetImageView(); + } + } + + propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.textureMap")); + if (propertyIndex.IsValid()) + { + Data::Instance image = material->GetPropertyValue>(propertyIndex); + if (image.get()) + { + subMesh.m_textureFlags |= RayTracingSubMeshTextureFlags::Roughness; + subMesh.m_roughnessImageView = image->GetImageView(); + } } } - RayTracingFeatureProcessor::SubMesh subMesh; - subMesh.m_vertexFormat = StreamFormat; - subMesh.m_positionVertexBufferView = streamBufferViews[0]; - subMesh.m_positionShaderBufferView = const_cast(streamBufferViews[0].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_normalVertexBufferView = streamBufferViews[1]; - subMesh.m_normalShaderBufferView = const_cast(streamBufferViews[1].GetBuffer())->GetBufferView(vertexBufferDescriptor); - subMesh.m_indexBufferView = mesh.m_indexBufferView; - subMesh.m_indexShaderBufferView = const_cast(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor); - subMesh.m_irradianceColor = irradianceColor; subMeshes.push_back(subMesh); } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp index 2bb2fa2ac2..92cd41b4e8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp @@ -115,11 +115,11 @@ namespace AZ } } - // update and compile the RayTracingSceneSrg + // update and compile the RayTracingSceneSrg and RayTracingMaterialSrg // Note: the timing of this update is very important, it needs to be updated after the TLAS is allocated so it can // be set on the RayTracingSceneSrg for this frame, and the ray tracing mesh data in the RayTracingSceneSrg must // exactly match the TLAS. Any mismatch in this data may result in a TDR. - rayTracingFeatureProcessor->UpdateRayTracingSceneSrg(); + rayTracingFeatureProcessor->UpdateRayTracingSrgs(); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index 7c13daea3b..10c7c2d378 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -71,6 +71,13 @@ namespace AZ AZ_Assert(rayTracingSceneSrgAsset.IsReady(), "Failed to load RayTracingSceneSrg asset"); m_rayTracingSceneSrg = RPI::ShaderResourceGroup::Create(rayTracingSceneSrgAsset); + + // load the RayTracingMaterialSrg asset + Data::Asset rayTracingMaterialSrgAsset = + RPI::AssetUtils::LoadAssetByProductPath("shaderlib/atom/features/raytracing/raytracingmaterialsrg_raytracingmaterialsrg.azsrg", RPI::AssetUtils::TraceLevel::Error); + AZ_Assert(rayTracingMaterialSrgAsset.IsReady(), "Failed to load RayTracingMaterialSrg asset"); + + m_rayTracingMaterialSrg = RPI::ShaderResourceGroup::Create(rayTracingMaterialSrgAsset); } void RayTracingFeatureProcessor::SetMesh(const ObjectId objectId, const SubMeshVector& subMeshes) @@ -104,7 +111,7 @@ namespace AZ RHI::RayTracingBlasDescriptor blasDescriptor; blasDescriptor.Build() ->Geometry() - ->VertexFormat(subMesh.m_vertexFormat) + ->VertexFormat(subMesh.m_positionFormat) ->VertexBuffer(subMesh.m_positionVertexBufferView) ->IndexBuffer(subMesh.m_indexBufferView) ; @@ -124,6 +131,7 @@ namespace AZ m_subMeshCount += aznumeric_cast(subMeshes.size()); m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::RemoveMesh(const ObjectId objectId) @@ -142,6 +150,7 @@ namespace AZ } m_meshInfoBufferNeedsUpdate = true; + m_materialInfoBufferNeedsUpdate = true; } void RayTracingFeatureProcessor::SetMeshTransform(const ObjectId objectId, const AZ::Transform transform, const AZ::Vector3 nonUniformScale) @@ -162,14 +171,14 @@ namespace AZ m_meshInfoBufferNeedsUpdate = true; } - void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + void RayTracingFeatureProcessor::UpdateRayTracingSrgs() { if (!m_tlas->GetTlasBuffer()) { return; } - if (m_rayTracingSceneSrg->IsQueuedForCompile()) + if (m_rayTracingSceneSrg->IsQueuedForCompile() || m_rayTracingMaterialSrg->IsQueuedForCompile()) { //[GFX TODO][ATOM-14792] AtomSampleViewer: Reset scene and feature processors before switching to sample return; @@ -178,7 +187,148 @@ namespace AZ // update the mesh info buffer with the latest ray tracing enabled meshes UpdateMeshInfoBuffer(); + // update the material info buffer with the latest ray tracing enabled meshes + UpdateMaterialInfoBuffer(); + // update the RayTracingSceneSrg + UpdateRayTracingSceneSrg(); + + // update the RayTracingMaterialSrg + UpdateRayTracingMaterialSrg(); + } + + void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + { + if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); + + AZStd::vector meshInfos; + meshInfos.reserve(m_subMeshCount); + + uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); + + if (m_meshInfoBuffer == nullptr) + { + AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); + + // allocate the MeshInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = AZStd::string::format("RayTracingMeshInfo_%s", uuidString.c_str()); + desc.m_byteCount = newMeshByteCount; + desc.m_elementSize = sizeof(MeshInfo); + m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) + { + // resize for the new sub-mesh count + m_meshInfoBuffer->Resize(newMeshByteCount); + } + + // keep track of the start index of the buffers for each mesh, this is put into the MeshInfo + // entry for each mesh so it knows where to find the start of its buffers in the unbounded array + uint32_t bufferStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); + AZ::Transform noScaleTransform = meshTransform; + noScaleTransform.ExtractUniformScale(); + AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); + rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); + + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MeshInfo meshInfo; + meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); + meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); + meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); + meshInfo.m_tangentOffset = subMesh.m_tangentVertexBufferView.GetByteOffset(); + meshInfo.m_bitangentOffset = subMesh.m_bitangentVertexBufferView.GetByteOffset(); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshInfo.m_uvOffset = subMesh.m_uvVertexBufferView.GetByteOffset(); + } + + subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); + rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); + meshInfo.m_bufferFlags = subMesh.m_bufferFlags; + meshInfo.m_bufferStartIndex = bufferStartIndex; + + // add the count of buffers present in this subMesh to the start index for the next subMesh + // note that the Index, Position, Normal, Tangent, and Bitangent buffers are always counted since they are guaranteed + static const uint32_t RayTracingSubMeshFixedStreamCount = 5; + bufferStartIndex += (RayTracingSubMeshFixedStreamCount + RHI::CountBitsSet(aznumeric_cast(meshInfo.m_bufferFlags))); + + meshInfos.emplace_back(meshInfo); + } + } + + m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); + m_meshInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateMaterialInfoBuffer() + { + if (m_materialInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + { + AZStd::vector materialInfos; + materialInfos.reserve(m_subMeshCount); + + uint32_t newMaterialByteCount = m_subMeshCount * sizeof(MaterialInfo); + + if (m_materialInfoBuffer == nullptr) + { + AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString(); + + // allocate the MaterialInfo structured buffer + RPI::CommonBufferDescriptor desc; + desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; + desc.m_bufferName = AZStd::string::format("RayTracingMaterialInfo_%s", uuidString.c_str()); + desc.m_byteCount = newMaterialByteCount; + desc.m_elementSize = sizeof(MaterialInfo); + m_materialInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); + } + else if (m_materialInfoBuffer->GetBufferSize() < newMaterialByteCount) + { + // resize for the new sub-mesh count + m_materialInfoBuffer->Resize(newMaterialByteCount); + } + + // keep track of the start index of the textures for each mesh, this is put into the MaterialInfo + // entry for each mesh so it knows where to find the start of its textures in the unbounded array + uint32_t textureStartIndex = 0; + + for (const auto& mesh : m_meshes) + { + const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + for (const auto& subMesh : subMeshes) + { + MaterialInfo materialInfo; + subMesh.m_baseColor.StoreToFloat4(materialInfo.m_baseColor.data()); + materialInfo.m_metallicFactor = subMesh.m_metallicFactor; + materialInfo.m_roughnessFactor = subMesh.m_roughnessFactor; + materialInfo.m_textureFlags = subMesh.m_textureFlags; + materialInfo.m_textureStartIndex = textureStartIndex; + + // add the count of textures present in this subMesh to the start index for the next subMesh + textureStartIndex += RHI::CountBitsSet(aznumeric_cast(materialInfo.m_textureFlags)); + + materialInfos.emplace_back(materialInfo); + } + } + + m_materialInfoBuffer->UpdateData(materialInfos.data(), newMaterialByteCount); + m_materialInfoBufferNeedsUpdate = false; + } + } + + void RayTracingFeatureProcessor::UpdateRayTracingSceneSrg() + { const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingSceneSrg->GetLayout(); RHI::ShaderInputImageIndex imageIndex; RHI::ShaderInputBufferIndex bufferIndex; @@ -272,11 +422,18 @@ namespace AZ const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - // add the index, position, and normal buffers for this sub-mesh to the mesh buffer list, this will - // go into the shader as an unbounded array in the Srg + // add the stream buffers for this sub-mesh to the mesh buffer list, + // this is sent to the shader as an unbounded array in the Srg meshBuffers.push_back(subMesh.m_indexShaderBufferView.get()); meshBuffers.push_back(subMesh.m_positionShaderBufferView.get()); meshBuffers.push_back(subMesh.m_normalShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_tangentShaderBufferView.get()); + meshBuffers.push_back(subMesh.m_bitangentShaderBufferView.get()); + + if (RHI::CheckBitsAll(subMesh.m_bufferFlags, RayTracingSubMeshBufferFlags::UV)) + { + meshBuffers.push_back(subMesh.m_uvShaderBufferView.get()); + } } } @@ -287,58 +444,53 @@ namespace AZ m_rayTracingSceneSrg->Compile(); } - void RayTracingFeatureProcessor::UpdateMeshInfoBuffer() + void RayTracingFeatureProcessor::UpdateRayTracingMaterialSrg() { - if (m_meshInfoBufferNeedsUpdate && (m_subMeshCount > 0)) + const RHI::ShaderResourceGroupLayout* srgLayout = m_rayTracingMaterialSrg->GetLayout(); + RHI::ShaderInputImageIndex imageIndex; + RHI::ShaderInputBufferIndex bufferIndex; + RHI::ShaderInputConstantIndex constantIndex; + + bufferIndex = srgLayout->FindShaderInputBufferIndex(AZ::Name("m_materialInfo")); + m_rayTracingMaterialSrg->SetBufferView(bufferIndex, m_materialInfoBuffer->GetBufferView()); + + if (m_subMeshCount) { - TransformServiceFeatureProcessor* transformFeatureProcessor = GetParentScene()->GetFeatureProcessor(); - - AZStd::vector meshInfos; - meshInfos.reserve(m_subMeshCount); - - uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo); - - if (m_meshInfoBuffer == nullptr) - { - // allocate the MeshInfo structured buffer - RPI::CommonBufferDescriptor desc; - desc.m_poolType = RPI::CommonBufferPoolType::ReadOnly; - desc.m_bufferName = "RayTracingMeshInfo"; - desc.m_byteCount = newMeshByteCount; - desc.m_elementSize = sizeof(MeshInfo); - m_meshInfoBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc); - } - else if (m_meshInfoBuffer->GetBufferSize() < newMeshByteCount) - { - // resize for the new sub-mesh count - m_meshInfoBuffer->Resize(newMeshByteCount); - } - + AZStd::vector materialTextures; for (const auto& mesh : m_meshes) { - AZ::Transform meshTransform = transformFeatureProcessor->GetTransformForId(TransformServiceFeatureProcessorInterface::ObjectId(mesh.first)); - AZ::Transform noScaleTransform = meshTransform; - noScaleTransform.ExtractUniformScale(); - AZ::Matrix3x3 rotationMatrix = Matrix3x3::CreateFromTransform(noScaleTransform); - rotationMatrix = rotationMatrix.GetInverseFull().GetTranspose(); - - const RayTracingFeatureProcessor::SubMeshVector& subMeshes = mesh.second.m_subMeshes; + const SubMeshVector& subMeshes = mesh.second.m_subMeshes; for (const auto& subMesh : subMeshes) { - MeshInfo meshInfo; - meshInfo.m_indexOffset = subMesh.m_indexBufferView.GetByteOffset(); - meshInfo.m_positionOffset = subMesh.m_positionVertexBufferView.GetByteOffset(); - meshInfo.m_normalOffset = subMesh.m_normalVertexBufferView.GetByteOffset(); - subMesh.m_irradianceColor.StoreToFloat4(meshInfo.m_irradianceColor.data()); - rotationMatrix.StoreToRowMajorFloat9(meshInfo.m_worldInvTranspose.data()); + // add the baseColor, normal, metallic, and roughness images for this sub-mesh to the material texture list, + // this is sent to the shader as an unbounded array in the Srg + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::BaseColor)) + { + materialTextures.push_back(subMesh.m_baseColorImageView.get()); + } - meshInfos.emplace_back(meshInfo); + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Normal)) + { + materialTextures.push_back(subMesh.m_normalImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Metallic)) + { + materialTextures.push_back(subMesh.m_metallicImageView.get()); + } + + if (RHI::CheckBitsAll(subMesh.m_textureFlags, RayTracingSubMeshTextureFlags::Roughness)) + { + materialTextures.push_back(subMesh.m_roughnessImageView.get()); + } } } - m_meshInfoBuffer->UpdateData(meshInfos.data(), newMeshByteCount); - m_meshInfoBufferNeedsUpdate = false; + RHI::ShaderInputImageUnboundedArrayIndex textureUnboundedArrayIndex = srgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name("m_materialTextures")); + m_rayTracingMaterialSrg->SetImageViewUnboundedArray(textureUnboundedArrayIndex, materialTextures); } + + m_rayTracingMaterialSrg->Compile(); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index f317f1c096..90a9383ae6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,28 @@ namespace AZ { namespace Render { + static const uint32_t RayTracingGlobalSrgBindingSlot = 0; + static const uint32_t RayTracingSceneSrgBindingSlot = 1; + static const uint32_t RayTracingMaterialSrgBindingSlot = 2; + + enum class RayTracingSubMeshBufferFlags : uint32_t + { + None = 0, + + UV = AZ_BIT(0) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshBufferFlags); + + enum class RayTracingSubMeshTextureFlags : uint32_t + { + None = 0, + BaseColor = AZ_BIT(0), + Normal = AZ_BIT(1), + Metallic = AZ_BIT(2), + Roughness = AZ_BIT(3) + }; + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::Render::RayTracingSubMeshTextureFlags); + //! This feature processor manages ray tracing data for a Scene class RayTracingFeatureProcessor : public RPI::FeatureProcessor @@ -42,20 +65,53 @@ namespace AZ //! Contains data for a single sub-mesh struct SubMesh { - // vertex/index buffer data - RHI::Format m_vertexFormat = RHI::Format::Unknown; + // vertex streams + RHI::Format m_positionFormat = RHI::Format::Unknown; RHI::StreamBufferView m_positionVertexBufferView; RHI::Ptr m_positionShaderBufferView; + + RHI::Format m_normalFormat = RHI::Format::Unknown; RHI::StreamBufferView m_normalVertexBufferView; RHI::Ptr m_normalShaderBufferView; + + RHI::Format m_tangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_tangentVertexBufferView; + RHI::Ptr m_tangentShaderBufferView; + + RHI::Format m_bitangentFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_bitangentVertexBufferView; + RHI::Ptr m_bitangentShaderBufferView; + + RHI::Format m_uvFormat = RHI::Format::Unknown; + RHI::StreamBufferView m_uvVertexBufferView; + RHI::Ptr m_uvShaderBufferView; + + // index buffer RHI::IndexBufferView m_indexBufferView; RHI::Ptr m_indexShaderBufferView; + // vertex buffer usage flags + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + // color of the bounced light from this sub-mesh - AZ::Color m_irradianceColor; + AZ::Color m_irradianceColor = AZ::Color(1.0f); // ray tracing Blas RHI::Ptr m_blas; + + // material data + AZ::Color m_baseColor = AZ::Color(0.0f); + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + + // material texture usage flags + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + + // material textures + RHI::Ptr m_baseColorImageView; + RHI::Ptr m_normalImageView; + RHI::Ptr m_metallicImageView; + RHI::Ptr m_roughnessImageView; }; using SubMeshVector = AZStd::vector; @@ -98,6 +154,9 @@ namespace AZ //! Retrieves the RayTracingSceneSrg Data::Instance GetRayTracingSceneSrg() const { return m_rayTracingSceneSrg; } + //! Retrieves the RayTracingMaterialSrg + Data::Instance GetRayTracingMaterialSrg() const { return m_rayTracingMaterialSrg; } + //! Retrieves the RayTracingTlas const RHI::Ptr& GetTlas() const { return m_tlas; } RHI::Ptr& GetTlas() { return m_tlas; } @@ -118,14 +177,20 @@ namespace AZ //! Retrieves the GPU buffer containing information for all ray tracing meshes. const Data::Instance GetMeshInfoBuffer() const { return m_meshInfoBuffer; } - //! Updates the RayTracingSceneSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass - void UpdateRayTracingSceneSrg(); + //! Retrieves the GPU buffer containing information for all ray tracing materials. + const Data::Instance GetMaterialInfoBuffer() const { return m_materialInfoBuffer; } + + //! Updates the RayTracingSceneSrg and RayTracingMaterialSrg, called after the TLAS is allocated in the RayTracingAccelerationStructurePass + void UpdateRayTracingSrgs(); private: AZ_DISABLE_COPY_MOVE(RayTracingFeatureProcessor); void UpdateMeshInfoBuffer(); + void UpdateMaterialInfoBuffer(); + void UpdateRayTracingSceneSrg(); + void UpdateRayTracingMaterialSrg(); // flag indicating if RayTracing is enabled, currently based on device support bool m_rayTracingEnabled = false; @@ -143,6 +208,9 @@ namespace AZ // ray tracing scene Srg Data::Instance m_rayTracingSceneSrg; + // ray tracing material Srg + Data::Instance m_rayTracingMaterialSrg; + // current revision number of ray tracing data uint32_t m_revision = 0; @@ -158,18 +226,43 @@ namespace AZ // structure for data in the m_meshInfoBuffer, shaders that use the buffer must match this type struct MeshInfo { - uint32_t m_indexOffset; - uint32_t m_positionOffset; - uint32_t m_normalOffset; + uint32_t m_indexOffset; + uint32_t m_positionOffset; + uint32_t m_normalOffset; + uint32_t m_tangentOffset; + uint32_t m_bitangentOffset; + uint32_t m_uvOffset; + float m_padding0[2]; + AZStd::array m_irradianceColor; // float4 AZStd::array m_worldInvTranspose; // float3x3 + float m_padding1; + + RayTracingSubMeshBufferFlags m_bufferFlags = RayTracingSubMeshBufferFlags::None; + uint32_t m_bufferStartIndex = 0; }; // buffer containing a MeshInfo for each sub-mesh Data::Instance m_meshInfoBuffer; - // flag indicating we need to update the mesh info GPU buffer + // structure for data in the m_materialInfoBuffer, shaders that use the buffer must match this type + struct MaterialInfo + { + AZStd::array m_baseColor; // float4 + float m_metallicFactor = 0.0f; + float m_roughnessFactor = 0.0f; + RayTracingSubMeshTextureFlags m_textureFlags = RayTracingSubMeshTextureFlags::None; + uint32_t m_textureStartIndex = 0; + }; + + // buffer containing a MaterialInfo for each sub-mesh + Data::Instance m_materialInfoBuffer; + + // flag indicating we need to update the meshInfo buffer bool m_meshInfoBufferNeedsUpdate = false; + + // flag indicating we need to update the materialInfo buffer + bool m_materialInfoBufferNeedsUpdate = false; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp new file mode 100644 index 0000000000..988870cc0e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp @@ -0,0 +1,362 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr RayTracingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew RayTracingPass(descriptor); + return pass; + } + + RayTracingPass::RayTracingPass(const RPI::PassDescriptor& descriptor) + : RenderPass(descriptor) + , m_passDescriptor(descriptor) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + if (device->GetFeatures().m_rayTracing == false) + { + // raytracing is not supported on this platform + SetEnabled(false); + return; + } + + Init(); + } + + RayTracingPass::~RayTracingPass() + { + RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); + } + + void RayTracingPass::Init() + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + + m_passData = RPI::PassUtils::GetPassData(m_passDescriptor); + if (m_passData == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Invalid RayTracingPassData", GetPathName().GetCStr()); + return; + } + + // ray generation shader + m_rayGenerationShader = LoadShader(m_passData->m_rayGenerationShaderAssetReference); + if (m_rayGenerationShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load RayGeneration shader [%s]", GetPathName().GetCStr(), m_passData->m_rayGenerationShaderAssetReference.m_filePath.data()); + return; + } + + auto shaderVariant = m_rayGenerationShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing rayGenerationShaderDescriptor; + shaderVariant.ConfigurePipelineState(rayGenerationShaderDescriptor); + + // closest hit shader + m_closestHitShader = LoadShader(m_passData->m_closestHitShaderAssetReference); + if (m_closestHitShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load ClosestHit shader [%s]", GetPathName().GetCStr(), m_passData->m_closestHitShaderAssetReference.m_filePath.data()); + return; + } + + shaderVariant = m_closestHitShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing closestHitShaderDescriptor; + shaderVariant.ConfigurePipelineState(closestHitShaderDescriptor); + + // miss shader + m_missShader = LoadShader(m_passData->m_missShaderAssetReference); + if (m_missShader == nullptr) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load Miss shader [%s]", GetPathName().GetCStr(), m_passData->m_missShaderAssetReference.m_filePath.data()); + return; + } + + shaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId); + RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor; + shaderVariant.ConfigurePipelineState(missShaderDescriptor); + + // retrieve global pipeline state + m_globalPipelineState = m_rayGenerationShader->AcquirePipelineState(rayGenerationShaderDescriptor); + AZ_Assert(m_globalPipelineState, "Failed to acquire ray tracing global pipeline state"); + + // create global srg + Data::Asset globalSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingGlobalSrgBindingSlot); + AZ_Error("PassSystem", globalSrgAsset.GetId().IsValid(), "RayTracingPass [%s] Failed to find RayTracingGlobalSrg asset", GetPathName().GetCStr()); + AZ_Error("PassSystem", globalSrgAsset.IsReady(), "RayTracingPass [%s] asset is not loaded for shader", GetPathName().GetCStr()); + + m_shaderResourceGroup = RPI::ShaderResourceGroup::Create(globalSrgAsset); + AZ_Assert(m_shaderResourceGroup, "RayTracingPass [%s]: Failed to create RayTracingGlobalSrg", GetPathName().GetCStr()); + RPI::PassUtils::BindDataMappingsToSrg(m_passDescriptor, m_shaderResourceGroup.get()); + + // check to see if the shader requires the View and RayTracingMaterial Srgs + Data::Asset viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View); + m_requiresViewSrg = viewSrgAsset.GetId().IsValid(); + + Data::Asset rayTracingMaterialSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingMaterialSrgBindingSlot); + m_requiresRayTracingMaterialSrg = rayTracingMaterialSrgAsset.GetId().IsValid(); + + // build the ray tracing pipeline state descriptor + RHI::RayTracingPipelineStateDescriptor descriptor; + descriptor.Build() + ->PipelineState(m_globalPipelineState.get()) + ->MaxPayloadSize(m_passData->m_maxPayloadSize) + ->MaxAttributeSize(m_passData->m_maxAttributeSize) + ->MaxRecursionDepth(m_passData->m_maxRecursionDepth) + ->ShaderLibrary(rayGenerationShaderDescriptor) + ->RayGenerationShaderName(AZ::Name(m_passData->m_rayGenerationShaderName.c_str())) + ->ShaderLibrary(missShaderDescriptor) + ->MissShaderName(AZ::Name(m_passData->m_missShaderName.c_str())) + ->ShaderLibrary(closestHitShaderDescriptor) + ->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str())) + ->HitGroup(AZ::Name("HitGroup")) + ->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str())); + + // create the ray tracing pipeline state object + m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState(); + m_rayTracingPipelineState->Init(*device.get(), &descriptor); + + // make sure the shader table rebuilds if we're hotreloading + m_rayTracingRevision = 0; + + RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect(); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_rayGenerationShaderAssetReference.m_assetId); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_closestHitShaderAssetReference.m_assetId); + RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_missShaderAssetReference.m_assetId); + } + + Data::Instance RayTracingPass::LoadShader(const RPI::AssetReference& shaderAssetReference) + { + Data::Asset shaderAsset; + if (shaderAssetReference.m_assetId.IsValid()) + { + shaderAsset = RPI::FindShaderAsset(shaderAssetReference.m_assetId, shaderAssetReference.m_filePath); + } + + if (!shaderAsset.GetId().IsValid()) + { + AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load shader asset [%s]", GetPathName().GetCStr(), shaderAssetReference.m_filePath.data()); + return nullptr; + } + + return RPI::Shader::FindOrCreate(shaderAsset); + } + + void RayTracingPass::FrameBeginInternal(FramePrepareParams params) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + if (!rayTracingFeatureProcessor) + { + return; + } + + if (!m_rayTracingShaderTable) + { + RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); + RHI::RayTracingBufferPools& rayTracingBufferPools = rayTracingFeatureProcessor->GetBufferPools(); + + m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable(); + m_rayTracingShaderTable->Init(*device.get(), rayTracingBufferPools); + } + + RPI::RenderPass::FrameBeginInternal(params); + } + + void RayTracingPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + RPI::RenderPass::SetupFrameGraphDependencies(frameGraph); + frameGraph.SetEstimatedItemCount(1); + + // TLAS + { + const RHI::Ptr& rayTracingTlasBuffer = rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer(); + if (rayTracingTlasBuffer) + { + AZ::RHI::AttachmentId tlasAttachmentId = rayTracingFeatureProcessor->GetTlasAttachmentId(); + if (frameGraph.GetAttachmentDatabase().IsAttachmentValid(tlasAttachmentId) == false) + { + [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(tlasAttachmentId, rayTracingTlasBuffer); + AZ_Assert(result == RHI::ResultCode::Success, "Failed to import ray tracing TLAS buffer with error %d", result); + } + + uint32_t tlasBufferByteCount = aznumeric_cast(rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer()->GetDescriptor().m_byteCount); + RHI::BufferViewDescriptor tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tlasBufferByteCount); + + RHI::BufferScopeAttachmentDescriptor desc; + desc.m_attachmentId = tlasAttachmentId; + desc.m_bufferViewDescriptor = tlasBufferViewDescriptor; + desc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load; + + frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite); + } + } + } + + void RayTracingPass::CompileResources(const RHI::FrameGraphCompileContext& context) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + if (m_shaderResourceGroup != nullptr) + { + BindPassSrg(context, m_shaderResourceGroup); + m_shaderResourceGroup->Compile(); + } + + uint32_t rayTracingRevision = rayTracingFeatureProcessor->GetRevision(); + if (m_rayTracingRevision != rayTracingRevision) + { + // scene changed, need to rebuild the shader table + m_rayTracingRevision = rayTracingRevision; + + AZStd::shared_ptr descriptor = AZStd::make_shared(); + + if (rayTracingFeatureProcessor->GetSubMeshCount()) + { + // build the ray tracing shader table descriptor + RHI::RayTracingShaderTableDescriptor* descriptorBuild = descriptor->Build(AZ::Name("RayTracingShaderTable"), m_rayTracingPipelineState) + ->RayGenerationRecord(AZ::Name(m_passData->m_rayGenerationShaderName.c_str())) + ->MissRecord(AZ::Name(m_passData->m_missShaderName.c_str())); + + // add a hit group for each mesh to the shader table + for (uint32_t i = 0; i < rayTracingFeatureProcessor->GetSubMeshCount(); ++i) + { + descriptorBuild->HitGroupRecord(AZ::Name("HitGroup")); + } + } + + m_rayTracingShaderTable->Build(descriptor); + } + } + + void RayTracingPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) + { + RPI::Scene* scene = m_pipeline->GetScene(); + RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor(); + AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor"); + + if (!rayTracingFeatureProcessor || + !rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer() || + !rayTracingFeatureProcessor->GetSubMeshCount() || + !m_rayTracingShaderTable) + { + return; + } + + RHI::DispatchRaysItem dispatchRaysItem; + + // calculate thread counts if this is a full screen raytracing pass + if (m_passData->m_makeFullscreenPass) + { + RPI::PassAttachment* outputAttachment = nullptr; + + if (GetOutputCount() > 0) + { + outputAttachment = GetOutputBinding(0).m_attachment.get(); + } + else if (GetInputOutputCount() > 0) + { + outputAttachment = GetInputOutputBinding(0).m_attachment.get(); + } + + AZ_Assert(outputAttachment != nullptr, "[RayTracingPass '%s']: A fullscreen RayTracing pass must have a valid output or input/output.", GetPathName().GetCStr()); + AZ_Assert(outputAttachment->GetAttachmentType() == RHI::AttachmentType::Image, "[RayTracingPass '%s']: The output of a fullscreen RayTracing pass must be an image.", GetPathName().GetCStr()); + + RHI::Size imageSize = outputAttachment->m_descriptor.m_image.m_size; + + dispatchRaysItem.m_width = imageSize.m_width; + dispatchRaysItem.m_height = imageSize.m_height; + dispatchRaysItem.m_depth = imageSize.m_depth; + } + else + { + dispatchRaysItem.m_width = m_passData->m_threadCountX; + dispatchRaysItem.m_height = m_passData->m_threadCountY; + dispatchRaysItem.m_depth = m_passData->m_threadCountZ; + } + + // bind RayTracingGlobal, RayTracingScene, and View Srgs + // [GFX TODO][ATOM-15610] Add RenderPass::SetSrgsForRayTracingDispatch + AZStd::vector shaderResourceGroups = + { + m_shaderResourceGroup->GetRHIShaderResourceGroup(), + rayTracingFeatureProcessor->GetRayTracingSceneSrg()->GetRHIShaderResourceGroup() + }; + + if (m_requiresViewSrg) + { + const AZStd::vector& views = m_pipeline->GetViews(m_passData->m_pipelineViewTag); + if (views.size() > 0) + { + shaderResourceGroups.push_back(views[0]->GetRHIShaderResourceGroup()); + } + } + + if (m_requiresRayTracingMaterialSrg) + { + shaderResourceGroups.push_back(rayTracingFeatureProcessor->GetRayTracingMaterialSrg()->GetRHIShaderResourceGroup()); + } + + dispatchRaysItem.m_shaderResourceGroupCount = aznumeric_cast(shaderResourceGroups.size()); + dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups.data(); + dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get(); + dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get(); + dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get(); + + // submit the DispatchRays item + context.GetCommandList()->Submit(dispatchRaysItem); + } + + void RayTracingPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader& shader) + { + Init(); + } + + void RayTracingPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset& shaderAsset) + { + Init(); + } + + void RayTracingPass::OnShaderVariantReinitialized([[maybe_unused]] const RPI::Shader& shader, [[maybe_unused]] const RPI::ShaderVariantId& shaderVariantId, [[maybe_unused]] RPI::ShaderVariantStableId shaderVariantStableId) + { + Init(); + } + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h new file mode 100644 index 0000000000..6ad082e894 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h @@ -0,0 +1,82 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + struct RayTracingPassData; + + //! This pass executes a raytracing shader as specified in the PassData. + class RayTracingPass + : public RPI::RenderPass + , private RPI::ShaderReloadNotificationBus::MultiHandler + { + AZ_RPI_PASS(RayTracingPass); + + public: + AZ_RTTI(RayTracingPass, "{7A68A36E-956A-4258-93FE-38686042C4D9}", RPI::RenderPass); + AZ_CLASS_ALLOCATOR(RayTracingPass, SystemAllocator, 0); + virtual ~RayTracingPass(); + + //! Creates a RayTracingPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + RayTracingPass(const RPI::PassDescriptor& descriptor); + + // Pass overrides + void FrameBeginInternal(FramePrepareParams params) override; + + // Scope producer functions + void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override; + void CompileResources(const RHI::FrameGraphCompileContext& context) override; + void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override; + + // ShaderReloadNotificationBus::Handler overrides + void OnShaderReinitialized(const RPI::Shader& shader) override; + void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; + void OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId shaderVariantStableId) override; + + // load the raytracing shaders and setup pipeline states + void Init(); + + // helper for loading a shader from a shader asset reference + Data::Instance LoadShader(const RPI::AssetReference& shaderAssetReference); + + // pass data + RPI::PassDescriptor m_passDescriptor; + const RayTracingPassData* m_passData = nullptr; + + // revision number of the ray tracing TLAS when the shader table was built + uint32_t m_rayTracingRevision = 0; + + // raytracing shaders, pipeline states, and shader table + Data::Instance m_rayGenerationShader; + Data::Instance m_missShader; + Data::Instance m_closestHitShader; + RHI::Ptr m_rayTracingPipelineState; + RHI::ConstPtr m_globalPipelineState; + RHI::Ptr m_rayTracingShaderTable; + bool m_requiresViewSrg = false; + bool m_requiresRayTracingMaterialSrg = false; + }; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h new file mode 100644 index 0000000000..bc15a8372c --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h @@ -0,0 +1,73 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + //! Custom data for the RayTracingPass, specified in the PassRequest. + struct RayTracingPassData + : public RPI::RenderPassData + { + AZ_RTTI(RayTracingPassData, "{26C2E2FD-D30A-4142-82A3-0167BC94B3EE}", RPI::RenderPassData); + AZ_CLASS_ALLOCATOR(RayTracingPassData, SystemAllocator, 0); + + RayTracingPassData() = default; + virtual ~RayTracingPassData() = default; + + static void Reflect(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("RayGenerationShaderAsset", &RayTracingPassData::m_rayGenerationShaderAssetReference) + ->Field("RayGenerationShaderName", &RayTracingPassData::m_rayGenerationShaderName) + ->Field("ClosestHitShaderAsset", &RayTracingPassData::m_closestHitShaderAssetReference) + ->Field("ClosestHitShaderName", &RayTracingPassData::m_closestHitShaderName) + ->Field("MissShaderAsset", &RayTracingPassData::m_missShaderAssetReference) + ->Field("MissShaderName", &RayTracingPassData::m_missShaderName) + ->Field("MaxPayloadSize", &RayTracingPassData::m_maxPayloadSize) + ->Field("MaxAttributeSize", &RayTracingPassData::m_maxAttributeSize) + ->Field("MaxRecursionDepth", &RayTracingPassData::m_maxRecursionDepth) + ->Field("Thread Count X", &RayTracingPassData::m_threadCountX) + ->Field("Thread Count Y", &RayTracingPassData::m_threadCountY) + ->Field("Thread Count Z", &RayTracingPassData::m_threadCountZ) + ->Field("Make Fullscreen Pass", &RayTracingPassData::m_makeFullscreenPass) + ; + } + } + + RPI::AssetReference m_rayGenerationShaderAssetReference; + AZStd::string m_rayGenerationShaderName; + RPI::AssetReference m_closestHitShaderAssetReference; + AZStd::string m_closestHitShaderName; + RPI::AssetReference m_missShaderAssetReference; + AZStd::string m_missShaderName; + + uint32_t m_maxPayloadSize = 64; + uint32_t m_maxAttributeSize = 32; + uint32_t m_maxRecursionDepth = 1; + + uint32_t m_threadCountX = 1; + uint32_t m_threadCountY = 1; + uint32_t m_threadCountZ = 1; + + bool m_makeFullscreenPass = false; + }; + } // 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 4f98046adb..76b71e5fae 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -250,6 +250,9 @@ set(FILES Source/RayTracing/RayTracingFeatureProcessor.cpp Source/RayTracing/RayTracingAccelerationStructurePass.cpp Source/RayTracing/RayTracingAccelerationStructurePass.h + Source/RayTracing/RayTracingPass.cpp + Source/RayTracing/RayTracingPass.h + Source/RayTracing/RayTracingPassData.h Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp Source/ReflectionProbe/ReflectionProbe.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 9baf80bae0..c8b406f94b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -32,7 +32,7 @@ namespace AZ //! In the source data, properties and UV names are loaded separately. //! However, treating UV names as a special property group can greatly simplify the editor code. //! See MaterialInspector::AddUvNamesGroup() for more details. - static constexpr const char UvGroupName[] = "UvNames"; + static constexpr const char UvGroupName[] = "uvSets"; class MaterialAsset; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h index c376112290..52b75c4157 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h @@ -59,7 +59,10 @@ namespace AZ static constexpr uint32_t Draw = 0; static constexpr uint32_t Object = 1; static constexpr uint32_t Material = 2; + static constexpr uint32_t SubPass = 3; static constexpr uint32_t Pass = 4; + static constexpr uint32_t View = 5; + static constexpr uint32_t Scene = 6; }; } } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 6fb3edfa22..896d9f8043 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,11 @@ namespace AtomToolsFramework { + AZ_CVAR( + AZ::Color, ed_cameraSystemOrbitPointColor, AZ::Color::CreateFromRgba(255, 255, 255, 255), nullptr, AZ::ConsoleFunctorFlags::Null, + ""); + AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.5f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) { @@ -73,7 +79,8 @@ namespace AtomToolsFramework if (auto viewportContext = RetrieveViewportContext(GetViewportId())) { - auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&) { + auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&) + { if (!m_updatingTransform) { UpdateCameraFromTransform(m_targetCamera, viewportContext->GetCameraTransform()); @@ -137,7 +144,10 @@ namespace AtomToolsFramework } else if (m_cameraMode == CameraMode::Animation) { - const auto smootherStepFn = [](const float t) { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; + const auto smootherStepFn = [](const float t) + { + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + }; const float transitionT = smootherStepFn(m_animationT); const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation( @@ -169,8 +179,9 @@ namespace AtomToolsFramework { if (const float alpha = AZStd::min(-m_camera.m_lookDist / 5.0f, 1.0f); alpha > AZ::Constants::FloatEpsilon) { - debugDisplay.SetColor(1.0f, 1.0f, 1.0f, alpha); - debugDisplay.DrawWireSphere(m_camera.m_lookAt, 0.5f); + const AZ::Color orbitPointColor = ed_cameraSystemOrbitPointColor; + debugDisplay.SetColor(orbitPointColor.GetR(), orbitPointColor.GetG(), orbitPointColor.GetB(), alpha); + debugDisplay.DrawWireSphere(m_camera.m_lookAt, ed_cameraSystemOrbitPointSize); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h index c71d500d8c..7f36d3fabc 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h @@ -33,7 +33,7 @@ namespace AZ namespace MaterialEditor { //! UVs are processed in a property group but will be handled differently. - static constexpr const char UvGroupName[] = "UvNames"; + static constexpr const char UvGroupName[] = "uvSets"; class MaterialDocumentRequests : public AZ::EBusTraits diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 301fd69025..2242d3af1b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -819,10 +819,10 @@ namespace MaterialEditor // is implemented. AtomToolsFramework::DynamicPropertyConfig propertyConfig; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; - propertyConfig.m_id = "details.materialType"; + propertyConfig.m_id = "overview.materialType"; propertyConfig.m_nameId = "materialType"; propertyConfig.m_displayName = "Material Type"; - propertyConfig.m_groupName = "Details"; + propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = "The material type defines the layout, properties, default values, shader connections, and other " "data needed to create and edit a derived material."; propertyConfig.m_defaultValue = AZStd::any(materialTypeAsset); @@ -834,10 +834,10 @@ namespace MaterialEditor propertyConfig = {}; propertyConfig.m_dataType = AtomToolsFramework::DynamicPropertyType::Asset; - propertyConfig.m_id = "details.parentMaterial"; + propertyConfig.m_id = "overview.parentMaterial"; propertyConfig.m_nameId = "parentMaterial"; propertyConfig.m_displayName = "Parent Material"; - propertyConfig.m_groupName = "Details"; + propertyConfig.m_groupName = "Overview"; propertyConfig.m_description = "The parent material provides an initial configuration whose properties are inherited and overriden by a derived material."; propertyConfig.m_defaultValue = AZStd::any(parentMaterialAsset); @@ -860,7 +860,7 @@ namespace MaterialEditor propertyConfig.m_id = MaterialPropertyId(UvGroupName, shaderInput).GetCStr(); propertyConfig.m_nameId = shaderInput; propertyConfig.m_displayName = shaderInput; - propertyConfig.m_groupName = "UV Names"; + propertyConfig.m_groupName = "UV Sets"; propertyConfig.m_description = shaderInput; propertyConfig.m_defaultValue = uvName; propertyConfig.m_originalValue = uvName; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 706d365027..560c82be9b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -79,8 +79,8 @@ namespace MaterialEditor if (!m_documentId.IsNull() && isOpen) { - // Create the top group for displaying details about the material - AddDetailsGroup(); + // Create the top group for displaying overview info about the material + AddOverviewGroup(); // Create groups for displaying editable UV names AddUvNamesGroup(); // Create groups for displaying editable properties @@ -105,25 +105,25 @@ namespace MaterialEditor return property && AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue); } - void MaterialInspector::AddDetailsGroup() + void MaterialInspector::AddOverviewGroup() { const AZ::RPI::MaterialTypeSourceData* materialTypeSourceData = nullptr; MaterialDocumentRequestBus::EventResult( materialTypeSourceData, m_documentId, &MaterialDocumentRequestBus::Events::GetMaterialTypeSourceData); - const AZStd::string groupNameId = "details"; - const AZStd::string groupDisplayName = "Details"; + const AZStd::string groupNameId = "overview"; + const AZStd::string groupDisplayName = "Overview"; const AZStd::string groupDescription = materialTypeSourceData->m_description; auto& group = m_groups[groupNameId]; AtomToolsFramework::DynamicProperty property; MaterialDocumentRequestBus::EventResult( - property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.materialType")); + property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("overview.materialType")); group.m_properties.push_back(property); property = {}; MaterialDocumentRequestBus::EventResult( - property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("details.parentMaterial")); + property, m_documentId, &MaterialDocumentRequestBus::Events::GetProperty, AZ::Name("overview.parentMaterial")); group.m_properties.push_back(property); // Passing in same group as main and comparison instance to enable custom value comparison for highlighting modified properties @@ -139,7 +139,7 @@ namespace MaterialEditor MaterialDocumentRequestBus::EventResult(materialAsset, m_documentId, &MaterialDocumentRequestBus::Events::GetAsset); const AZStd::string groupNameId = UvGroupName; - const AZStd::string groupDisplayName = "UV Names"; + const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; auto& group = m_groups[groupNameId]; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h index 4080430ff5..65d095f42f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h @@ -52,7 +52,7 @@ namespace MaterialEditor bool CompareInstanceNodeProperties( const AzToolsFramework::InstanceDataNode* source, const AzToolsFramework::InstanceDataNode* target) const; - void AddDetailsGroup(); + void AddOverviewGroup(); void AddUvNamesGroup(); void AddPropertiesGroup(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 3192900ca4..229606a238 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -184,7 +184,7 @@ namespace AZ void MaterialPropertyInspector::AddUvNamesGroup() { const AZStd::string groupNameId = AZ::RPI::UvGroupName; - const AZStd::string groupDisplayName = "UV Names"; + const AZStd::string groupDisplayName = "UV Sets"; const AZStd::string groupDescription = "UV set names in this material, which can be renamed to match those in the model."; auto& group = m_groups[groupNameId]; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 8cf1eeeb58..79fbb4a99e 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -383,6 +383,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) -}} + {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false)|indent(8) -}} + {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} @@ -456,7 +458,6 @@ namespace {{ Component.attrib['Namespace'] }} {% endif %} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', false)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} @@ -481,8 +482,6 @@ namespace {{ Component.attrib['Namespace'] }} //! @} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 0feff5c07c..5cfd0bfc4d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -796,12 +796,27 @@ enum class NetworkProperties AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) return {{ Property.attrib['Type'] }}(); } +{% if (ReplicateTo == 'Autonomous') or (ReplicateFrom == 'Autonomous' and ReplicateTo == 'Authority') %} + // {{ UpperFirst(Property.attrib['Name']) }} is only sent and received between contoller objects (ie Authority, Autonomous); we must go through the controller in order to get this property + {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); + if (!controller) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This property is replicated to autonomous network entities, because this entity doesn't have a controller, it must not be automonous. Please check your network context before attempting to get {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) + return {{ Property.attrib['Type'] }}(); + } +{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} + return controller->Get{{ UpperFirst(Property.attrib['Name']) }}(index); +{% else %} + return controller->Get{{ UpperFirst(Property.attrib['Name']) }}(); +{% endif %} +{% else %} {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}(index); {% else %} return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}(); {% endif %} +{% endif %} }) {% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} ->Method("Set{{ UpperFirst(Property.attrib['Name']) }}", [](AZ::EntityId id, int32_t index, const {{ Property.attrib['Type'] }}& value) -> void @@ -1480,12 +1495,8 @@ namespace {{ Component.attrib['Namespace'] }} } {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', true, ComponentBaseName)|indent(4) }} {{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}} diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index ef8627fe54..0818f605df 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -438,19 +438,16 @@ namespace Multiplayer m_connAcquiredEvent.Signal(datum); } + // Hosts will spawn a new default player prefab for the user that just connected if (GetAgentType() == MultiplayerAgentType::ClientServer || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); - INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); - - NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); + if (controlledEntity.Exists()) { - controlledEntity = entityList[0]; controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); } - + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); @@ -522,6 +519,17 @@ namespace Multiplayer } } m_agentType = multiplayerType; + + // Spawn the default player for this host since the host is also a player (not a dedicated server) + if (m_agentType == MultiplayerAgentType::ClientServer) + { + NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); + if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + { + controlledEntityNetBindComponent->SetAllowAutonomy(true); + } + } + AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); } @@ -629,6 +637,19 @@ namespace Multiplayer } } + NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() + { + PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); + INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); + + NetworkEntityHandle controlledEntity; + if (entityList.size() > 0) + { + controlledEntity = entityList[0]; + } + return controlledEntity; + } + void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index db83c50fb5..e8e05a9d4c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -104,7 +104,8 @@ namespace Multiplayer void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); - + NetworkEntityHandle SpawnDefaultPlayerPrefab(); + AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); AzNetworking::INetworkInterface* m_networkInterface = nullptr; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index dda3f18ad4..eaf89f3489 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -402,8 +402,12 @@ namespace Multiplayer const AZ::Transform& transform ) { - INetworkEntityManager::EntityList returnList; - + EntityList returnList; + if (!AZ::Data::AssetManager::IsReady()) + { + return returnList; + } + auto spawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(prefabEntryId.m_prefabName); // Required for sync-instantiation. Todo: keep the reference in NetworkSpawnableLibrary auto netSpawnableAsset = AZ::Data::AssetManager::Instance().GetAsset(spawnableAssetId, AZ::Data::AssetLoadBehavior::PreLoad); diff --git a/Gems/PhysX/Code/CMakeLists.txt b/Gems/PhysX/Code/CMakeLists.txt index b0318af9f2..d281270ce4 100644 --- a/Gems/PhysX/Code/CMakeLists.txt +++ b/Gems/PhysX/Code/CMakeLists.txt @@ -128,6 +128,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AUTOMOC FILES_CMAKE physx_editor_shared_files.cmake + COMPILE_DEFINITIONS + PUBLIC + PHYSX_ENABLE_MULTI_THREADING INCLUDE_DIRECTORIES PRIVATE . diff --git a/Gems/PythonAssetBuilder/Code/CMakeLists.txt b/Gems/PythonAssetBuilder/Code/CMakeLists.txt index 4af266f56d..73fa90ab67 100644 --- a/Gems/PythonAssetBuilder/Code/CMakeLists.txt +++ b/Gems/PythonAssetBuilder/Code/CMakeLists.txt @@ -13,24 +13,11 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) return() endif() -set(static_files pythonassetbuilder_common_files.cmake) -set(editor_files pythonassetbuilder_editor_files.cmake) -set(shared_files pythonassetbuilder_shared_files.cmake) -set(static_dependencies - 3rdParty::Python - Gem::EditorPythonBindings.Static - AZ::AssetBuilderSDK -) -set(editor_dependencies - Gem::EditorPythonBindings.Static - AZ::AssetBuilderSDK -) - ly_add_target( NAME PythonAssetBuilder.Static STATIC NAMESPACE Gem FILES_CMAKE - ${static_files} + pythonassetbuilder_common_files.cmake PLATFORM_INCLUDE_FILES Source/Platform/Common/${PAL_TRAIT_COMPILER_ID}/pythonassetbuilder_static_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES @@ -43,7 +30,9 @@ ly_add_target( PRIVATE AZ::AzCore PUBLIC - ${static_dependencies} + 3rdParty::Python + Gem::EditorPythonBindings.Static + AZ::AssetBuilderSDK AZ::AzToolsFramework ) @@ -52,8 +41,8 @@ ly_add_target( NAMESPACE Gem FILES_CMAKE - ${editor_files} - ${shared_files} + pythonassetbuilder_editor_files.cmake + pythonassetbuilder_shared_files.cmake PLATFORM_INCLUDE_FILES Source/Platform/Common/${PAL_TRAIT_COMPILER_ID}/pythonassetbuilder_static_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake INCLUDE_DIRECTORIES @@ -64,7 +53,8 @@ ly_add_target( Include BUILD_DEPENDENCIES PRIVATE - ${editor_dependencies} + Gem::EditorPythonBindings.Static + AZ::AssetBuilderSDK RUNTIME_DEPENDENCIES Gem::EditorPythonBindings.Editor ) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp index b93844b989..21d1fc5264 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp @@ -118,7 +118,7 @@ namespace ScriptCanvas::Nodeables::Spawning AZ::Vector3 rotationCopy = rotation; AZ::Quaternion rotationQuat = AZ::Quaternion::CreateFromEulerAnglesDegrees(rotationCopy); - entityTransform->SetWorldTM(AZ::Transform(translation, rotationQuat, AZ::Vector3(scale, scale, scale))); + entityTransform->SetWorldTM(AZ::Transform(translation, rotationQuat, scale)); } }; diff --git a/README.md b/README.md index e0442adc16..0c59837a62 100644 --- a/README.md +++ b/README.md @@ -118,11 +118,6 @@ If you have the Git credential manager core or other credential helpers installe ``` python\get_python.bat ``` - -1. While still within the repo folder, register the engine with this command: - ``` - scripts\o3de.bat register --this-engine - ``` 1. Configure the source into a solution using this command line, replacing and <3rdParty cache path> to a path you've created: ``` @@ -146,7 +141,11 @@ If you have the Git credential manager core or other credential helpers installe 1. This will compile after some time and binaries will be available in the build path you've specified -### Setting up new projects +### Setting up new projects +1. While still within the repo folder, register the engine with this command: + ``` + scripts\o3de.bat register --this-engine + ``` 1. Setup new projects using the `o3de create-project` command. In the 0.5 branch, the project directory must be a subdirectory in the repo folder. ``` \scripts\o3de.bat create-project --project-path @@ -160,10 +159,10 @@ If you have the Git credential manager core or other credential helpers installe cmake -B -S -G "Visual Studio 16" -DLY_3RDPARTY_PATH=<3rdParty cache path> // For the 0.5 branch, you must build a new Editor for each project: - cmake --build --target Editor --config profile -- /m + cmake --build --target .GameLauncher Editor --config profile -- /m // For all other branches, just build the project: - cmake --build --target --config profile -- /m + cmake --build --target .GameLauncher --config profile -- /m ``` For a tutorial on project configuration, see [Creating Projects Using the Command Line](https://docs.o3de.org/docs/welcome-guide/get-started/project-config/creating-projects-using-cli) in the documentation. diff --git a/cmake/Platform/Common/RuntimeDependencies_common.cmake b/cmake/Platform/Common/RuntimeDependencies_common.cmake index d9d0fe4c7f..4ae914744f 100644 --- a/cmake/Platform/Common/RuntimeDependencies_common.cmake +++ b/cmake/Platform/Common/RuntimeDependencies_common.cmake @@ -105,7 +105,7 @@ function(ly_get_runtime_dependencies ly_RUNTIME_DEPENDENCIES ly_TARGET) set(skip_imported TRUE) endif() endif() - if(target_type MATCHES "(INTERFACE_LIBRARY|STATIC_LIBRARY)") + if(target_type MATCHES "(STATIC_LIBRARY)") # No need to copy these dependencies since the outputs are not used at runtime set(skip_imported TRUE) endif() diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 6443077457..75e0cd2970 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -10,7 +10,9 @@ # import imghdr +import configparser import datetime +import fnmatch import logging import os import json @@ -33,6 +35,13 @@ if ROOT_DEV_PATH not in sys.path: from cmake.Tools import common +ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP = { + '4.2.0': {'min_gradle_version': '6.7.1', + 'sdk_build': '30.0.2', + 'default_ndk': '21.4.7075529', + 'min_cmake_version': '3.20'} +} + APP_NAME = 'app' ANDROID_MANIFEST_FILE = 'AndroidManifest.xml' ANDROID_LIBRARIES_JSON_FILE = 'android_libraries.json' @@ -86,83 +95,93 @@ PYTHON_SCRIPT = 'python.cmd' if platform.system() == 'Windows' else 'python.sh' ANDROID_LAUNCHER_NAME_PATTERN = "{project_name}.GameLauncher" + class AndroidProjectManifestEnvironment(object): """ - This class manages the environment for the AndroidManifiest.xml template file, based on project settings and environments + This class manages the environment for the AndroidManifest.xml template file, based on project settings and environments that were passed in or calculated from the command line arguments. """ - def __init__(self, engine_root, project_path, android_sdk_version_number, android_ndk_platform_number, is_test:bool): + def __init__(self, engine_root, project_path, android_sdk_version_number, is_test:bool): """ Initialize the object with the project specific parameters and values for the game project :param engine_root: The path where the engine is located :param project_path: The path were the project is located :param android_sdk_version_number: The android SDK platform version - :param android_ndk_platform_number: The android NDK platform version :param is_test: Indicates if theAzTestRunner application should be run """ - if is_test: - # The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json - project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json' - else: - # The project.json file is located under the game name folder - project_properties_path = project_path / 'project.json' - # Read and parse the project.json file into a dictionary to process the specific attributes needed for the manifest template - project_properties_content = project_properties_path.resolve(strict=True)\ - .read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, - errors=common.ENCODING_ERROR_HANDLINGS) - self.project_path = project_path + try: + if is_test: + # The AzTestRunner project.json is located under {engine_root}/Code/Tools/AzTestRunner/Platform/Android/android_project.json + project_properties_path = engine_root / 'Code' / 'Tools' / 'AzTestRunner' / 'Platform' / 'Android' / 'android_project.json' + assert project_properties_path.is_file(), f'Missing required android settings file {project_properties_path.resolve()}' + project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + project_json = json.loads(project_properties_content) - # Extract the key attributes we need to process and build up our environment table - project_json = json.loads(project_properties_content) + android_settings = project_json['android_settings'] - project_name = project_json.get('project_name') - if not project_name: - raise common.LmbrCmdError(f"Missing required 'project_name' from project.json for project at '{str(project_path)}'") - product_name = project_json.get('product_name', project_name) + else: + # O3DE projects have both a project.json and an android_project.json files (unless its internal) + project_properties_path = project_path / 'project.json' + assert project_properties_path.is_file(), f'Missing required project settings file {project_properties_path.resolve()}' + project_properties_content = project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + project_json = json.loads(project_properties_content) - game_project_android_settings = project_json['android_settings'] + android_project_properties_path = project_path / 'Platform' / 'Android' / 'android_project.json' + if android_project_properties_path.is_file(): + android_project_properties_content = android_project_properties_path.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, + errors=common.ENCODING_ERROR_HANDLINGS) + android_project_json = json.loads(android_project_properties_content) + android_settings = android_project_json['android_settings'] + else: + android_settings = project_json['android_settings'] - package_name = game_project_android_settings["package_name"] + self.project_path = project_path - package_path = package_name.replace('.', '/') + project_name = project_json['project_name'] + product_name = project_json.get('product_name', project_name) + package_name = android_settings["package_name"] + package_path = package_name.replace('.', '/') - project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity' + project_activity = f'{TEST_RUNNER_PROJECT}Activity' if is_test else f'{project_name}Activity' - # Multiview options require special processing - multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(game_project_android_settings) + # Multiview options require special processing + multi_window_options = AndroidProjectManifestEnvironment.process_android_multi_window_options(android_settings) - self.internal_dict = { - 'ANDROID_PACKAGE': package_name, - 'ANDROID_PACKAGE_PATH': package_path, - 'ANDROID_VERSION_NUMBER': game_project_android_settings["version_number"], - "ANDROID_VERSION_NAME": game_project_android_settings["version_name"], - "ANDROID_SCREEN_ORIENTATION": game_project_android_settings["orientation"], - 'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name - 'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name - 'ANDROID_PROJECT_ACTIVITY': project_activity, - 'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name), - 'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'], - 'ANDROID_APP_PUBLIC_KEY': game_project_android_settings.get('app_public_key', 'NoKey'), - 'ANDROID_APP_OBFUSCATOR_SALT': game_project_android_settings.get('app_obfuscator_salt', ''), - 'ANDROID_USE_MAIN_OBB': game_project_android_settings.get('use_main_obb', 'false'), - 'ANDROID_USE_PATCH_OBB': game_project_android_settings.get('use_patch_obb', 'false'), - 'ANDROID_ENABLE_KEEP_SCREEN_ON': game_project_android_settings.get('enable_keep_screen_on', 'false'), - 'ANDROID_DISABLE_IMMERSIVE_MODE': game_project_android_settings.get('disable_immersive_mode', 'false'), - 'ANDROID_MIN_SDK_VERSION': android_ndk_platform_number, - 'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number, - 'ICONS': game_project_android_settings.get('icons', None), - 'SPLASH_SCREEN': game_project_android_settings.get('splash_screen', None), + self.internal_dict = { + 'ANDROID_PACKAGE': package_name, + 'ANDROID_PACKAGE_PATH': package_path, + 'ANDROID_VERSION_NUMBER': android_settings["version_number"], + "ANDROID_VERSION_NAME": android_settings["version_name"], + "ANDROID_SCREEN_ORIENTATION": android_settings["orientation"], + 'ANDROID_APP_NAME': TEST_RUNNER_PROJECT if is_test else product_name, # external facing name + 'ANDROID_PROJECT_NAME': TEST_RUNNER_PROJECT if is_test else project_name, # internal facing name + 'ANDROID_PROJECT_ACTIVITY': project_activity, + 'ANDROID_LAUNCHER_NAME': TEST_RUNNER_PROJECT if is_test else ANDROID_LAUNCHER_NAME_PATTERN.format(project_name=project_name), + 'ANDROID_CONFIG_CHANGES': multi_window_options['ANDROID_CONFIG_CHANGES'], + 'ANDROID_APP_PUBLIC_KEY': android_settings.get('app_public_key', 'NoKey'), + 'ANDROID_APP_OBFUSCATOR_SALT': android_settings.get('app_obfuscator_salt', ''), + 'ANDROID_USE_MAIN_OBB': android_settings.get('use_main_obb', 'false'), + 'ANDROID_USE_PATCH_OBB': android_settings.get('use_patch_obb', 'false'), + 'ANDROID_ENABLE_KEEP_SCREEN_ON': android_settings.get('enable_keep_screen_on', 'false'), + 'ANDROID_DISABLE_IMMERSIVE_MODE': android_settings.get('disable_immersive_mode', 'false'), + 'ANDROID_TARGET_SDK_VERSION': android_sdk_version_number, + 'ICONS': android_settings.get('icons', None), + 'SPLASH_SCREEN': android_settings.get('splash_screen', None), - 'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'], - 'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'], + 'ANDROID_MULTI_WINDOW': multi_window_options['ANDROID_MULTI_WINDOW'], + 'ANDROID_MULTI_WINDOW_PROPERTIES': multi_window_options['ANDROID_MULTI_WINDOW_PROPERTIES'], - 'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'], - 'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'], - 'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT'] - } + 'SAMSUNG_DEX_KEEP_ALIVE': multi_window_options['SAMSUNG_DEX_KEEP_ALIVE'], + 'SAMSUNG_DEX_LAUNCH_WIDTH': multi_window_options['SAMSUNG_DEX_LAUNCH_WIDTH'], + 'SAMSUNG_DEX_LAUNCH_HEIGHT': multi_window_options['SAMSUNG_DEX_LAUNCH_HEIGHT'] + } + except KeyError as e: + raise common.LmbrCmdError(f"Missing key from android project settings for project at {project_path}:'{e}' ") def __getitem__(self, item): return self.internal_dict.get(item) @@ -306,6 +325,7 @@ asset_deploy_type={asset_type} android_sdk_path={android_sdk_path} embed_assets_in_apk={embed_assets_in_apk} is_unit_test={is_unit_test} +android_gradle_plugin={android_gradle_plugin_version} """ NATIVE_CMAKE_SECTION_ANDROID_FORMAT = """ @@ -425,26 +445,28 @@ class AndroidProjectGenerator(object): Class the manages the process to generate an android project folder in order to build with gradle/android studio """ - def __init__(self, engine_root, build_dir, android_ndk_path, android_sdk_path, android_sdk_version, android_ndk_platform, - project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, override_ninja_path, - android_sdk_build_tool_version, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False, + def __init__(self, engine_root, build_dir, android_sdk_path, build_tool, android_sdk_platform, android_native_api_level, android_ndk, + project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, gradle_version, gradle_plugin_version, + override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, is_test_project=False, overwrite_existing=True): """ Initialize the object with all the required parameters needed to create an Android Project. The parameters should be verified before initializing this object - + :param engine_root: The engine root that contains the engine :param build_dir: The target folder under the where the android project folder will be created - :param android_ndk_path: The path to the ANDROID_NDK used for building the native android code :param android_sdk_path: The path to the ANDROID_SDK used for building the android java code - :param android_sdk_version: The android platform version number to use for the Android SDK related builds - :param android_ndk_platform: The android platform version number to use for the Android NDK related builds + :param build_tool: The android SDK build-tool version. + :param android_sdk_platform: The android sdk platform version number to use for the Android SDK related builds + :param android_native_api_level:The android native API level (ANDROID_NATIVE_API_LEVEL) to set + :param android_ndk: The android ndk version number to use for the native builds :param project_path: The path to the project :param third_party_path: The required path to the lumberyard 3rd party path :param cmake_version: The version number of cmake that will be used by gradle :param override_cmake_path: The override path to cmake if it does not exists in the system path :param override_gradle_path: The override path to gradle if it does not exists in the system path + :param gradle_version: The detected version of gradle being used + :param gradle_plugin_version: The android gradle plugin version :param override_ninja_path: The override path to ninja if it does not exists in the system path - :param android_sdk_build_tool_version: The preferred android SDK build-tool version. Will default to the first one detected in the android sdk path :param include_assets_in_apk: :param asset_mode: :param asset_type: @@ -458,17 +480,16 @@ class AndroidProjectGenerator(object): self.build_dir = build_dir - self.android_ndk_path = android_ndk_path - self.android_sdk_path = android_sdk_path self.android_project_builder_path = self.engine_root / 'Code/Tools/Android/ProjectBuilder' - self.android_sdk_version = android_sdk_version + self.android_sdk_platform = android_sdk_platform + self.android_sdk_build_tool_version = build_tool.version - self.android_sdk_build_tool_version = android_sdk_build_tool_version - - self.android_ndk_platform = android_ndk_platform + self.android_ndk = android_ndk + self.android_ndk_version = android_ndk.version + self.android_native_api_level = android_native_api_level self.project_path = project_path @@ -480,6 +501,10 @@ class AndroidProjectGenerator(object): self.override_gradle_path = override_gradle_path + self.gradle_version = gradle_version + + self.gradle_plugin_version = gradle_plugin_version + self.override_ninja_path = override_ninja_path self.include_assets_in_apk = include_assets_in_apk @@ -511,8 +536,10 @@ class AndroidProjectGenerator(object): project_names.extend(self.create_lumberyard_app(project_names)) root_gradle_env = { - 'SDK_VER': self.android_sdk_version, - 'NDK_PLATFORM_VER': self.android_ndk_platform, + 'ANDROID_GRADLE_PLUGIN_VERSION': str(self.gradle_plugin_version), + 'SDK_VER': self.android_sdk_platform, + 'MIN_SDK_VER': self.android_sdk_platform, + 'NDK_VERSION': self.android_ndk_version, 'SDK_BUILD_TOOL_VER': self.android_sdk_build_tool_version, 'LY_ENGINE_ROOT': common.normalize_path_for_settings(self.engine_root) } @@ -557,7 +584,7 @@ class AndroidProjectGenerator(object): if self.override_gradle_path: gradle_wrapper_cmd = [self.override_gradle_path] else: - gradle_wrapper_cmd = ['gradle.bat' if platform.system() == 'Windows' else 'gradle'] + gradle_wrapper_cmd = ['gradle'] gradle_wrapper_cmd.extend(['wrapper', '-p', str(self.build_dir.resolve())]) @@ -580,7 +607,8 @@ class AndroidProjectGenerator(object): asset_type='', android_sdk_path=str(self.android_sdk_path), embed_assets_in_apk=True, - is_unit_test=True) + is_unit_test=True, + android_gradle_plugin_version=self.gradle_plugin_version) else: platform_settings_content = PLATFORM_SETTINGS_FORMAT.format(generation_timestamp=str(datetime.datetime.now().strftime("%c")), platform='android', @@ -589,16 +617,28 @@ class AndroidProjectGenerator(object): asset_type=self.asset_type, android_sdk_path=str(self.android_sdk_path), embed_assets_in_apk=str(self.include_assets_in_apk), - is_unit_test=False) + is_unit_test=False, + android_gradle_plugin_version=self.gradle_plugin_version) platform_settings_file = self.build_dir / 'platform.settings' + + # Check if there already exists the build folder and a 'platform.settings' file. If there is an android gradle + # plugin version set and it is different than the one configured here, we will always overwrite it since + # there could be significant differences from one plug-in to the next + if platform_settings_file.is_file(): + config = configparser.ConfigParser() + config.read([str(platform_settings_file.resolve(strict=True))]) + if config.has_option('android', 'android_gradle_plugin'): + exist_agp_version = config.get('android', 'android_gradle_plugin') + if exist_agp_version != self.gradle_plugin_version: + self.overwrite_existing = True + platform_settings_file.open('w').write(platform_settings_content) def create_default_local_properties(self): """ Create the default 'local.properties' file in the build folder """ - template_android_ndk_path = common.normalize_path_for_settings(self.android_ndk_path, True) template_android_sdk_path = common.normalize_path_for_settings(self.android_sdk_path, True) if self.override_cmake_path: # The cmake dir references the base cmake folder, not the executable path itself, so resolve to the base folder @@ -608,7 +648,6 @@ class AndroidProjectGenerator(object): local_properties_env = { "GENERATION_TIMESTAMP": str(datetime.datetime.now().strftime("%c")), - "ANDROID_NDK_PATH": template_android_ndk_path, "ANDROID_SDK_PATH": template_android_sdk_path, "CMAKE_DIR_LINE": f'cmake.dir={template_cmake_path}' if template_cmake_path else '' } @@ -626,8 +665,7 @@ class AndroidProjectGenerator(object): # before we can process it. android_libraries_substitution_table = { "ANDROID_SDK_HOME": common.normalize_path_for_settings(self.android_sdk_path, False), - "ANDROID_NDK_HOME": common.normalize_path_for_settings(self.android_ndk_path, False), - "ANDROID_SDK_VERSION": "android-".format(self.android_sdk_version) + "ANDROID_SDK_VERSION": f"android-{self.android_sdk_platform}" } android_libraries_template_json_path = self.android_project_builder_path / ANDROID_LIBRARIES_JSON_FILE @@ -717,7 +755,7 @@ class AndroidProjectGenerator(object): template_engine_root = common.normalize_path_for_settings(self.engine_root) template_third_party_path = common.normalize_path_for_settings(self.third_party_path) - template_ndk_path = common.normalize_path_for_settings(self.android_ndk_path) + template_ndk_path = common.normalize_path_for_settings(os.path.join(self.android_sdk_path, self.android_ndk.location)) gradle_build_env = dict() @@ -733,7 +771,6 @@ class AndroidProjectGenerator(object): gradle_build_env['OVERRIDE_JAVA_SOURCESET'] = OVERRIDE_JAVA_SOURCESET_STR.format(absolute_azandroid_path=absolute_azandroid_path) - gradle_build_env['OPTIONAL_JNI_SRC_LIB_SET'] = ', "outputs/native-lib"' for native_config in BUILD_CONFIGURATIONS: @@ -755,7 +792,7 @@ class AndroidProjectGenerator(object): cmake_argument_list.append('"-DLY_TEST_PROJECT=1"') cmake_argument_list.extend([ - f'"-DANDROID_NATIVE_API_LEVEL={self.android_ndk_platform}"', + f'"-DANDROID_NATIVE_API_LEVEL={self.android_native_api_level}"', f'"-DLY_NDK_DIR={template_ndk_path}"', '"-DANDROID_STL=c++_shared"', '"-Wno-deprecated"', @@ -835,8 +872,7 @@ class AndroidProjectGenerator(object): dest_src_main_path.mkdir(parents=True) az_android_package_env = AndroidProjectManifestEnvironment(engine_root=self.engine_root, project_path=self.project_path, - android_sdk_version_number=self.android_sdk_version, - android_ndk_platform_number=self.android_ndk_platform, + android_sdk_version_number=self.android_sdk_platform, is_test=self.is_test_project) self.create_file_from_project_template(src_template_file=ANDROID_MANIFEST_FILE, template_env=az_android_package_env, @@ -1304,218 +1340,7 @@ class AndroidProjectGenerator(object): self.new = new -ANDROID_PLATFORM_PATTERN = re.compile(r'([\w\d]*-)?(\d+\d*)') # Regex to handle android platform naming for both SDKs and NDKs - - -def validate_android_platform_input(input_android_platform, platform_variable_type, min_version, max_version): - """ - Helper tool to support android platform number inputs and perform min/max version validation - - :param input_android_platform: The inpuit argument to evaluate - :param platform_variable_type: The type of platform version to validate (android sdk / android ndk) - :param min_version: The minimum version to validate against - :param max_version: The maximum version to validate against - :return: The int version of the extracted platform number from the input - """ - # Validate the platform number's format and against the supported versions - platform_number_match = ANDROID_PLATFORM_PATTERN.search(input_android_platform) - if not platform_number_match or not platform_number_match.group(2) or (platform_number_match.group(1) and platform_number_match.group(1) != 'android-'): - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}). It must be " - f"either 'XX' or android-'XX' where 'XX' is a platform number.", - common.ERROR_CODE_INVALID_PARAMETER) - - android_platform_number = int(platform_number_match.group(2)) - if android_platform_number < min_version: - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is less than the minimum " - f"supported version ({min_version}).", - common.ERROR_CODE_INVALID_PARAMETER) - if android_platform_number > max_version: - raise common.LmbrCmdError(f"Invalid {platform_variable_type} version value ({input_android_platform}) is greater than the maximum " - f"supported version ({max_version}).", - common.ERROR_CODE_INVALID_PARAMETER) - return android_platform_number - - ANDROID_SDK_ENV_NAME = 'ANDROID_SDK' -ANDROID_SDK_MIN_PLATFORM = 28 -ANDROID_SDK_MAX_PLATFORM = 29 - - -def verify_android_sdk(android_sdk_platform, argument_name, override_android_sdk_path=None, preferred_sdk_build_tools_ver=None): - """ - Verify the android sdk and the requested platform platform against the android sdk path - - :param android_sdk_platform: The android sdk platform to use (e.g. '28' or 'android-28') - :param argument_name: The name of the argument for descriptive errors to present - :param override_android_sdk_path: The location of the android SDK path if not set through the environment variable - :param preferred_sdk_build_tools_ver: Option prefered built tool version under the android SDK if available. Will fallback to the first one discovered - :returns tuple of the verified android sdk platform number, path to the Android SDK path and the build tool version - """ - android_sdk_platform_number = validate_android_platform_input(input_android_platform=android_sdk_platform, - platform_variable_type='android sdk', - min_version=ANDROID_SDK_MIN_PLATFORM, - max_version=ANDROID_SDK_MAX_PLATFORM) - - # Get the candidate android sdk path from either the override argument or the system environment variable - if override_android_sdk_path: - check_android_sdk_path = override_android_sdk_path - else: - check_android_sdk_path = os.environ.get(ANDROID_SDK_ENV_NAME) - if not check_android_sdk_path: - raise common.LmbrCmdError(f"Android SDK path not set. Make sure that either the '{ANDROID_SDK_ENV_NAME}' environment is " - f"set or it is passed in through the {argument_name} argument") - - # The android sdk folder structure is expected to have a 'platforms' sub folder based on the android sdk-platform number - check_android_sdk_path = pathlib.Path(check_android_sdk_path) - android_sdk_platforms_path = check_android_sdk_path / 'platforms' - if not android_sdk_platforms_path.is_dir(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Missing 'platforms' directory.") - - # Collect the available platform numbers from the platforms subdirectory - validated_android_platforms = [] - for dir_item in android_sdk_platforms_path.iterdir(): - if not dir_item.is_dir(): - continue - check_file = dir_item / 'package.xml' - if check_file.is_file(): - validated_android_platforms.append(dir_item.name) - - if not validated_android_platforms: - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any android platforms.") - - # Normalize the android_sdk argument to fit the same folder name pattern - android_sdk_platform_name = f'android-{android_sdk_platform_number}' - if android_sdk_platform_name not in validated_android_platforms: - raise common.LmbrCmdError(f"Android SDK platform {android_sdk_platform_name} is not a valid for the android SDK located under '{str(check_android_sdk_path)}'") - - # Enumerate through the build tools under android sdk - android_sdk_build_tools_dir = check_android_sdk_path / 'build-tools' - if not android_sdk_build_tools_dir.is_dir(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools folder.") - supported_build_tools = [str(build_tool.name) for build_tool in android_sdk_build_tools_dir.iterdir() if build_tool.is_dir()] - if not supported_build_tools: - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(check_android_sdk_path)}': Unable to find any built-tools.") - if preferred_sdk_build_tools_ver: - if preferred_sdk_build_tools_ver in supported_build_tools: - validated_build_tool = preferred_sdk_build_tools_ver - else: - validated_build_tool = supported_build_tools[0] - logging.warning("Unable to locate android sdk build tool version {preferred_sdk_build_tools_ver}. Defaulting to version {validated_build_tool}") - - else: - validated_build_tool = supported_build_tools[0] - - return android_sdk_platform_number, check_android_sdk_path, validated_build_tool - - -ANDROID_NDK_ENV_NAME = 'ANDROID_NDK' -ANDROID_NDK_MIN_PLATFORM = 21 -ANDROID_NDK_MAX_PLATFORM = 29 -ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN = re.compile(r'Pkg.Revision\s*=\s*(\d+.\d+.\d+)') - - -def verify_android_ndk(android_ndk_platform, argument_name, override_android_ndk_path=None): - """ - Verify the android ndk and requested platform against the android ndk path - - :param android_ndk_platform: The android ndk platform to use (e.g. '21' or 'android-21') - :param argument_name: The name of the argument for descriptive errors to present - :param override_android_ndk_path: The location of the android NDK path if not set through the environment variable - :returns tuple of the verified android ndk platform number and the Path to the Android SDK path and the - """ - - android_ndk_platform_number = validate_android_platform_input(input_android_platform=android_ndk_platform, - platform_variable_type='android ndk', - min_version=ANDROID_NDK_MIN_PLATFORM, - max_version=ANDROID_NDK_MAX_PLATFORM) - - # Get the candidate android ndk path from either the override argument or the system environment variable - if override_android_ndk_path: - check_android_ndk_path = str(override_android_ndk_path) - else: - check_android_ndk_path = os.environ.get(ANDROID_NDK_ENV_NAME) - if not check_android_ndk_path: - raise common.LmbrCmdError(f"Android NDK path not set. Make sure that either the {ANDROID_NDK_ENV_NAME} environment " - f"is set or it is passed in through the {argument_name} argument") - check_android_ndk_path = pathlib.Path(check_android_ndk_path) - - # Validate the android ndk path - - # Determine the NDK revision by reading the source.properties file - ndk_source_properties_file = check_android_ndk_path / 'source.properties' - if not ndk_source_properties_file.is_file(): - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'source.properties' file.", - common.ERROR_CODE_INVALID_PARAMETER) - ndk_source_properties_file_content = ndk_source_properties_file.read_text(encoding=common.DEFAULT_TEXT_READ_ENCODING, - errors=common.ENCODING_ERROR_HANDLINGS) - - ndk_revision_match = ANDROID_NDK_SOURCE_PROPERTIES_REVISION_PATTERN.search(ndk_source_properties_file_content) - if not ndk_revision_match: - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Unable to extract version from 'source.properties' file.", - common.ERROR_CODE_INVALID_PARAMETER) - ndk_revision_number = LooseVersion(ndk_revision_match.group(1)) - logging.info(f"Detected Android NDK Revision {str(ndk_revision_number)}") - - # Collect the supported android platforms from the required 'platforms' folder under the ndk path - android_ndk_platforms_path = check_android_ndk_path / 'platforms' - if not android_ndk_platforms_path.is_dir(): - raise common.LmbrCmdError(f"Invalid Android NDK path '{str(check_android_ndk_path)}'. Missing 'platforms' folder.", - common.ERROR_CODE_INVALID_PARAMETER) - - validated_android_platforms = [] - for dir_item in android_ndk_platforms_path.iterdir(): - if not dir_item.is_dir(): - continue - api_version_match = ANDROID_PLATFORM_PATTERN.search(dir_item.name) - if not api_version_match or api_version_match.group(1) != 'android-': - continue - - check_lib_path = dir_item / 'arch-arm64/usr/lib' - if check_lib_path.is_dir(): - validated_android_platforms.append(dir_item.name) - - # For NDK revisions 19 and up, there is a mapping file for version numbers that map to other version. - platforms_map_aliases = {} - if ndk_revision_number >= LooseVersion('19.0.0'): - platforms_map_file = check_android_ndk_path / 'meta/platforms.json' - if platforms_map_file.exists(): - with open(platforms_map_file, 'r') as platforms_map_file_handle: - platforms_map_file_json = json.load(platforms_map_file_handle) - platforms_map_aliases = platforms_map_file_json['aliases'] - elif validated_android_platforms: - # Revisions before 19 does not have a mapping file for API versions, they fall back to the previous one - # So we need to make a mapping file that does the same - platforms_map_aliases = {} - validated_android_platforms.sort() - max_supported_api_number = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platforms[-1]).group(2)) - for validated_android_platform in validated_android_platforms: - current_api_version = int(ANDROID_PLATFORM_PATTERN.search(validated_android_platform).group(2)) - next_api_version = current_api_version + 1 - while f'android-{next_api_version}' not in validated_android_platforms and next_api_version <= max_supported_api_number: - platforms_map_aliases[str(next_api_version)] = current_api_version - next_api_version += 1 - - # Go through the aliases and add to the validated platforms if it is mapped to an existing platform - for alias_key, alias_value in platforms_map_aliases.items(): - if not ANDROID_PLATFORM_PATTERN.search(f'android-{alias_key}'): - # Skip any non android-XX (XX = number) aliases - continue - aliased_platform_key = f'android-{alias_value}' - if aliased_platform_key in validated_android_platforms: - validated_android_platforms.append(f'android-{alias_key}') - - if not validated_android_platforms: - raise common.LmbrCmdError(f"Invalid Android NDK path {str(check_android_ndk_path)}") - - # Verify the ndk platform against the ndk path - android_ndk_platform_name = f'android-{android_ndk_platform_number}' - if android_ndk_platform_name not in validated_android_platforms: - raise common.LmbrCmdError(f"Android NDK platform {android_ndk_platform_name} is not a valid for the Android NDK located under '{str(check_android_ndk_path)}'") - - return android_ndk_platform_number, check_android_ndk_path - - -ADB_TARGET = 'adb.exe' if platform.system() == 'Windows' else 'adb' def resolve_adb_tool(android_sdk_path): @@ -1528,9 +1353,16 @@ def resolve_adb_tool(android_sdk_path): if isinstance(android_sdk_path, str): android_sdk_path = pathlib.Path(android_sdk_path) - check_adb_target = android_sdk_path / 'platform-tools' / ADB_TARGET - if not check_adb_target.exists(): - raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate '{ADB_TARGET}'.") + file_found = False + for executable_path_ext in common.PLATFORM_EXECUTABLE_EXTENSIONS: + check_adb_target = android_sdk_path / 'platform-tools' / f'adb{executable_path_ext}' + if check_adb_target.is_file(): + file_found = True + break + + if not file_found: + raise common.LmbrCmdError(f"Invalid Android SDK path '{str(android_sdk_path)}': Unable to locate 'adb'.") + return check_adb_target @@ -1633,3 +1465,196 @@ class AdbTool(common.CommandLineExec): else: adb_params = arguments return super().popen(adb_params, cwd) + + +class AndroidGradlePluginInfo(object): + + def __init__(self, android_gradle_plugin_version): + + if android_gradle_plugin_version not in ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys(): + raise common.LmbrCmdError(f"Android Gradle Plugin version {android_gradle_plugin_version} is not supported. " + f"Only the following version(s) are supported: {','.join(ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP.keys())}") + + details = ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP[android_gradle_plugin_version] + self.default_sdk_build_tools_version = LooseVersion(details.get('sdk_build')) + + self.default_ndk_version = LooseVersion(details.get('default_ndk')) + + self.min_gradle_version = LooseVersion(details.get('min_gradle_version')) + + self.min_cmake_version = LooseVersion(details.get('min_cmake_version')) + + max_cmake_version_number = details.get('max_cmake_version') + self.max_cmake_version = None if max_cmake_version_number is None else LooseVersion(max_cmake_version_number) + + +class AndroidSDKResolver(object): + """ + Class that manages the Android SDK tool to validate, install packages (e.g. built tools, sdk platforms, ndk, etc) + """ + + class InstalledPackage(object): + def __init__(self, installed_package_components): + assert len(installed_package_components) == 4, '4 sections expected for installed package components (path, version, description, location)' + self.path = installed_package_components[0] + self.version = LooseVersion(installed_package_components[1]) + self.description = installed_package_components[2] + self.location = installed_package_components[3] + + class AvailablePackage(object): + def __init__(self, available_package_components): + assert len(available_package_components) == 3, '3 sections expected for installed package components (path, version, description)' + self.path = available_package_components[0] + self.version = LooseVersion(available_package_components[1]) + self.description = available_package_components[2] + + class AvailableUpdate(object): + def __init__(self, available_update_components): + assert len(available_update_components) == 3, '3 sections expected for installed package components (path, version, available)' + self.path = available_update_components[0] + self.version = LooseVersion(available_update_components[1]) + self.available = available_update_components[2] + + def __init__(self, android_sdk_path): + + self.android_sdk_path = android_sdk_path or os.environ.get(ANDROID_SDK_ENV_NAME) + if not self.android_sdk_path: + raise common.LmbrCmdError(f"Android SDK path not set or it was not passed into the command to generate the android project") + if not os.path.isdir(self.android_sdk_path): + raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid") + if platform.system() == 'Windows': + self.sdk_manager_path = pathlib.Path(self.android_sdk_path) / 'tools' / 'bin' / 'sdkmanager.bat' + else: + raise common.LmbrCmdError(f"This tool is not supported on the current platform {platform.system()}") + if not self.sdk_manager_path.is_file(): + raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid or complete. Missing {self.sdk_manager_path}") + + self.sdk_manager = common.CommandLineExec(str(self.sdk_manager_path.resolve())) + + self.installed_packages = {} + self.available_packages = {} + self.available_updates = {} + self.refresh_sdk_installation() + + def refresh_sdk_installation(self): + """ + Utilize the sdk_manager command line tool from the Android SDK to collect / refresh the list of + installed, available, and updateable packages that are managed by the android SDK. + """ + self.installed_packages = {} + self.available_packages = {} + self.available_updates = {} + + def _factory_installed_package(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.InstalledPackage(item_components) + + def _factory_available_package(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.AvailablePackage(item_components) + + def _factory_available_update(package_map, item_components): + package_map[item_components[0]] = AndroidSDKResolver.AvailableUpdate(item_components) + + # Use the SDK manager to collect the available and installed packages + result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--list'], capture_stdout=True, suppress_stderr=True) + + current_append_map = None + current_item_factory = None + for package_item in result_stdout.split('\n'): + package_item_stripped = package_item.strip() + if not package_item_stripped: + continue + if '|' not in package_item_stripped: + if package_item_stripped.upper() == 'INSTALLED PACKAGES:': + current_append_map = self.installed_packages + current_item_factory = _factory_installed_package + elif package_item_stripped.upper() == 'AVAILABLE PACKAGES:': + current_append_map = self.available_packages + current_item_factory = _factory_available_package + elif package_item_stripped.upper() == 'AVAILABLE UPDATES:': + current_append_map = self.available_updates + current_item_factory = _factory_available_update + else: + current_append_map = None + current_item_factory = None + continue + item_parts = [split.strip() for split in package_item_stripped.split('|')] + if len(item_parts) < 3: + continue + elif item_parts[1].upper() in ('VERSION', 'INSTALLED', '-------'): + continue + elif current_append_map is None: + continue + if current_append_map is not None and current_item_factory is not None: + current_item_factory(current_append_map, item_parts) + + def is_package_installed(self, search_package_path): + """ + Check if a package path to see if its a package that is installed. The path can use wildcard '*'s + The function will return a list of the results that match the package paths, ordered by the newest version first + """ + def _package_sort(package): + return package.version + package_detail_result_list = [] + for installed_package_path, installed_package_details in self.installed_packages.items(): + if fnmatch.fnmatch(installed_package_path, search_package_path): + package_detail_result_list.append(installed_package_details) + package_detail_result_list.sort(reverse=True, key=_package_sort) + return package_detail_result_list + + def is_package_available(self, search_package_path): + """ + Check if a package path to see if its an available package to install. The path can use wildcard '*'s + The function will return a list of the results that match the package paths, ordered by the newest version first + """ + def _package_sort(package): + return package.version + package_detail_result_list = [] + for available_package_path, available_package_details in self.available_packages.items(): + if fnmatch.fnmatch(available_package_path, search_package_path): + package_detail_result_list.append(available_package_details) + package_detail_result_list.sort(reverse=True, key=_package_sort) + return package_detail_result_list + + def install_package(self, package_install_path, package_description): + """ + Install a package based on the path of an available android sdk package + """ + + # Skip installation if the package is already installed + package_result_list = self.is_package_installed(package_install_path) + if package_result_list: + installed_package_detail = package_result_list[0] + logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Detected") + return installed_package_detail + + # Make sure the package name is available + package_result_list = self.is_package_available(package_install_path) + if not package_result_list: + raise common.LmbrCmdError(f"Invalid Android SDK Package {package_description}: Bad package path {package_install_path}") + + # Reverse sort and pick the first item, which should be the latest (if the install path contains wildcards) + def _available_sort(item): + return item.path + + package_result_list.sort(reverse=True, key=_available_sort) + + available_package_to_install = package_result_list[0] # For multiple hits, resolve to the first item which will be the latest version + + # Perform the package installation + logging.info(f"Installing {available_package_to_install.description} ...") + result_code, result_stdout, result_stderr = self.sdk_manager.exec(['--install', available_package_to_install.path], capture_stdout=True, suppress_stderr=True) + if result_code != 0: + raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}") + + # Refresh the tracked SDK Contents + self.refresh_sdk_installation() + + # Get the package details to verify + package_result_list = self.is_package_installed(package_install_path) + if package_result_list: + installed_package_detail = package_result_list[0] + logging.info(f"{installed_package_detail.description} (version {installed_package_detail.version}) Installed") + return installed_package_detail + else: + raise common.LmbrCmdError(f"Error installing package {available_package_to_install.path}: \n{result_stderr}") + diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 9a0e2760f5..d25b62dde8 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -27,7 +27,7 @@ from cmake.Tools import common from cmake.Tools.Platform.Android import android_support GRADLE_ARGUMENT_NAME = '--gradle-install-path' -GRADLE_MIN_VERSION = LooseVersion('4.10.1') +GRADLE_MIN_VERSION = LooseVersion('6.5') GRADLE_MAX_VERSION = LooseVersion('7.0.0') GRADLE_VERSION_REGEX = re.compile(r"Gradle\s(\d+.\d+.?\d*)") GRADLE_EXECUTABLE = 'gradle.bat' if platform.system() == 'Windows' else 'gradle' @@ -48,9 +48,9 @@ def verify_gradle(override_gradle_path=None): CMAKE_ARGUMENT_NAME = '--cmake-install-path' -CMAKE_MIN_VERSION = LooseVersion('3.17.0') +CMAKE_MIN_VERSION = LooseVersion('3.19.0') CMAKE_VERSION_REGEX = re.compile(r'cmake version (\d+.\d+.?\d*)') -CMAKE_EXECUTABLE = 'cmake.exe' if platform.system() == 'Windows' else 'cmake' +CMAKE_EXECUTABLE = 'cmake' def verify_cmake(override_cmake_path=None): @@ -69,7 +69,7 @@ def verify_cmake(override_cmake_path=None): NINJA_ARGUMENT_NAME = '--ninja-install-path' NINJA_VERSION_REGEX = re.compile(r'(\d+.\d+.?\d*)') -NINJA_EXECUTABLE = 'ninja.exe' if platform.system() == 'Windows' else 'ninja' +NINJA_EXECUTABLE = 'ninja' def verify_ninja(override_ninja_path=None): @@ -78,7 +78,7 @@ def verify_ninja(override_ninja_path=None): """ return common.verify_tool(override_tool_path=override_ninja_path, tool_name='ninja', - tool_filename='ninja.exe' if platform.system() == 'Windows' else 'ninja', + tool_filename='ninja', argument_name=NINJA_ARGUMENT_NAME, tool_version_argument='--version', tool_version_regex=NINJA_VERSION_REGEX, @@ -103,13 +103,21 @@ def build_optional_signing_profile(store_file, store_password, key_alias, key_pa ANDROID_SDK_ARGUMENT_NAME = '--android-sdk-path' -ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-version' +ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-platform' ANDROID_SDK_PREFERRED_TOOL_VER = '--android-sdk-build-tool-version' +ANDROID_NATIVE_API_LEVEL = '--android-native-api-level' + + +MIN_ANDROID_SDK_PLATFORM = 28 # The minimum platform/api level that is supported for the SDK Platform +MIN_NATIVE_API_LEVEL = 24 # The minimum Native API level that is supported for the NDK + -ANDROID_NDK_ARGUMENT_NAME = '--android-ndk-path' ANDROID_NDK_PLATFORM_ARGUMENT_NAME = '--android-ndk-version' +ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME = '--gradle-plugin-version' +ANDROID_GRADLE_MIN_PLUGIN_VERSION = LooseVersion("4.2.0") + # Constants for asset-related options for APK generation INCLUDE_APK_ASSETS_ARGUMENT_NAME = "--include-apk-assets" ASSET_MODE_ARGUMENT_NAME = "--asset-mode" @@ -147,6 +155,7 @@ def main(args): parser = argparse.ArgumentParser(description="Prepare the android studio subfolder") + # Required Arguments parser.add_argument('--engine-root', help='The path to the engine root. Defaults to the current working directory.', default=os.getcwd()) @@ -160,32 +169,42 @@ def main(args): help='The path to the 3rd Party root directory', required=True) - parser.add_argument(ANDROID_NDK_ARGUMENT_NAME, - help='The path to the android NDK', - required=True) - parser.add_argument(ANDROID_SDK_ARGUMENT_NAME, help='The path to the android SDK', required=True) - parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME, - help='The android SDK version', + parser.add_argument('-g', '--project-path', + help='The project path to generate an android project', required=True) + parser.add_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME, + help=f'The android SDK platform number version to use for the APK. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + type=int, + default=-1) + + parser.add_argument(ANDROID_NATIVE_API_LEVEL, + help=f'The android native API level to use for the APK. If not set, this will default to the android SDK platform. (Minimum {MIN_ANDROID_SDK_PLATFORM})', + type=int, + default=-1) + + # Override arguments parser.add_argument(ANDROID_SDK_PREFERRED_TOOL_VER, - help='The preferred android sdk build version (i.e. 28.0.3). Will default to the first one detected under the android sdk', - default=None, + help='The android SDK build tools version.', required=False) parser.add_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME, help='The android NDK version', - required=True) + required=False) parser.add_argument(GRADLE_ARGUMENT_NAME, help=f'The path to installed gradle. The version of gradle must fall in between {str(GRADLE_MIN_VERSION)} and {str(GRADLE_MAX_VERSION)}.', default=None, required=False) + parser.add_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME, + help=f'The version of the android gradle plugin to use. Defaults to the minimum version ({ANDROID_GRADLE_MIN_PLUGIN_VERSION})', + default=str(ANDROID_GRADLE_MIN_PLUGIN_VERSION)) + parser.add_argument(CMAKE_ARGUMENT_NAME, help=f'The path to cmake build tool if not installed on the system path. The version of cmake must be at least version {str(CMAKE_MIN_VERSION)}.', default=None, @@ -196,9 +215,6 @@ def main(args): default=None, required=False) - parser.add_argument('-g', '--project-path', - help='The project path to generate an android project') - # Asset Options parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME, action='store_true', @@ -207,11 +223,11 @@ def main(args): parser.add_argument(ASSET_MODE_ARGUMENT_NAME, choices=ALL_ASSET_MODES, default=ASSET_MODE_LOOSE, - help='Asset Mode (vfs|pak|loose) to use when including assets into the APK') + help=f'Asset Mode (vfs|pak|loose) to use when including assets into the APK. (Defaults to {ASSET_MODE_LOOSE})') parser.add_argument(ASSET_TYPE_ARGUMENT_NAME, default=DEFAULT_ASSET_TYPE, - help='Asset Type to use when including assets into the APK') + help=f'Asset Type to use when including assets into the APK. (Defaults to {DEFAULT_ASSET_TYPE})') parser.add_argument('--debug', action='store_true', @@ -260,16 +276,81 @@ def main(args): ninja_version, override_ninja_path = verify_ninja(override_ninja_path=parsed_args.get_argument(NINJA_ARGUMENT_NAME)) logging.info("Detected Ninja version %s", str(ninja_version)) - # Verify the android sdk path and sdk version - verified_android_sdk_platform, verified_android_sdk_path, android_sdk_build_tool_ver = android_support.verify_android_sdk(android_sdk_platform=parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME), - argument_name=ANDROID_SDK_ARGUMENT_NAME, - override_android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME), - preferred_sdk_build_tools_ver=parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER)) + # Get the android sdk platform version to use from the arguments, but also handle the deprecated argument name + android_sdk_platform_version = parsed_args.get_argument(ANDROID_SDK_PLATFORM_ARGUMENT_NAME) - # Verify the android ndk path and ndk version - verified_android_ndk_platform, verified_android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME), - argument_name=ANDROID_NDK_ARGUMENT_NAME, - override_android_ndk_path=parsed_args.get_argument(ANDROID_NDK_ARGUMENT_NAME)) + # Get the gradle plugin details and validate against the current environment + android_gradle_plugin_version = parsed_args.get_argument(ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME) + android_gradle_plugin = android_support.AndroidGradlePluginInfo(android_gradle_plugin_version) + logging.info(f"Generating Android Gradle Plugin version {android_gradle_plugin_version} based project") + + if gradle_version < android_gradle_plugin.min_gradle_version: + raise common.LmbrCmdError(f"The current version of gradle ({gradle_version}) does not satisfy the minimum version " + f"({android_gradle_plugin.min_gradle_version}) needed for the android gradle plugin " + f"({android_gradle_plugin_version}). Please upgrade your gradle.") + if cmake_version < android_gradle_plugin.min_cmake_version: + raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) does not satisfy the minimum version " + f"({android_gradle_plugin.min_cmake_version}) needed for the android gradle plugin " + f"({android_gradle_plugin_version}). Please upgrade your cmake.") + if android_gradle_plugin.max_cmake_version and cmake_version > android_gradle_plugin.max_cmake_version: + raise common.LmbrCmdError(f"The current version of cmake ({cmake_version}) exceeds the maximum version " + f"({android_gradle_plugin.max_cmake_version}) of the android gradle plugin " + f"({android_gradle_plugin_version}).") + + # Use the SDK Resolver to make sure the build tools and ndk + android_sdk = android_support.AndroidSDKResolver(android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME)) + + # If no SDK platform is provided, check for any installed one + if android_sdk_platform_version < 0: + android_sdk_platform_version = MIN_ANDROID_SDK_PLATFORM + installed_android_sdk_platforms = android_sdk.is_package_installed('platforms;*') + if installed_android_sdk_platforms: + # If there are installed platforms, check the most recent one + latest_platform_version = -1 + for installed_android_sdk_platform in installed_android_sdk_platforms: + platform_number_match = re.match(r'platforms;android-([0-9]*)', installed_android_sdk_platform.path) + if not platform_number_match: + continue + check_platform_version = int(platform_number_match.group(1)) + if check_platform_version > latest_platform_version: + latest_platform_version = check_platform_version + if latest_platform_version >= MIN_ANDROID_SDK_PLATFORM: + android_sdk_platform_version = latest_platform_version + else: + if android_sdk_platform_version < MIN_ANDROID_SDK_PLATFORM: + raise common.LmbrCmdError(f"Invalid argument for {ANDROID_SDK_PLATFORM_ARGUMENT_NAME} ({android_sdk_platform_version}). Must be greater than the minimum value supported {MIN_ANDROID_SDK_PLATFORM}.") + + # Get the android native api level from the arguments. Default to the sdk platform version if not provided + android_native_api_level = parsed_args.get_argument(ANDROID_NATIVE_API_LEVEL) + if android_native_api_level < 0: + android_native_api_level = android_sdk_platform_version + else: + if android_native_api_level < MIN_NATIVE_API_LEVEL: + raise common.LmbrCmdError(f"Invalid argument for {ANDROID_NATIVE_API_LEVEL} ({android_native_api_level}). Must be greater than the minimum value supported {MIN_NATIVE_API_LEVEL}.") + + # Check and make sure that the requested sdk platform exists, download if necessary + platform_package_name = f"platforms;android-{android_sdk_platform_version}" + android_sdk.install_package(package_install_path=platform_package_name, + package_description=f'Android SDK Platform {android_sdk_platform_version}') + + # Make sure we have the extra android packages "market_apk_expansion" and "market_licensing" which is needed by the APK + android_sdk.install_package(package_install_path='extras;google;market_apk_expansion', + package_description='Google APK Expansion Library') + + android_sdk.install_package(package_install_path='extras;google;market_licensing', + package_description='Google Play Licensing Library') + + # Install either the requested SDK build tools or the default one for the android gradle plugin version + build_tools_version = parsed_args.get_argument(ANDROID_SDK_PREFERRED_TOOL_VER) or android_gradle_plugin.default_sdk_build_tools_version + build_tools_package_name = f'build-tools;{build_tools_version}' + build_tools_package = android_sdk.install_package(package_install_path=build_tools_package_name, + package_description='Android SDK Build Tools') + + # Install either the requested NDK version or the default one for the android gradle plugin version + android_ndk_version = parsed_args.get_argument(ANDROID_NDK_PLATFORM_ARGUMENT_NAME) or android_gradle_plugin.default_ndk_version + android_ndk_package_name = f'ndk;{android_ndk_version}' + android_ndk_package = android_sdk.install_package(package_install_path=android_ndk_package_name, + package_description='Android NDK') # Verify the engine root path and project path verified_project_path, verified_engine_root = common.verify_project_and_engine_root(project_root=parsed_args.project_path, @@ -277,10 +358,9 @@ def main(args): is_test_project = parsed_args.unit_test # Verify the 3rd Party Root Path - third_party_path = pathlib.Path(parsed_args.third_party_path) / '3rdParty.txt' - if not third_party_path.is_file(): - raise common.LmbrCmdError("Invalid --third-party-path '{}'. Make sure it exists and contains " - "3rdParty.txt".format(parsed_args.third_party_path), + third_party_path = pathlib.Path(parsed_args.third_party_path) + if not third_party_path.is_dir(): + raise common.LmbrCmdError(f"Invalid --third-party-path '{parsed_args.third_party_path}'.", common.ERROR_CODE_INVALID_PARAMETER) third_party_path = third_party_path.parent @@ -293,23 +373,23 @@ def main(args): logging.debug("Engine Root : %s", str(verified_engine_root.resolve())) logging.debug("Build Path : %s", str(build_dir.resolve())) - logging.debug("Android NDK Path : %s", str(verified_android_ndk_path.resolve())) - logging.debug("Android SDK Path : %s", str(verified_android_sdk_path.resolve())) # Prepare the generator and execute generator = android_support.AndroidProjectGenerator(engine_root=verified_engine_root, - project_path=verified_project_path, build_dir=build_dir, - android_sdk_path=verified_android_sdk_path, - android_ndk_path=verified_android_ndk_path, - android_sdk_version=verified_android_sdk_platform, - android_ndk_platform=verified_android_ndk_platform, + android_sdk_path=android_sdk.android_sdk_path, + build_tool=build_tools_package, + android_sdk_platform=android_sdk_platform_version, + android_native_api_level=android_native_api_level, + android_ndk=android_ndk_package, + project_path=verified_project_path, third_party_path=third_party_path, cmake_version=cmake_version, override_cmake_path=override_cmake_path, override_gradle_path=override_gradle_path, + gradle_version=gradle_version, + gradle_plugin_version=android_gradle_plugin_version, override_ninja_path=override_ninja_path, - android_sdk_build_tool_version=android_sdk_build_tool_ver, include_assets_in_apk=parsed_args.get_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME), asset_mode=parsed_args.get_argument(ASSET_MODE_ARGUMENT_NAME), asset_type=parsed_args.get_argument(ASSET_TYPE_ARGUMENT_NAME), diff --git a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py index 5598942046..0cd0f16eaf 100755 --- a/cmake/Tools/Platform/Android/unit_test_generate_android_project.py +++ b/cmake/Tools/Platform/Android/unit_test_generate_android_project.py @@ -170,117 +170,3 @@ def test_verify_ninja(tmpdir, from_override, version_str, expected_result): finally: subprocess.check_output = orig_check_output - -TEST_VALIDATE_VERSION_MIN = 19 -TEST_VALIDATE_VERSION_MAX = 21 - - -@pytest.mark.parametrize( - "test_input, expected", [ - pytest.param('20', 20), - pytest.param('android-20', 20), - pytest.param('bad-21', "android-'XX'"), - pytest.param('10', "minimum"), - pytest.param('30', "maximum") - ] -) -def test_validate_android_platform_input(test_input, expected): - try: - result = android_support.validate_android_platform_input(input_android_platform=test_input, - platform_variable_type='test', - min_version=TEST_VALIDATE_VERSION_MIN, - max_version=TEST_VALIDATE_VERSION_MAX) - assert isinstance(expected, int) - assert result == expected - except Exception as e: - assert expected in str(e) - - -def test_verify_android_sdk_success(tmpdir): - - test_android_path = 'android_sdk' - sdk_version_number = 28 - sdk_version = f'android-{sdk_version_number}' - - tmpdir.ensure(f'{test_android_path}/platforms/{sdk_version}/package.xml') - - tmpdir.ensure(f'{test_android_path}/build-tools/28.0.3/package.xml') - tmpdir.ensure(f'{test_android_path}/build-tools/29.0.3/package.xml') - - input_sdk_path = tmpdir.join(test_android_path).realpath() - argument_name = '--android-sdk' - - requested_build_tool_version = '29.0.3' - - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path, - preferred_sdk_build_tools_ver=requested_build_tool_version) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == requested_build_tool_version - - sdk_version_number_only = str(sdk_version_number) - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version_number_only, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == '28.0.3' - - requested_build_tool_version = '30.0.3' - result_sdk_version, result_sdk_path, result_build_tool_version = android_support.verify_android_sdk(android_sdk_platform=sdk_version, - argument_name=argument_name, - override_android_sdk_path=input_sdk_path, - preferred_sdk_build_tools_ver=requested_build_tool_version) - assert result_sdk_version == sdk_version_number - assert result_sdk_path == input_sdk_path - assert result_build_tool_version == '28.0.3' - - -@pytest.mark.parametrize( - "desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error", [ - pytest.param(21, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19ExactMatch'), - pytest.param(23, [21, 22, 24], '15.2.4203891', None, False, id='preNdk19FallbackMatch'), - pytest.param(22, [21, 22, 24], '19.2.4203891', {'23': 21}, False, id='postNdk19ExactMatch'), - pytest.param(23, [21, 22, 24], '21.2.4203891', {'23': 21}, False, id='postNdk19MappingMatch'), - pytest.param(android_support.ANDROID_NDK_MIN_PLATFORM-1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19BelowMinVer'), - pytest.param(android_support.ANDROID_NDK_MAX_PLATFORM+1, [21, 22, 24], '15.2.4203891', None, True, id='preNdk19AboveMaxVer'), - pytest.param(25, [21, 22, 24], '19.2.4203891', {'23': 21}, True, id='postNdk19NoMatch') - ] -) -def test_verify_android_ndk_success(tmpdir, desired_ndk_version_number, available_ndk_revisions, pkg_revision, mappings, expect_error): - - test_android_path = 'android_ndk' - for ndk_number in available_ndk_revisions: - tmpdir.ensure(f'{test_android_path}/platforms/android-{ndk_number}/arch-arm64/usr/lib/libc.so') - - tmpdir.ensure(f'{test_android_path}/source.properties') - test_ndk_source_properties_file = tmpdir / test_android_path / 'source.properties' - test_ndk_source_properties_file.write_text(f'Pkg.Desc = Android NDK\nPkg.Revision = {pkg_revision}\n', encoding='ASCII') - - if mappings: - platform_mapping = { - # min and max are arbitrary for now since we dont use it during evaluation, but if we do, parameterize it here as well - "min": 16, # - "max": 29, - "aliases": {} - } - for key, value in mappings.items(): - platform_mapping['aliases'][key] = value - tmpdir.ensure(f'{test_android_path}/meta/platforms.json') - platform_mapping_file = tmpdir / test_android_path / 'meta/platforms.json' - platform_mapping_file.write_text(json.dumps(platform_mapping), encoding='ASCII') - - input_ndk_path = tmpdir.join(test_android_path).realpath() - - try: - android_ndk_platform_number, android_ndk_path = android_support.verify_android_ndk(android_ndk_platform=str(desired_ndk_version_number), - argument_name="--android-ndk", - override_android_ndk_path=input_ndk_path) - assert not expect_error - assert android_ndk_platform_number == desired_ndk_version_number - assert android_ndk_path == input_ndk_path - except Exception: - assert expect_error - diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index c6a3e89e67..9c0d31cd53 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -55,6 +55,7 @@ ENGINE_ROOT_CHECK_FILE = 'engine.json' HASH_CHUNK_SIZE = 200000 + class LmbrCmdError(Exception): """ Wrapper class to the general exception class where will absorb and prevent the printing of stack. @@ -244,6 +245,19 @@ def load_template_file(template_file_path, template_env): raise FileNotFoundError(f"Invalid file path. Cannot find template file located at {str(template_file_path)}") +# Determine the possible file extensions for executable files based on the host platform +PLATFORM_EXECUTABLE_EXTENSIONS = [''] # Files without extensions are always considered + +if platform.system() == 'Windows': + # Windows manages its executable extensions through the %PATHEXT% environment variable + path_extensions_str = os.environ.get('PATHEXT', default='.EXE;.COM;.BAT;.CMD') + PLATFORM_EXECUTABLE_EXTENSIONS.extend([pathext.lower() for pathext in path_extensions_str.split(';')]) +elif platform.system() == 'Linux': + PLATFORM_EXECUTABLE_EXTENSIONS = ['', '.out'] +else: + PLATFORM_EXECUTABLE_EXTENSIONS = [''] + + def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, tool_version_argument, tool_version_regex, min_version, max_version): """ Support method to validate a required system tool needed for the build either through an installed tool in the @@ -270,12 +284,21 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too elif not isinstance(override_tool_path, pathlib.Path): raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' must be a string or Path", ERROR_CODE_INVALID_PARAMETER) - check_tool_path = override_tool_path / tool_filename - if not check_tool_path.is_file(): - check_tool_path = pathlib.Path(override_tool_path) / 'bin' / tool_filename + file_found = False + for executable_path_ext in PLATFORM_EXECUTABLE_EXTENSIONS: + check_tool_filename = f'{tool_filename}{executable_path_ext}' - if not check_tool_path.is_file(): + check_tool_path = override_tool_path / check_tool_filename + if check_tool_path.is_file(): + file_found = True + break + check_tool_path = override_tool_path / 'bin' / check_tool_filename + if check_tool_path.is_file(): + file_found = True + break + + if not file_found: raise LmbrCmdError(f"Invalid {tool_name} path argument. '{override_tool_path}' is not a valid {tool_name} path", ERROR_CODE_INVALID_PARAMETER) resolved_override_tool_path = str(check_tool_path.resolve()) @@ -284,7 +307,7 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too else: resolved_override_tool_path = None tool_source = tool_name - tool_desc = "installed gradle in the system path" + tool_desc = f"installed {tool_name} in the system path" # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], @@ -296,10 +319,10 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too result_version = LooseVersion(str(version_match.group(1)).strip()) if min_version and result_version < min_version: - raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of gradle required ({str(min_version)}).", + raise LmbrCmdError(f"The {tool_desc} does not meet the minimum version of {tool_name} required ({str(min_version)}).", ERROR_CODE_ENVIRONMENT_ERROR) elif max_version and result_version > max_version: - raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of gradle supported ({str(max_version)}).", + raise LmbrCmdError(f"The {tool_desc} exceeds maximum version of {tool_name} supported ({str(max_version)}).", ERROR_CODE_ENVIRONMENT_ERROR) return result_version, resolved_override_tool_path diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index adaa417380..b871670cd0 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -141,10 +141,8 @@ "COMMAND":"gradle_windows.cmd", "PARAMETERS": { "CONFIGURATION":"profile", - "OUTPUT_DIRECTORY":"build\\android_gradle", + "OUTPUT_DIRECTORY":"build\\ad_grd", "GAME_PROJECT": "AutomatedTesting", - "ANDROID_NDK_PLATFORM": "21", - "ANDROID_SDK_PLATFORM": "29", "SIGN_APK": "false", "GRADLE_BUILD_CMD": "build", "ADDITIONAL_GENERATE_ARGS": "" @@ -158,8 +156,6 @@ "CONFIGURATION":"profile", "OUTPUT_DIRECTORY":"build\\android_unittest", "GAME_PROJECT": "AutomatedTesting", - "ANDROID_NDK_PLATFORM": "21", - "ANDROID_SDK_PLATFORM": "29", "SIGN_APK": "true", "GRADLE_BUILD_CMD": "assemble", "ADDITIONAL_GENERATE_ARGS": "--unit-test" diff --git a/scripts/build/Platform/Android/gradle_windows.cmd b/scripts/build/Platform/Android/gradle_windows.cmd index 56423af95a..dd5285bdbf 100644 --- a/scripts/build/Platform/Android/gradle_windows.cmd +++ b/scripts/build/Platform/Android/gradle_windows.cmd @@ -17,20 +17,12 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" ( GOTO :error ) -IF NOT EXIST "%GRADLE_HOME%" ( +IF NOT EXIST "%GRADLE_BUILD_HOME%" ( REM This is the default for developers - SET GRADLE_HOME=C:\Gradle\gradle-5.6.4 + SET GRADLE_BUILD_HOME=C:\Gradle\gradle-7.0 ) -IF NOT EXIST "%GRADLE_HOME%" ( - ECHO [ci_build] FAIL: GRADLE_HOME=%GRADLE_HOME% - GOTO :error -) - -IF NOT EXIST "%CMAKE_HOME%" ( - SET CMAKE_HOME=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/ -) -IF NOT EXIST "%CMAKE_HOME%" ( - ECHO [ci_build] FAIL: CMAKE_HOME=%CMAKE_HOME% +IF NOT EXIST "%GRADLE_BUILD_HOME%" ( + ECHO [ci_build] FAIL: GRADLE_BUILD_HOME=%GRADLE_BUILD_HOME% GOTO :error ) @@ -50,20 +42,9 @@ ECHO Ninja wasnt in the call path, add the value set by LY_NINJA_PATH SET PATH=%PATH%;%LY_NINJA_PATH% :ninja_on_path -IF NOT EXIST "%LY_ANDROID_SDK%" ( - SET LY_ANDROID_SDK=!LY_3RDPARTY_PATH!/android-sdk/platform-29 -) -IF NOT EXIST "%LY_ANDROID_SDK%" ( - ECHO [ci_build] FAIL: LY_ANDROID_SDK=!LY_ANDROID_SDK! - GOTO :error -) -IF NOT EXIST "%LY_ANDROID_NDK%" ( - set LY_ANDROID_NDK=!LY_3RDPARTY_PATH!/android-ndk/r21d -) -IF NOT EXIST "%LY_ANDROID_NDK%" ( - ECHO [ci_build] LY_ANDROID_NDK=!LY_ANDROID_NDK! - GOTO :error +IF NOT "%ANDROID_GRADLE_PLUGIN%" == "" ( + set ANDROID_GRADLE_PLUGIN_OPTION=--gradle-plugin-version=%ANDROID_GRADLE_PLUGIN% ) IF NOT EXIST %OUTPUT_DIRECTORY% ( @@ -154,11 +135,11 @@ IF "%GENERATE_SIGNED_APK%"=="true" ( ECHO Using keystore file at %CI_ANDROID_KEYSTORE_FILE_ABS% ) - ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %OPTIONAL_TEST_FLAG% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing - CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing ) ELSE ( - ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing - CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% %GRADLE_OVERRIDE_OPTION% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing + CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing ) REM Validate the android project generation diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index ed10e7022d..551374a027 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -1,7 +1,7 @@ { "ENV": { - "GRADLE_HOME": "C:/Gradle/gradle-5.6.4", - "NODE_LABEL": "windows-047e5cdf", + "GRADLE_HOME": "C:/Gradle/gradle-7.0", + "NODE_LABEL": "windows-b3c8994f1", "LY_3RDPARTY_PATH": "C:/ly/3rdParty", "TIMEOUT": 30, "WORKSPACE": "D:/workspace",