Merge branch 'main' into Prism/gem-db

This commit is contained in:
mgwynn
2021-06-01 12:23:34 -04:00
77 changed files with 2554 additions and 1160 deletions
@@ -4,17 +4,152 @@
<Class name="AZStd::vector" field="Properties" type="{A8E59F8C-2F9A-525A-B549-A9E197EB9632}">
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Debug" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="1000.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="1.0000000 1.0000000 1.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
<Class name="AZStd::string" field="SurfaceType" value="Character" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.7000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.8000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.3000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="2" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="985.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.9183642 0.6973526 0.4447700 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{B072A405-BAFA-4B0A-9164-B3A424E642A9}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="AZ::Uuid" field="MaterialId" value="{FDECD8B6-5BAF-42CB-AEFE-C66E1E1CF557}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Concrete" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.8000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.9000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.3800000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="2400.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.5918365 0.4927596 0.3795224 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{A9CACCFF-E0D2-4149-8891-E92319229B2D}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Glass" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.4000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.5000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.7000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="2500.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.4825971 0.8975662 0.9523766 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{FD59CBE9-D1C4-4119-81CB-CD7AD72FC295}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Metal" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.4200000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.7800000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.4000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="8050.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.2312963 0.2312963 0.2312963 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{76CDC778-ACA9-449F-BFD7-C361F89F3207}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Plastic" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.3500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.3000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.6900000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="900.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.9394675 1.0000000 0.2735485 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{E2FFB000-D15B-4760-A819-9E490D1D3741}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Rubber" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.8500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="1200.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.1088426 0.1088426 0.1088426 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{8C7A6011-61C2-46B7-9BF4-8D4DD2A624F1}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Terrain_Dirt" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.4000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.4000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.3000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="1600.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.3333333 0.2619974 0.1973144 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{303C5A49-22F2-45A8-B24C-9F2C3CA13402}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Terrain_Grass" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.2500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.3500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.2000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="1400.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="0.1483177 0.5986419 0.1073777 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{53733840-A095-40C4-B653-C40D233B3BE1}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Vehicle" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.2000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.5000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.3000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="140.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="1.0000000 0.0000000 0.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{4080A6D4-AF4E-41CE-B7C9-7699C07123E7}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
<Class name="Physics::MaterialFromAssetConfiguration" field="element" version="1" type="{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}">
<Class name="Physics::MaterialConfiguration" field="Configuration" version="3" type="{8807CAA1-AD08-4238-8FDB-2154ADD084A1}">
<Class name="AZStd::string" field="SurfaceType" value="Wood" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="float" field="DynamicFriction" value="0.5000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="StaticFriction" value="0.6000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="float" field="Restitution" value="0.6000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="unsigned char" field="FrictionCombine" value="3" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="unsigned char" field="RestitutionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
<Class name="float" field="Density" value="540.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
<Class name="Color" field="DebugColor" value="1.0000000 0.7318379 0.3004501 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
</Class>
<Class name="Physics::MaterialId" field="UID" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
<Class name="AZ::Uuid" field="MaterialId" value="{6ACE67AA-CB32-41CD-8740-58371CCCD3F3}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
</Class>
</Class>
</Class>
@@ -77,6 +77,27 @@
#endif // defined(AZ_ENABLE_DEBUG_TOOLS)
#include <AzCore/Module/Environment.h>
#include <AzCore/std/string/conversions.h>
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
{
@@ -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, "");
@@ -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);
@@ -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
@@ -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
@@ -64,7 +64,7 @@ namespace AzToolsFramework
m_prefabUndoCache.Destroy();
}
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath)
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& 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();
}
@@ -46,7 +46,7 @@ namespace AzToolsFramework
void UnregisterPrefabPublicHandlerInterface();
// PrefabPublicInterface...
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) override;
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& 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;
@@ -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<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) = 0;
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath) = 0;
/**
* Instantiate a prefab from a prefab file.
@@ -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())
{
@@ -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}
}
@@ -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}
@@ -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}'
}
@@ -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",
@@ -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",
File diff suppressed because it is too large Load Diff
@@ -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 ]
},
@@ -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 <Atom/Features/SrgSemantics.azsli>
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<MaterialInfo> 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[];
}
@@ -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;
}
@@ -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<MeshInfo> 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[];
}
@@ -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;
}
@@ -64,3 +64,8 @@ ShaderResourceGroupSemantic SRG_RayTracingScene
{
FrequencyId = 1;
};
ShaderResourceGroupSemantic SRG_RayTracingMaterial
{
FrequencyId = 2;
};
@@ -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",
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bc9981393c88c6d30a0a5a6837e6f6246a9f550042b3c4be39dd34e479b90569
size 16793931
@@ -1,69 +0,0 @@
<ObjectStream version="3">
<Class name="TextureSettings" version="1" type="{980132FF-C450-425D-8AE0-BD96A8486177}">
<Class name="AZ::Uuid" field="PresetID" value="{46D9F30F-793C-4449-BCEF-12A396E61B2C}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="unsigned int" field="SizeReduceLevel" value="0" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="bool" field="EngineReduce" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="EnableMipmap" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="MaintainAlphaCoverage" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="AZStd::vector" field="MipMapAlphaAdjustments" type="{3349AACD-BE04-50BC-9478-528BF2ACFD55}">
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="unsigned int" field="element" value="50" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
</Class>
<Class name="unsigned int" field="MipMapGenEval" value="0" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="ImageProcessing::MipGenType" field="MipMapGenType" value="1" type="{8524F650-1417-44DA-BBB0-C707A7A1A709}"/>
<Class name="AZStd::map" field="PlatformSpecificOverrides" type="{74E4843B-0924-583D-8C6E-A37B09BD51FE}">
<Class name="AZStd::pair" field="element" type="{CAC4E67F-D626-5452-A057-ACB57D53F549}">
<Class name="AZStd::string" field="value1" value="android" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="DataPatch" field="value2" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
<Class name="AZ::Uuid" field="m_targetClassId" value="{980132FF-C450-425D-8AE0-BD96A8486177}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="unsigned int" field="m_targetClassVersion" value="1" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}"/>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{CAC4E67F-D626-5452-A057-ACB57D53F549}">
<Class name="AZStd::string" field="value1" value="ios" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="DataPatch" field="value2" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
<Class name="AZ::Uuid" field="m_targetClassId" value="{980132FF-C450-425D-8AE0-BD96A8486177}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="unsigned int" field="m_targetClassVersion" value="1" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}">
<Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
<Class name="AddressType" field="value1" value="AZStd::map({74E4843B-0924-583D-8C6E-A37B09BD51FE})::PlatformSpecificOverrides·0/AZStd::pair({CAC4E67F-D626-5452-A057-ACB57D53F549})#0·0/" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
<Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}"/>
</Class>
</Class>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{CAC4E67F-D626-5452-A057-ACB57D53F549}">
<Class name="AZStd::string" field="value1" value="mac" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="DataPatch" field="value2" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
<Class name="AZ::Uuid" field="m_targetClassId" value="{980132FF-C450-425D-8AE0-BD96A8486177}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="unsigned int" field="m_targetClassVersion" value="1" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}">
<Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
<Class name="AddressType" field="value1" value="AZStd::map({74E4843B-0924-583D-8C6E-A37B09BD51FE})::PlatformSpecificOverrides·0/AZStd::pair({CAC4E67F-D626-5452-A057-ACB57D53F549})#1·0/" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
<Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}"/>
</Class>
<Class name="AZStd::pair" field="element" type="{FED51EB4-F646-51FF-9646-9852CF90F353}">
<Class name="AddressType" field="value1" value="AZStd::map({74E4843B-0924-583D-8C6E-A37B09BD51FE})::PlatformSpecificOverrides·0/AZStd::pair({CAC4E67F-D626-5452-A057-ACB57D53F549})#0·0/" version="1" type="{90752F2D-CBD3-4EE9-9CDD-447E797C8408}"/>
<Class name="any" field="value2" type="{03924488-C7F4-4D6D-948B-ABC2D1AE2FD3}"/>
</Class>
</Class>
</Class>
</Class>
<Class name="AZStd::pair" field="element" type="{CAC4E67F-D626-5452-A057-ACB57D53F549}">
<Class name="AZStd::string" field="value1" value="pc" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
<Class name="DataPatch" field="value2" type="{BFF7A3F5-9014-4000-92C7-9B2BC7913DA9}">
<Class name="AZ::Uuid" field="m_targetClassId" value="{980132FF-C450-425D-8AE0-BD96A8486177}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="unsigned int" field="m_targetClassVersion" value="1" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
<Class name="AZStd::unordered_map" field="m_patch" type="{CEA836FC-77E0-5E46-BD0F-2E5A39D845E9}"/>
</Class>
</Class>
</Class>
<Class name="AZStd::string" field="OverridingPlatform" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
</Class>
</ObjectStream>
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:440bcb1579d4ad667c040bda914ed3121980526a94f23879a0c482c799fd5132
size 1310848
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf4b22481726214c062ac27fb2d9d8a49e760a76d8fff03330ff9c5f9275a8da
size 1966208
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b4c99faffc34988c268613948f2004f40e9bd51de915461a4cb74edc5e8bae6
size 134217920
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f7edea42ded8143764654f12c19e0c9b74c74afb21f435ebb59c0a4d203892a3
size 536871104
@@ -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...
@@ -89,6 +89,8 @@
#include <ImGui/ImGuiPass.h>
#include <RayTracing/RayTracingAccelerationStructurePass.h>
#include <RayTracing/RayTracingPass.h>
#include <RayTracing/RayTracingPassData.h>
#include <DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h>
#include <DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h>
#include <DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h>
@@ -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);
@@ -21,6 +21,8 @@
#include <Atom/Utils/PpmFile.h>
#include <AtomCore/Serialization/Json/JsonUtils.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobCompletion.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/RTTI/BehaviorContext.h>
@@ -55,6 +57,43 @@ namespace AZ
FrameCaptureOutputResult PngFrameCaptureOutput(
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
{
AZStd::shared_ptr<AZStd::vector<uint8_t>> 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<AZStd::vector<uint8_t>>(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<ImageOutput> 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
@@ -710,27 +710,53 @@ namespace AZ
uint32_t rayTracingLod = aznumeric_cast<uint32_t>(modelLods.size() - 1);
const Data::Instance<RPI::ModelLod>& 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<RHI::Buffer*>(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<RPI::Material> 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<RHI::Buffer*>(streamBufferViews[0].GetBuffer())->GetDescriptor().m_byteCount;
RHI::BufferViewDescriptor positionBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, positionBufferByteCount);
uint32_t normalBufferByteCount = const_cast<RHI::Buffer*>(streamBufferViews[1].GetBuffer())->GetDescriptor().m_byteCount;
RHI::BufferViewDescriptor normalBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, normalBufferByteCount);
uint32_t tangentBufferByteCount = const_cast<RHI::Buffer*>(streamBufferViews[2].GetBuffer())->GetDescriptor().m_byteCount;
RHI::BufferViewDescriptor tangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tangentBufferByteCount);
uint32_t bitangentBufferByteCount = const_cast<RHI::Buffer*>(streamBufferViews[3].GetBuffer())->GetDescriptor().m_byteCount;
RHI::BufferViewDescriptor bitangentBufferDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bitangentBufferByteCount);
uint32_t uvBufferByteCount = const_cast<RHI::Buffer*>(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<RHI::Buffer*>(streamBufferViews[0].GetBuffer())->GetBufferView(positionBufferDescriptor);
subMesh.m_normalFormat = NormalStreamFormat;
subMesh.m_normalVertexBufferView = streamBufferViews[1];
subMesh.m_normalShaderBufferView = const_cast<RHI::Buffer*>(streamBufferViews[1].GetBuffer())->GetBufferView(normalBufferDescriptor);
subMesh.m_tangentFormat = TangentStreamFormat;
subMesh.m_tangentVertexBufferView = streamBufferViews[2];
subMesh.m_tangentShaderBufferView = const_cast<RHI::Buffer*>(streamBufferViews[2].GetBuffer())->GetBufferView(tangentBufferDescriptor);
subMesh.m_bitangentFormat = BitangentStreamFormat;
subMesh.m_bitangentVertexBufferView = streamBufferViews[3];
subMesh.m_bitangentShaderBufferView = const_cast<RHI::Buffer*>(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<RHI::Buffer*>(streamBufferViews[4].GetBuffer())->GetBufferView(uvBufferDescriptor);
}
subMesh.m_indexBufferView = mesh.m_indexBufferView;
subMesh.m_indexShaderBufferView = const_cast<RHI::Buffer*>(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<AZ::Color>(propertyIndex);
subMesh.m_irradianceColor = material->GetPropertyValue<AZ::Color>(propertyIndex);
}
propertyIndex = material->FindPropertyIndex(AZ::Name("irradiance.factor"));
if (propertyIndex.IsValid())
{
irradianceColor *= material->GetPropertyValue<float>(propertyIndex);
subMesh.m_irradianceColor *= material->GetPropertyValue<float>(propertyIndex);
}
// base color
propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.color"));
if (propertyIndex.IsValid())
{
subMesh.m_baseColor = material->GetPropertyValue<AZ::Color>(propertyIndex);
}
propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.factor"));
if (propertyIndex.IsValid())
{
subMesh.m_baseColor *= material->GetPropertyValue<float>(propertyIndex);
}
// metallic
propertyIndex = material->FindPropertyIndex(AZ::Name("metallic.factor"));
if (propertyIndex.IsValid())
{
subMesh.m_metallicFactor = material->GetPropertyValue<float>(propertyIndex);
}
// roughness
propertyIndex = material->FindPropertyIndex(AZ::Name("roughness.factor"));
if (propertyIndex.IsValid())
{
subMesh.m_roughnessFactor = material->GetPropertyValue<float>(propertyIndex);
}
// textures
propertyIndex = material->FindPropertyIndex(AZ::Name("baseColor.textureMap"));
if (propertyIndex.IsValid())
{
Data::Instance<RPI::Image> image = material->GetPropertyValue<Data::Instance<RPI::Image>>(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<RPI::Image> image = material->GetPropertyValue<Data::Instance<RPI::Image>>(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<RPI::Image> image = material->GetPropertyValue<Data::Instance<RPI::Image>>(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<RPI::Image> image = material->GetPropertyValue<Data::Instance<RPI::Image>>(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<RHI::Buffer*>(streamBufferViews[0].GetBuffer())->GetBufferView(vertexBufferDescriptor);
subMesh.m_normalVertexBufferView = streamBufferViews[1];
subMesh.m_normalShaderBufferView = const_cast<RHI::Buffer*>(streamBufferViews[1].GetBuffer())->GetBufferView(vertexBufferDescriptor);
subMesh.m_indexBufferView = mesh.m_indexBufferView;
subMesh.m_indexShaderBufferView = const_cast<RHI::Buffer*>(mesh.m_indexBufferView.GetBuffer())->GetBufferView(indexBufferDescriptor);
subMesh.m_irradianceColor = irradianceColor;
subMeshes.push_back(subMesh);
}
@@ -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();
}
}
@@ -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<RPI::ShaderResourceGroupAsset> rayTracingMaterialSrgAsset =
RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderResourceGroupAsset>("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<uint32_t>(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<TransformServiceFeatureProcessor>();
AZStd::vector<MeshInfo> meshInfos;
meshInfos.reserve(m_subMeshCount);
uint32_t newMeshByteCount = m_subMeshCount * sizeof(MeshInfo);
if (m_meshInfoBuffer == nullptr)
{
AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString<AZStd::string>();
// 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<uint32_t>(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<MaterialInfo> materialInfos;
materialInfos.reserve(m_subMeshCount);
uint32_t newMaterialByteCount = m_subMeshCount * sizeof(MaterialInfo);
if (m_materialInfoBuffer == nullptr)
{
AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString<AZStd::string>();
// 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<uint32_t>(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<TransformServiceFeatureProcessor>();
AZStd::vector<MeshInfo> 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<const RHI::ImageView*> 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();
}
}
}
@@ -16,6 +16,7 @@
#include <Atom/RHI/RayTracingAccelerationStructure.h>
#include <Atom/RHI/RayTracingBufferPools.h>
#include <Atom/RHI/BufferView.h>
#include <Atom/RHI/ImageView.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Transform.h>
@@ -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<RHI::BufferView> m_positionShaderBufferView;
RHI::Format m_normalFormat = RHI::Format::Unknown;
RHI::StreamBufferView m_normalVertexBufferView;
RHI::Ptr<RHI::BufferView> m_normalShaderBufferView;
RHI::Format m_tangentFormat = RHI::Format::Unknown;
RHI::StreamBufferView m_tangentVertexBufferView;
RHI::Ptr<RHI::BufferView> m_tangentShaderBufferView;
RHI::Format m_bitangentFormat = RHI::Format::Unknown;
RHI::StreamBufferView m_bitangentVertexBufferView;
RHI::Ptr<RHI::BufferView> m_bitangentShaderBufferView;
RHI::Format m_uvFormat = RHI::Format::Unknown;
RHI::StreamBufferView m_uvVertexBufferView;
RHI::Ptr<RHI::BufferView> m_uvShaderBufferView;
// index buffer
RHI::IndexBufferView m_indexBufferView;
RHI::Ptr<RHI::BufferView> 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<RHI::RayTracingBlas> 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<const RHI::ImageView> m_baseColorImageView;
RHI::Ptr<const RHI::ImageView> m_normalImageView;
RHI::Ptr<const RHI::ImageView> m_metallicImageView;
RHI::Ptr<const RHI::ImageView> m_roughnessImageView;
};
using SubMeshVector = AZStd::vector<SubMesh>;
@@ -98,6 +154,9 @@ namespace AZ
//! Retrieves the RayTracingSceneSrg
Data::Instance<RPI::ShaderResourceGroup> GetRayTracingSceneSrg() const { return m_rayTracingSceneSrg; }
//! Retrieves the RayTracingMaterialSrg
Data::Instance<RPI::ShaderResourceGroup> GetRayTracingMaterialSrg() const { return m_rayTracingMaterialSrg; }
//! Retrieves the RayTracingTlas
const RHI::Ptr<RHI::RayTracingTlas>& GetTlas() const { return m_tlas; }
RHI::Ptr<RHI::RayTracingTlas>& GetTlas() { return m_tlas; }
@@ -118,14 +177,20 @@ namespace AZ
//! Retrieves the GPU buffer containing information for all ray tracing meshes.
const Data::Instance<RPI::Buffer> 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<RPI::Buffer> 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<RPI::ShaderResourceGroup> m_rayTracingSceneSrg;
// ray tracing material Srg
Data::Instance<RPI::ShaderResourceGroup> 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<float, 4> m_irradianceColor; // float4
AZStd::array<float, 9> 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<RPI::Buffer> 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<float, 4> 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<RPI::Buffer> 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;
};
}
}
@@ -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 <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <Atom/RHI/CommandList.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/FrameScheduler.h>
#include <Atom/RHI/DispatchRaysItem.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RHI/PipelineState.h>
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
#include <RayTracing/RayTracingPass.h>
#include <RayTracing/RayTracingPassData.h>
#include <RayTracing/RayTracingFeatureProcessor.h>
namespace AZ
{
namespace Render
{
RPI::Ptr<RayTracingPass> RayTracingPass::Create(const RPI::PassDescriptor& descriptor)
{
RPI::Ptr<RayTracingPass> pass = aznew RayTracingPass(descriptor);
return pass;
}
RayTracingPass::RayTracingPass(const RPI::PassDescriptor& descriptor)
: RenderPass(descriptor)
, m_passDescriptor(descriptor)
{
RHI::Ptr<RHI::Device> 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<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
m_passData = RPI::PassUtils::GetPassData<RayTracingPassData>(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<RPI::ShaderResourceGroupAsset> 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<RPI::ShaderResourceGroupAsset> viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View);
m_requiresViewSrg = viewSrgAsset.GetId().IsValid();
Data::Asset<RPI::ShaderResourceGroupAsset> 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<RPI::Shader> RayTracingPass::LoadShader(const RPI::AssetReference& shaderAssetReference)
{
Data::Asset<RPI::ShaderAsset> 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<RayTracingFeatureProcessor>();
if (!rayTracingFeatureProcessor)
{
return;
}
if (!m_rayTracingShaderTable)
{
RHI::Ptr<RHI::Device> 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<RayTracingFeatureProcessor>();
AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor");
RPI::RenderPass::SetupFrameGraphDependencies(frameGraph);
frameGraph.SetEstimatedItemCount(1);
// TLAS
{
const RHI::Ptr<RHI::Buffer>& 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<uint32_t>(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<RayTracingFeatureProcessor>();
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<RHI::RayTracingShaderTableDescriptor> descriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
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<RayTracingFeatureProcessor>();
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<RHI::ShaderResourceGroup*> shaderResourceGroups =
{
m_shaderResourceGroup->GetRHIShaderResourceGroup(),
rayTracingFeatureProcessor->GetRayTracingSceneSrg()->GetRHIShaderResourceGroup()
};
if (m_requiresViewSrg)
{
const AZStd::vector<RPI::ViewPtr>& 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<uint32_t>(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<RPI::ShaderAsset>& 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
@@ -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 <AzCore/Memory/SystemAllocator.h>
#include <Atom/RHI/RayTracingPipelineState.h>
#include <Atom/RHI/RayTracingShaderTable.h>
#include <Atom/RPI.Public/Pass/RenderPass.h>
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
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<RayTracingPass> 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<RPI::ShaderAsset>& 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<RPI::Shader> 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<RPI::Shader> m_rayGenerationShader;
Data::Instance<RPI::Shader> m_missShader;
Data::Instance<RPI::Shader> m_closestHitShader;
RHI::Ptr<RHI::RayTracingPipelineState> m_rayTracingPipelineState;
RHI::ConstPtr<RHI::PipelineState> m_globalPipelineState;
RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
bool m_requiresViewSrg = false;
bool m_requiresRayTracingMaterialSrg = false;
};
} // namespace RPI
} // namespace AZ
@@ -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 <Atom/RPI.Reflect/Asset/AssetReference.h>
#include <Atom/RPI.Reflect/Pass/RenderPassData.h>
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<SerializeContext*>(context))
{
serializeContext->Class<RayTracingPassData, RenderPassData>()
->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
@@ -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
@@ -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;
@@ -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;
};
}
}
@@ -15,6 +15,7 @@
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Color.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
@@ -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);
}
}
@@ -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
@@ -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;
@@ -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];
@@ -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();
@@ -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];
@@ -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) -}}
@@ -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) -}}
@@ -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<AZ::CVarFixedString>(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<AZ::CVarFixedString>(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;
@@ -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<Multiplayer::LongNetworkString, 32>& commands);
NetworkEntityHandle SpawnDefaultPlayerPrefab();
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
AzNetworking::INetworkInterface* m_networkInterface = nullptr;
@@ -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<AzFramework::Spawnable>(spawnableAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
+3
View File
@@ -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
.
+8 -18
View File
@@ -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
)
@@ -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));
}
};
+7 -8
View File
@@ -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 <your build path> 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.
```
<Repo path>\scripts\o3de.bat create-project --project-path <your new project path>
@@ -160,10 +159,10 @@ If you have the Git credential manager core or other credential helpers installe
cmake -B <your project build path> -S <your new project source path> -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 <your project build path> --target <New Project Name> Editor --config profile -- /m
cmake --build <your project build path> --target <New Project Name>.GameLauncher Editor --config profile -- /m
// For all other branches, just build the project:
cmake --build <your project build path> --target <New Project Name> --config profile -- /m
cmake --build <your project build path> --target <New Project Name>.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.
@@ -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()
+320 -295
View File
@@ -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}")
@@ -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),
@@ -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
+30 -7
View File
@@ -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
@@ -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"
@@ -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
+2 -2
View File
@@ -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",